Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
50.55% |
228 / 451 |
|
36.36% |
12 / 33 |
CRAP | |
0.00% |
0 / 1 |
Searcher | |
50.55% |
228 / 451 |
|
36.36% |
12 / 33 |
1713.75 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
5 | |||
search | |
89.47% |
17 / 19 |
|
0.00% |
0 / 1 |
3.01 | |||
setResultsType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isReturnRaw | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSort | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
limitSearchToLocalWiki | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
nearMatchTitleSearch | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
countContentWords | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
prefixSearch | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
buildFullTextSearch | |
84.21% |
16 / 19 |
|
0.00% |
0 / 1 |
5.10 | |||
searchTextInternal | |
59.18% |
29 / 49 |
|
0.00% |
0 / 1 |
27.33 | |||
get | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
72 | |||
findNamespace | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
buildSearch | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
searchOne | |
38.46% |
5 / 13 |
|
0.00% |
0 / 1 |
10.83 | |||
searchMulti | |
38.89% |
42 / 108 |
|
0.00% |
0 / 1 |
132.46 | |||
updateNamespacesFromQuery | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 | |||
getSearchContext | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPoolCounterType | |
78.57% |
11 / 14 |
|
0.00% |
0 / 1 |
5.25 | |||
isAutomatedRequest | |
40.00% |
4 / 10 |
|
0.00% |
0 / 1 |
4.94 | |||
getOverriddenConnection | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
3.58 | |||
recordQueryCacheMetrics | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
newLog | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
processRawReturn | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
searchArchive | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
1 | |||
areSearchesTheSame | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
buildInterleaveSearcher | |
50.00% |
5 / 10 |
|
0.00% |
0 / 1 |
6.00 | |||
emptyResultSet | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
applyDebugOptionsToQuery | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
makeSearcher | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
setOffsetLimit | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getOffsetLimit | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
buildFullTextBuilder | |
90.48% |
19 / 21 |
|
0.00% |
0 / 1 |
4.01 |
1 | <?php |
2 | |
3 | namespace CirrusSearch; |
4 | |
5 | use CirrusSearch\Fallbacks\FallbackRunner; |
6 | use CirrusSearch\Fallbacks\SearcherFactory; |
7 | use CirrusSearch\Maintenance\NullPrinter; |
8 | use CirrusSearch\MetaStore\MetaStoreIndex; |
9 | use CirrusSearch\Parser\BasicQueryClassifier; |
10 | use CirrusSearch\Parser\FullTextKeywordRegistry; |
11 | use CirrusSearch\Parser\NamespacePrefixParser; |
12 | use CirrusSearch\Profile\SearchProfileService; |
13 | use CirrusSearch\Query\CountContentWordsBuilder; |
14 | use CirrusSearch\Query\FullTextQueryBuilder; |
15 | use CirrusSearch\Query\KeywordFeature; |
16 | use CirrusSearch\Query\NearMatchQueryBuilder; |
17 | use CirrusSearch\Query\PrefixSearchQueryBuilder; |
18 | use CirrusSearch\Search\BaseCirrusSearchResultSet; |
19 | use CirrusSearch\Search\FullTextResultsType; |
20 | use CirrusSearch\Search\MSearchRequests; |
21 | use CirrusSearch\Search\MSearchResponses; |
22 | use CirrusSearch\Search\ResultsType; |
23 | use CirrusSearch\Search\SearchContext; |
24 | use CirrusSearch\Search\SearchQuery; |
25 | use CirrusSearch\Search\SearchRequestBuilder; |
26 | use CirrusSearch\Search\TeamDraftInterleaver; |
27 | use CirrusSearch\Search\TitleHelper; |
28 | use CirrusSearch\Search\TitleResultsType; |
29 | use Elastica\Exception\ResponseException; |
30 | use Elastica\Exception\RuntimeException; |
31 | use Elastica\Multi\Search as MultiSearch; |
32 | use Elastica\Query; |
33 | use Elastica\Query\BoolQuery; |
34 | use Elastica\Query\MultiMatch; |
35 | use Elastica\Search; |
36 | use MediaWiki\Context\RequestContext; |
37 | use MediaWiki\Logger\LoggerFactory; |
38 | use MediaWiki\MediaWikiServices; |
39 | use MediaWiki\Request\WebRequest; |
40 | use MediaWiki\Status\Status; |
41 | use MediaWiki\Title\Title; |
42 | use MediaWiki\User\User; |
43 | use MediaWiki\WikiMap\WikiMap; |
44 | use Wikimedia\Assert\Assert; |
45 | use Wikimedia\ObjectFactory\ObjectFactory; |
46 | use Wikimedia\Stats\StatsFactory; |
47 | |
48 | /** |
49 | * Performs searches using Elasticsearch. Note that each instance of this class |
50 | * is single use only. |
51 | * |
52 | * This program is free software; you can redistribute it and/or modify |
53 | * it under the terms of the GNU General Public License as published by |
54 | * the Free Software Foundation; either version 2 of the License, or |
55 | * (at your option) any later version. |
56 | * |
57 | * This program is distributed in the hope that it will be useful, |
58 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
59 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
60 | * GNU General Public License for more details. |
61 | * |
62 | * You should have received a copy of the GNU General Public License along |
63 | * with this program; if not, write to the Free Software Foundation, Inc., |
64 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
65 | * http://www.gnu.org/copyleft/gpl.html |
66 | */ |
67 | class Searcher extends ElasticsearchIntermediary implements SearcherFactory { |
68 | public const SUGGESTION_HIGHLIGHT_PRE = '<em>'; |
69 | public const SUGGESTION_HIGHLIGHT_POST = '</em>'; |
70 | public const HIGHLIGHT_PRE_MARKER = ''; // \uE000. Can't be a unicode literal until php7 |
71 | public const HIGHLIGHT_PRE = '<span class="searchmatch">'; |
72 | public const HIGHLIGHT_POST_MARKER = ''; // \uE001 |
73 | public const HIGHLIGHT_POST = '</span>'; |
74 | |
75 | /** |
76 | * Maximum offset + limit depth allowed. As in the deepest possible result |
77 | * to return. Too deep will cause very slow queries. 10,000 feels plenty |
78 | * deep. This should be <= index.max_result_window in elasticsearch. |
79 | */ |
80 | private const MAX_OFFSET_LIMIT = 10000; |
81 | |
82 | /** |
83 | * Identifies the main search in MSearchRequests/MSearchResponses |
84 | */ |
85 | public const MAINSEARCH_MSEARCH_KEY = '__main__'; |
86 | |
87 | /** |
88 | * Identifies the "tested" search request in MSearchRequests/MSearchResponses |
89 | */ |
90 | private const INTERLEAVED_MSEARCH_KEY = '__interleaved__'; |
91 | |
92 | /** |
93 | * @var int search offset |
94 | */ |
95 | protected $offset; |
96 | |
97 | /** |
98 | * @var int maximum number of result |
99 | */ |
100 | protected $limit; |
101 | |
102 | /** |
103 | * @var string sort type |
104 | */ |
105 | private $sort = 'relevance'; |
106 | |
107 | /** |
108 | * @var string index base name to use |
109 | */ |
110 | protected $indexBaseName; |
111 | |
112 | /** |
113 | * Search environment configuration |
114 | * @var SearchConfig |
115 | */ |
116 | protected $config; |
117 | |
118 | /** |
119 | * @var SearchContext |
120 | */ |
121 | protected $searchContext; |
122 | |
123 | /** |
124 | * Indexing type we'll be using. |
125 | * @var string|\Elastica\Index |
126 | */ |
127 | private $index; |
128 | |
129 | /** |
130 | * @var NamespacePrefixParser|null |
131 | */ |
132 | private $namespacePrefixParser; |
133 | /** |
134 | * @var InterwikiResolver |
135 | */ |
136 | protected $interwikiResolver; |
137 | |
138 | /** @var TitleHelper */ |
139 | protected $titleHelper; |
140 | /** |
141 | * @var CirrusSearchHookRunner |
142 | */ |
143 | protected $cirrusSearchHookRunner; |
144 | |
145 | /** |
146 | * @param Connection $conn |
147 | * @param int $offset Offset the results by this much |
148 | * @param int $limit Limit the results to this many |
149 | * @param SearchConfig $config Configuration settings |
150 | * @param int[]|null $namespaces Array of namespace numbers to search or null to search all namespaces. |
151 | * @param User|null $user user for which this search is being performed. Attached to slow request logs. |
152 | * @param string|bool $index Base name for index to search from, defaults to $wgCirrusSearchIndexBaseName |
153 | * @param CirrusDebugOptions|null $options the debugging options to use or null to use defaults |
154 | * @param NamespacePrefixParser|null $namespacePrefixParser |
155 | * @param InterwikiResolver|null $interwikiResolver |
156 | * @param TitleHelper|null $titleHelper |
157 | * @param CirrusSearchHookRunner|null $cirrusSearchHookRunner |
158 | * @see CirrusDebugOptions::defaultOptions() |
159 | */ |
160 | public function __construct( |
161 | Connection $conn, $offset, |
162 | $limit, |
163 | SearchConfig $config, |
164 | ?array $namespaces = null, |
165 | ?User $user = null, |
166 | $index = false, |
167 | ?CirrusDebugOptions $options = null, |
168 | ?NamespacePrefixParser $namespacePrefixParser = null, |
169 | ?InterwikiResolver $interwikiResolver = null, |
170 | ?TitleHelper $titleHelper = null, |
171 | ?CirrusSearchHookRunner $cirrusSearchHookRunner = null |
172 | ) { |
173 | parent::__construct( |
174 | $conn, |
175 | $user, |
176 | $config->get( 'CirrusSearchSlowSearch' ), |
177 | $config->get( 'CirrusSearchExtraBackendLatency' ) |
178 | ); |
179 | $this->config = $config; |
180 | $this->setOffsetLimit( $offset, $limit ); |
181 | $this->indexBaseName = $index ?: $config->get( SearchConfig::INDEX_BASE_NAME ); |
182 | // TODO: Make these params mandatory once WBCS stops extending this class |
183 | $this->namespacePrefixParser = $namespacePrefixParser; |
184 | $this->interwikiResolver = $interwikiResolver ?: MediaWikiServices::getInstance()->getService( InterwikiResolver::SERVICE ); |
185 | $this->titleHelper = $titleHelper ?: new TitleHelper( WikiMap::getCurrentWikiId(), $this->interwikiResolver ); |
186 | $this->cirrusSearchHookRunner = $cirrusSearchHookRunner ?: new CirrusSearchHookRunner( |
187 | MediaWikiServices::getInstance()->getHookContainer() ); |
188 | $this->searchContext = new SearchContext( $this->config, $namespaces, $options, null, null, $this->cirrusSearchHookRunner ); |
189 | } |
190 | |
191 | /** |
192 | * Unified search public entry-point. |
193 | * |
194 | * NOTE: only fulltext search supported for now. |
195 | * @param SearchQuery $query |
196 | * @return Status |
197 | */ |
198 | public function search( SearchQuery $query ) { |
199 | if ( $query->getDebugOptions()->isCirrusDumpQueryAST() ) { |
200 | return Status::newGood( [ 'ast' => $query->getParsedQuery()->toArray() ] ); |
201 | } |
202 | // TODO: properly pass the profile context name and its params once we have a dispatch service. |
203 | $this->searchContext = SearchContext::fromSearchQuery( $query, FallbackRunner::create( $query, $this->interwikiResolver ), |
204 | $this->cirrusSearchHookRunner ); |
205 | $this->setOffsetLimit( $query->getOffset(), $query->getLimit() ); |
206 | $this->config = $query->getSearchConfig(); |
207 | $this->sort = $query->getSort(); |
208 | |
209 | if ( $query->getSearchEngineEntryPoint() === SearchQuery::SEARCH_TEXT ) { |
210 | $this->searchContext->setResultsType( |
211 | new FullTextResultsType( |
212 | $this->searchContext->getFetchPhaseBuilder(), |