29 use \MediaWiki\Api\SearchApi;
40 parent::__construct( $query, $moduleName,
'sr' );
42 $this->searchEngineConfig = $searchEngineConfig;
43 $this->searchEngineFactory = $searchEngineFactory;
44 $this->titleMatcher = $titleMatcher;
53 $this->run( $resultPageSet );
60 private function run( $resultPageSet =
null ) {
64 $query = $params[
'search'];
65 $what = $params[
'what'];
66 $interwiki = $params[
'interwiki'];
67 $searchInfo = array_fill_keys( $params[
'info'],
true );
68 $prop = array_fill_keys( $params[
'prop'],
true );
72 if ( isset( $params[
'sort'] ) ) {
73 $search->setSort( $params[
'sort'] );
75 $search->setFeatureData(
'rewrite', (
bool)$params[
'enablerewrites'] );
76 $search->setFeatureData(
'interwiki', (
bool)$interwiki );
78 $search->setFeatureData(
'snippets', $this->decideSnippets( $prop ) );
81 if ( $what ==
'text' ) {
82 $matches = $search->searchText( $query );
83 } elseif ( $what ==
'title' ) {
84 $matches = $search->searchTitle( $query );
85 } elseif ( $what ==
'nearmatch' ) {
88 $matches = $this->titleMatcher->getNearMatchResultSet( $params[
'search'] );
95 $matches = $search->searchTitle( $query );
103 $matches = $search->searchText( $query );
115 if ( $status->isOK() ) {
116 $this->
getMain()->getErrorFormatter()->addMessagesFromStatus(
124 $this->
dieWithError( [
'apierror-searchdisabled', $what ],
"search-{$what}-disabled" );
129 if ( isset( $searchInfo[
'totalhits'] ) ) {
130 $totalhits =
$matches->getTotalHits();
131 if ( $totalhits !==
null ) {
132 $apiResult->addValue( [
'query',
'searchinfo' ],
133 'totalhits', $totalhits );
134 if (
$matches->isApproximateTotalHits() ) {
135 $apiResult->addValue( [
'query',
'searchinfo' ],
136 'approximate_totalhits',
$matches->isApproximateTotalHits() );
140 if ( isset( $searchInfo[
'suggestion'] ) &&
$matches->hasSuggestion() ) {
141 $apiResult->addValue( [
'query',
'searchinfo' ],
142 'suggestion',
$matches->getSuggestionQuery() );
143 $apiResult->addValue( [
'query',
'searchinfo' ],
144 'suggestionsnippet', HtmlArmor::getHtml(
$matches->getSuggestionSnippet() ) );
146 if ( isset( $searchInfo[
'rewrittenquery'] ) &&
$matches->hasRewrittenQuery() ) {
147 $apiResult->addValue( [
'query',
'searchinfo' ],
148 'rewrittenquery',
$matches->getQueryAfterRewrite() );
149 $apiResult->addValue( [
'query',
'searchinfo' ],
150 'rewrittenquerysnippet', HtmlArmor::getHtml(
$matches->getQueryAfterRewriteSnippet() ) );
164 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
168 $vals = $this->getSearchResultData( $result, $prop );
170 if ( $resultPageSet ===
null ) {
173 $fit = $apiResult->addValue( [
'query', $this->
getModuleName() ],
null, $vals );
180 $titles[] = $result->getTitle();
181 $data[] = $vals ?: [];
190 $canAddInterwiki = (bool)$params[
'enablerewrites'] && ( $resultPageSet ===
null );
191 if ( $canAddInterwiki ) {
193 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'additional',
198 if ( $interwiki && $resultPageSet ===
null ) {
200 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'interwiki',
204 if ( $resultPageSet ===
null ) {
205 $apiResult->addIndexedTagName( [
209 $resultPageSet->setRedirectMergePolicy(
static function ( $current, $new ) {
210 if ( !isset( $current[
'index'] ) || $new[
'index'] < $current[
'index'] ) {
211 $current[
'index'] = $new[
'index'];
215 $resultPageSet->populateFromTitles( $titles );
216 $offset =
$params[
'offset'] + 1;
217 foreach ( $titles as $index => $title ) {
218 $resultPageSet->setGeneratorData(
220 $data[ $index ] + [
'index' => $index + $offset ]
232 private function getSearchResultData( SearchResult $result, $prop ) {
234 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
240 $title = $result->getTitle();
242 $vals[
'pageid'] = $title->getArticleID();
244 if ( isset( $prop[
'size'] ) ) {
245 $vals[
'size'] = $result->getByteSize();
247 if ( isset( $prop[
'wordcount'] ) ) {
248 $vals[
'wordcount'] = $result->getWordCount();
250 if ( isset( $prop[
'snippet'] ) ) {
251 $vals[
'snippet'] = $result->getTextSnippet();
253 if ( isset( $prop[
'timestamp'] ) ) {
254 $vals[
'timestamp'] =
wfTimestamp( TS::ISO_8601, $result->getTimestamp() );
256 if ( isset( $prop[
'titlesnippet'] ) ) {
257 $vals[
'titlesnippet'] = $result->getTitleSnippet();
259 if ( isset( $prop[
'categorysnippet'] ) ) {
260 $vals[
'categorysnippet'] = $result->getCategorySnippet();
262 if ( $result->getRedirectTitle() !==
null ) {
263 if ( isset( $prop[
'redirecttitle'] ) ) {
264 $vals[
'redirecttitle'] = $result->getRedirectTitle()->getPrefixedText();
266 if ( isset( $prop[
'redirectsnippet'] ) ) {
267 $vals[
'redirectsnippet'] = $result->getRedirectSnippet();
270 if ( $result->getSectionTitle() !==
null ) {
271 if ( isset( $prop[
'sectiontitle'] ) ) {
272 $vals[
'sectiontitle'] = $result->getSectionTitle()->getFragment();
274 if ( isset( $prop[
'sectionsnippet'] ) ) {
275 $vals[
'sectionsnippet'] = $result->getSectionSnippet();
278 if ( isset( $prop[
'isfilematch'] ) ) {
279 $vals[
'isfilematch'] = $result->isFileMatch();
282 if ( isset( $prop[
'extensiondata'] ) ) {
283 $extra = $result->getExtensionData();
303 private function addInterwikiResults(
304 ISearchResultSet
$matches, ApiResult $apiResult, $prop,
308 $approximateTotalHits =
false;
309 if (
$matches->hasInterwikiResults( $type ) ) {
310 foreach (
$matches->getInterwikiResults( $type ) as $interwikiMatches ) {
312 $interwikiTotalHits = $interwikiMatches->getTotalHits();
313 if ( $interwikiTotalHits !==
null ) {
314 $totalhits += $interwikiTotalHits;
315 $approximateTotalHits = $approximateTotalHits || $interwikiMatches->isApproximateTotalHits();
318 foreach ( $interwikiMatches as $result ) {
319 $title = $result->getTitle();
320 $vals = $this->getSearchResultData( $result, $prop );
322 $vals[
'namespace'] = $result->getInterwikiNamespaceText();
323 $vals[
'title'] = $title->getText();
324 $vals[
'url'] = $title->getFullURL();
327 $fit = $apiResult->addValue( [
330 $result->getInterwikiPrefix()
340 if ( $totalhits !==
null ) {
341 $apiResult->addValue( [
'query', $section .
'searchinfo' ],
'totalhits', $totalhits );
342 if ( $approximateTotalHits ) {
343 $apiResult->addValue( [
'query', $section .
'searchinfo' ],
'approximate_totalhits',
true );
345 $apiResult->addIndexedTagName( [
353 private function decideSnippets( array $prop ): array {
358 if ( isset( $prop[
'titlesnippet'] ) ) {
364 if ( isset( $prop[
'redirectsnippet'] ) || isset( $prop[
'redirecttitle'] ) ) {
365 $fields[] =
'redirect';
367 if ( isset( $prop[
'categorysnippet'] ) ) {
368 $fields[] =
'category';
370 if ( isset( $prop[
'sectionsnippet'] ) || isset( $prop[
'sectiontitle'] ) ) {
371 $fields[] =
'heading';
385 ParamValidator::PARAM_TYPE => [
392 ParamValidator::PARAM_DEFAULT =>
'totalhits|suggestion|rewrittenquery',
393 ParamValidator::PARAM_TYPE => [
398 ParamValidator::PARAM_ISMULTI =>
true,
401 ParamValidator::PARAM_DEFAULT =>
'size|wordcount|timestamp|snippet',
402 ParamValidator::PARAM_TYPE => [
418 ParamValidator::PARAM_ISMULTI =>
true,
420 EnumDef::PARAM_DEPRECATED_VALUES => [
425 'interwiki' =>
false,
426 'enablerewrites' =>
false,
430 if ( $this->isInGeneratorMode() ) {
431 $allowedParams[
'prop'][ParamValidator::PARAM_DEFAULT] =
'';
432 $allowedParams[
'info'][ParamValidator::PARAM_DEFAULT] =
'';
437 $alternatives = $this->searchEngineConfig->getSearchTypes();
438 if ( count( $alternatives ) == 1 ) {
439 $allowedParams[
'sort'] = [
440 ParamValidator::PARAM_DEFAULT => SearchEngine::DEFAULT_SORT,
441 ParamValidator::PARAM_TYPE => $this->searchEngineFactory->create()->getValidSorts(),
445 return $allowedParams;
453 'profile-type' => SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
454 'help-message' =>
'apihelp-query+search-param-qiprofile',
458 'profile-type' => SearchEngine::FT_QUERY_DEP_PROFILE_TYPE,
459 'help-message' =>
'apihelp-query+search-param-qdprofile',
467 'action=query&list=search&srsearch=meaning'
468 =>
'apihelp-query+search-example-simple',
469 'action=query&list=search&srwhat=text&srsearch=meaning'
470 =>
'apihelp-query+search-example-text',
471 'action=query&generator=search&gsrsearch=meaning&prop=info'
472 =>
'apihelp-query+search-example-generator',
478 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Search';