jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/371942 )

Change subject: Add subpageof keyword
......................................................................


Add subpageof keyword

It's a specialized version of the prefix keywork to find subpages.
It does not have the limitation of prefix:
- non-greedy
- does not try to handle namespace
- search on redirects

NOTE: The keyword is subpageof, but could be changed to something else.
(feel free to amend the patch).

If reusing the prefix elastic field is not sufficient we'll have to dig
into more dedicated field.

Bug: T159321
Change-Id: Ic1489b00b6179d60d662cd6645d54ba3428343d9
---
M autoload.php
A includes/Query/SubPageOf.php
M includes/Searcher.php
A tests/unit/fixtures/searchText/subpageof_001.default.expected
A tests/unit/fixtures/searchText/subpageof_001.fullyfeatured-interwiki.expected
A tests/unit/fixtures/searchText/subpageof_001.fullyfeatured.expected
A tests/unit/fixtures/searchText/subpageof_001.query
7 files changed, 1,392 insertions(+), 0 deletions(-)

Approvals:
  Smalyshev: Looks good to me, but someone else must approve
  Cindy-the-browser-test-bot: Looks good to me, but someone else must approve
  EBernhardson: Looks good to me, approved
  jenkins-bot: Verified
  Thiemo Mättig (WMDE): Looks good to me, but someone else must approve



diff --git a/autoload.php b/autoload.php
index 6b8cdda..2e72740 100644
--- a/autoload.php
+++ b/autoload.php
@@ -135,6 +135,7 @@
        'CirrusSearch\\Query\\RegexInSourceFeature' => __DIR__ . 
'/includes/Query/RegexInSourceFeature.php',
        'CirrusSearch\\Query\\SimpleInSourceFeature' => __DIR__ . 
'/includes/Query/SimpleInSourceFeature.php',
        'CirrusSearch\\Query\\SimpleKeywordFeature' => __DIR__ . 
'/includes/Query/SimpleKeywordFeature.php',
+       'CirrusSearch\\Query\\SubPageOfFeature' => __DIR__ . 
'/includes/Query/SubPageOf.php',
        'CirrusSearch\\RequestLog' => __DIR__ . '/includes/RequestLog.php',
        'CirrusSearch\\RequestLogger' => __DIR__ . 
'/includes/RequestLogger.php',
        'CirrusSearch\\RescoreProfiles' => __DIR__ . 
'/profiles/RescoreProfiles.php',
diff --git a/includes/Query/SubPageOf.php b/includes/Query/SubPageOf.php
new file mode 100644
index 0000000..04fdf4c
--- /dev/null
+++ b/includes/Query/SubPageOf.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace CirrusSearch\Query;
+
+use CirrusSearch\Search\Filters;
+use CirrusSearch\Search\SearchContext;
+
+/**
+ * subpagesof, find subpages of a given page
+ * uses the prefix field, very similar to the prefix except
+ * that it enforces a trailing / and is not a greedy keyword
+ */
+class SubPageOfFeature extends SimpleKeywordFeature {
+       /**
+        * @return string[]
+        */
+       protected function getKeywords() {
+               return [ 'subpageof' ];
+       }
+
+       /**
+        * @param SearchContext $context
+        * @param string $key The keyword
+        * @param string $value The value attached to the keyword with quotes 
stripped
+        * @param string $quotedValue The original value in the search string, 
including quotes if used
+        * @param bool $negated Is the search negated? Not used to generate the 
returned AbstractQuery,
+        *  that will be negated as necessary. Used for any other 
building/context necessary.
+        * @return array Two element array, first an AbstractQuery or null to 
apply to the
+        *  query. Second a boolean indicating if the quotedValue should be 
kept in the search
+        *  string.
+        */
+       protected function doApply( SearchContext $context, $key, $value, 
$quotedValue, $negated ) {
+               if ( empty( $value ) ) {
+                       return [ null, false ];
+               }
+               if ( substr( $value, -1 ) != '/' ) {
+                       $value .= '/';
+               }
+               $query = new \Elastica\Query\MultiMatch();
+               $query->setFields( [ 'title.prefix', 'redirect.title.prefix' ] 
);
+               $query->setQuery( $value );
+               return [ $query, false ];
+       }
+}
diff --git a/includes/Searcher.php b/includes/Searcher.php
index 766cb38..74d69cb 100644
--- a/includes/Searcher.php
+++ b/includes/Searcher.php
@@ -351,6 +351,8 @@
                        new Query\FileNumericFeature(),
                        // Content model feature
                        new Query\ContentModelFeature(),
