MediaWiki
REL1_19
|
00001 <?php 00032 class ApiQuerySearch extends ApiQueryGeneratorBase { 00033 00034 public function __construct( $query, $moduleName ) { 00035 parent::__construct( $query, $moduleName, 'sr' ); 00036 } 00037 00038 public function execute() { 00039 $this->run(); 00040 } 00041 00042 public function executeGenerator( $resultPageSet ) { 00043 $this->run( $resultPageSet ); 00044 } 00045 00050 private function run( $resultPageSet = null ) { 00051 global $wgContLang; 00052 $params = $this->extractRequestParams(); 00053 00054 // Extract parameters 00055 $limit = $params['limit']; 00056 $query = $params['search']; 00057 $what = $params['what']; 00058 $searchInfo = array_flip( $params['info'] ); 00059 $prop = array_flip( $params['prop'] ); 00060 00061 // Create search engine instance and set options 00062 $search = SearchEngine::create(); 00063 $search->setLimitOffset( $limit + 1, $params['offset'] ); 00064 $search->setNamespaces( $params['namespace'] ); 00065 $search->showRedirects = $params['redirects']; 00066 00067 $query = $search->transformSearchTerm( $query ); 00068 $query = $search->replacePrefixes( $query ); 00069 00070 // Perform the actual search 00071 if ( $what == 'text' ) { 00072 $matches = $search->searchText( $query ); 00073 } elseif ( $what == 'title' ) { 00074 $matches = $search->searchTitle( $query ); 00075 } elseif ( $what == 'nearmatch' ) { 00076 $matches = SearchEngine::getNearMatchResultSet( $query ); 00077 } else { 00078 // We default to title searches; this is a terrible legacy 00079 // of the way we initially set up the MySQL fulltext-based 00080 // search engine with separate title and text fields. 00081 // In the future, the default should be for a combined index. 00082 $what = 'title'; 00083 $matches = $search->searchTitle( $query ); 00084 00085 // Not all search engines support a separate title search, 00086 // for instance the Lucene-based engine we use on Wikipedia. 00087 // In this case, fall back to full-text search (which will 00088 // include titles in it!) 00089 if ( is_null( $matches ) ) { 00090 $what = 'text'; 00091 $matches = $search->searchText( $query ); 00092 } 00093 } 00094 if ( is_null( $matches ) ) { 00095 $this->dieUsage( "{$what} search is disabled", "search-{$what}-disabled" ); 00096 } 00097 00098 $apiResult = $this->getResult(); 00099 // Add search meta data to result 00100 if ( isset( $searchInfo['totalhits'] ) ) { 00101 $totalhits = $matches->getTotalHits(); 00102 if ( $totalhits !== null ) { 00103 $apiResult->addValue( array( 'query', 'searchinfo' ), 00104 'totalhits', $totalhits ); 00105 } 00106 } 00107 if ( isset( $searchInfo['suggestion'] ) && $matches->hasSuggestion() ) { 00108 $apiResult->addValue( array( 'query', 'searchinfo' ), 00109 'suggestion', $matches->getSuggestionQuery() ); 00110 } 00111 00112 // Add the search results to the result 00113 $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); 00114 $titles = array(); 00115 $count = 0; 00116 $result = $matches->next(); 00117 00118 while ( $result ) { 00119 if ( ++ $count > $limit ) { 00120 // We've reached the one extra which shows that there are additional items to be had. Stop here... 00121 $this->setContinueEnumParameter( 'offset', $params['offset'] + $params['limit'] ); 00122 break; 00123 } 00124 00125 // Silently skip broken and missing titles 00126 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) { 00127 $result = $matches->next(); 00128 continue; 00129 } 00130 00131 $title = $result->getTitle(); 00132 if ( is_null( $resultPageSet ) ) { 00133 $vals = array(); 00134 ApiQueryBase::addTitleInfo( $vals, $title ); 00135 00136 if ( isset( $prop['snippet'] ) ) { 00137 $vals['snippet'] = $result->getTextSnippet( $terms ); 00138 } 00139 if ( isset( $prop['size'] ) ) { 00140 $vals['size'] = $result->getByteSize(); 00141 } 00142 if ( isset( $prop['wordcount'] ) ) { 00143 $vals['wordcount'] = $result->getWordCount(); 00144 } 00145 if ( isset( $prop['timestamp'] ) ) { 00146 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() ); 00147 } 00148 if ( !is_null( $result->getScore() ) && isset( $prop['score'] ) ) { 00149 $vals['score'] = $result->getScore(); 00150 } 00151 if ( isset( $prop['titlesnippet'] ) ) { 00152 $vals['titlesnippet'] = $result->getTitleSnippet( $terms ); 00153 } 00154 if ( !is_null( $result->getRedirectTitle() ) ) { 00155 if ( isset( $prop['redirecttitle'] ) ) { 00156 $vals['redirecttitle'] = $result->getRedirectTitle(); 00157 } 00158 if ( isset( $prop['redirectsnippet'] ) ) { 00159 $vals['redirectsnippet'] = $result->getRedirectSnippet( $terms ); 00160 } 00161 } 00162 if ( !is_null( $result->getSectionTitle() ) ) { 00163 if ( isset( $prop['sectiontitle'] ) ) { 00164 $vals['sectiontitle'] = $result->getSectionTitle()->getFragment(); 00165 } 00166 if ( isset( $prop['sectionsnippet'] ) ) { 00167 $vals['sectionsnippet'] = $result->getSectionSnippet(); 00168 } 00169 } 00170 if ( isset( $prop['hasrelated'] ) && $result->hasRelated() ) { 00171 $vals['hasrelated'] = ""; 00172 } 00173 00174 // Add item to results and see whether it fits 00175 $fit = $apiResult->addValue( array( 'query', $this->getModuleName() ), 00176 null, $vals ); 00177 if ( !$fit ) { 00178 $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 ); 00179 break; 00180 } 00181 } else { 00182 $titles[] = $title; 00183 } 00184 00185 $result = $matches->next(); 00186 } 00187 00188 if ( is_null( $resultPageSet ) ) { 00189 $apiResult->setIndexedTagName_internal( array( 00190 'query', $this->getModuleName() 00191 ), 'p' ); 00192 } else { 00193 $resultPageSet->populateFromTitles( $titles ); 00194 } 00195 } 00196 00197 public function getCacheMode( $params ) { 00198 return 'public'; 00199 } 00200 00201 public function getAllowedParams() { 00202 return array( 00203 'search' => array( 00204 ApiBase::PARAM_TYPE => 'string', 00205 ApiBase::PARAM_REQUIRED => true 00206 ), 00207 'namespace' => array( 00208 ApiBase::PARAM_DFLT => 0, 00209 ApiBase::PARAM_TYPE => 'namespace', 00210 ApiBase::PARAM_ISMULTI => true, 00211 ), 00212 'what' => array( 00213 ApiBase::PARAM_DFLT => null, 00214 ApiBase::PARAM_TYPE => array( 00215 'title', 00216 'text', 00217 'nearmatch', 00218 ) 00219 ), 00220 'info' => array( 00221 ApiBase::PARAM_DFLT => 'totalhits|suggestion', 00222 ApiBase::PARAM_TYPE => array( 00223 'totalhits', 00224 'suggestion', 00225 ), 00226 ApiBase::PARAM_ISMULTI => true, 00227 ), 00228 'prop' => array( 00229 ApiBase::PARAM_DFLT => 'size|wordcount|timestamp|snippet', 00230 ApiBase::PARAM_TYPE => array( 00231 'size', 00232 'wordcount', 00233 'timestamp', 00234 'score', 00235 'snippet', 00236 'titlesnippet', 00237 'redirecttitle', 00238 'redirectsnippet', 00239 'sectiontitle', 00240 'sectionsnippet', 00241 'hasrelated', 00242 ), 00243 ApiBase::PARAM_ISMULTI => true, 00244 ), 00245 'redirects' => false, 00246 'offset' => 0, 00247 'limit' => array( 00248 ApiBase::PARAM_DFLT => 10, 00249 ApiBase::PARAM_TYPE => 'limit', 00250 ApiBase::PARAM_MIN => 1, 00251 ApiBase::PARAM_MAX => ApiBase::LIMIT_SML1, 00252 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_SML2 00253 ) 00254 ); 00255 } 00256 00257 public function getParamDescription() { 00258 return array( 00259 'search' => 'Search for all page titles (or content) that has this value', 00260 'namespace' => 'The namespace(s) to enumerate', 00261 'what' => 'Search inside the text or titles', 00262 'info' => 'What metadata to return', 00263 'prop' => array( 00264 'What properties to return', 00265 ' size - Adds the size of the page in bytes', 00266 ' wordcount - Adds the word count of the page', 00267 ' timestamp - Adds the timestamp of when the page was last edited', 00268 ' score - Adds the score (if any) from the search engine', 00269 ' snippet - Adds a parsed snippet of the page', 00270 ' titlesnippet - Adds a parsed snippet of the page title', 00271 ' redirectsnippet - Adds a parsed snippet of the redirect title', 00272 ' redirecttitle - Adds the title of the matching redirect', 00273 ' sectionsnippet - Adds a parsed snippet of the matching section title', 00274 ' sectiontitle - Adds the title of the matching section', 00275 ' hasrelated - Indicates whether a related search is available', 00276 ), 00277 'redirects' => 'Include redirect pages in the search', 00278 'offset' => 'Use this value to continue paging (return by query)', 00279 'limit' => 'How many total pages to return' 00280 ); 00281 } 00282 00283 public function getDescription() { 00284 return 'Perform a full text search'; 00285 } 00286 00287 public function getPossibleErrors() { 00288 return array_merge( parent::getPossibleErrors(), array( 00289 array( 'code' => 'search-text-disabled', 'info' => 'text search is disabled' ), 00290 array( 'code' => 'search-title-disabled', 'info' => 'title search is disabled' ), 00291 ) ); 00292 } 00293 00294 public function getExamples() { 00295 return array( 00296 'api.php?action=query&list=search&srsearch=meaning', 00297 'api.php?action=query&list=search&srwhat=text&srsearch=meaning', 00298 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info', 00299 ); 00300 } 00301 00302 public function getHelpUrls() { 00303 return 'https://www.mediawiki.org/wiki/API:Search'; 00304 } 00305 00306 public function getVersion() { 00307 return __CLASS__ . ': $Id$'; 00308 } 00309 }