DCausse has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/389781 )

Change subject: Add prefix search to comp suggest
......................................................................

Add prefix search to comp suggest

Add second search request to the main index using the prefix search
query builder if one of the namespace requested is not NS_MAIN.
Results are simply appended to the comp suggest results.

Ideally this would need more tests, esp a browser test.

Bug: T178474
Change-Id: Ibb8130b267ebeb9aa396bc7855bdd532db8ec08e
---
M includes/CirrusSearch.php
M includes/CompletionRequestLog.php
M includes/CompletionSuggester.php
M includes/Query/CompSuggestQueryBuilder.php
M includes/Query/PrefixSearchQueryBuilder.php
M includes/Search/CompletionResultsCollector.php
M includes/Search/ResultsType.php
M includes/Search/SearchRequestBuilder.php
M tests/unit/RequestLoggerTest.php
A tests/unit/fixtures/requestLogging/completion_basic_003.expected
A tests/unit/fixtures/requestLogging/completion_basic_003.request
A tests/unit/fixtures/requestLogging/completion_basic_003.response
12 files changed, 544 insertions(+), 95 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CirrusSearch 
refs/changes/81/389781/1

diff --git a/includes/CirrusSearch.php b/includes/CirrusSearch.php
index 24cd5da..6308360 100644
--- a/includes/CirrusSearch.php
+++ b/includes/CirrusSearch.php
@@ -584,10 +584,8 @@
                        return $this->prefixSearch( $search, $variants );
                }
 
-               if ( count( $this->namespaces ) != 1 ||
-                       reset( $this->namespaces ) != NS_MAIN
-               ) {
-                       // for now, suggester only works for main namespace
+               // the completion suggester is only worth a try if NS_MAIN is 
requested
+               if ( !in_array( NS_MAIN, $this->namespaces ) ) {
                        return $this->prefixSearch( $search, $variants );
                }
 
@@ -650,17 +648,7 @@
                                // No need to unpack the simple title matches 
from non-fancy TitleResultsType
                                return SearchSuggestionSet::fromTitles( 
$status->getValue() );
                        }
-                       $results = array_filter( array_map( function ( $match ) 
{
-                               if ( isset( $match[ 'titleMatch' ] ) ) {
-                                       return $match[ 'titleMatch' ];
-                               } else {
-                                       if ( isset( $match[ 'redirectMatches' 
][ 0 ] ) ) {
-                                               // TODO maybe dig around in the 
redirect matches and find the best one?
-                                               return $match[ 
'redirectMatches' ][0];
-                                       }
-                               }
-                               return false;
-                       }, $status->getValue() ) );
+                       $results = array_filter( 
FancyTitleResultsType::chooseBests( $status->getValue() ) );
                        return SearchSuggestionSet::fromTitles( $results );
                }
 
diff --git a/includes/CompletionRequestLog.php 
b/includes/CompletionRequestLog.php
index 33294a2..68e4d9b 100644
--- a/includes/CompletionRequestLog.php
+++ b/includes/CompletionRequestLog.php
@@ -29,7 +29,22 @@
        /**
         * @var int
         */
+       private $prefixTookMs = 0;
+
+       /**
+        * @var int
+        */
        private $hitsTotal;
+
+       /**
+        * @var int[]|null
+        */
+       private $namespaces;
+
+       public function __construct( $description, $queryType, $extra = [], 
$namespaces = null ) {
+               parent::__construct( $description, $queryType, $extra );
+               $this->namespaces = $namespaces;
+       }
 
        /**
         * @param SearchSuggestion[] $result The set of suggestion results that
@@ -49,7 +64,7 @@
                        $pageId = $suggestion->getSuggestedTitleID() ?: -1;
                        $maxScore = $maxScore !== null ? max( $maxScore, 
$suggestion->getScore() ) : $suggestion->getScore();
                        $this->hits[] = [
-                               'title' => $title ? (string)$title : 
$suggestion->getText(),
+                               'title' => $title ? $title->getPrefixedText() : 
$suggestion->getText(),
                                'index' => $index,
                                'pageId' => (int)$pageId,
                                'score' => $suggestion->getScore(),
@@ -102,7 +117,7 @@
                $vars = $this->getLogVariables() + [
                        'hits' => $this->hits,
                ];
-
+               $vars['namespaces'] = $this->namespaces;
                return [ $vars ];
        }
 
@@ -121,6 +136,13 @@
        }
 
        /**
+        * @param int $prefixTookMs
+        */
+       public function setPrefixTookMs( $prefixTookMs ) {
+               $this->prefixTookMs = $prefixTookMs;
+       }
+
+       /**
         * Add an index used by this request
         * @param string $indexName
         */