+                       // subpageof keyword
+                       new Query\SubPageOfFeature(),
                ];
 
                $extraFeatures = [];
diff --git a/tests/unit/fixtures/searchText/subpageof_001.default.expected 
b/tests/unit/fixtures/searchText/subpageof_001.default.expected
new file mode 100644
index 0000000..2fd4d6c
--- /dev/null
+++ b/tests/unit/fixtures/searchText/subpageof_001.default.expected
@@ -0,0 +1,220 @@
+{
+    "description": "full_text search for 'subpageof:\"parent page\"'",
+    "options": {
+        "search_type": "dfs_query_then_fetch",
+        "timeout": "20s"
+    },
+    "params": {
+        "search_type": "dfs_query_then_fetch",
+        "timeout": "20s"
+    },
+    "query": {
+        "_source": [
+            "namespace",
+            "namespace_text",
+            "redirect.*",
+            "text_bytes",
+            "timestamp",
+            "title",
+            "wiki"
+        ],
+        "highlight": {
+            "fields": {
+                "auxiliary_text": {
+                    "fragment_size": 150,
+                    "fragmenter": "scan",
+                    "matched_fields": [
+                        "auxiliary_text",
+                        "auxiliary_text.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "boost_before": {
+                            "20": 2,
+                            "50": 1.8,
+                            "200": 1.5,
+                            "1000": 1.2
+                        },
+                        "max_fragments_scored": 5000,
+                        "skip_if_last_matched": true,
+                        "top_scoring": true
+                    },
+                    "type": "experimental"
+                },
+                "category": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "category",
+                        "category.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "heading": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "heading",
+                        "heading.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "redirect.title": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "redirect.title",
+                        "redirect.title.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "text": {
+                    "fragment_size": 150,
+                    "fragmenter": "scan",
+                    "matched_fields": [
+                        "text",
+                        "text.plain"
+                    ],
+                    "no_match_size": 150,
+                    "number_of_fragments": 1,
+                    "options": {
+                        "boost_before": {
+                            "20": 2,
+                            "50": 1.8,
+                            "200": 1.5,
+                            "1000": 1.2
+                        },
+                        "max_fragments_scored": 5000,
+                        "top_scoring": true
+                    },
+                    "type": "experimental"
+                },
+                "title": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "title",
+                        "title.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "type": "experimental"
+                }
+            },
+            "post_tags": [
+                "<\/span>"
+            ],
+            "pre_tags": [
+                "<span class=\"searchmatch\">"
+            ]
+        },
+        "query": {
+            "bool": {
+                "filter": [
+                    {
+                        "bool": {
+                            "must": [
+                                {
+                                    "multi_match": {
+                                        "fields": [
+                                            "redirect.title.prefix",
+                                            "title.prefix"
+                                        ],
+                                        "query": "parent page\/"
+                                    }
+                                },
+                                {
+                                    "terms": {
+                                        "namespace": [
+                                            0,
+                                            1,
+                                            2,
+                                            3
+                                        ]
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                ],
+                "must": [
+                    {
+                        "match_all": []
+                    }
+                ]
+            }
+        },
+        "rescore": [
+            {
+                "query": {
+                    "query_weight": 1,
+                    "rescore_query": {
+                        "function_score": {
+                            "functions": [
+                                {
+                                    "field_value_factor": {
+                                        "field": "incoming_links",
+                                        "missing": 0,
+                                        "modifier": "log2p"
+                                    }
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                1
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.25"
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                2
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.05"
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                3
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.0125"
+                                }
+                            ]
+                        }
+                    },
+                    "rescore_query_weight": 1,
+                    "score_mode": "multiply"
+                },
+                "window_size": 8192
+            }
+        ],
+        "size": 20,
+        "stats": [
+            "filter_only",
+            "full_text",
+            "subpageof"
+        ],
+        "stored_fields": [
+            "text.word_count"
+        ]
+    }
+}
\ No newline at end of file
diff --git 
a/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured-interwiki.expected 
b/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured-interwiki.expected
new file mode 100644
index 0000000..8afab83
--- /dev/null
+++ 
b/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured-interwiki.expected
@@ -0,0 +1,902 @@
+[
+    {
+        "description": "full_text search for 'subpageof:\"parent page\"'",
+        "options": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "params": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "path": "itwikibooks\/page\/_search",
+        "query": {
+            "_source": [
+                "namespace",
+                "namespace_text",
+                "redirect.*",
+                "text_bytes",
+                "timestamp",
+                "title",
+                "wiki"
+            ],
+            "highlight": {
+                "fields": {
+                    "auxiliary_text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "auxiliary_text",
+                            "auxiliary_text.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "skip_if_last_matched": true,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "category": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "category",
+                            "category.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "heading": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "heading",
+                            "heading.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "redirect.title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "redirect.title",
+                            "redirect.title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "text",
+                            "text.plain"
+                        ],
+                        "no_match_size": 150,
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "title",
+                            "title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "type": "experimental"
+                    }
+                },
+                "post_tags": [
+                    "<\/span>"
+                ],
+                "pre_tags": [
+                    "<span class=\"searchmatch\">"
+                ]
+            },
+            "query": {
+                "bool": {
+                    "filter": [
+                        {
+                            "bool": {
+                                "must": [
+                                    {
+                                        "multi_match": {
+                                            "fields": [
+                                                "redirect.title.prefix",
+                                                "title.prefix"
+                                            ],
+                                            "query": "parent page\/"
+                                        }
+                                    },
+                                    {
+                                        "terms": {
+                                            "namespace": [
+                                                0,
+                                                1,
+                                                2,
+                                                3
+                                            ]
+                                        }
+                                    }
+                                ]
+                            }
+                        }
+                    ],
+                    "must": [
+                        {
+                            "match_all": []
+                        }
+                    ]
+                }
+            },
+            "rescore": [
+                {
+                    "query": {
+                        "query_weight": 1,
+                        "rescore_query": {
+                            "function_score": {
+                                "functions": [
+                                    {
+                                        "field_value_factor": {
+                                            "field": "incoming_links",
+                                            "missing": 0,
+                                            "modifier": "log2p"
+                                        }
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    1
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.25"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    2
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.05"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    3
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.0125"
+                                    }
+                                ]
+                            }
+                        },
+                        "rescore_query_weight": 1,
+                        "score_mode": "multiply"
+                    },
+                    "window_size": 8192
+                }
+            ],
+            "size": 5,
+            "stats": [
+                "filter_only",
+                "full_text",
+                "subpageof"
+            ],
+            "stored_fields": [
+                "text.word_count"
+            ]
+        }
+    },
+    {
+        "description": "full_text search for 'subpageof:\"parent page\"'",
+        "options": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "params": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "path": "itwikivoyage\/page\/_search",
+        "query": {
+            "_source": [
+                "namespace",
+                "namespace_text",
+                "redirect.*",
+                "text_bytes",
+                "timestamp",
+                "title",
+                "wiki"
+            ],
+            "highlight": {
+                "fields": {
+                    "auxiliary_text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "auxiliary_text",
+                            "auxiliary_text.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "skip_if_last_matched": true,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "category": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "category",
+                            "category.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "heading": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "heading",
+                            "heading.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "redirect.title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "redirect.title",
+                            "redirect.title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "text",
+                            "text.plain"
+                        ],
+                        "no_match_size": 150,
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "title",
+                            "title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "type": "experimental"
+                    }
+                },
+                "post_tags": [
+                    "<\/span>"
+                ],
+                "pre_tags": [
+                    "<span class=\"searchmatch\">"
+                ]
+            },
+            "query": {
+                "bool": {
+                    "filter": [
+                        {
+                            "bool": {
+                                "must": [
+                                    {
+                                        "multi_match": {
+                                            "fields": [
+                                                "redirect.title.prefix",
+                                                "title.prefix"
+                                            ],
+                                            "query": "parent page\/"
+                                        }
+                                    },
+                                    {
+                                        "terms": {
+                                            "namespace": [
+                                                0,
+                                                1,
+                                                2,
+                                                3
+                                            ]
+                                        }
+                                    }
+                                ]
+                            }
+                        }
+                    ],
+                    "must": [
+                        {
+                            "match_all": []
+                        }
+                    ]
+                }
+            },
+            "rescore": [
+                {
+                    "query": {
+                        "query_weight": 1,
+                        "rescore_query": {
+                            "function_score": {
+                                "functions": [
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    1
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.25"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    2
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.05"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    3
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.0125"
+                                    }
+                                ]
+                            }
+                        },
+                        "rescore_query_weight": 1,
+                        "score_mode": "multiply"
+                    },
+                    "window_size": 8192
+                },
+                {
+                    "query": {
+                        "query_weight": 1,
+                        "rescore_query": {
+                            "function_score": {
+                                "functions": [
+                                    {
+                                        "script_score": {
+                                            "script": {
+                                                "inline": 
"pow(doc['incoming_links'].value , 0.7) \/ ( pow(doc['incoming_links'].value, 
0.7) + pow(30,0.7))",
+                                                "lang": "expression"
+                                            }
+                                        },
+                                        "weight": 13
+                                    }
+                                ]
+                            }
+                        },
+                        "rescore_query_weight": 1,
+                        "score_mode": "total"
+                    },
+                    "window_size": 8192
+                }
+            ],
+            "size": 5,
+            "stats": [
+                "filter_only",
+                "full_text",
+                "subpageof"
+            ],
+            "stored_fields": [
+                "text.word_count"
+            ]
+        }
+    },
+    {
+        "description": "full_text search for 'subpageof:\"parent page\"'",
+        "options": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "params": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "path": "itwiktionary\/page\/_search",
+        "query": {
+            "_source": [
+                "namespace",
+                "namespace_text",
+                "redirect.*",
+                "text_bytes",
+                "timestamp",
+                "title",
+                "wiki"
+            ],
+            "highlight": {
+                "fields": {
+                    "auxiliary_text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "auxiliary_text",
+                            "auxiliary_text.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "skip_if_last_matched": true,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "category": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "category",
+                            "category.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "heading": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "heading",
+                            "heading.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "redirect.title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "redirect.title",
+                            "redirect.title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "text",
+                            "text.plain"
+                        ],
+                        "no_match_size": 150,
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "title",
+                            "title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "type": "experimental"
+                    }
+                },
+                "post_tags": [
+                    "<\/span>"
+                ],
+                "pre_tags": [
+                    "<span class=\"searchmatch\">"
+                ]
+            },
+            "query": {
+                "bool": {
+                    "filter": [
+                        {
+                            "bool": {
+                                "must": [
+                                    {
+                                        "multi_match": {
+                                            "fields": [
+                                                "redirect.title.prefix",
+                                                "title.prefix"
+                                            ],
+                                            "query": "parent page\/"
+                                        }
+                                    },
+                                    {
+                                        "terms": {
+                                            "namespace": [
+                                                0,
+                                                1,
+                                                2,
+                                                3
+                                            ]
+                                        }
+                                    }
+                                ]
+                            }
+                        }
+                    ],
+                    "must": [
+                        {
+                            "match_all": []
+                        }
+                    ]
+                }
+            },
+            "rescore": [
+                {
+                    "query": {
+                        "query_weight": 1,
+                        "rescore_query": {
+                            "function_score": {
+                                "functions": [
+                                    {
+                                        "field_value_factor": {
+                                            "field": "incoming_links",
+                                            "missing": 0,
+                                            "modifier": "log2p"
+                                        }
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    1
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.25"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    2
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.05"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    3
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.0125"
+                                    }
+                                ]
+                            }
+                        },
+                        "rescore_query_weight": 1,
+                        "score_mode": "multiply"
+                    },
+                    "window_size": 8192
+                }
+            ],
+            "size": 5,
+            "stats": [
+                "filter_only",
+                "full_text",
+                "subpageof"
+            ],
+            "stored_fields": [
+                "text.word_count"
+            ]
+        }
+    },
+    {
+        "description": "full_text search for 'subpageof:\"parent page\"'",
+        "options": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "params": {
+            "search_type": "dfs_query_then_fetch",
+            "timeout": "20s"
+        },
+        "path": "wiki\/page\/_search",
+        "query": {
+            "_source": [
+                "namespace",
+                "namespace_text",
+                "redirect.*",
+                "text_bytes",
+                "timestamp",
+                "title",
+                "wiki"
+            ],
+            "highlight": {
+                "fields": {
+                    "auxiliary_text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "auxiliary_text",
+                            "auxiliary_text.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "skip_if_last_matched": true,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "category": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "category",
+                            "category.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "heading": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "heading",
+                            "heading.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "redirect.title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "redirect.title",
+                            "redirect.title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "options": {
+                            "skip_if_last_matched": true
+                        },
+                        "order": "score",
+                        "type": "experimental"
+                    },
+                    "text": {
+                        "fragment_size": 150,
+                        "fragmenter": "scan",
+                        "matched_fields": [
+                            "text",
+                            "text.plain"
+                        ],
+                        "no_match_size": 150,
+                        "number_of_fragments": 1,
+                        "options": {
+                            "boost_before": {
+                                "20": 2,
+                                "50": 1.8,
+                                "200": 1.5,
+                                "1000": 1.2
+                            },
+                            "max_fragments_scored": 5000,
+                            "top_scoring": true
+                        },
+                        "type": "experimental"
+                    },
+                    "title": {
+                        "fragmenter": "none",
+                        "matched_fields": [
+                            "title",
+                            "title.plain"
+                        ],
+                        "number_of_fragments": 1,
+                        "type": "experimental"
+                    }
+                },
+                "post_tags": [
+                    "<\/span>"
+                ],
+                "pre_tags": [
+                    "<span class=\"searchmatch\">"
+                ]
+            },
+            "query": {
+                "bool": {
+                    "filter": [
+                        {
+                            "bool": {
+                                "must": [
+                                    {
+                                        "multi_match": {
+                                            "fields": [
+                                                "redirect.title.prefix",
+                                                "title.prefix"
+                                            ],
+                                            "query": "parent page\/"
+                                        }
+                                    },
+                                    {
+                                        "terms": {
+                                            "namespace": [
+                                                0,
+                                                1,
+                                                2,
+                                                3
+                                            ]
+                                        }
+                                    }
+                                ]
+                            }
+                        }
+                    ],
+                    "must": [
+                        {
+                            "match_all": []
+                        }
+                    ]
+                }
+            },
+            "rescore": [
+                {
+                    "query": {
+                        "query_weight": 1,
+                        "rescore_query": {
+                            "function_score": {
+                                "functions": [
+                                    {
+                                        "field_value_factor": {
+                                            "field": "incoming_links",
+                                            "missing": 0,
+                                            "modifier": "log2p"
+                                        }
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    1
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.25"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    2
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.05"
+                                    },
+                                    {
+                                        "filter": {
+                                            "terms": {
+                                                "namespace": [
+                                                    3
+                                                ]
+                                            }
+                                        },
+                                        "weight": "0.0125"
+                                    }
+                                ]
+                            }
+                        },
+                        "rescore_query_weight": 1,
+                        "score_mode": "multiply"
+                    },
+                    "window_size": 8192
+                }
+            ],
+            "size": 20,
+            "stats": [
+                "filter_only",
+                "full_text",
+                "subpageof"
+            ],
+            "stored_fields": [
+                "text.word_count"
+            ]
+        }
+    }
+]
\ No newline at end of file
diff --git 
a/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured.expected 
b/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured.expected
new file mode 100644
index 0000000..2fd4d6c
--- /dev/null
+++ b/tests/unit/fixtures/searchText/subpageof_001.fullyfeatured.expected
@@ -0,0 +1,220 @@
+{
+    "description": "full_text search for 'subpageof:\"parent page\"'",
+    "options": {
+        "search_type": "dfs_query_then_fetch",
+        "timeout": "20s"
+    },
+    "params": {
+        "search_type": "dfs_query_then_fetch",
+        "timeout": "20s"
+    },
+    "query": {
+        "_source": [
+            "namespace",
+            "namespace_text",
+            "redirect.*",
+            "text_bytes",
+            "timestamp",
+            "title",
+            "wiki"
+        ],
+        "highlight": {
+            "fields": {
+                "auxiliary_text": {
+                    "fragment_size": 150,
+                    "fragmenter": "scan",
+                    "matched_fields": [
+                        "auxiliary_text",
+                        "auxiliary_text.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "boost_before": {
+                            "20": 2,
+                            "50": 1.8,
+                            "200": 1.5,
+                            "1000": 1.2
+                        },
+                        "max_fragments_scored": 5000,
+                        "skip_if_last_matched": true,
+                        "top_scoring": true
+                    },
+                    "type": "experimental"
+                },
+                "category": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "category",
+                        "category.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "heading": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "heading",
+                        "heading.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "redirect.title": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "redirect.title",
+                        "redirect.title.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "options": {
+                        "skip_if_last_matched": true
+                    },
+                    "order": "score",
+                    "type": "experimental"
+                },
+                "text": {
+                    "fragment_size": 150,
+                    "fragmenter": "scan",
+                    "matched_fields": [
+                        "text",
+                        "text.plain"
+                    ],
+                    "no_match_size": 150,
+                    "number_of_fragments": 1,
+                    "options": {
+                        "boost_before": {
+                            "20": 2,
+                            "50": 1.8,
+                            "200": 1.5,
+                            "1000": 1.2
+                        },
+                        "max_fragments_scored": 5000,
+                        "top_scoring": true
+                    },
+                    "type": "experimental"
+                },
+                "title": {
+                    "fragmenter": "none",
+                    "matched_fields": [
+                        "title",
+                        "title.plain"
+                    ],
+                    "number_of_fragments": 1,
+                    "type": "experimental"
+                }
+            },
+            "post_tags": [
+                "<\/span>"
+            ],
+            "pre_tags": [
+                "<span class=\"searchmatch\">"
+            ]
+        },
+        "query": {
+            "bool": {
+                "filter": [
+                    {
+                        "bool": {
+                            "must": [
+                                {
+                                    "multi_match": {
+                                        "fields": [
+                                            "redirect.title.prefix",
+                                            "title.prefix"
+                                        ],
+                                        "query": "parent page\/"
+                                    }
+                                },
+                                {
+                                    "terms": {
+                                        "namespace": [
+                                            0,
+                                            1,
+                                            2,
+                                            3
+                                        ]
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                ],
+                "must": [
+                    {
+                        "match_all": []
+                    }
+                ]
+            }
+        },
+        "rescore": [
+            {
+                "query": {
+                    "query_weight": 1,
+                    "rescore_query": {
+                        "function_score": {
+                            "functions": [
+                                {
+                                    "field_value_factor": {
+                                        "field": "incoming_links",
+                                        "missing": 0,
+                                        "modifier": "log2p"
+                                    }
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                1
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.25"
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                2
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.05"
+                                },
+                                {
+                                    "filter": {
+                                        "terms": {
+                                            "namespace": [
+                                                3
+                                            ]
+                                        }
+                                    },
+                                    "weight": "0.0125"
+                                }
+                            ]
+                        }
+                    },
+                    "rescore_query_weight": 1,
+                    "score_mode": "multiply"
+                },
+                "window_size": 8192
+            }
+        ],
+        "size": 20,
+        "stats": [
+            "filter_only",
+            "full_text",
+            "subpageof"
+        ],
+        "stored_fields": [
+            "text.word_count"
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/unit/fixtures/searchText/subpageof_001.query 
b/tests/unit/fixtures/searchText/subpageof_001.query
new file mode 100644
index 0000000..54b8de2
--- /dev/null
+++ b/tests/unit/fixtures/searchText/subpageof_001.query
@@ -0,0 +1,3 @@
+{
+    "query": "subpageof:\"parent page\""
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/371942
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ic1489b00b6179d60d662cd6645d54ba3428343d9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CirrusSearch
Gerrit-Branch: master
Gerrit-Owner: DCausse <dcau...@wikimedia.org>
Gerrit-Reviewer: Cindy-the-browser-test-bot <bernhardsone...@gmail.com>
Gerrit-Reviewer: DCausse <dcau...@wikimedia.org>
Gerrit-Reviewer: EBernhardson <ebernhard...@wikimedia.org>
Gerrit-Reviewer: Smalyshev <smalys...@wikimedia.org>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to