42 use \MediaWiki\Api\SearchApi;
53 parent::__construct( $query, $moduleName,
'sr' );
55 $this->searchEngineConfig = $searchEngineConfig;
56 $this->searchEngineFactory = $searchEngineFactory;
57 $this->titleMatcher = $titleMatcher;
65 $this->
run( $resultPageSet );
72 private function run( $resultPageSet =
null ) {
78 $interwiki =
$params[
'interwiki'];
79 $searchInfo = array_fill_keys(
$params[
'info'],
true );
80 $prop = array_fill_keys(
$params[
'prop'],
true );
84 if ( isset(
$params[
'sort'] ) ) {
85 $search->setSort(
$params[
'sort'] );
87 $search->setFeatureData(
'rewrite', (
bool)
$params[
'enablerewrites'] );
88 $search->setFeatureData(
'interwiki', (
bool)$interwiki );
90 $search->setFeatureData(
'snippets', $this->decideSnippets( $prop ) );
92 $nquery = $search->replacePrefixes( $query );
93 if ( $nquery !== $query ) {
96 get_class( $search ) .
', this was deprecated in MediaWiki 1.32',
100 if ( $what ==
'text' ) {
101 $matches = $search->searchText( $query );
102 } elseif ( $what ==
'title' ) {
103 $matches = $search->searchTitle( $query );
104 } elseif ( $what ==
'nearmatch' ) {
107 $matches = $this->titleMatcher->getNearMatchResultSet(
$params[
'search'] );
114 $matches = $search->searchTitle( $query );
122 $matches = $search->searchText( $query );
134 if ( $status->isOK() ) {
135 $this->
getMain()->getErrorFormatter()->addMessagesFromStatus(
143 $this->
dieWithError( [
'apierror-searchdisabled', $what ],
"search-{$what}-disabled" );
148 if ( isset( $searchInfo[
'totalhits'] ) ) {
149 $totalhits =
$matches->getTotalHits();
150 if ( $totalhits !==
null ) {
151 $apiResult->addValue( [
'query',
'searchinfo' ],
152 'totalhits', $totalhits );
155 if ( isset( $searchInfo[
'suggestion'] ) &&
$matches->hasSuggestion() ) {
156 $apiResult->addValue( [
'query',
'searchinfo' ],
157 'suggestion',
$matches->getSuggestionQuery() );
158 $apiResult->addValue( [
'query',
'searchinfo' ],
159 'suggestionsnippet', HtmlArmor::getHtml(
$matches->getSuggestionSnippet() ) );
161 if ( isset( $searchInfo[
'rewrittenquery'] ) &&
$matches->hasRewrittenQuery() ) {
162 $apiResult->addValue( [
'query',
'searchinfo' ],
163 'rewrittenquery',
$matches->getQueryAfterRewrite() );
164 $apiResult->addValue( [
'query',
'searchinfo' ],
165 'rewrittenquerysnippet', HtmlArmor::getHtml(
$matches->getQueryAfterRewriteSnippet() ) );
179 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
183 $vals = $this->getSearchResultData( $result, $prop );
185 if ( $resultPageSet ===
null ) {
188 $fit = $apiResult->addValue( [
'query', $this->
getModuleName() ],
null, $vals );
195 $titles[] = $result->getTitle();
196 $data[] = $vals ?: [];
205 $canAddInterwiki = (bool)
$params[
'enablerewrites'] && ( $resultPageSet ===
null );
206 if ( $canAddInterwiki ) {
207 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'additional',
212 if ( $interwiki && $resultPageSet ===
null ) {
213 $this->addInterwikiResults(
$matches, $apiResult, $prop,
'interwiki',
217 if ( $resultPageSet ===
null ) {
218 $apiResult->addIndexedTagName( [
222 $resultPageSet->setRedirectMergePolicy(
static function ( $current, $new ) {
223 if ( !isset( $current[
'index'] ) || $new[
'index'] < $current[
'index'] ) {
224 $current[
'index'] = $new[
'index'];
228 $resultPageSet->populateFromTitles( $titles );
229 $offset =
$params[
'offset'] + 1;
230 foreach ( $titles as $index => $title ) {
231 $resultPageSet->setGeneratorData(
233 $data[ $index ] + [
'index' => $index + $offset ]
245 private function getSearchResultData(
SearchResult $result, $prop ) {
247 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
253 $title = $result->getTitle();
255 $vals[
'pageid'] = $title->getArticleID();
257 if ( isset( $prop[
'size'] ) ) {
258 $vals[
'size'] = $result->getByteSize();
260 if ( isset( $prop[
'wordcount'] ) ) {
261 $vals[
'wordcount'] = $result->getWordCount();
263 if ( isset( $prop[
'snippet'] ) ) {
264 $vals[
'snippet'] = $result->getTextSnippet();
266 if ( isset( $prop[
'timestamp'] ) ) {
267 $vals[
'timestamp'] =
wfTimestamp( TS_ISO_8601, $result->getTimestamp() );
269 if ( isset( $prop[
'titlesnippet'] ) ) {
270 $vals[
'titlesnippet'] = $result->getTitleSnippet();
272 if ( isset( $prop[
'categorysnippet'] ) ) {
273 $vals[
'categorysnippet'] = $result->getCategorySnippet();
275 if ( $result->getRedirectTitle() !==
null ) {
276 if ( isset( $prop[
'redirecttitle'] ) ) {
277 $vals[
'redirecttitle'] = $result->getRedirectTitle()->getPrefixedText();
279 if ( isset( $prop[
'redirectsnippet'] ) ) {
280 $vals[
'redirectsnippet'] = $result->getRedirectSnippet();
283 if ( $result->getSectionTitle() !==
null ) {
284 if ( isset( $prop[
'sectiontitle'] ) ) {
285 $vals[
'sectiontitle'] = $result->getSectionTitle()->getFragment();
287 if ( isset( $prop[
'sectionsnippet'] ) ) {
288 $vals[
'sectionsnippet'] = $result->getSectionSnippet();
291 if ( isset( $prop[
'isfilematch'] ) ) {
292 $vals[
'isfilematch'] = $result->isFileMatch();
295 if ( isset( $prop[
'extensiondata'] ) ) {
296 $extra = $result->getExtensionData();
316 private function addInterwikiResults(
321 if (
$matches->hasInterwikiResults( $type ) ) {
322 foreach (
$matches->getInterwikiResults( $type ) as $interwikiMatches ) {
324 $totalhits += $interwikiMatches->getTotalHits();
326 foreach ( $interwikiMatches as $result ) {
327 $title = $result->getTitle();
328 $vals = $this->getSearchResultData( $result, $prop );
330 $vals[
'namespace'] = $result->getInterwikiNamespaceText();
331 $vals[
'title'] = $title->getText();
332 $vals[
'url'] = $title->getFullURL();
335 $fit = $apiResult->addValue( [
338 $result->getInterwikiPrefix()
348 if ( $totalhits !==
null ) {
349 $apiResult->addValue( [
'query', $section .
'searchinfo' ],
'totalhits', $totalhits );
350 $apiResult->addIndexedTagName( [
358 private function decideSnippets( array $prop ): array {
363 if ( isset( $prop[
'titlesnippet'] ) ) {
369 if ( isset( $prop[
'redirectsnippet'] ) || isset( $prop[
'redirecttitle'] ) ) {
370 $fields[] =
'redirect';
372 if ( isset( $prop[
'categorysnippet'] ) ) {
373 $fields[] =
'category';
375 if ( isset( $prop[
'sectionsnippet'] ) || isset( $prop[
'sectiontitle'] ) ) {
376 $fields[] =
'heading';
388 ParamValidator::PARAM_TYPE => [
395 ParamValidator::PARAM_DEFAULT =>
'totalhits|suggestion|rewrittenquery',
396 ParamValidator::PARAM_TYPE => [
401 ParamValidator::PARAM_ISMULTI =>
true,
404 ParamValidator::PARAM_DEFAULT =>
'size|wordcount|timestamp|snippet',
405 ParamValidator::PARAM_TYPE => [
421 ParamValidator::PARAM_ISMULTI =>
true,
423 EnumDef::PARAM_DEPRECATED_VALUES => [
428 'interwiki' =>
false,
429 'enablerewrites' =>
false,
433 if ( $this->isInGeneratorMode() ) {
434 $allowedParams[
'prop'][ParamValidator::PARAM_DEFAULT] =
'';
435 $allowedParams[
'info'][ParamValidator::PARAM_DEFAULT] =
'';
440 $alternatives = $this->searchEngineConfig->getSearchTypes();
441 if ( count( $alternatives ) == 1 ) {
442 $allowedParams[
'sort'] = [
443 ParamValidator::PARAM_DEFAULT => SearchEngine::DEFAULT_SORT,
444 ParamValidator::PARAM_TYPE => $this->searchEngineFactory->create()->getValidSorts(),
448 return $allowedParams;
454 'profile-type' => SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
455 'help-message' =>
'apihelp-query+search-param-qiprofile',
462 'action=query&list=search&srsearch=meaning'
463 =>
'apihelp-query+search-example-simple',
464 'action=query&list=search&srwhat=text&srsearch=meaning'
465 =>
'apihelp-query+search-example-text',
466 'action=query&generator=search&gsrsearch=meaning&prop=info'
467 =>
'apihelp-query+search-example-generator',
472 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Search';