diff --git a/includes/CompletionSuggester.php b/includes/CompletionSuggester.php
index 94f1794..c3d6933 100644
--- a/includes/CompletionSuggester.php
+++ b/includes/CompletionSuggester.php
@@ -5,6 +5,8 @@
 use CirrusSearch\Query\CompSuggestQueryBuilder;
 use CirrusSearch\Query\PrefixSearchQueryBuilder;
 use CirrusSearch\Search\CompletionResultsCollector;
+use CirrusSearch\Search\FancyTitleResultsType;
+use CirrusSearch\Search\SearchRequestBuilder;
 use Elastica\Exception\ExceptionInterface;
 use Elastica\Index;
 use Elastica\Multi\ResultSet;
@@ -58,12 +60,20 @@
  * 2. The redirect suggestions
  * Because the same canonical title can be returned twice we support 
fetch_limit_factor
  * in suggest profiles to fetch more than what the use asked.
+ *
+ * Additionally if the namespaces request include non NS_MAIN a prefix search 
query
+ * is sent to the main index. Results are appended to the suggest results.
  */
 class CompletionSuggester extends ElasticsearchIntermediary {
        /**
         * @const string multisearch key to identify the comp suggest request
         */
        const MSEARCH_KEY_SUGGEST = "suggest";
+
+       /**
+        * @const string multisearch key to identify the prefix search request
+        */
+       const MSEARCH_KEY_PREFIX = "prefix";
 
        /**
         * @var integer maximum number of result (final)
@@ -105,6 +115,11 @@
         * @var PrefixSearchQueryBuilder $prefixSearchQueryBuilder (final)
         */
        private $prefixSearchQueryBuilder;
+
+       /**
+        * @var SearchRequestBuilder the builder to build the search for prefix 
search queries
+        */
+       private $prefixSearchRequestBuilder;
 
        /**
         * @param Connection $conn
@@ -165,11 +180,16 @@
                        $msearch->addSearch( $suggestSearch, 
self::MSEARCH_KEY_SUGGEST );
                }
 
+               $prefixSearch = $this->getPrefixSearchRequest( $text, $variants 
);
+               if ( $prefixSearch !== null ) {
+                       $msearch->addSearch( $prefixSearch, 
self::MSEARCH_KEY_PREFIX );
+               }
+
                if ( empty( $msearch->getSearches() ) ) {
                        return Status::newGood( 
SearchSuggestionSet::emptySuggestionSet() );
                }
 
-               $this->connection->setTimeout( $this->config->getElement( 
'wgCirrusSearchClientSideSearchTimeout', 'default' ) );
+               $this->connection->setTimeout( $this->config->getElement( 
'CirrusSearchClientSideSearchTimeout', 'default' ) );
                $result = Util::doPoolCounterWork(
                        'CirrusSearch-Completion',
                        $this->user,
@@ -204,19 +224,71 @@
         */
        private function processMSearchResponse( ResultSet $results, 
CompletionRequestLog $log ) {
                $collector = new CompletionResultsCollector( $this->limit, 
$this->offset );
+               $totalHits = $this->collectCompSuggestResults( $collector, 
$results, $log );
+               $totalHits += $this->collectPrefixSearchResults( $collector, 
$results, $log );
+               $log->setTotalHits( $totalHits );
+               return $collector->logAndGetSet( $log );
+       }
+
+       /**
+        * @param CompletionResultsCollector $collector
+        * @param ResultSet $results
+        * @param CompletionRequestLog $log
+        * @return int
+        */
+       private function collectCompSuggestResults( CompletionResultsCollector 
$collector, ResultSet $results, CompletionRequestLog $log ) {
+               $totalHits = 0;
                if ( isset( 
$results->getResultSets()[self::MSEARCH_KEY_SUGGEST] ) ) {
                        $log->addIndex( $this->completionIndex->getName() );
                        $suggestResults = 
$results->getResultSets()[self::MSEARCH_KEY_SUGGEST];
                        $log->setSuggestTookMs( intval( 
$suggestResults->getResponse()->getQueryTime() * 1000 ) );
-                       $totalHits = $this->compSuggestBuilder->postProcess(
+                       $totalHits += $this->compSuggestBuilder->postProcess(
                                $collector,
                                $suggestResults,
                                $this->completionIndex->getName()
                        );
-
-                       $log->setTotalHits( $totalHits );
                }
-               return $collector->logAndGetSet( $log );
+               return $totalHits;
+       }
+
+       /**
+        * @param CompletionResultsCollector $collector
+        * @param ResultSet $results
+        * @param CompletionRequestLog $log
+        * @return int
+        */
+       private function collectPrefixSearchResults( CompletionResultsCollector 
$collector, ResultSet $results, CompletionRequestLog $log ) {
+               $totalHits = 0;
+               if ( isset( $results->getResultSets()[self::MSEARCH_KEY_PREFIX] 
) ) {
+                       $indexName = 
$this->prefixSearchRequestBuilder->getPageType()->getIndex()->getName();
+                       $prefixResults = 
$results->getResultSets()[self::MSEARCH_KEY_PREFIX];
+                       $totalHits += $prefixResults->getTotalHits();
+                       $log->addIndex( $indexName );
+                       $log->setPrefixTookMs( intval( 
$prefixResults->getResponse()->getQueryTime() * 1000 ) );
+                       // We only append as we can't really compare scores 
without more complex code/evaluation
+                       if ( !$collector->isFull() ) {
+                               $rType = 
$this->prefixSearchRequestBuilder->getSearchContext()->getResultsType();
+                               assert( $rType instanceof FancyTitleResultsType 
);
+                               // scores can go negative, it's not a problem 
we only use scores for sorting
+                               // they'll be forgotten in client response
+                               $score = $collector->getMinScore() !== null ? 
$collector->getMinScore() - 1 : count( $prefixResults->getResults() );
+
+                               foreach ( $prefixResults->getResults() as $res 
) {
+                                       $pageId = $this->config->makePageId( 
$res->getId() );
+                                       $title = 
FancyTitleResultsType::chooseBest( $rType->transformOneElasticResult( $res ) );
+                                       if ( $title === false ) {
+                                               continue;
+                                       }
+                                       $suggestion = new \SearchSuggestion( 
$score--, $title->getPrefixedText(), $title, $pageId );
+                                       if ( !$collector->collect( $suggestion, 
'prefix', $indexName ) ) {
+                                               if ( $collector->isFull() ) {
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return $totalHits;
        }
 
        /**
@@ -242,6 +314,38 @@
        }
 
        /**
+        * @param string $text Search term
+        * @param string[]|null $variants Search term variants
+        * (usually issued from $wgContLang->autoConvertToAllVariants( $text ) )
+        * @return Search|null
+        */
+       private function getPrefixSearchRequest( $term, $variants ) {
+               $namespaces = $this->searchContext->getNamespaces();
+               if ( $namespaces === null ) {
+                       return null;
+               }
+               $namespaces = array_filter( $namespaces, function ( $k, $v ) {
+                       return $v !== NS_MAIN;
+               }, ARRAY_FILTER_USE_BOTH );
+               if ( $namespaces === [] ) {
+                       return null;
+               }
+               $limit = CompSuggestQueryBuilder::computeHardLimit( 
$this->limit, $this->offset, $this->config );
+               if ( $this->offset > $limit ) {
+                       return null;
+               }
+               $prefixSearchContext = new SearchContext( $this->config, 
$namespaces );
+               $prefixSearchContext->setResultsType( new 
FancyTitleResultsType( 'prefix' ) );
+               $this->prefixSearchQueryBuilder->build( $prefixSearchContext, 
$term, $variants );
+               $this->prefixSearchRequestBuilder = new SearchRequestBuilder( 
$prefixSearchContext, $this->connection, $this->indexBaseName );
+               return $this->prefixSearchRequestBuilder->setLimit( $limit )
+                       // collect all results up to $limit, $this->offset is 
the offset the client wants
+                       // not the offset in prefix search results.
+                       ->setOffset( 0 )
+                       ->build();
+       }
+
+       /**
         * @param string $description
         * @param string $queryType
         * @param string[] $extra
@@ -251,7 +355,8 @@
                return new CompletionRequestLog(
                        $description,
                        $queryType,
-                       $extra
+                       $extra,
+                       $this->searchContext->getNamespaces()
                );
        }
 
diff --git a/includes/Query/CompSuggestQueryBuilder.php 
b/includes/Query/CompSuggestQueryBuilder.php
index 6ca1f58..fb63d1f 100644
--- a/includes/Query/CompSuggestQueryBuilder.php
+++ b/includes/Query/CompSuggestQueryBuilder.php
@@ -65,6 +65,10 @@
         * @return bool true if results are possible false otherwise
         */
        public function areResultsPossible() {
+               if ( $this->searchContext->getNamespaces() !== null
+                               && !in_array( NS_MAIN, 
$this->searchContext->getNamespaces() ) ) {
+                       return false;
+               }
                // If the offset requested is greater than the hard limit
                // allowed we will always return an empty set so let's do it
                // asap.
@@ -275,7 +279,7 @@
         * @param SearchConfig $config
         * @return int the number of results to fetch from elastic
         */
-       private static function computeHardLimit( $limit, $offset, SearchConfig 
$config ) {
+       public static function computeHardLimit( $limit, $offset, SearchConfig 
$config ) {
                $limit = $limit + $offset;
                $hardLimit = $config->get( 
'CirrusSearchCompletionSuggesterHardLimit' );
                if ( $hardLimit === null ) {
diff --git a/includes/Query/PrefixSearchQueryBuilder.php 
b/includes/Query/PrefixSearchQueryBuilder.php
index a41cb3c..cacbbb3 100644
--- a/includes/Query/PrefixSearchQueryBuilder.php
+++ b/includes/Query/PrefixSearchQueryBuilder.php
@@ -17,10 +17,10 @@
        /**
         * @param SearchContext $searchContext the search context
         * @param $term string the original search term
-        * @param array $variants list of variants
+        * @param array|null $variants list of variants
         * @throws \ApiUsageException if the query is too long
         */
-       public function build( SearchContext $searchContext, $term, $variants = 
[] ) {
+       public function build( SearchContext $searchContext, $term, $variants = 
null ) {
                $this->checkTitleSearchRequestLength( $term );
                $searchContext->setOriginalSearchTerm( $term );
                $searchContext->setRescoreProfile(
@@ -67,9 +67,11 @@
                                $query->setMinimumShouldMatch( 1 );
                                $weight = 1;
                                $query->addShould( $buildMatch( $term, $weight 
) );
-                               foreach ( $variants as $variant ) {
-                                       $weight *= 0.2;
-                                       $query->addShould( $buildMatch( 
$variant, $weight ) );
+                               if ( $variants ) {
+                                       foreach ( $variants as $variant ) {
+                                               $weight *= 0.2;
+                                               $query->addShould( $buildMatch( 
$variant, $weight ) );
+                                       }
                                }
                                $searchContext->setMainQuery( $query );
                        }
diff --git a/includes/Search/CompletionResultsCollector.php 
b/includes/Search/CompletionResultsCollector.php
index 4ff0495..74205df 100644
--- a/includes/Search/CompletionResultsCollector.php
+++ b/includes/Search/CompletionResultsCollector.php
@@ -85,6 +85,7 @@
         * is better than a suggestion already collected.
         * @param SearchSuggestion $suggestion
         * @param string $profileName
+        * @param string $index
         * @return bool true if the doc was added false otherwise
         */
        public function collect( SearchSuggestion $suggestion, $profileName, 
$index ) {
diff --git a/includes/Search/ResultsType.php b/includes/Search/ResultsType.php
index 98e3f19..b9060ad 100644
--- a/includes/Search/ResultsType.php
+++ b/includes/Search/ResultsType.php
@@ -189,53 +189,36 @@
        public function transformElasticsearchResult( SearchContext $context, 
\Elastica\ResultSet $resultSet ) {
                $results = [];
                foreach ( $resultSet->getResults() as $r ) {
-                       $title = TitleHelper::makeTitle( $r );
-                       $highlights = $r->getHighlights();
-                       $resultForTitle = [];
-
-                       // Now we have to use the highlights to figure out 
whether it was the title or the redirect
-                       // that matched.  It is kind of a shame we can't really 
give the highlighting to the client
-                       // though.
-                       if ( isset( $highlights[ "title.$this->matchedAnalyzer" 
] ) ) {
-                               $resultForTitle[ 'titleMatch' ] = $title;
-                       } elseif ( isset( $highlights[ 
"title.{$this->matchedAnalyzer}_asciifolding" ] ) ) {
-                               $resultForTitle[ 'titleMatch' ] = $title;
-                       }
-                       $redirectHighlights = [];
-
-                       if ( isset( $highlights[ 
"redirect.title.$this->matchedAnalyzer" ] ) ) {
-                               $redirectHighlights = $highlights[ 
"redirect.title.$this->matchedAnalyzer" ];
-                       }
-                       if ( isset( $highlights[ 
"redirect.title.{$this->matchedAnalyzer}_asciifolding" ] ) ) {
-                               $redirectHighlights = array_merge( 
$redirectHighlights,
-                                       $highlights[ 
"redirect.title.{$this->matchedAnalyzer}_asciifolding" ] );
-                       }
-                       if ( count( $redirectHighlights ) !== 0 ) {
-                               foreach ( $redirectHighlights as $redirectTitle 
) {
-                                       // The match was against a redirect so 
we should replace the $title with one that
-                                       // represents the redirect.
-                                       // The first step is to strip the 
actual highlighting from the title.
-                                       $redirectTitle = str_replace( 
Searcher::HIGHLIGHT_PRE, '', $redirectTitle );
-                                       $redirectTitle = str_replace( 
Searcher::HIGHLIGHT_POST, '', $redirectTitle );
-
-                                       // Instead of getting the redirect's 
real namespace we're going to just use the namespace
-                                       // of the title.  This is not great but 
OK given that we can't find cross namespace
-                                       // redirects properly any way.
-                                       $redirectTitle = 
TitleHelper::makeRedirectTitle( $r, $redirectTitle, $r->namespace );
-                                       $resultForTitle[ 'redirectMatches' ][] 
= $redirectTitle;
-                               }
-                       }
-                       if ( count( $resultForTitle ) === 0 ) {
-                               // We're not really sure where the match came 
from so lets just pretend it was the title.
-                               LoggerFactory::getInstance( 'CirrusSearch' 
)->warning(
-                                       "Title search result type hit a match 
but we can't " .
-                                       "figure out what caused the match:  
$r->namespace:$r->title"
-                               );
-                               $resultForTitle[ 'titleMatch' ] = $title;
-                       }
-                       $results[] = $resultForTitle;
+                       $results[] = $this->transformOneElasticResult( $r );
                }
                return $results;
+       }
+
+       /**
+        * Choose best match between title or redirects
+        * @param array $results (array
+        * @return array of \Title or false if nothing matched.
+        */
+       public static function chooseBests( array $results ) {
+               return array_map( [ static::class, 'chooseBest' ], $results );
+       }
+
+       /**
+        * Finds best title or redirect
+        * @param array $match array returned by self::transformOneElasticResult
+        * @return \Title|false choose best
+        */
+       public static function chooseBest( array $match ) {
+               if ( isset( $match['titleMatch'] ) ) {
+                       return $match['titleMatch'];
+               } else {
+                       if ( isset( $match['redirectMatches'][0] ) ) {
+                               // TODO maybe dig around in the redirect 
matches and find the best one?
+                               return $match['redirectMatches'][0];
+                       }
+               }
+
+               return false;
        }
 
        /**
@@ -244,6 +227,60 @@
        public function createEmptyResult() {
                return [];
        }
+
+       /**
+        * @param \Elastica\Result $r
+        * @return array
+        */
+       public function transformOneElasticResult( \Elastica\Result $r ) {
+               $title = TitleHelper::makeTitle( $r );
+               $highlights = $r->getHighlights();
+               $resultForTitle = [];
+
+               // Now we have to use the highlights to figure out whether it 
was the title or the redirect
+               // that matched.  It is kind of a shame we can't really give 
the highlighting to the client
+               // though.
+               if ( isset( $highlights["title.$this->matchedAnalyzer"] ) ) {
+                       $resultForTitle['titleMatch'] = $title;
+               } elseif ( isset( 
$highlights["title.{$this->matchedAnalyzer}_asciifolding"] ) ) {
+                       $resultForTitle['titleMatch'] = $title;
+               }
+               $redirectHighlights = [];
+
+               if ( isset( 
$highlights["redirect.title.$this->matchedAnalyzer"] ) ) {
+                       $redirectHighlights = 
$highlights["redirect.title.$this->matchedAnalyzer"];
+               }
+               if ( isset( 
$highlights["redirect.title.{$this->matchedAnalyzer}_asciifolding"] ) ) {
+                       $redirectHighlights =
+                               array_merge( $redirectHighlights,
+                                       
$highlights["redirect.title.{$this->matchedAnalyzer}_asciifolding"] );
+               }
+               if ( count( $redirectHighlights ) !== 0 ) {
+                       foreach ( $redirectHighlights as $redirectTitle ) {
+                               // The match was against a redirect so we 
should replace the $title with one that
+                               // represents the redirect.
+                               // The first step is to strip the actual 
highlighting from the title.
+                               $redirectTitle = str_replace( 
Searcher::HIGHLIGHT_PRE, '', $redirectTitle );
+                               $redirectTitle = str_replace( 
Searcher::HIGHLIGHT_POST, '', $redirectTitle );
+
+                               // Instead of getting the redirect's real 
namespace we're going to just use the namespace
+                               // of the title.  This is not great but OK 
given that we can't find cross namespace
+                               // redirects properly any way.
+                               $redirectTitle =
+                                       TitleHelper::makeRedirectTitle( $r, 
$redirectTitle, $r->namespace );
+                               $resultForTitle['redirectMatches'][] = 
$redirectTitle;
+                       }
+               }
+               if ( count( $resultForTitle ) === 0 ) {
+                       // We're not really sure where the match came from so 
lets just pretend it was the title.
+                       LoggerFactory::getInstance( 'CirrusSearch' )
+                               ->warning( "Title search result type hit a 
match but we can't " .
+                                                  "figure out what caused the 
match:  $r->namespace:$r->title" );
+                       $resultForTitle['titleMatch'] = $title;
+               }
+
+               return $resultForTitle;
+       }
 }
 
 /**
diff --git a/includes/Search/SearchRequestBuilder.php 
b/includes/Search/SearchRequestBuilder.php
index 8e69378..1342a6b 100644
--- a/includes/Search/SearchRequestBuilder.php
+++ b/includes/Search/SearchRequestBuilder.php
@@ -145,13 +145,7 @@
                        $queryOptions[\Elastica\Search::OPTION_SEARCH_TYPE] = 
\Elastica\Search::OPTION_SEARCH_TYPE_DFS_QUERY_THEN_FETCH;
                }
 
-               if ( $this->pageType ) {
-                       $pageType = $this->pageType;
-               } else {
-                       $indexType = 
$this->connection->pickIndexTypeForNamespaces(
-                               $this->searchContext->getNamespaces() );
-                       $pageType = $this->connection->getPageType( 
$this->indexBaseName, $indexType );
-               }
+               $pageType = $this->getPageType();
 
                $search = $pageType->createSearch( $query, $queryOptions );
                foreach ( $extraIndexes as $i ) {
@@ -234,10 +228,16 @@
        }
 
        /**
-        * @return Type|null
+        * @return Type
         */
        public function getPageType() {
-               return $this->pageType;
+               if ( $this->pageType ) {
+                       return $this->pageType;
+               } else {
+                       $indexType = 
$this->connection->pickIndexTypeForNamespaces(
+                               $this->searchContext->getNamespaces() );
+                       return $this->connection->getPageType( 
$this->indexBaseName, $indexType );
+               }
        }
 
        /**
@@ -266,4 +266,11 @@
 
                return $this;
        }
+
+       /**
+        * @return SearchContext
+        */
+       public function getSearchContext() {
+               return $this->searchContext;
+       }
 }
diff --git a/tests/unit/RequestLoggerTest.php b/tests/unit/RequestLoggerTest.php
index f1079f4..595ca77 100644
--- a/tests/unit/RequestLoggerTest.php
+++ b/tests/unit/RequestLoggerTest.php
@@ -106,22 +106,22 @@
         * @dataProvider requestLoggingProvider
         */
        public function testRequestLogging( array $query, $responses = null, 
$expectedLogs ) {
+               $globals = [
+                       'wgCirrusSearchFullTextQueryBuilderProfile' => 
'default',
+                       'wgCirrusSearchInterwikiSources' => [],
+               ];
+               if ( isset( $query['interwiki'] ) ) {
+                       $globals['wgCirrusSearchInterwikiSources'] = 
$query['interwiki'];
+                       $globals['wgCirrusSearchEnableCrossProjectSearch'] = 
true;
+               }
+               $this->setMwGlobals( $globals );
+
                switch ( $query['type'] ) {
                case 'fulltext':
                        $work = function ( $config, $connection ) use ( $query 
) {
                                $offset = isset( $query['offset'] ) ? 
$query['offset'] : 0;
                                $limit = isset( $query['limit'] ) ? 
$query['limit'] : 20;
                                $namespaces = isset( $query['namespaces'] ) ? 
$query['namespaces'] : null;
-
-                               $globals = [
-                                       
'wgCirrusSearchFullTextQueryBuilderProfile' => 'default',
-                                       'wgCirrusSearchInterwikiSources' => [],
-                               ];
-                               if ( isset( $query['interwiki'] ) ) {
-                                       
$globals['wgCirrusSearchInterwikiSources'] = $query['interwiki'];
-                                       
$globals['wgCirrusSearchEnableCrossProjectSearch'] = true;
-                               }
-                               $this->setMwGlobals( $globals );
 
                                $searchEngine = new CirrusSearch( 'wiki' );
                                $searchEngine->setConnection( $connection );
@@ -160,8 +160,8 @@
                        $work = function ( $config, $connection ) use ( $query 
) {
                                $limit = isset( $query['limit'] ) ? 
$query['limit'] : 5;
                                $offset = isset( $query['offset'] ) ? 
$query['offset'] : 0;
-
-                               $suggester = new CompletionSuggester( 
$connection, $limit, $offset, $config, null, null, 'wiki' );
+                               $namespaces = isset( $query['namespaces'] ) ? 
$query['namespaces'] : null;
+                               $suggester = new CompletionSuggester( 
$connection, $limit, $offset, $config, $namespaces, null, 'wiki' );
                                $suggester->suggest( $query['term'] );
                        };
                        break;
diff --git a/tests/unit/fixtures/requestLogging/completion_basic_003.expected 
b/tests/unit/fixtures/requestLogging/completion_basic_003.expected
new file mode 100644
index 0000000..03e348e
--- /dev/null
+++ b/tests/unit/fixtures/requestLogging/completion_basic_003.expected
@@ -0,0 +1,99 @@
+[
+    {
+        "channel": "CirrusSearchRequestSet",
+        "context": {
+            "backendUserTests": [],
+            "hits": [],
+            "ip": "127.0.0.1",
+            "payload": {
+                "acceptLang": "",
+                "hascookies": "0",
+                "queryString": "",
+                "referer": ""
+            },
+            "requests": [
+                {
+                    "elasticTookMs": 0,
+                    "hits": [
+                        {
+                            "index": "wiki_titlesuggest",
+                            "pageId": 14,
+                            "profileName": "plain",
+                            "score": 4713,
+                            "title": "Test2"
+                        },
+                        {
+                            "index": "wiki_titlesuggest",
+                            "pageId": 17,
+                            "profileName": "plain",
+                            "score": 131,
+                            "title": "Test2\/en"
+                        },
+                        {
+                            "index": "wiki_titlesuggest",
+                            "pageId": 19,
+                            "profileName": "plain",
+                            "score": 131,
+                            "title": "Test2\/fr"
+                        },
+                        {
+                            "index": "wiki_titlesuggest",
+                            "pageId": 20,
+                            "profileName": "plain",
+                            "score": 78,
+                            "title": "Test\/Subpage 1"
+                        },
+                        {
+                            "index": "wiki_general",
+                            "pageId": 34,
+                            "profileName": "prefix",
+                            "score": 77,
+                            "title": "Help:Test"
+                        }
+                    ],
+                    "hitsOffset": 0,
+                    "hitsReturned": 5,
+                    "hitsTotal": 9,
+                    "indices": [
+                        "wiki_titlesuggest",
+                        "wiki_general"
+                    ],
+                    "limit": -1,
+                    "maxScore": 4713,
+                    "namespaces": [
+                        0,
+                        12
+                    ],
+                    "payload": [],
+                    "query": "Te",
+                    "queryType": "comp_suggest",
+                    "suggestion": "",
+                    "suggestionRequested": false
+                }
+            ],
+            "source": "cli",
+            "userAgent": ""
+        },
+        "level": "debug",
+        "message": ""
+    },
+    {
+        "channel": "CirrusSearchRequests",
+        "context": {
+            "elasticTookMs": 0,
+            "executor": "123456789",
+            "hitsOffset": 0,
+            "hitsReturned": 5,
+            "hitsTotal": 9,
+            "identity": "2c33c9c64851a02e2ba8143c6550a3b8",
+            "index": "wiki_titlesuggest,wiki_general",
+            "maxScore": 4713,
+            "query": "Te",
+            "queryType": "comp_suggest",
+            "source": "cli",
+            "tookMs": 0
+        },
+        "level": "debug",
+        "message": "{queryType} search for '{query}' against {index} took 
{tookMs} millis and {elasticTookMs} Elasticsearch millis. Found {hitsTotal} 
total results and returned {hitsReturned} of them starting at {hitsOffset}. 
Requested via {source} for {identity} by executor {executor}"
+    }
+]
diff --git a/tests/unit/fixtures/requestLogging/completion_basic_003.request 
b/tests/unit/fixtures/requestLogging/completion_basic_003.request
new file mode 100644
index 0000000..248696e
--- /dev/null
+++ b/tests/unit/fixtures/requestLogging/completion_basic_003.request
@@ -0,0 +1,5 @@
+{
+       "type": "completion",
+       "term": "Te",
+       "namespaces" : [0,12]
+}
diff --git a/tests/unit/fixtures/requestLogging/completion_basic_003.response 
b/tests/unit/fixtures/requestLogging/completion_basic_003.response
new file mode 100644
index 0000000..65e0549
--- /dev/null
+++ b/tests/unit/fixtures/requestLogging/completion_basic_003.response
@@ -0,0 +1,179 @@
+[{
+    "responses": [
+        {
+            "took": 3,
+            "timed_out": false,
+            "_shards": {
+                "total": 4,
+                "successful": 4,
+                "failed": 0
+            },
+            "hits": {
+                "total": 0,
+                "max_score": 0,
+                "hits": []
+            },
+            "suggest": {
+                "plain": [
+                    {
+                        "text": "Te",
+                        "offset": 0,
+                        "length": 2,
+                        "options": [
+                            {
+                                "text": "Test2",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "14t",
+                                "_score": 4713,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test2\/en",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "17t",
+                                "_score": 131,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2\/en"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test2\/fr",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "19t",
+                                "_score": 131,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2\/fr"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test\/Subpage 1",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "20t",
+                                "_score": 78,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test\/Subpage 1"
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                ],
+                "plain_stop": [
+                    {
+                        "text": "Te",
+                        "offset": 0,
+                        "length": 2,
+                        "options": [
+                            {
+                                "text": "Test2",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "14t",
+                                "_score": 4713,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test2\/en",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "17t",
+                                "_score": 131,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2\/en"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test2\/fr",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "19t",
+                                "_score": 131,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test2\/fr"
+                                    }
+                                }
+                            },
+                            {
+                                "text": "Test\/Subpage 1",
+                                "_index": "wiki_titlesuggest_1504788820",
+                                "_type": "titlesuggest",
+                                "_id": "20t",
+                                "_score": 78,
+                                "_source": {
+                                    "target_title": {
+                                        "namespace": 0,
+                                        "title": "Test\/Subpage 1"
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                ]
+            },
+            "status": 200
+        },
+        {
+            "took": 2,
+            "timed_out": false,
+            "_shards": {
+                "total": 4,
+                "successful": 4,
+                "failed": 0
+            },
+            "hits": {
+                "total": 1,
+                "max_score": 3.5431085,
+                "hits": [
+                    {
+                        "_index": "wiki_general_1504788773",
+                        "_type": "page",
+                        "_id": "34",
+                        "_score": 3.5431085,
+                        "_source": {
+                            "namespace_text": "Help",
+                            "wiki": "wiki",
+                            "namespace": 12,
+                            "title": "Test"
+                        },
+                        "highlight": {
+                            "title.prefix_asciifolding": [
+                                "<span class=\"searchmatch\">Te<\/span>st"
+                            ],
+                            "title.prefix": [
+                                "<span class=\"searchmatch\">Te<\/span>st"
+                            ]
+                        }
+                    }
+                ]
+            },
+            "status": 200
+        }
+    ]
+}]

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibb8130b267ebeb9aa396bc7855bdd532db8ec08e
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CirrusSearch
Gerrit-Branch: master
Gerrit-Owner: DCausse <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to