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