Index: trunk/phase3/includes/SearchEngine.php |
— | — | @@ -1,6 +1,9 @@ |
2 | 2 | <?php |
3 | 3 | # See search.doc |
4 | 4 | |
| 5 | +define( "MW_SEARCH_OK", true ); |
| 6 | +define( "MW_SEARCH_BAD_QUERY", false ); |
| 7 | + |
5 | 8 | class SearchEngine { |
6 | 9 | /* private */ var $mRawtext, $mUsertext, $mSearchterms; |
7 | 10 | /* private */ var $mTitlecond, $mTextcond; |
— | — | @@ -22,6 +25,8 @@ |
23 | 26 | $this->mUsertext = trim( preg_replace( "/[^{$lc}]/", " ", $text ) ); |
24 | 27 | $this->mSearchterms = array(); |
25 | 28 | $this->mStrictMatching = true; # Google-style, add '+' on all terms |
| 29 | + |
| 30 | + $this->db =& wfGetDB( DB_SLAVE ); |
26 | 31 | } |
27 | 32 | |
28 | 33 | function queryNamespaces() |
— | — | @@ -62,7 +67,7 @@ |
63 | 68 | global $wgUser, $wgOut, $wgLang, $wgTitle, $wgRequest; |
64 | 69 | $sk =& $wgUser->getSkin(); |
65 | 70 | |
66 | | - $search = $wgRequest->getText( 'search' ); |
| 71 | + $search = $this->mRawtext; |
67 | 72 | $searchx = $wgRequest->getVal( 'searchx' ); |
68 | 73 | $listredirs = $wgRequest->getVal( 'redirs' ); |
69 | 74 | |
— | — | @@ -156,11 +161,11 @@ |
157 | 162 | # Perform the search and construct the results page |
158 | 163 | function showResults() |
159 | 164 | { |
160 | | - global $wgUser, $wgTitle, $wgOut, $wgLang, $wgRequest; |
| 165 | + global $wgUser, $wgTitle, $wgOut, $wgLang; |
161 | 166 | global $wgDisableTextSearch, $wgInputEncoding; |
162 | 167 | $fname = "SearchEngine::showResults"; |
163 | 168 | |
164 | | - $search = $wgRequest->getText( 'search' ); |
| 169 | + $search = $this->mRawtext; |
165 | 170 | |
166 | 171 | $powersearch = $this->powersearch(); /* Need side-effects here? */ |
167 | 172 | |
— | — | @@ -171,8 +176,7 @@ |
172 | 177 | wfMsg( "searchhelppage" ), wfMsg( "searchingwikipedia" ) ) ); |
173 | 178 | $wgOut->addHTML( $header ); |
174 | 179 | |
175 | | - $dbr =& wfGetDB( DB_SLAVE ); |
176 | | - $this->parseQuery( $dbr ); |
| 180 | + $this->parseQuery(); |
177 | 181 | if ( "" == $this->mTitlecond || "" == $this->mTextcond ) { |
178 | 182 | $wgOut->addHTML( "<h2>" . wfMsg( "badquery" ) . "</h2>\n" . |
179 | 183 | "<p>" . wfMsg( "badquerytext" ) . "</p>\n" ); |
— | — | @@ -182,62 +186,35 @@ |
183 | 187 | |
184 | 188 | $searchnamespaces = $this->queryNamespaces(); |
185 | 189 | $redircond = $this->searchRedirects(); |
186 | | - $searchindex = $dbr->tableName( 'searchindex' ); |
187 | | - $cur = $dbr->tableName( 'cur' ); |
188 | 190 | |
189 | 191 | if ( $wgDisableTextSearch ) { |
190 | 192 | $wgOut->addHTML( wfMsg( "searchdisabled" ) ); |
191 | | - $wgOut->addHTML( wfMsg( "googlesearch", htmlspecialchars( $search ), $GLOBALS['wgInputEncoding'] ) ); |
| 193 | + $wgOut->addHTML( wfMsg( "googlesearch", htmlspecialchars( $search ), htmlspecialchars( $wgInputEncoding ) ) ); |
192 | 194 | } else { |
193 | | - $sql = "SELECT cur_id,cur_namespace,cur_title," . |
194 | | - "cur_text FROM $cur,$searchindex " . |
195 | | - "WHERE cur_id=si_page AND {$this->mTitlecond} " . |
196 | | - "{$searchnamespaces} {$redircond}" . |
197 | | - "LIMIT {$offset}, {$limit}"; |
198 | | - $res1 = $dbr->query( $sql, $fname ); |
199 | | - $num = $dbr->numRows($res1); |
200 | | - |
201 | | - $sk = $wgUser->getSkin(); |
202 | | - $text = ""; |
203 | | - |
204 | | - $this->parseQuery( $dbr ); |
205 | | - if ( "" == $this->mTitlecond || "" == $this->mTextcond ) { |
| 195 | + if( $this->parseQuery() == MW_SEARCH_BAD_QUERY ) { |
206 | 196 | $wgOut->addHTML( "<h2>" . wfMsg( "badquery" ) . "</h2>\n" . |
207 | 197 | "<p>" . wfMsg( "badquerytext" ) . "</p>\n" ); |
208 | 198 | return; |
209 | 199 | } |
| 200 | + |
210 | 201 | list( $limit, $offset ) = wfCheckLimits( 20, "searchlimit" ); |
211 | | - |
212 | | - $searchnamespaces = $this->queryNamespaces(); |
213 | | - $redircond = $this->searchRedirects(); |
214 | | - |
215 | | - $sql = "SELECT cur_id,cur_namespace,cur_title," . |
216 | | - "cur_text FROM $cur,$searchindex " . |
217 | | - "WHERE cur_id=si_page AND {$this->mTitlecond} " . |
218 | | - "{$searchnamespaces} {$redircond}" . |
219 | | - "LIMIT {$offset}, {$limit}"; |
220 | | - $res1 = $dbr->query( $sql, $fname ); |
221 | | - $num = $dbr->numRows($res1); |
222 | | - |
223 | | - $sql = "SELECT cur_id,cur_namespace,cur_title," . |
224 | | - "cur_text FROM $cur,$searchindex " . |
225 | | - "WHERE cur_id=si_page AND {$this->mTextcond} " . |
226 | | - "{$searchnamespaces} {$redircond} " . |
227 | | - "LIMIT {$offset}, {$limit}"; |
228 | | - $res2 = $dbr->query( $sql, $fname ); |
229 | | - $num = $num + $dbr->numRows($res2); |
| 202 | + $titleMatches = $this->getMatches( $this->mTitlecond, $limit, $offset ); |
| 203 | + $textMatches = $this->getMatches( $this->mTextcond, $limit, $offset ); |
230 | 204 | |
231 | | - if ( $num == $limit ) { |
232 | | - $top = wfShowingResults( $offset, $limit); |
| 205 | + $sk = $wgUser->getSkin(); |
| 206 | + |
| 207 | + $num = count( $titleMatches ) + count( $textMatches ); |
| 208 | + if ( $num >= $limit ) { |
| 209 | + $top = wfShowingResults( $offset, $limit ); |
233 | 210 | } else { |
234 | | - $top = wfShowingResultsNum( $offset, $limit, $num ); |
| 211 | + $top = wfShowingResultsNum( $offset, $limit, $num ); |
235 | 212 | } |
236 | 213 | $wgOut->addHTML( "<p>{$top}</p>\n" ); |
237 | 214 | |
238 | 215 | # For powersearch |
239 | 216 | |
240 | | - $a2l = "" ; |
241 | | - $akk = array_keys( $this->addtoquery ) ; |
| 217 | + $a2l = ""; |
| 218 | + $akk = array_keys( $this->addtoquery ); |
242 | 219 | foreach ( $akk AS $ak ) { |
243 | 220 | $a2l .= "&{$ak}={$this->addtoquery[$ak]}" ; |
244 | 221 | } |
— | — | @@ -246,38 +223,9 @@ |
247 | 224 | "search=" . wfUrlencode( $this->mUsertext ) . $a2l ); |
248 | 225 | $wgOut->addHTML( "<br />{$sl}\n" ); |
249 | 226 | |
250 | | - $foundsome = false; |
251 | | - |
252 | | - if ( 0 == $dbr->numRows( $res1 ) ) { |
253 | | - $wgOut->addHTML( "<h2>" . wfMsg( "notitlematches" ) . |
254 | | - "</h2>\n" ); |
255 | | - } else { |
256 | | - $foundsome = true; |
257 | | - $off = $offset + 1; |
258 | | - $wgOut->addHTML( "<h2>" . wfMsg( "titlematches" ) . |
259 | | - "</h2>\n<ol start='{$off}'>" ); |
260 | | - |
261 | | - while ( $row = $dbr->fetchObject( $res1 ) ) { |
262 | | - $this->showHit( $row ); |
263 | | - } |
264 | | - $dbr->freeResult( $res1 ); |
265 | | - $wgOut->addHTML( "</ol>\n" ); |
266 | | - } |
267 | | - |
268 | | - if ( 0 == $dbr->numRows( $res2 ) ) { |
269 | | - $wgOut->addHTML( "<h2>" . wfMsg( "notextmatches" ) . |
270 | | - "</h2>\n" ); |
271 | | - } else { |
272 | | - $foundsome = true; |
273 | | - $off = $offset + 1; |
274 | | - $wgOut->addHTML( "<h2>" . wfMsg( "textmatches" ) . "</h2>\n" . |
275 | | - "<ol start='{$off}'>" ); |
276 | | - while ( $row = $dbr->fetchObject( $res2 ) ) { |
277 | | - $this->showHit( $row ); |
278 | | - } |
279 | | - $dbr->freeResult( $res2 ); |
280 | | - $wgOut->addHTML( "</ol>\n" ); |
281 | | - } |
| 227 | + $foundsome = $this->showMatches( $titleMatches, $offset, "notitlematches", "titlematches" ) |
| 228 | + || $this->showMatches( $textMatches, $offset, "notextmatches", "textmatches" ); |
| 229 | + |
282 | 230 | if ( ! $foundsome ) { |
283 | 231 | $wgOut->addHTML( "<p>" . wfMsg( "nonefound" ) . "</p>\n" ); |
284 | 232 | } |
— | — | @@ -292,13 +240,13 @@ |
293 | 241 | return $lc; |
294 | 242 | } |
295 | 243 | |
296 | | - function parseQuery( &$db ) |
| 244 | + function parseQuery() |
297 | 245 | { |
298 | 246 | global $wgDBminWordLen, $wgLang, $wgDBmysql4; |
299 | 247 | |
300 | 248 | if( $wgDBmysql4 ) { |
301 | 249 | # Use cleaner boolean search if available |
302 | | - return $this->parseQuery4( $db ); |
| 250 | + return $this->parseQuery4( $this->db ); |
303 | 251 | } |
304 | 252 | # on non mysql4 database: get list of words we don't want to search for |
305 | 253 | require_once( "FulltextStoplist.php" ); |
— | — | @@ -322,21 +270,25 @@ |
323 | 271 | } else { |
324 | 272 | if ( "" != $last ) { $cond .= " AND"; } |
325 | 273 | $cond .= " (MATCH (##field##) AGAINST ('" . |
326 | | - $db->strencode( $word ). "'))"; |
| 274 | + $this->db->strencode( $word ). "'))"; |
327 | 275 | $last = $word; |
328 | 276 | array_push( $this->mSearchterms, "\\b" . $word . "\\b" ); |
329 | 277 | } |
330 | 278 | } |
331 | | - if ( 0 == count( $this->mSearchterms ) ) { return; } |
| 279 | + if ( 0 == count( $this->mSearchterms ) ) { |
| 280 | + return MW_SEARCH_BAD_QUERY; |
| 281 | + } |
332 | 282 | |
333 | 283 | $this->mTitlecond = "(" . str_replace( "##field##", |
334 | 284 | "si_title", $cond ) . " )"; |
335 | 285 | |
336 | 286 | $this->mTextcond = "(" . str_replace( "##field##", |
337 | 287 | "si_text", $cond ) . " AND (cur_is_redirect=0) )"; |
| 288 | + |
| 289 | + return MW_SEARCH_OK; |
338 | 290 | } |
339 | 291 | |
340 | | - function parseQuery4( &$db ) |
| 292 | + function parseQuery4() |
341 | 293 | { |
342 | 294 | global $wgLang; |
343 | 295 | $lc = SearchEngine::legalSearchChars(); |
— | — | @@ -366,11 +318,53 @@ |
367 | 319 | wfDebug( "Can't understand search query '$this->mUsertext'\n" ); |
368 | 320 | } |
369 | 321 | |
370 | | - $searchon = $db->strencode( $searchon ); |
| 322 | + $searchon = $this->db->strencode( $searchon ); |
371 | 323 | $this->mTitlecond = " MATCH(si_title) AGAINST('$searchon' IN BOOLEAN MODE)"; |
372 | 324 | $this->mTextcond = " (MATCH(si_text) AGAINST('$searchon' IN BOOLEAN MODE) AND cur_is_redirect=0)"; |
| 325 | + return MW_SEARCH_OK; |
373 | 326 | } |
374 | 327 | |
| 328 | + function &getMatches( $cond, $limit, $offset ) { |
| 329 | + $searchindex = $this->db->tableName( 'searchindex' ); |
| 330 | + $cur = $this->db->tableName( 'cur' ); |
| 331 | + $searchnamespaces = $this->queryNamespaces(); |
| 332 | + $redircond = $this->searchRedirects(); |
| 333 | + |
| 334 | + $sql = "SELECT cur_id,cur_namespace,cur_title," . |
| 335 | + "cur_text FROM $cur,$searchindex " . |
| 336 | + "WHERE cur_id=si_page AND {$cond} " . |
| 337 | + "{$searchnamespaces} {$redircond} " . |
| 338 | + "LIMIT {$offset}, {$limit}"; |
| 339 | + |
| 340 | + $res = $this->db->query( $sql, "SearchEngine::getMatches" ); |
| 341 | + $matches = array(); |
| 342 | + while ( $row = $this->db->fetchObject( $res ) ) { |
| 343 | + $matches[] = $row; |
| 344 | + } |
| 345 | + $this->db->freeResult( $res ); |
| 346 | + |
| 347 | + return $matches; |
| 348 | + } |
| 349 | + |
| 350 | + function showMatches( &$matches, $offset, $msgEmpty, $msgFound ) { |
| 351 | + global $wgOut; |
| 352 | + if ( 0 == count( $matches ) ) { |
| 353 | + $wgOut->addHTML( "<h2>" . wfMsg( $msgEmpty ) . |
| 354 | + "</h2>\n" ); |
| 355 | + return false; |
| 356 | + } else { |
| 357 | + $off = $offset + 1; |
| 358 | + $wgOut->addHTML( "<h2>" . wfMsg( $msgFound ) . |
| 359 | + "</h2>\n<ol start='{$off}'>" ); |
| 360 | + |
| 361 | + foreach( $matches as $row ) { |
| 362 | + $this->showHit( $row ); |
| 363 | + } |
| 364 | + $wgOut->addHTML( "</ol>\n" ); |
| 365 | + return true; |
| 366 | + } |
| 367 | + } |
| 368 | + |
375 | 369 | function showHit( $row ) |
376 | 370 | { |
377 | 371 | global $wgUser, $wgOut, $wgLang; |
— | — | @@ -423,11 +417,11 @@ |
424 | 418 | |
425 | 419 | function goResult() |
426 | 420 | { |
427 | | - global $wgOut, $wgRequest, $wgGoToEdit; |
| 421 | + global $wgOut, $wgGoToEdit; |
428 | 422 | global $wgDisableTextSearch; |
429 | 423 | $fname = "SearchEngine::goResult"; |
430 | 424 | |
431 | | - $search = trim( $wgRequest->getText( "search" ) ); |
| 425 | + $search = trim( $this->mRawtext ); |
432 | 426 | |
433 | 427 | # Try to go to page as entered. |
434 | 428 | # |
— | — | @@ -588,10 +582,9 @@ |
589 | 583 | |
590 | 584 | $wgMemc->set( $mkeyts, time() ); |
591 | 585 | |
592 | | - $dbr =& wfGetDB( DB_SLAVE ); |
593 | | - $res = $dbr->select( 'cur', array( 'cur_title', 'cur_namespace' ), false, $fname ); |
| 586 | + $res = $this->db->select( 'cur', array( 'cur_title', 'cur_namespace' ), false, $fname ); |
594 | 587 | $titles = array(); // length, ns, [titles] |
595 | | - while( $obj = $dbr->fetchObject( $res ) ){ |
| 588 | + while( $obj = $this->db->fetchObject( $res ) ){ |
596 | 589 | $title = $obj->cur_title; |
597 | 590 | $ns = $obj->cur_namespace; |
598 | 591 | $len = strlen( $title ); |