[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * 5 * Created on July 30, 2007 6 * 7 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 * http://www.gnu.org/copyleft/gpl.html 23 * 24 * @file 25 */ 26 27 /** 28 * Query module to perform full text search within wiki titles and content 29 * 30 * @ingroup API 31 */ 32 class ApiQuerySearch extends ApiQueryGeneratorBase { 33 34 /** 35 * When $wgSearchType is null, $wgSearchAlternatives[0] is null. Null isn't 36 * a valid option for an array for PARAM_TYPE, so we'll use a fake name 37 * that can't possibly be a class name and describes what the null behavior 38 * does 39 */ 40 const BACKEND_NULL_PARAM = 'database-backed'; 41 42 public function __construct( ApiQuery $query, $moduleName ) { 43 parent::__construct( $query, $moduleName, 'sr' ); 44 } 45 46 public function execute() { 47 $this->run(); 48 } 49 50 public function executeGenerator( $resultPageSet ) { 51 $this->run( $resultPageSet ); 52 } 53 54 /** 55 * @param ApiPageSet $resultPageSet 56 * @return void 57 */ 58 private function run( $resultPageSet = null ) { 59 global $wgContLang; 60 $params = $this->extractRequestParams(); 61 62 // Extract parameters 63 $limit = $params['limit']; 64 $query = $params['search']; 65 $what = $params['what']; 66 $interwiki = $params['interwiki']; 67 $searchInfo = array_flip( $params['info'] ); 68 $prop = array_flip( $params['prop'] ); 69 70 // Deprecated parameters 71 if ( isset( $prop['hasrelated'] ) ) { 72 $this->logFeatureUsage( 'action=search&srprop=hasrelated' ); 73 $this->setWarning( 'srprop=hasrelated has been deprecated' ); 74 } 75 if ( isset( $prop['score'] ) ) { 76 $this->logFeatureUsage( 'action=search&srprop=score' ); 77 $this->setWarning( 'srprop=score has been deprecated' ); 78 } 79 80 // Create search engine instance and set options 81 $search = isset( $params['backend'] ) && $params['backend'] != self::BACKEND_NULL_PARAM ? 82 SearchEngine::create( $params['backend'] ) : SearchEngine::create(); 83 $search->setLimitOffset( $limit + 1, $params['offset'] ); 84 $search->setNamespaces( $params['namespace'] ); 85 86 $query = $search->transformSearchTerm( $query ); 87 $query = $search->replacePrefixes( $query ); 88 89 // Perform the actual search 90 if ( $what == 'text' ) { 91 $matches = $search->searchText( $query ); 92 } elseif ( $what == 'title' ) { 93 $matches = $search->searchTitle( $query ); 94 } elseif ( $what == 'nearmatch' ) { 95 $matches = SearchEngine::getNearMatchResultSet( $query ); 96 } else { 97 // We default to title searches; this is a terrible legacy 98 // of the way we initially set up the MySQL fulltext-based 99 // search engine with separate title and text fields. 100 // In the future, the default should be for a combined index. 101 $what = 'title'; 102 $matches = $search->searchTitle( $query ); 103 104 // Not all search engines support a separate title search, 105 // for instance the Lucene-based engine we use on Wikipedia. 106 // In this case, fall back to full-text search (which will 107 // include titles in it!) 108 if ( is_null( $matches ) ) { 109 $what = 'text'; 110 $matches = $search->searchText( $query ); 111 } 112 } 113 if ( is_null( $matches ) ) { 114 $this->dieUsage( "{$what} search is disabled", "search-{$what}-disabled" ); 115 } elseif ( $matches instanceof Status && !$matches->isGood() ) { 116 $this->dieUsage( $matches->getWikiText(), 'search-error' ); 117 } 118 119 $apiResult = $this->getResult(); 120 // Add search meta data to result 121 if ( isset( $searchInfo['totalhits'] ) ) { 122 $totalhits = $matches->getTotalHits(); 123 if ( $totalhits !== null ) { 124 $apiResult->addValue( array( 'query', 'searchinfo' ), 125 'totalhits', $totalhits ); 126 } 127 } 128 if ( isset( $searchInfo['suggestion'] ) && $matches->hasSuggestion() ) { 129 $apiResult->addValue( array( 'query', 'searchinfo' ), 130 'suggestion', $matches->getSuggestionQuery() ); 131 } 132 133 // Add the search results to the result 134 $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); 135 $titles = array(); 136 $count = 0; 137 $result = $matches->next(); 138 139 while ( $result ) { 140 if ( ++$count > $limit ) { 141 // We've reached the one extra which shows that there are 142 // additional items to be had. Stop here... 143 $this->setContinueEnumParameter( 'offset', $params['offset'] + $params['limit'] ); 144 break; 145 } 146 147 // Silently skip broken and missing titles 148 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) { 149 $result = $matches->next(); 150 continue; 151 } 152 153 $title = $result->getTitle(); 154 if ( is_null( $resultPageSet ) ) { 155 $vals = array(); 156 ApiQueryBase::addTitleInfo( $vals, $title ); 157 158 if ( isset( $prop['snippet'] ) ) { 159 $vals['snippet'] = $result->getTextSnippet( $terms ); 160 } 161 if ( isset( $prop['size'] ) ) { 162 $vals['size'] = $result->getByteSize(); 163 } 164 if ( isset( $prop['wordcount'] ) ) { 165 $vals['wordcount'] = $result->getWordCount(); 166 } 167 if ( isset( $prop['timestamp'] ) ) { 168 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() ); 169 } 170 if ( isset( $prop['titlesnippet'] ) ) { 171 $vals['titlesnippet'] = $result->getTitleSnippet( $terms ); 172 } 173 if ( !is_null( $result->getRedirectTitle() ) ) { 174 if ( isset( $prop['redirecttitle'] ) ) { 175 $vals['redirecttitle'] = $result->getRedirectTitle(); 176 } 177 if ( isset( $prop['redirectsnippet'] ) ) { 178 $vals['redirectsnippet'] = $result->getRedirectSnippet( $terms ); 179 } 180 } 181 if ( !is_null( $result->getSectionTitle() ) ) { 182 if ( isset( $prop['sectiontitle'] ) ) { 183 $vals['sectiontitle'] = $result->getSectionTitle()->getFragment(); 184 } 185 if ( isset( $prop['sectionsnippet'] ) ) { 186 $vals['sectionsnippet'] = $result->getSectionSnippet(); 187 } 188 } 189 190 // Add item to results and see whether it fits 191 $fit = $apiResult->addValue( array( 'query', $this->getModuleName() ), 192 null, $vals ); 193 if ( !$fit ) { 194 $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 ); 195 break; 196 } 197 } else { 198 $titles[] = $title; 199 } 200 201 $result = $matches->next(); 202 } 203 204 $hasInterwikiResults = false; 205 if ( $interwiki && $resultPageSet === null && $matches->hasInterwikiResults() ) { 206 $matches = $matches->getInterwikiResults(); 207 $hasInterwikiResults = true; 208 209 // Include number of results if requested 210 if ( isset( $searchInfo['totalhits'] ) ) { 211 $totalhits = $matches->getTotalHits(); 212 if ( $totalhits !== null ) { 213 $apiResult->addValue( array( 'query', 'interwikisearchinfo' ), 214 'totalhits', $totalhits ); 215 } 216 } 217 218 $result = $matches->next(); 219 while ( $result ) { 220 $title = $result->getTitle(); 221 $vals = array( 222 'namespace' => $result->getInterwikiNamespaceText(), 223 'title' => $title->getText(), 224 'url' => $title->getFullUrl(), 225 ); 226 227 // Add item to results and see whether it fits 228 $fit = $apiResult->addValue( 229 array( 'query', 'interwiki' . $this->getModuleName(), $result->getInterwikiPrefix() ), 230 null, 231 $vals 232 ); 233 234 if ( !$fit ) { 235 // We hit the limit. We can't really provide any meaningful 236 // pagination info so just bail out 237 break; 238 } 239 240 $result = $matches->next(); 241 } 242 } 243 244 if ( is_null( $resultPageSet ) ) { 245 $apiResult->setIndexedTagName_internal( array( 246 'query', $this->getModuleName() 247 ), 'p' ); 248 if ( $hasInterwikiResults ) { 249 $apiResult->setIndexedTagName_internal( array( 250 'query', 'interwiki' . $this->getModuleName() 251 ), 'p' ); 252 } 253 } else { 254 $resultPageSet->populateFromTitles( $titles ); 255 } 256 } 257 258 public function getCacheMode( $params ) { 259 return 'public'; 260 } 261 262 public function getAllowedParams() { 263 $params = array( 264 'search' => array( 265 ApiBase::PARAM_TYPE => 'string', 266 ApiBase::PARAM_REQUIRED => true 267 ), 268 'namespace' => array( 269 ApiBase::PARAM_DFLT => NS_MAIN, 270 ApiBase::PARAM_TYPE => 'namespace', 271 ApiBase::PARAM_ISMULTI => true, 272 ), 273 'what' => array( 274 ApiBase::PARAM_DFLT => null, 275 ApiBase::PARAM_TYPE => array( 276 'title', 277 'text', 278 'nearmatch', 279 ) 280 ), 281 'info' => array( 282 ApiBase::PARAM_DFLT => 'totalhits|suggestion', 283 ApiBase::PARAM_TYPE => array( 284 'totalhits', 285 'suggestion', 286 ), 287 ApiBase::PARAM_ISMULTI => true, 288 ), 289 'prop' => array( 290 ApiBase::PARAM_DFLT => 'size|wordcount|timestamp|snippet', 291 ApiBase::PARAM_TYPE => array( 292 'size', 293 'wordcount', 294 'timestamp', 295 'score', 296 'snippet', 297 'titlesnippet', 298 'redirecttitle', 299 'redirectsnippet', 300 'sectiontitle', 301 'sectionsnippet', 302 'hasrelated', 303 ), 304 ApiBase::PARAM_ISMULTI => true, 305 ), 306 'offset' => 0, 307 'limit' => array( 308 ApiBase::PARAM_DFLT => 10, 309 ApiBase::PARAM_TYPE => 'limit', 310 ApiBase::PARAM_MIN => 1, 311 ApiBase::PARAM_MAX => ApiBase::LIMIT_SML1, 312 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_SML2 313 ), 314 'interwiki' => false, 315 ); 316 317 $alternatives = SearchEngine::getSearchTypes(); 318 if ( count( $alternatives ) > 1 ) { 319 if ( $alternatives[0] === null ) { 320 $alternatives[0] = self::BACKEND_NULL_PARAM; 321 } 322 $params['backend'] = array( 323 ApiBase::PARAM_DFLT => $this->getConfig()->get( 'SearchType' ), 324 ApiBase::PARAM_TYPE => $alternatives, 325 ); 326 } 327 328 return $params; 329 } 330 331 public function getParamDescription() { 332 $descriptions = array( 333 'search' => 'Search for all page titles (or content) that has this value', 334 'namespace' => 'The namespace(s) to enumerate', 335 'what' => 'Search inside the text or titles', 336 'info' => 'What metadata to return', 337 'prop' => array( 338 'What properties to return', 339 ' size - Adds the size of the page in bytes', 340 ' wordcount - Adds the word count of the page', 341 ' timestamp - Adds the timestamp of when the page was last edited', 342 ' score - DEPRECATED and IGNORED', 343 ' snippet - Adds a parsed snippet of the page', 344 ' titlesnippet - Adds a parsed snippet of the page title', 345 ' redirectsnippet - Adds a parsed snippet of the redirect title', 346 ' redirecttitle - Adds the title of the matching redirect', 347 ' sectionsnippet - Adds a parsed snippet of the matching section title', 348 ' sectiontitle - Adds the title of the matching section', 349 ' hasrelated - DEPRECATED and IGNORED', 350 ), 351 'offset' => 'Use this value to continue paging (return by query)', 352 'limit' => 'How many total pages to return', 353 'interwiki' => 'Include interwiki results in the search, if available' 354 ); 355 356 if ( count( SearchEngine::getSearchTypes() ) > 1 ) { 357 $descriptions['backend'] = 'Which search backend to use, if not the default'; 358 } 359 360 return $descriptions; 361 } 362 363 public function getDescription() { 364 return 'Perform a full text search.'; 365 } 366 367 public function getExamples() { 368 return array( 369 'api.php?action=query&list=search&srsearch=meaning', 370 'api.php?action=query&list=search&srwhat=text&srsearch=meaning', 371 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info', 372 ); 373 } 374 375 public function getHelpUrls() { 376 return 'https://www.mediawiki.org/wiki/API:Search'; 377 } 378 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |