52 parent::__construct( $query, $moduleName,
'sr' );
54 $this->searchEngineConfig = $searchEngineConfig;
55 $this->searchEngineFactory = $searchEngineFactory;
56 $this->titleMatcher = $titleMatcher;
64 $this->
run( $resultPageSet );
71 private function run( $resultPageSet =
null ) {
77 $interwiki =
$params[
'interwiki'];
78 $searchInfo = array_fill_keys(
$params[
'info'],
true );
79 $prop = array_fill_keys(
$params[
'prop'],
true );
83 if ( isset(
$params[
'sort'] ) ) {
84 $search->setSort(
$params[
'sort'] );
86 $search->setFeatureData(
'rewrite', (
bool)
$params[
'enablerewrites'] );
87 $search->setFeatureData(
'interwiki', (
bool)$interwiki );
89 $search->setFeatureData(
'snippets', $this->decideSnippets( $prop ) );
91 $nquery = $search->replacePrefixes( $query );
92 if ( $nquery !== $query ) {
95 get_class( $search ) .
', this was deprecated in MediaWiki 1.32',
99 if ( $what ==
'text' ) {
100 $matches = $search->searchText( $query );
101 } elseif ( $what ==
'title' ) {
102 $matches = $search->searchTitle( $query );
103 } elseif ( $what ==
'nearmatch' ) {
106 $matches = $this->titleMatcher->getNearMatchResultSet(
$params[
'search'] );
113 $matches = $search->searchTitle( $query );
121 $matches = $search->searchText( $query );
133 if ( $status->isOK() ) {
134 $this->
getMain()->getErrorFormatter()->addMessagesFromStatus(
142 $this->
dieWithError( [
'apierror-searchdisabled', $what ],
"search-{$what}-disabled" );
147 if ( isset( $searchInfo[
'totalhits'] ) ) {
148 $totalhits =
$matches->getTotalHits();
149 if ( $totalhits !==
null ) {
150 $apiResult->
addValue( [
'query',
'searchinfo' ],
151 'totalhits', $totalhits );
154 if ( isset( $searchInfo[
'suggestion'] ) &&
$matches->hasSuggestion() ) {
155 $apiResult->
addValue( [
'query',
'searchinfo' ],
156 'suggestion',
$matches->getSuggestionQuery() );
157 $apiResult->
addValue( [
'query',
'searchinfo' ],
158 'suggestionsnippet', HtmlArmor::getHtml(
$matches->getSuggestionSnippet() ) );
160 if ( isset( $searchInfo[
'rewrittenquery'] ) &&
$matches->hasRewrittenQuery() ) {
161 $apiResult->
addValue( [
'query',
'searchinfo' ],
162 'rewrittenquery',
$matches->getQueryAfterRewrite() );
163 $apiResult->
addValue( [
'query',
'searchinfo' ],
164 'rewrittenquerysnippet', HtmlArmor::getHtml(
$matches->getQueryAfterRewriteSnippet() ) );
178 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
182 $vals = $this->getSearchResultData( $result, $prop );
184 if ( $resultPageSet ===
null ) {
194 $titles[] = $result->getTitle();
195 $data[] = $vals ?: [];
204 $canAddInterwiki = (bool)
$params[
'enablerewrites'] && ( $resultPageSet ===
null );
205 if ( $canAddInterwiki ) {
206 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'additional',
207 ISearchResultSet::INLINE_RESULTS );
211 if ( $interwiki && $resultPageSet ===
null ) {
212 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'interwiki',
213 ISearchResultSet::SECONDARY_RESULTS );
216 if ( $resultPageSet ===
null ) {
221 $resultPageSet->setRedirectMergePolicy(
static function ( $current, $new ) {
222 if ( !isset( $current[
'index'] ) || $new[
'index'] < $current[
'index'] ) {
223 $current[
'index'] = $new[
'index'];
227 $resultPageSet->populateFromTitles( $titles );
228 $offset =
$params[
'offset'] + 1;
229 foreach ( $titles as $index => $title ) {
230 $resultPageSet->setGeneratorData(
232 $data[ $index ] + [
'index' => $index + $offset ]
244 private function getSearchResultData(
SearchResult $result, $prop ) {
246 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
252 $title = $result->getTitle();
254 $vals[
'pageid'] = $title->getArticleID();
256 if ( isset( $prop[
'size'] ) ) {
257 $vals[
'size'] = $result->getByteSize();
259 if ( isset( $prop[
'wordcount'] ) ) {
260 $vals[
'wordcount'] = $result->getWordCount();
262 if ( isset( $prop[
'snippet'] ) ) {
263 $vals[
'snippet'] = $result->getTextSnippet();
265 if ( isset( $prop[
'timestamp'] ) ) {
266 $vals[
'timestamp'] =
wfTimestamp( TS_ISO_8601, $result->getTimestamp() );
268 if ( isset( $prop[
'titlesnippet'] ) ) {
269 $vals[
'titlesnippet'] = $result->getTitleSnippet();
271 if ( isset( $prop[
'categorysnippet'] ) ) {
272 $vals[
'categorysnippet'] = $result->getCategorySnippet();
274 if ( $result->getRedirectTitle() !==
null ) {
275 if ( isset( $prop[
'redirecttitle'] ) ) {
276 $vals[
'redirecttitle'] = $result->getRedirectTitle()->getPrefixedText();
278 if ( isset( $prop[
'redirectsnippet'] ) ) {
279 $vals[
'redirectsnippet'] = $result->getRedirectSnippet();
282 if ( $result->getSectionTitle() !==
null ) {
283 if ( isset( $prop[
'sectiontitle'] ) ) {
284 $vals[
'sectiontitle'] = $result->getSectionTitle()->getFragment();
286 if ( isset( $prop[
'sectionsnippet'] ) ) {
287 $vals[
'sectionsnippet'] = $result->getSectionSnippet();
290 if ( isset( $prop[
'isfilematch'] ) ) {
291 $vals[
'isfilematch'] = $result->isFileMatch();
294 if ( isset( $prop[
'extensiondata'] ) ) {
295 $extra = $result->getExtensionData();
315 private function addInterwikiResults(
320 if (
$matches->hasInterwikiResults( $type ) ) {
321 foreach (
$matches->getInterwikiResults( $type ) as $interwikiMatches ) {
323 $totalhits += $interwikiMatches->getTotalHits();
325 foreach ( $interwikiMatches as $result ) {
326 $title = $result->getTitle();
327 $vals = $this->getSearchResultData( $result, $prop );
329 $vals[
'namespace'] = $result->getInterwikiNamespaceText();
330 $vals[
'title'] = $title->getText();
331 $vals[
'url'] = $title->getFullURL();
337 $result->getInterwikiPrefix()
347 if ( $totalhits !==
null ) {
348 $apiResult->
addValue( [
'query', $section .
'searchinfo' ],
'totalhits', $totalhits );
357 private function decideSnippets( array $prop ): array {
362 if ( isset( $prop[
'titlesnippet'] ) ) {
368 if ( isset( $prop[
'redirectsnippet'] ) || isset( $prop[
'redirecttitle'] ) ) {
369 $fields[] =
'redirect';
371 if ( isset( $prop[
'categorysnippet'] ) ) {
372 $fields[] =
'category';
374 if ( isset( $prop[
'sectionsnippet'] ) || isset( $prop[
'sectiontitle'] ) ) {
375 $fields[] =
'heading';
387 ParamValidator::PARAM_TYPE => [
394 ParamValidator::PARAM_DEFAULT =>
'totalhits|suggestion|rewrittenquery',
395 ParamValidator::PARAM_TYPE => [
400 ParamValidator::PARAM_ISMULTI =>
true,
403 ParamValidator::PARAM_DEFAULT =>
'size|wordcount|timestamp|snippet',
404 ParamValidator::PARAM_TYPE => [
420 ParamValidator::PARAM_ISMULTI =>
true,
422 EnumDef::PARAM_DEPRECATED_VALUES => [
427 'interwiki' =>
false,
428 'enablerewrites' =>
false,
432 if ( $this->isInGeneratorMode() ) {
433 $allowedParams[
'prop'][ParamValidator::PARAM_DEFAULT] =
'';
434 $allowedParams[
'info'][ParamValidator::PARAM_DEFAULT] =
'';
439 $alternatives = $this->searchEngineConfig->getSearchTypes();
440 if ( count( $alternatives ) == 1 ) {
441 $allowedParams[
'sort'] = [
442 ParamValidator::PARAM_DEFAULT => SearchEngine::DEFAULT_SORT,
443 ParamValidator::PARAM_TYPE => $this->searchEngineFactory->create()->getValidSorts(),
447 return $allowedParams;
453 'profile-type' => SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
454 'help-message' =>
'apihelp-query+search-param-qiprofile',
461 'action=query&list=search&srsearch=meaning'
462 =>
'apihelp-query+search-example-simple',
463 'action=query&list=search&srwhat=text&srsearch=meaning'
464 =>
'apihelp-query+search-example-text',
465 'action=query&generator=search&gsrsearch=meaning&prop=info'
466 =>
'apihelp-query+search-example-generator',
471 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Search';