Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
39.39% |
13 / 33 |
CRAP | |
55.41% |
210 / 379 |
Searcher | |
0.00% |
0 / 1 |
|
39.39% |
13 / 33 |
1082.51 | |
55.41% |
210 / 379 |
__construct | |
100.00% |
1 / 1 |
5 | |
100.00% |
13 / 13 |
|||
search | |
0.00% |
0 / 1 |
3.02 | |
87.50% |
14 / 16 |
|||
setResultsType | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
isReturnRaw | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
setSort | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
limitSearchToLocalWiki | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
nearMatchTitleSearch | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
countContentWords | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
prefixSearch | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
buildFullTextSearch | |
0.00% |
0 / 1 |
2.00 | |
90.00% |
9 / 10 |
|||
searchTextInternal | |
0.00% |
0 / 1 |
15.36 | |
71.43% |
30 / 42 |
|||
get | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 26 |
|||
findNamespace | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 16 |
|||
buildSearch | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
searchOne | |
0.00% |
0 / 1 |
10.83 | |
38.46% |
5 / 13 |
|||
searchMulti | |
0.00% |
0 / 1 |
137.94 | |
37.89% |
36 / 95 |
|||
updateNamespacesFromQuery | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 14 |
|||
getSearchContext | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getPoolCounterType | |
0.00% |
0 / 1 |
5.93 | |
66.67% |
6 / 9 |
|||
isAutomatedRequest | |
0.00% |
0 / 1 |
4.94 | |
40.00% |
4 / 10 |
|||
getOverriddenConnection | |
0.00% |
0 / 1 |
3.58 | |
60.00% |
3 / 5 |
|||
getQueryCacheStatsKey | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
newLog | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
processRawReturn | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
searchArchive | |
100.00% |
1 / 1 |
1 | |
100.00% |
24 / 24 |
|||
areSearchesTheSame | |
0.00% |
0 / 1 |
6.44 | |
76.92% |
10 / 13 |
|||
buildInterleaveSearcher | |
100.00% |
1 / 1 |
4 | |
100.00% |
10 / 10 |
|||
emptyResultSet | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
applyDebugOptionsToQuery | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
makeSearcher | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
setOffsetLimit | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
getOffsetLimit | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
buildFullTextBuilder | |
0.00% |
0 / 1 |
4.06 | |
84.62% |
11 / 13 |
<?php | |
namespace CirrusSearch; | |
use CirrusSearch\Fallbacks\FallbackRunner; | |
use CirrusSearch\Fallbacks\SearcherFactory; | |
use CirrusSearch\Maintenance\NullPrinter; | |
use CirrusSearch\MetaStore\MetaStoreIndex; | |
use CirrusSearch\Parser\BasicQueryClassifier; | |
use CirrusSearch\Parser\FullTextKeywordRegistry; | |
use CirrusSearch\Parser\NamespacePrefixParser; | |
use CirrusSearch\Profile\SearchProfileService; | |
use CirrusSearch\Query\CountContentWordsBuilder; | |
use CirrusSearch\Query\FullTextQueryBuilder; | |
use CirrusSearch\Query\KeywordFeature; | |
use CirrusSearch\Query\NearMatchQueryBuilder; | |
use CirrusSearch\Query\PrefixSearchQueryBuilder; | |
use CirrusSearch\Search\BaseCirrusSearchResultSet; | |
use CirrusSearch\Search\FullTextResultsType; | |
use CirrusSearch\Search\MSearchRequests; | |
use CirrusSearch\Search\MSearchResponses; | |
use CirrusSearch\Search\ResultsType; | |
use CirrusSearch\Search\SearchContext; | |
use CirrusSearch\Search\SearchQuery; | |
use CirrusSearch\Search\SearchRequestBuilder; | |
use CirrusSearch\Search\TeamDraftInterleaver; | |
use CirrusSearch\Search\TitleHelper; | |
use CirrusSearch\Search\TitleResultsType; | |
use Elastica\Exception\RuntimeException; | |
use Elastica\Multi\Search as MultiSearch; | |
use Elastica\Query; | |
use Elastica\Query\BoolQuery; | |
use Elastica\Query\MultiMatch; | |
use Elastica\Search; | |
use MediaWiki\Logger\LoggerFactory; | |
use MediaWiki\MediaWikiServices; | |
use RequestContext; | |
use Status; | |
use Title; | |
use User; | |
use WebRequest; | |
use WikiMap; | |
use Wikimedia\Assert\Assert; | |
use Wikimedia\ObjectFactory\ObjectFactory; | |
/** | |
* Performs searches using Elasticsearch. Note that each instance of this class | |
* is single use only. | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; either version 2 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License along | |
* with this program; if not, write to the Free Software Foundation, Inc., | |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
* http://www.gnu.org/copyleft/gpl.html | |
*/ | |
class Searcher extends ElasticsearchIntermediary implements SearcherFactory { | |
public const SUGGESTION_HIGHLIGHT_PRE = '<em>'; | |
public const SUGGESTION_HIGHLIGHT_POST = '</em>'; | |
public const HIGHLIGHT_PRE_MARKER = ''; // \uE000. Can't be a unicode literal until php7 | |
public const HIGHLIGHT_PRE = '<span class="searchmatch">'; | |
public const HIGHLIGHT_POST_MARKER = ''; // \uE001 | |
public const HIGHLIGHT_POST = '</span>'; | |
/** | |
* Maximum offset + limit depth allowed. As in the deepest possible result | |
* to return. Too deep will cause very slow queries. 10,000 feels plenty | |
* deep. This should be <= index.max_result_window in elasticsearch. | |
*/ | |
private const MAX_OFFSET_LIMIT = 10000; | |
/** | |
* Identifies the main search in MSearchRequests/MSearchResponses | |
*/ | |
public const MAINSEARCH_MSEARCH_KEY = '__main__'; | |
/** | |
* Identifies the "tested" search request in MSearchRequests/MSearchResponses | |
*/ | |
private const INTERLEAVED_MSEARCH_KEY = '__interleaved__'; | |
/** | |
* @var int search offset | |
*/ | |
protected $offset; | |
/** | |
* @var int maximum number of result | |
*/ | |
protected $limit; | |
/** | |
* @var string sort type | |
*/ | |
private $sort = 'relevance'; | |
/** | |
* @var string index base name to use | |
*/ | |
protected $indexBaseName; | |
/** | |
* Search environment configuration | |
* @var SearchConfig | |
*/ | |
protected $config; | |
/** | |
* @var SearchContext | |
*/ | |
protected $searchContext; | |
/** | |
* Indexing type we'll be using. | |
* @var string|\Elastica\Index | |
*/ | |
private $index; | |
/** | |
* @var NamespacePrefixParser|null | |
*/ | |
private $namespacePrefixParser; | |
/** | |
* @var InterwikiResolver | |
*/ | |
protected $interwikiResolver; | |
/** @var TitleHelper */ | |
protected $titleHelper; | |
/** | |
* @var CirrusSearchHookRunner | |
*/ | |
protected $cirrusSearchHookRunner; | |
/** | |
* @param Connection $conn | |
* @param int $offset Offset the results by this much | |
* @param int $limit Limit the results to this many | |
* @param SearchConfig $config Configuration settings | |
* @param int[]|null $namespaces Array of namespace numbers to search or null to search all namespaces. | |
* @param User|null $user user for which this search is being performed. Attached to slow request logs. | |
* @param string|bool $index Base name for index to search from, defaults to $wgCirrusSearchIndexBaseName | |
* @param CirrusDebugOptions|null $options the debugging options to use or null to use defaults | |
* @param NamespacePrefixParser|null $namespacePrefixParser | |
* @param InterwikiResolver|null $interwikiResolver | |
* @param TitleHelper|null $titleHelper | |
* @param CirrusSearchHookRunner|null $cirrusSearchHookRunner | |
* @see CirrusDebugOptions::defaultOptions() | |
*/ | |
public function __construct( | |
Connection $conn, $offset, | |
$limit, | |
SearchConfig $config, | |
array $namespaces = null, | |
User $user = null, | |
$index = false, | |
CirrusDebugOptions $options = null, | |
NamespacePrefixParser $namespacePrefixParser = null, | |
InterwikiResolver $interwikiResolver = null, | |
TitleHelper $titleHelper = null, | |
CirrusSearchHookRunner $cirrusSearchHookRunner = null | |
) { | |
parent::__construct( | |
$conn, | |
$user, | |
$config->get( 'CirrusSearchSlowSearch' ), | |
$config->get( 'CirrusSearchExtraBackendLatency' ) | |
); | |
$this->config = $config; | |
$this->setOffsetLimit( $offset, $limit ); | |
$this->indexBaseName = $index ?: $config->get( SearchConfig::INDEX_BASE_NAME ); | |
// TODO: Make these params mandatory once WBCS stops extending this class | |
$this->namespacePrefixParser = $namespacePrefixParser; | |
$this->interwikiResolver = $interwikiResolver ?: MediaWikiServices::getInstance()->getService( InterwikiResolver::SERVICE ); | |
$this->titleHelper = $titleHelper ?: new TitleHelper( WikiMap::getCurrentWikiId(), $this->interwikiResolver ); | |
$this->cirrusSearchHookRunner = $cirrusSearchHookRunner ?: new CirrusSearchHookRunner( | |
MediaWikiServices::getInstance()->getHookContainer() ); | |
$this->searchContext = new SearchContext( $this->config, $namespaces, $options, null, null, $this->cirrusSearchHookRunner ); | |
} | |
/** | |
* Unified search public entry-point. | |
* | |
* NOTE: only fulltext search supported for now. | |
* @param SearchQuery $query | |
* @return Status | |
*/ | |
public function search( SearchQuery $query ) { | |
if ( $query->getDebugOptions()->isCirrusDumpQueryAST() ) { | |
return Status::newGood( [ 'ast' => $query->getParsedQuery()->toArray() ] ); | |
} | |
// TODO: properly pass the profile context name and its params once we have a dispatch service. | |
$this->searchContext = SearchContext::fromSearchQuery( $query, FallbackRunner::create( $query, $this->interwikiResolver ), | |
$this->cirrusSearchHookRunner ); | |
$this->setOffsetLimit( $query->getOffset(), $query->getLimit() ); | |
$this->config = $query->getSearchConfig(); | |
$this->sort = $query->getSort(); | |
if ( $query->getSearchEngineEntryPoint() === SearchQuery::SEARCH_TEXT ) { | |
$this->searchContext->setResultsType( | |
new FullTextResultsType( | |
$this->searchContext->getFetchPhaseBuilder(), | |
$query->getParsedQuery()->isQueryOfClass( BasicQueryClassifier::COMPLEX_QUERY ), | |
$this->titleHelper, | |
$query->getExtraFieldsToExtract() | |
) | |
); | |