Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
39.39% covered (danger)
39.39%
13 / 33
CRAP
55.41% covered (warning)
55.41%
210 / 379
Searcher
0.00% covered (danger)
0.00%
0 / 1
39.39% covered (danger)
39.39%
13 / 33
1082.51
55.41% covered (warning)
55.41%
210 / 379
 __construct
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
13 / 13
 search
0.00% covered (danger)
0.00%
0 / 1
3.02
87.50% covered (warning)
87.50%
14 / 16
 setResultsType
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 isReturnRaw
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 setSort
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 limitSearchToLocalWiki
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 nearMatchTitleSearch
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 countContentWords
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 prefixSearch
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 buildFullTextSearch
0.00% covered (danger)
0.00%
0 / 1
2.00
90.00% covered (success)
90.00%
9 / 10
 searchTextInternal
0.00% covered (danger)
0.00%
0 / 1
15.36
71.43% covered (warning)
71.43%
30 / 42
 get
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 26
 findNamespace
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 16
 buildSearch
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
8 / 8
 searchOne
0.00% covered (danger)
0.00%
0 / 1
10.83
38.46% covered (danger)
38.46%
5 / 13
 searchMulti
0.00% covered (danger)
0.00%
0 / 1
137.94
37.89% covered (danger)
37.89%
36 / 95
 updateNamespacesFromQuery
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 14
 getSearchContext
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getPoolCounterType
0.00% covered (danger)
0.00%
0 / 1
5.93
66.67% covered (warning)
66.67%
6 / 9
 isAutomatedRequest
0.00% covered (danger)
0.00%
0 / 1
4.94
40.00% covered (danger)
40.00%
4 / 10
 getOverriddenConnection
0.00% covered (danger)
0.00%
0 / 1
3.58
60.00% covered (warning)
60.00%
3 / 5
 getQueryCacheStatsKey
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 newLog
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 processRawReturn
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 searchArchive
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
24 / 24
 areSearchesTheSame
0.00% covered (danger)
0.00%
0 / 1
6.44
76.92% covered (warning)
76.92%
10 / 13
 buildInterleaveSearcher
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
10 / 10
 emptyResultSet
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
8 / 8
 applyDebugOptionsToQuery
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 makeSearcher
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 setOffsetLimit
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 getOffsetLimit
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 buildFullTextBuilder
0.00% covered (danger)
0.00%
0 / 1
4.06
84.62% covered (warning)
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()
                )
            );