98 * @return array |
98 * @return array |
99 */ |
99 */ |
100 |
100 |
101 function perform_search($query, &$warnings, $case_sensitive = false, &$word_list) |
101 function perform_search($query, &$warnings, $case_sensitive = false, &$word_list) |
102 { |
102 { |
103 global $db, $session, $paths, $template, $plugins; // Common objects |
103 global $db, $session, $paths, $template, $plugins; // Common objects |
104 global $lang; |
104 global $lang; |
105 |
105 |
106 $warnings = array(); |
106 $warnings = array(); |
107 |
107 |
108 // |
108 // |
109 // STAGE 0: PARSE SEARCH QUERY |
109 // STAGE 0: PARSE SEARCH QUERY |
110 // Identify all terms of the query. Separate between what is required and what is not, and what should be sent through the index as |
110 // Identify all terms of the query. Separate between what is required and what is not, and what should be sent through the index as |
111 // opposed to straight-out LIKE-selected. |
111 // opposed to straight-out LIKE-selected. |
112 // |
112 // |
113 |
113 |
114 $query = parse_search_query($query, $warnings); |
114 $query = parse_search_query($query, $warnings); |
115 |
115 |
116 // Segregate search terms containing spaces |
116 // Segregate search terms containing spaces |
117 $query_phrase = array( |
117 $query_phrase = array( |
118 'any' => array(), |
118 'any' => array(), |
119 'req' => array() |
119 'req' => array() |
120 ); |
120 ); |
121 |
121 |
122 foreach ( $query['any'] as $i => $_ ) |
122 foreach ( $query['any'] as $i => $_ ) |
123 { |
123 { |
124 $term =& $query['any'][$i]; |
124 $term =& $query['any'][$i]; |
125 $term = trim($term); |
125 $term = trim($term); |
126 // the indexer only indexes words a-z with apostrophes |
126 // the indexer only indexes words a-z with apostrophes |
127 if ( preg_match('/[^A-Za-z\']/', $term) ) |
127 if ( preg_match('/[^A-Za-z\']/', $term) ) |
128 { |
128 { |
129 $query_phrase['any'][] = $term; |
129 $query_phrase['any'][] = $term; |
130 unset($term, $query['any'][$i]); |
130 unset($term, $query['any'][$i]); |
131 } |
131 } |
132 } |
132 } |
133 unset($term); |
133 unset($term); |
134 $query['any'] = array_values($query['any']); |
134 $query['any'] = array_values($query['any']); |
135 |
135 |
136 foreach ( $query['req'] as $i => $_ ) |
136 foreach ( $query['req'] as $i => $_ ) |
137 { |
137 { |
138 $term =& $query['req'][$i]; |
138 $term =& $query['req'][$i]; |
139 $term = trim($term); |
139 $term = trim($term); |
140 if ( preg_match('/[^A-Za-z\']/', $term) ) |
140 if ( preg_match('/[^A-Za-z\']/', $term) ) |
141 { |
141 { |
142 $query_phrase['req'][] = $term; |
142 $query_phrase['req'][] = $term; |
143 unset($term, $query['req'][$i]); |
143 unset($term, $query['req'][$i]); |
144 } |
144 } |
145 } |
145 } |
146 unset($term); |
146 unset($term); |
147 $query['req'] = array_values($query['req']); |
147 $query['req'] = array_values($query['req']); |
148 |
148 |
149 $results = array(); |
149 $results = array(); |
150 $scores = array(); |
150 $scores = array(); |
151 $ns_list = '(' . implode('|', array_keys($paths->nslist)) . ')'; |
151 $ns_list = '(' . implode('|', array_keys($paths->nslist)) . ')'; |
152 |
152 |
153 // FIXME: Update to use FULLTEXT algo when available. |
153 // FIXME: Update to use FULLTEXT algo when available. |
154 |
154 |
155 // Build an SQL query to load from the index table |
155 // Build an SQL query to load from the index table |
156 if ( count($query['any']) < 1 && count($query['req']) < 1 && count($query_phrase['any']) < 1 && count($query_phrase['req']) < 1 ) |
156 if ( count($query['any']) < 1 && count($query['req']) < 1 && count($query_phrase['any']) < 1 && count($query_phrase['req']) < 1 ) |
157 { |
157 { |
158 // This is both because of technical restrictions and devastation that would occur on shared servers/large sites. |
158 // This is both because of technical restrictions and devastation that would occur on shared servers/large sites. |
159 $warnings[] = $lang->get('search_err_query_no_positive'); |
159 $warnings[] = $lang->get('search_err_query_no_positive'); |
160 return array(); |
160 return array(); |
161 } |
161 } |
162 |
162 |
163 // |
163 // |
164 // STAGE 1 |
164 // STAGE 1 |
165 // Get all possible result pages from the search index. Tally which pages have the most words, and later sort them by boolean relevance |
165 // Get all possible result pages from the search index. Tally which pages have the most words, and later sort them by boolean relevance |
166 // |
166 // |
167 |
167 |
168 // Skip this if no indexable words are included |
168 // Skip this if no indexable words are included |
169 |
169 |
170 if ( count($query['any']) > 0 || count($query['req']) > 0 ) |
170 if ( count($query['any']) > 0 || count($query['req']) > 0 ) |
171 { |
171 { |
172 $where_any = array(); |
172 $where_any = array(); |
173 foreach ( $query['any'] as $term ) |
173 foreach ( $query['any'] as $term ) |
174 { |
174 { |
175 $term = escape_string_like($term); |
175 $term = escape_string_like($term); |
176 if ( !$case_sensitive ) |
176 if ( !$case_sensitive ) |
177 $term = strtolower($term); |
177 $term = strtolower($term); |
178 $where_any[] = $term; |
178 $where_any[] = $term; |
179 } |
179 } |
180 foreach ( $query['req'] as $term ) |
180 foreach ( $query['req'] as $term ) |
181 { |
181 { |
182 $term = escape_string_like($term); |
182 $term = escape_string_like($term); |
183 if ( !$case_sensitive ) |
183 if ( !$case_sensitive ) |
184 $term = strtolower($term); |
184 $term = strtolower($term); |
185 $where_any[] = $term; |
185 $where_any[] = $term; |
186 } |
186 } |
187 |
187 |
188 $col_word = ( $case_sensitive ) ? 'word' : 'word_lcase'; |
188 $col_word = ( $case_sensitive ) ? 'word' : 'word_lcase'; |
189 $where_any_str = ( count($where_any) > 0 ) ? '( ' . $col_word . ' LIKE \'%' . implode('%\' OR ' . $col_word . ' LIKE \'%', $where_any) . '%\' )' : ''; |
189 $where_any_str = ( count($where_any) > 0 ) ? '( ' . $col_word . ' LIKE \'%' . implode('%\' OR ' . $col_word . ' LIKE \'%', $where_any) . '%\' )' : ''; |
190 |
190 |
191 // generate query |
191 // generate query |
192 $sql = "SELECT word, page_names FROM " . table_prefix . "search_index WHERE {$where_any_str}"; |
192 $sql = "SELECT word, page_names FROM " . table_prefix . "search_index WHERE {$where_any_str}"; |
193 if ( !($q = $db->sql_query($sql)) ) |
193 if ( !($q = $db->sql_query($sql)) ) |
194 $db->_die('Error is in perform_search(), includes/search.php, query 1'); |
194 $db->_die('Error is in perform_search(), includes/search.php, query 1'); |
195 |
195 |
196 $word_tracking = array(); |
196 $word_tracking = array(); |
197 if ( $row = $db->fetchrow($q) ) |
197 if ( $row = $db->fetchrow($q) ) |
198 { |
198 { |
199 do |
199 do |
200 { |
200 { |
201 // get page list |
201 // get page list |
202 $pages =& $row['page_names']; |
202 $pages =& $row['page_names']; |
203 |
203 |
204 // Find page IDs that contain commas |
204 // Find page IDs that contain commas |
205 // This should never happen because commas are escaped by sanitize_page_id(). Nevertheless for compatibility with older |
205 // This should never happen because commas are escaped by sanitize_page_id(). Nevertheless for compatibility with older |
206 // databases, and to alleviate the concerns of hackers, we'll accommodate for page IDs with commas here by checking for |
206 // databases, and to alleviate the concerns of hackers, we'll accommodate for page IDs with commas here by checking for |
207 // IDs that don't match the pattern for stringified page ID + namespace. If it doesn't match, that means it's a continuation |
207 // IDs that don't match the pattern for stringified page ID + namespace. If it doesn't match, that means it's a continuation |
208 // of the previous ID and should be concatenated to the previous entry. |
208 // of the previous ID and should be concatenated to the previous entry. |
209 $matches = strpos($pages, ',') ? explode(',', $pages) : array($pages); |
209 $matches = strpos($pages, ',') ? explode(',', $pages) : array($pages); |
210 $prev = false; |
210 $prev = false; |
211 foreach ( $matches as $i => $_ ) |
211 foreach ( $matches as $i => $_ ) |
212 { |
212 { |
213 $match =& $matches[$i]; |
213 $match =& $matches[$i]; |
214 if ( !preg_match("/^ns=$ns_list;pid=(.+)$/", $match) && $prev ) |
214 if ( !preg_match("/^ns=$ns_list;pid=(.+)$/", $match) && $prev ) |
215 { |
215 { |
216 $matches[$prev] .= ',' . $match; |
216 $matches[$prev] .= ',' . $match; |
217 unset($match, $matches[$i]); |
217 unset($match, $matches[$i]); |
218 continue; |
218 continue; |
219 } |
219 } |
220 $prev = $i; |
220 $prev = $i; |
221 } |
221 } |
222 unset($match); |
222 unset($match); |
223 |
223 |
224 // Iterate through each of the results, assigning scores based on how many times the page has shown up. |
224 // Iterate through each of the results, assigning scores based on how many times the page has shown up. |
225 // This works because this phase of the search is strongly word-based not page-based. If a page shows up |
225 // This works because this phase of the search is strongly word-based not page-based. If a page shows up |
226 // multiple times while fetching the result rows from the search_index table, it simply means that page |
226 // multiple times while fetching the result rows from the search_index table, it simply means that page |
227 // contains more than one of the terms the user searched for. |
227 // contains more than one of the terms the user searched for. |
228 |
228 |
229 foreach ( $matches as $match ) |
229 foreach ( $matches as $match ) |
230 { |
230 { |
231 $word_cs = (( $case_sensitive ) ? $row['word'] : strtolower($row['word'])); |
231 $word_cs = (( $case_sensitive ) ? $row['word'] : strtolower($row['word'])); |
232 if ( isset($word_tracking[$match]) && in_array($word_cs, $word_tracking[$match]) ) |
232 if ( isset($word_tracking[$match]) && in_array($word_cs, $word_tracking[$match]) ) |
233 { |
233 { |
234 continue; |
234 continue; |
235 } |
235 } |
236 if ( isset($word_tracking[$match]) ) |
236 if ( isset($word_tracking[$match]) ) |
237 { |
237 { |
238 if ( isset($word_tracking[$match]) ) |
238 if ( isset($word_tracking[$match]) ) |
239 { |
239 { |
240 $word_tracking[$match][] = $word_cs; |
240 $word_tracking[$match][] = $word_cs; |
241 } |
241 } |
242 } |
242 } |
243 else |
243 else |
244 { |
244 { |
245 $word_tracking[$match] = array($word_cs); |
245 $word_tracking[$match] = array($word_cs); |
246 } |
246 } |
247 |
247 |
248 // echo '<pre>' . print_r($word_tracking, true) . '</pre>'; |
248 // echo '<pre>' . print_r($word_tracking, true) . '</pre>'; |
249 |
249 |
250 $inc = 1; |
250 $inc = 1; |
251 |
251 |
252 // Is this search term present in the page's title? If so, give extra points |
252 // Is this search term present in the page's title? If so, give extra points |
253 preg_match("/^ns=$ns_list;pid=(.+)$/", $match, $piecesparts); |
253 preg_match("/^ns=$ns_list;pid=(.+)$/", $match, $piecesparts); |
254 $title = get_page_title_ns($piecesparts[2], $piecesparts[1]); |
254 $title = get_page_title_ns($piecesparts[2], $piecesparts[1]); |
255 |
255 |
256 $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; |
256 $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; |
257 if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) ) |
257 if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) ) |
258 { |
258 { |
259 $inc = 1.5; |
259 $inc = 1.5; |
260 } |
260 } |
261 |
261 |
262 // increase points if 2 or more words match a phrase in the title |
262 // increase points if 2 or more words match a phrase in the title |
263 for ( $i = 0; $i < count($where_any) - 1; $i++ ) |
263 for ( $i = 0; $i < count($where_any) - 1; $i++ ) |
264 { |
264 { |
265 $phrase = "{$where_any[$i]} {$where_any[$i + 1]}"; |
265 $phrase = "{$where_any[$i]} {$where_any[$i + 1]}"; |
266 if ( $test_func($title, $phrase) ) |
266 if ( $test_func($title, $phrase) ) |
267 { |
267 { |
268 $inc *= 1.25; |
268 $inc *= 1.25; |
269 } |
269 } |
270 } |
270 } |
271 |
271 |
272 // Deduct points if there are few similarities between the words |
272 // Deduct points if there are few similarities between the words |
273 $lev_array = array(); |
273 $lev_array = array(); |
274 foreach ( $where_any as $qword ) |
274 foreach ( $where_any as $qword ) |
275 { |
275 { |
276 if ( strstr($word_cs, $qword) ) |
276 if ( strstr($word_cs, $qword) ) |
277 $lev_array[ $qword ] = levenshtein($qword, $word_cs); |
277 $lev_array[ $qword ] = levenshtein($qword, $word_cs); |
278 } |
278 } |
279 if ( min($lev_array) > 3 ) |
279 if ( min($lev_array) > 3 ) |
280 { |
280 { |
281 $inc /= array_sum($lev_array) / count($lev_array); |
281 $inc /= array_sum($lev_array) / count($lev_array); |
282 } |
282 } |
283 |
283 |
284 if ( isset($scores[$match]) ) |
284 if ( isset($scores[$match]) ) |
285 { |
285 { |
286 $scores[$match] = $scores[$match] + $inc; |
286 $scores[$match] = $scores[$match] + $inc; |
287 } |
287 } |
288 else |
288 else |
289 { |
289 { |
290 $scores[$match] = $inc; |
290 $scores[$match] = $inc; |
291 } |
291 } |
292 } |
292 } |
293 } |
293 } |
294 while ( $row = $db->fetchrow($q) ); |
294 while ( $row = $db->fetchrow($q) ); |
295 } |
295 } |
296 $db->free_result($q); |
296 $db->free_result($q); |
297 |
297 |
298 // |
298 // |
299 // STAGE 2: FIRST ELIMINATION ROUND |
299 // STAGE 2: FIRST ELIMINATION ROUND |
300 // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it |
300 // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it |
301 // |
301 // |
302 |
302 |
303 foreach ( $query['req'] as $term ) |
303 foreach ( $query['req'] as $term ) |
304 { |
304 { |
305 foreach ( $word_tracking as $i => $page ) |
305 foreach ( $word_tracking as $i => $page ) |
306 { |
306 { |
307 if ( !in_array($term, $page) ) |
307 if ( !in_array($term, $page) ) |
308 { |
308 { |
309 unset($word_tracking[$i], $scores[$i]); |
309 unset($word_tracking[$i], $scores[$i]); |
310 } |
310 } |
311 } |
311 } |
312 } |
312 } |
313 } |
313 } |
314 |
314 |
315 // |
315 // |
316 // STAGE 3: PHRASE SEARCHING |
316 // STAGE 3: PHRASE SEARCHING |
317 // Use LIKE to find pages with specified phrases. We can do a super-picky single query without another elimination round because |
317 // Use LIKE to find pages with specified phrases. We can do a super-picky single query without another elimination round because |
318 // at this stage we can search the full page_text column instead of relying on a word list. |
318 // at this stage we can search the full page_text column instead of relying on a word list. |
319 // |
319 // |
320 |
320 |
321 // We can skip this stage if none of these special terms apply |
321 // We can skip this stage if none of these special terms apply |
322 |
322 |
323 $text_col = ( $case_sensitive ) ? 'page_text' : ENANO_SQLFUNC_LOWERCASE . '(page_text)'; |
323 $text_col = ( $case_sensitive ) ? 'page_text' : ENANO_SQLFUNC_LOWERCASE . '(page_text)'; |
324 $name_col = ( $case_sensitive ) ? 'name' : ENANO_SQLFUNC_LOWERCASE . '(name)'; |
324 $name_col = ( $case_sensitive ) ? 'name' : ENANO_SQLFUNC_LOWERCASE . '(name)'; |
325 $text_col_join = ( $case_sensitive ) ? 't.page_text' : ENANO_SQLFUNC_LOWERCASE . '(t.page_text)'; |
325 $text_col_join = ( $case_sensitive ) ? 't.page_text' : ENANO_SQLFUNC_LOWERCASE . '(t.page_text)'; |
326 $name_col_join = ( $case_sensitive ) ? 'p.name' : ENANO_SQLFUNC_LOWERCASE . '(p.name)'; |
326 $name_col_join = ( $case_sensitive ) ? 'p.name' : ENANO_SQLFUNC_LOWERCASE . '(p.name)'; |
327 |
327 |
328 $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ? |
328 $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ? |
329 'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' : |
329 'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' : |
330 "'ns=' || t.namespace || ';pid=' || t.page_id"; |
330 "'ns=' || t.namespace || ';pid=' || t.page_id"; |
331 |
331 |
332 if ( count($query_phrase['any']) > 0 || count($query_phrase['req']) > 0 ) |
332 if ( count($query_phrase['any']) > 0 || count($query_phrase['req']) > 0 ) |
333 { |
333 { |
334 |
334 |
335 $where_any = array(); |
335 $where_any = array(); |
336 foreach ( $query_phrase['any'] as $term ) |
336 foreach ( $query_phrase['any'] as $term ) |
337 { |
337 { |
338 $term = escape_string_like($term); |
338 $term = escape_string_like($term); |
339 if ( !$case_sensitive ) |
339 if ( !$case_sensitive ) |
340 $term = strtolower($term); |
340 $term = strtolower($term); |
341 $where_any[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )"; |
341 $where_any[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )"; |
342 } |
342 } |
343 |
343 |
344 $where_any = ( count($where_any) > 0 ) ? implode(" OR\n ", $where_any) : ''; |
344 $where_any = ( count($where_any) > 0 ) ? implode(" OR\n ", $where_any) : ''; |
345 |
345 |
346 // Also do required terms, but use AND to ensure that all required terms are included |
346 // Also do required terms, but use AND to ensure that all required terms are included |
347 $where_req = array(); |
347 $where_req = array(); |
348 foreach ( $query_phrase['req'] as $term ) |
348 foreach ( $query_phrase['req'] as $term ) |
349 { |
349 { |
350 $term = escape_string_like($term); |
350 $term = escape_string_like($term); |
351 if ( !$case_sensitive ) |
351 if ( !$case_sensitive ) |
352 $term = strtolower($term); |
352 $term = strtolower($term); |
353 $where_req[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )"; |
353 $where_req[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )"; |
354 } |
354 } |
355 $and_clause = ( $where_any != '' ) ? 'AND ' : ''; |
355 $and_clause = ( $where_any != '' ) ? 'AND ' : ''; |
356 $where_req = ( count($where_req) > 0 ) ? "{$and_clause}" . implode(" AND\n ", $where_req) : ''; |
356 $where_req = ( count($where_req) > 0 ) ? "{$and_clause}" . implode(" AND\n ", $where_req) : ''; |
357 |
357 |
358 $sql = 'SELECT ' . $concat_column . ' AS id, p.name, t.page_text FROM ' . table_prefix . "page_text AS t\n" |
358 $sql = 'SELECT ' . $concat_column . ' AS id, p.name, t.page_text FROM ' . table_prefix . "page_text AS t\n" |
359 . " LEFT JOIN " . table_prefix . "pages AS p\n" |
359 . " LEFT JOIN " . table_prefix . "pages AS p\n" |
360 . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n" |
360 . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n" |
361 . " WHERE p.visible = 1 AND (\n $where_any\n $where_req\n );"; |
361 . " WHERE p.visible = 1 AND (\n $where_any\n $where_req\n );"; |
362 if ( !($q = $db->sql_query($sql)) ) |
362 if ( !($q = $db->sql_query($sql)) ) |
363 $db->_die('Error is in perform_search(), includes/search.php, query 2. Parsed query dump follows:<pre>(indexable) ' . htmlspecialchars(print_r($query, true)) . '(non-indexable) ' . htmlspecialchars(print_r($query_phrase, true)) . '</pre>'); |
363 $db->_die('Error is in perform_search(), includes/search.php, query 2. Parsed query dump follows:<pre>(indexable) ' . htmlspecialchars(print_r($query, true)) . '(non-indexable) ' . htmlspecialchars(print_r($query_phrase, true)) . '</pre>'); |
364 |
364 |
365 if ( $row = $db->fetchrow() ) |
365 if ( $row = $db->fetchrow() ) |
366 { |
366 { |
367 do |
367 do |
368 { |
368 { |
369 $id =& $row['id']; |
369 $id =& $row['id']; |
370 $inc = 0.0; |
370 $inc = 0.0; |
371 |
371 |
372 $title = $row['name']; |
372 $title = $row['name']; |
373 $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; |
373 $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; |
374 |
374 |
375 // Is this search term present in the page's title? If so, give extra points |
375 // Is this search term present in the page's title? If so, give extra points |
376 $word_list = array_merge($query_phrase['any'], $query_phrase['req']); |
376 $word_list = array_merge($query_phrase['any'], $query_phrase['req']); |
377 foreach ( $word_list as $word ) |
377 foreach ( $word_list as $word ) |
378 { |
378 { |
379 if ( $test_func($title, $word) ) |
379 if ( $test_func($title, $word) ) |
380 $inc += 1.5; |
380 $inc += 1.5; |
381 else if ( $test_func($row['page_text'], $word) ) |
381 else if ( $test_func($row['page_text'], $word) ) |
382 $inc += 1.0; |
382 $inc += 1.0; |
383 } |
383 } |
384 |
384 |
385 // increase points if 2 or more words match a phrase in the title |
385 // increase points if 2 or more words match a phrase in the title |
386 for ( $i = 0; $i < count($word_list) - 1; $i++ ) |
386 for ( $i = 0; $i < count($word_list) - 1; $i++ ) |
387 { |
387 { |
388 $phrase = "{$word_list[$i]} {$word_list[$i + 1]}"; |
388 $phrase = "{$word_list[$i]} {$word_list[$i + 1]}"; |
389 if ( $test_func($title, $phrase) ) |
389 if ( $test_func($title, $phrase) ) |
390 $inc *= 1.25; |
390 $inc *= 1.25; |
391 else if ( $test_func($row['page_text'], $phrase) ) |
391 else if ( $test_func($row['page_text'], $phrase) ) |
392 $inc *= 1.125; |
392 $inc *= 1.125; |
393 } |
393 } |
394 |
394 |
395 if ( isset($scores[$id]) ) |
395 if ( isset($scores[$id]) ) |
396 { |
396 { |
397 $scores[$id] = $scores[$id] + $inc; |
397 $scores[$id] = $scores[$id] + $inc; |
398 } |
398 } |
399 else |
399 else |
400 { |
400 { |
401 $scores[$id] = $inc; |
401 $scores[$id] = $inc; |
402 } |
402 } |
403 } |
403 } |
404 while ( $row = $db->fetchrow() ); |
404 while ( $row = $db->fetchrow() ); |
405 } |
405 } |
406 $db->free_result(); |
406 $db->free_result(); |
407 } |
407 } |
408 |
408 |
409 // |
409 // |
410 // STAGE 4 - SELECT PAGE TEXT AND ELIMINATE NOTS |
410 // STAGE 4 - SELECT PAGE TEXT AND ELIMINATE NOTS |
411 // At this point, we have a complete list of all the possible pages. Now we want to obtain the page text, and within the same query |
411 // At this point, we have a complete list of all the possible pages. Now we want to obtain the page text, and within the same query |
412 // eliminate any terms that shouldn't be in there. |
412 // eliminate any terms that shouldn't be in there. |
413 // |
413 // |
414 |
414 |
415 // Generate master word list for the highlighter |
415 // Generate master word list for the highlighter |
416 $word_list = array_values(array_merge($query['any'], $query['req'], $query_phrase['any'], $query_phrase['req'])); |
416 $word_list = array_values(array_merge($query['any'], $query['req'], $query_phrase['any'], $query_phrase['req'])); |
417 |
417 |
418 $text_where = array(); |
418 $text_where = array(); |
419 foreach ( $scores as $page_id => $_ ) |
419 foreach ( $scores as $page_id => $_ ) |
420 { |
420 { |
421 $text_where[] = $db->escape($page_id); |
421 $text_where[] = $db->escape($page_id); |
422 } |
422 } |
423 $text_where = '( ' . $concat_column . ' = \'' . implode('\' OR ' . $concat_column . ' = \'', $text_where) . '\' )'; |
423 $text_where = '( ' . $concat_column . ' = \'' . implode('\' OR ' . $concat_column . ' = \'', $text_where) . '\' )'; |
424 |
424 |
425 if ( count($query['not']) > 0 ) |
425 if ( count($query['not']) > 0 ) |
426 $text_where .= ' AND'; |
426 $text_where .= ' AND'; |
427 |
427 |
428 $where_not = array(); |
428 $where_not = array(); |
429 foreach ( $query['not'] as $term ) |
429 foreach ( $query['not'] as $term ) |
430 { |
430 { |
431 $term = escape_string_like($term); |
431 $term = escape_string_like($term); |
432 if ( !$case_sensitive ) |
432 if ( !$case_sensitive ) |
433 $term = strtolower($term); |
433 $term = strtolower($term); |
434 $where_not[] = $term; |
434 $where_not[] = $term; |
435 } |
435 } |
436 $where_not = ( count($where_not) > 0 ) ? "$text_col NOT LIKE '%" . implode("%' AND $text_col NOT LIKE '%", $where_not) . "%'" : ''; |
436 $where_not = ( count($where_not) > 0 ) ? "$text_col NOT LIKE '%" . implode("%' AND $text_col NOT LIKE '%", $where_not) . "%'" : ''; |
437 |
437 |
438 $sql = 'SELECT ' . $concat_column . ' AS id, t.page_id, t.namespace, CHAR_LENGTH(t.page_text) AS page_length, t.page_text, p.name AS page_name FROM ' . table_prefix . "page_text AS t |
438 $sql = 'SELECT ' . $concat_column . ' AS id, t.page_id, t.namespace, CHAR_LENGTH(t.page_text) AS page_length, t.page_text, p.name AS page_name FROM ' . table_prefix . "page_text AS t |
439 LEFT JOIN " . table_prefix . "pages AS p |
439 LEFT JOIN " . table_prefix . "pages AS p |
440 ON ( p.urlname = t.page_id AND p.namespace = t.namespace ) |
440 ON ( p.urlname = t.page_id AND p.namespace = t.namespace ) |
441 WHERE p.visible = 1 AND ( $text_where $where_not );"; |
441 WHERE p.visible = 1 AND ( $text_where $where_not );"; |
442 if ( !($q = $db->sql_unbuffered_query($sql)) ) |
442 if ( !($q = $db->sql_unbuffered_query($sql)) ) |
443 $db->_die('Error is in perform_search(), includes/search.php, query 3'); |
443 $db->_die('Error is in perform_search(), includes/search.php, query 3'); |
444 |
444 |
445 $page_data = array(); |
445 $page_data = array(); |
446 if ( $row = $db->fetchrow() ) |
446 if ( $row = $db->fetchrow() ) |
447 { |
447 { |
448 do |
448 do |
449 { |
449 { |
450 $row['page_text'] = htmlspecialchars($row['page_text']); |
450 $row['page_text'] = htmlspecialchars($row['page_text']); |
451 $row['page_name'] = htmlspecialchars($row['page_name']); |
451 $row['page_name'] = htmlspecialchars($row['page_name']); |
452 |
452 |
453 // Highlight results (this is wonderfully automated) |
453 // Highlight results (this is wonderfully automated) |
454 $row['page_text'] = highlight_and_clip_search_result($row['page_text'], $word_list, $case_sensitive); |
454 $row['page_text'] = highlight_and_clip_search_result($row['page_text'], $word_list, $case_sensitive); |
455 if ( strlen($row['page_text']) > 250 && !preg_match('/^\.\.\.(.+)\.\.\.$/', $row['page_text']) ) |
455 if ( strlen($row['page_text']) > 250 && !preg_match('/^\.\.\.(.+)\.\.\.$/', $row['page_text']) ) |
456 { |
456 { |
457 $row['page_text'] = substr($row['page_text'], 0, 150) . '...'; |
457 $row['page_text'] = substr($row['page_text'], 0, 150) . '...'; |
458 } |
458 } |
459 $row['page_name'] = highlight_search_result($row['page_name'], $word_list, $case_sensitive); |
459 $row['page_name'] = highlight_search_result($row['page_name'], $word_list, $case_sensitive); |
460 |
460 |
461 $page_data[$row['id']] = $row; |
461 $page_data[$row['id']] = $row; |
462 } |
462 } |
463 while ( $row = $db->fetchrow() ); |
463 while ( $row = $db->fetchrow() ); |
464 } |
464 } |
465 $db->free_result(); |
465 $db->free_result(); |
466 |
466 |
467 // |
467 // |
468 // STAGE 5 - SPECIAL PAGE TITLE SEARCH |
468 // STAGE 5 - SPECIAL PAGE TITLE SEARCH |
469 // Iterate through $paths->pages and check the titles for search terms. Score accordingly. |
469 // Iterate through $paths->pages and check the titles for search terms. Score accordingly. |
470 // |
470 // |
471 |
471 |
472 foreach ( $paths->pages as $id => $page ) |
472 foreach ( $paths->pages as $id => $page ) |
473 { |
473 { |
474 if ( $page['namespace'] != 'Special' || $page['visible'] == 0 ) |
474 if ( $page['namespace'] != 'Special' || $page['visible'] == 0 ) |
475 continue; |
475 continue; |
476 $idstring = 'ns=' . $page['namespace'] . ';pid=' . $page['urlname_nons']; |
476 $idstring = 'ns=' . $page['namespace'] . ';pid=' . $page['urlname_nons']; |
477 $any = array_values(array_unique(array_merge($query['any'], $query_phrase['any']))); |
477 $any = array_values(array_unique(array_merge($query['any'], $query_phrase['any']))); |
478 foreach ( $any as $term ) |
478 foreach ( $any as $term ) |
479 { |
479 { |
480 if ( $case_sensitive ) |
480 if ( $case_sensitive ) |
481 { |
481 { |
482 if ( strstr($page['name'], $term) || strstr($page['urlname_nons'], $term) ) |
482 if ( strstr($page['name'], $term) || strstr($page['urlname_nons'], $term) ) |
483 { |
483 { |
484 ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5; |
484 ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5; |
485 } |
485 } |
486 } |
486 } |
487 else |
487 else |
488 { |
488 { |
489 if ( stristr($page['name'], $term) || stristr($page['urlname_nons'], $term) ) |
489 if ( stristr($page['name'], $term) || stristr($page['urlname_nons'], $term) ) |
490 { |
490 { |
491 ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5; |
491 ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5; |
492 } |
492 } |
493 } |
493 } |
494 } |
494 } |
495 if ( isset($scores[$idstring]) ) |
495 if ( isset($scores[$idstring]) ) |
496 { |
496 { |
497 $page_data[$idstring] = array( |
497 $page_data[$idstring] = array( |
498 'page_name' => highlight_search_result($page['name'], $word_list, $case_sensitive), |
498 'page_name' => highlight_search_result($page['name'], $word_list, $case_sensitive), |
499 'page_text' => '', |
499 'page_text' => '', |
500 'page_id' => $page['urlname_nons'], |
500 'page_id' => $page['urlname_nons'], |
501 'namespace' => $page['namespace'], |
501 'namespace' => $page['namespace'], |
502 'score' => $scores[$idstring], |
502 'score' => $scores[$idstring], |
503 'page_length' => 1, |
503 'page_length' => 1, |
504 'page_note' => '[' . $lang->get('search_result_tag_special') . ']' |
504 'page_note' => '[' . $lang->get('search_result_tag_special') . ']' |
505 ); |
505 ); |
506 } |
506 } |
507 } |
507 } |
508 |
508 |
509 // |
509 // |
510 // STAGE 6 - SECOND ELIMINATION ROUND |
510 // STAGE 6 - SECOND ELIMINATION ROUND |
511 // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it |
511 // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it |
512 // |
512 // |
513 |
513 |
514 $required = array_merge($query['req'], $query_phrase['req']); |
514 $required = array_merge($query['req'], $query_phrase['req']); |
515 foreach ( $required as $term ) |
515 foreach ( $required as $term ) |
516 { |
516 { |
517 foreach ( $page_data as $id => $page ) |
517 foreach ( $page_data as $id => $page ) |
518 { |
518 { |
519 if ( ( $page['namespace'] == 'Special' || ( $page['namespace'] != 'Special' && !strstr($page['page_text'], $term) ) ) && !strstr($page['page_id'], $term) && !strstr($page['page_name'], $term) ) |
519 if ( ( $page['namespace'] == 'Special' || ( $page['namespace'] != 'Special' && !strstr($page['page_text'], $term) ) ) && !strstr($page['page_id'], $term) && !strstr($page['page_name'], $term) ) |
520 { |
520 { |
521 unset($page_data[$id]); |
521 unset($page_data[$id]); |
522 } |
522 } |
523 } |
523 } |
524 } |
524 } |
525 |
525 |
526 // At this point, all of our normal results are in. However, we can also allow plugins to hook into the system and score their own |
526 // At this point, all of our normal results are in. However, we can also allow plugins to hook into the system and score their own |
527 // pages and add text, etc. as necessary. |
527 // pages and add text, etc. as necessary. |
528 // Plugins are COMPLETELY responsible for using the search terms and handling Boolean logic properly |
528 // Plugins are COMPLETELY responsible for using the search terms and handling Boolean logic properly |
529 |
529 |
530 inject_custom_search_results($query, $query_phrase, $scores, $page_data, $case_sensitive, $word_list); |
530 inject_custom_search_results($query, $query_phrase, $scores, $page_data, $case_sensitive, $word_list); |
531 |
531 |
532 $code = $plugins->setHook('search_global_inner'); |
532 $code = $plugins->setHook('search_global_inner'); |
533 foreach ( $code as $cmd ) |
533 foreach ( $code as $cmd ) |
534 { |
534 { |
535 eval($cmd); |
535 eval($cmd); |
536 } |
536 } |
537 |
537 |
538 // a marvelous debugging aid :-) |
538 // a marvelous debugging aid :-) |
539 // die('<pre>' . htmlspecialchars(print_r($page_data, true)) . '</pre>'); |
539 // die('<pre>' . htmlspecialchars(print_r($page_data, true)) . '</pre>'); |
540 |
540 |
541 // |
541 // |
542 // STAGE 7 - HIGHLIGHT, TRIM, AND SCORE RESULTS |
542 // STAGE 7 - HIGHLIGHT, TRIM, AND SCORE RESULTS |
543 // We now have the complete results of the search. We need to trim text down to show only portions of the page containing search |
543 // We now have the complete results of the search. We need to trim text down to show only portions of the page containing search |
544 // terms, highlight any search terms within the page, and sort the final results array in descending order of score. |
544 // terms, highlight any search terms within the page, and sort the final results array in descending order of score. |
545 // |
545 // |
546 |
546 |
547 // Sort scores array |
547 // Sort scores array |
548 arsort($scores); |
548 arsort($scores); |
549 |
549 |
550 // Divisor for calculating relevance scores |
550 // Divisor for calculating relevance scores |
551 $divisor = ( count($query['any']) + count($query_phrase['any']) + count($query['req']) + count($query['not']) ) * 1.5; |
551 $divisor = ( count($query['any']) + count($query_phrase['any']) + count($query['req']) + count($query['not']) ) * 1.5; |
552 $divisor = max($divisor, max($scores)); |
552 $divisor = max($divisor, max($scores)); |
553 |
553 |
554 foreach ( $scores as $page_id => $score ) |
554 foreach ( $scores as $page_id => $score ) |
555 { |
555 { |
556 if ( !isset($page_data[$page_id]) ) |
556 if ( !isset($page_data[$page_id]) ) |
557 // It's possible that $scores contains a score for a page that was later eliminated because it contained a disallowed term |
557 // It's possible that $scores contains a score for a page that was later eliminated because it contained a disallowed term |
558 continue; |
558 continue; |
559 |
559 |
560 // Make a copy of the datum, then delete the original (it frees up a LOT of RAM) |
560 // Make a copy of the datum, then delete the original (it frees up a LOT of RAM) |
561 $datum = $page_data[$page_id]; |
561 $datum = $page_data[$page_id]; |
562 unset($page_data[$page_id]); |
562 unset($page_data[$page_id]); |
563 |
563 |
564 // This is an internal value used for sorting - it's no longer needed. |
564 // This is an internal value used for sorting - it's no longer needed. |
565 unset($datum['id']); |
565 unset($datum['id']); |
566 |
566 |
567 // Calculate score |
567 // Calculate score |
568 // if ( $score > $divisor ) |
568 // if ( $score > $divisor ) |
569 // $score = $divisor; |
569 // $score = $divisor; |
570 $datum['score'] = round($score / $divisor, 2) * 100; |
570 $datum['score'] = round($score / $divisor, 2) * 100; |
571 |
571 |
572 // Highlight the URL |
572 // Highlight the URL |
573 $datum['url_highlight'] = makeUrlComplete($datum['namespace'], $datum['page_id']); |
573 $datum['url_highlight'] = makeUrlComplete($datum['namespace'], $datum['page_id']); |
574 $datum['url_highlight'] = preg_replace('/\?.+$/', '', $datum['url_highlight']); |
574 $datum['url_highlight'] = preg_replace('/\?.+$/', '', $datum['url_highlight']); |
575 $datum['url_highlight'] = highlight_search_result($datum['url_highlight'], $word_list, $case_sensitive); |
575 $datum['url_highlight'] = highlight_search_result($datum['url_highlight'], $word_list, $case_sensitive); |
576 |
576 |
577 // Store it in our until-now-unused results array |
577 // Store it in our until-now-unused results array |
578 $results[] = $datum; |
578 $results[] = $datum; |
579 } |
579 } |
580 |
580 |
581 // Our work here is done. :-D |
581 // Our work here is done. :-D |
582 return $results; |
582 return $results; |
583 } |
583 } |
584 |
584 |
585 /** |
585 /** |
586 * Parses a search query into an associative array. The resultant array will be filled with the following values, each an array: |
586 * Parses a search query into an associative array. The resultant array will be filled with the following values, each an array: |
587 * any: Search terms that can optionally be present |
587 * any: Search terms that can optionally be present |
592 * @return array |
592 * @return array |
593 */ |
593 */ |
594 |
594 |
595 function parse_search_query($query, &$warnings) |
595 function parse_search_query($query, &$warnings) |
596 { |
596 { |
597 global $lang; |
597 global $lang; |
598 |
598 |
599 $stopwords = get_stopwords(); |
599 $stopwords = get_stopwords(); |
600 $ret = array( |
600 $ret = array( |
601 'any' => array(), |
601 'any' => array(), |
602 'req' => array(), |
602 'req' => array(), |
603 'not' => array() |
603 'not' => array() |
604 ); |
604 ); |
605 $warnings = array(); |
605 $warnings = array(); |
606 $terms = array(); |
606 $terms = array(); |
607 $in_quote = false; |
607 $in_quote = false; |
608 $start_term = 0; |
608 $start_term = 0; |
609 $just_finished = false; |
609 $just_finished = false; |
610 for ( $i = 0; $i < strlen($query); $i++ ) |
610 for ( $i = 0; $i < strlen($query); $i++ ) |
611 { |
611 { |
612 $chr = $query{$i}; |
612 $chr = $query{$i}; |
613 $prev = ( $i > 0 ) ? $query{ $i - 1 } : ''; |
613 $prev = ( $i > 0 ) ? $query{ $i - 1 } : ''; |
614 $next = ( ( $i + 1 ) < strlen($query) ) ? $query{ $i + 1 } : ''; |
614 $next = ( ( $i + 1 ) < strlen($query) ) ? $query{ $i + 1 } : ''; |
615 |
615 |
616 if ( ( $chr == ' ' && !$in_quote ) || ( $i + 1 == strlen ( $query ) ) ) |
616 if ( ( $chr == ' ' && !$in_quote ) || ( $i + 1 == strlen ( $query ) ) ) |
617 { |
617 { |
618 $len = ( $next == '' ) ? $i + 1 : $i - $start_term; |
618 $len = ( $next == '' ) ? $i + 1 : $i - $start_term; |
619 $word = substr ( $query, $start_term, $len ); |
619 $word = substr ( $query, $start_term, $len ); |
620 $terms[] = $word; |
620 $terms[] = $word; |
621 $start_term = $i + 1; |
621 $start_term = $i + 1; |
622 } |
622 } |
623 |
623 |
624 elseif ( $chr == '"' && $in_quote && $prev != '\\' ) |
624 elseif ( $chr == '"' && $in_quote && $prev != '\\' ) |
625 { |
625 { |
626 $word = substr ( $query, $start_term, $i - $start_term + 1 ); |
626 $word = substr ( $query, $start_term, $i - $start_term + 1 ); |
627 $start_pos = ( $next == ' ' ) ? $i + 2 : $i + 1; |
627 $start_pos = ( $next == ' ' ) ? $i + 2 : $i + 1; |
628 $in_quote = false; |
628 $in_quote = false; |
629 } |
629 } |
630 |
630 |
631 elseif ( $chr == '"' && !$in_quote ) |
631 elseif ( $chr == '"' && !$in_quote ) |
632 { |
632 { |
633 $in_quote = true; |
633 $in_quote = true; |
634 $start_pos = $i; |
634 $start_pos = $i; |
635 } |
635 } |
636 |
636 |
637 } |
637 } |
638 |
638 |
639 $ticker = 0; |
639 $ticker = 0; |
640 |
640 |
641 foreach ( $terms as $element => $__unused ) |
641 foreach ( $terms as $element => $__unused ) |
642 { |
642 { |
643 $atom =& $terms[$element]; |
643 $atom =& $terms[$element]; |
644 |
644 |
645 $ticker++; |
645 $ticker++; |
646 |
646 |
647 if ( $ticker == 20 ) |
647 if ( $ticker == 20 ) |
648 { |
648 { |
649 $warnings[] = $lang->get('search_err_query_too_many_terms'); |
649 $warnings[] = $lang->get('search_err_query_too_many_terms'); |
650 break; |
650 break; |
651 } |
651 } |
652 |
652 |
653 if ( substr ( $atom, 0, 2 ) == '+"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) |
653 if ( substr ( $atom, 0, 2 ) == '+"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) |
654 { |
654 { |
655 $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); |
655 $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); |
656 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
656 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
657 { |
657 { |
658 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
658 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
659 $ticker--; |
659 $ticker--; |
660 continue; |
660 continue; |
661 } |
661 } |
662 if(in_array($word, $ret['req'])) |
662 if(in_array($word, $ret['req'])) |
663 { |
663 { |
664 $warnings[] = $lang->get('search_err_query_dup_terms'); |
664 $warnings[] = $lang->get('search_err_query_dup_terms'); |
665 $ticker--; |
665 $ticker--; |
666 continue; |
666 continue; |
667 } |
667 } |
668 $ret['req'][] = $word; |
668 $ret['req'][] = $word; |
669 } |
669 } |
670 elseif ( substr ( $atom, 0, 2 ) == '-"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) |
670 elseif ( substr ( $atom, 0, 2 ) == '-"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) |
671 { |
671 { |
672 $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); |
672 $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); |
673 if ( strlen ( $word ) < 4 ) |
673 if ( strlen ( $word ) < 4 ) |
674 { |
674 { |
675 $warnings[] = $lang->get('search_err_query_term_too_short'); |
675 $warnings[] = $lang->get('search_err_query_term_too_short'); |
676 $ticker--; |
676 $ticker--; |
677 continue; |
677 continue; |
678 } |
678 } |
679 if(in_array($word, $ret['not'])) |
679 if(in_array($word, $ret['not'])) |
680 { |
680 { |
681 $warnings[] = $lang->get('search_err_query_dup_terms'); |
681 $warnings[] = $lang->get('search_err_query_dup_terms'); |
682 $ticker--; |
682 $ticker--; |
683 continue; |
683 continue; |
684 } |
684 } |
685 $ret['not'][] = $word; |
685 $ret['not'][] = $word; |
686 } |
686 } |
687 elseif ( substr ( $atom, 0, 1 ) == '+' ) |
687 elseif ( substr ( $atom, 0, 1 ) == '+' ) |
688 { |
688 { |
689 $word = substr ( $atom, 1 ); |
689 $word = substr ( $atom, 1 ); |
690 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
690 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
691 { |
691 { |
692 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
692 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
693 $ticker--; |
693 $ticker--; |
694 continue; |
694 continue; |
695 } |
695 } |
696 if(in_array($word, $ret['req'])) |
696 if(in_array($word, $ret['req'])) |
697 { |
697 { |
698 $warnings[] = $lang->get('search_err_query_dup_terms'); |
698 $warnings[] = $lang->get('search_err_query_dup_terms'); |
699 $ticker--; |
699 $ticker--; |
700 continue; |
700 continue; |
701 } |
701 } |
702 $ret['req'][] = $word; |
702 $ret['req'][] = $word; |
703 } |
703 } |
704 elseif ( substr ( $atom, 0, 1 ) == '-' ) |
704 elseif ( substr ( $atom, 0, 1 ) == '-' ) |
705 { |
705 { |
706 $word = substr ( $atom, 1 ); |
706 $word = substr ( $atom, 1 ); |
707 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
707 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
708 { |
708 { |
709 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
709 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
710 $ticker--; |
710 $ticker--; |
711 continue; |
711 continue; |
712 } |
712 } |
713 if(in_array($word, $ret['not'])) |
713 if(in_array($word, $ret['not'])) |
714 { |
714 { |
715 $warnings[] = $lang->get('search_err_query_dup_terms'); |
715 $warnings[] = $lang->get('search_err_query_dup_terms'); |
716 $ticker--; |
716 $ticker--; |
717 continue; |
717 continue; |
718 } |
718 } |
719 $ret['not'][] = $word; |
719 $ret['not'][] = $word; |
720 } |
720 } |
721 elseif ( substr ( $atom, 0, 1 ) == '"' && substr ( $atom, ( strlen($atom) - 1 ), 1 ) == '"' ) |
721 elseif ( substr ( $atom, 0, 1 ) == '"' && substr ( $atom, ( strlen($atom) - 1 ), 1 ) == '"' ) |
722 { |
722 { |
723 $word = substr ( $atom, 1, ( strlen ( $atom ) - 2 ) ); |
723 $word = substr ( $atom, 1, ( strlen ( $atom ) - 2 ) ); |
724 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
724 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
725 { |
725 { |
726 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
726 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
727 $ticker--; |
727 $ticker--; |
728 continue; |
728 continue; |
729 } |
729 } |
730 if(in_array($word, $ret['any'])) |
730 if(in_array($word, $ret['any'])) |
731 { |
731 { |
732 $warnings[] = $lang->get('search_err_query_dup_terms'); |
732 $warnings[] = $lang->get('search_err_query_dup_terms'); |
733 $ticker--; |
733 $ticker--; |
734 continue; |
734 continue; |
735 } |
735 } |
736 $ret['any'][] = $word; |
736 $ret['any'][] = $word; |
737 } |
737 } |
738 else |
738 else |
739 { |
739 { |
740 $word = $atom; |
740 $word = $atom; |
741 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
741 if ( strlen ( $word ) < 2 || in_array($word, $stopwords) ) |
742 { |
742 { |
743 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
743 $warnings[] = $lang->get('search_err_query_has_stopwords'); |
744 $ticker--; |
744 $ticker--; |
745 continue; |
745 continue; |
746 } |
746 } |
747 if(in_array($word, $ret['any'])) |
747 if(in_array($word, $ret['any'])) |
748 { |
748 { |
749 $warnings[] = $lang->get('search_err_query_dup_terms'); |
749 $warnings[] = $lang->get('search_err_query_dup_terms'); |
750 $ticker--; |
750 $ticker--; |
751 continue; |
751 continue; |
752 } |
752 } |
753 $ret['any'][] = $word; |
753 $ret['any'][] = $word; |
754 } |
754 } |
755 } |
755 } |
756 return $ret; |
756 return $ret; |
757 } |
757 } |
758 |
758 |
759 /** |
759 /** |
760 * Escapes a string for use in a LIKE clause. |
760 * Escapes a string for use in a LIKE clause. |
761 * @param string |
761 * @param string |
762 * @return string |
762 * @return string |
763 */ |
763 */ |
764 |
764 |
765 function escape_string_like($string) |
765 function escape_string_like($string) |
766 { |
766 { |
767 global $db, $session, $paths, $template, $plugins; // Common objects |
767 global $db, $session, $paths, $template, $plugins; // Common objects |
768 $string = $db->escape($string); |
768 $string = $db->escape($string); |
769 $string = str_replace(array('%', '_'), array('\%', '\_'), $string); |
769 $string = str_replace(array('%', '_'), array('\%', '\_'), $string); |
770 return $string; |
770 return $string; |
771 } |
771 } |
772 |
772 |
773 /** |
773 /** |
774 * Wraps <highlight></highlight> tags around all words in both the specified array. Does not perform any clipping. |
774 * Wraps <highlight></highlight> tags around all words in both the specified array. Does not perform any clipping. |
775 * @param string Text to process |
775 * @param string Text to process |
803 * @return string |
803 * @return string |
804 */ |
804 */ |
805 |
805 |
806 function highlight_and_clip_search_result($pt, $words, $case_sensitive = false) |
806 function highlight_and_clip_search_result($pt, $words, $case_sensitive = false) |
807 { |
807 { |
808 $cut_off = false; |
808 $cut_off = false; |
809 |
809 |
810 $space_chars = Array("\t", "\n", "\r", " "); |
810 $space_chars = Array("\t", "\n", "\r", " "); |
811 |
811 |
812 $pt = highlight_search_result($pt, $words, $case_sensitive); |
812 $pt = highlight_search_result($pt, $words, $case_sensitive); |
813 |
813 |
814 foreach ( $words as $word ) |
814 foreach ( $words as $word ) |
815 { |
815 { |
816 // Boldface searched words |
816 // Boldface searched words |
817 $ptlen = strlen($pt); |
817 $ptlen = strlen($pt); |
818 for ( $i = 0; $i < $ptlen; $i++ ) |
818 for ( $i = 0; $i < $ptlen; $i++ ) |
819 { |
819 { |
820 $len = strlen($word); |
820 $len = strlen($word); |
821 if ( strtolower(substr($pt, $i, $len)) == strtolower($word) ) |
821 if ( strtolower(substr($pt, $i, $len)) == strtolower($word) ) |
822 { |
822 { |
823 $chunk1 = substr($pt, 0, $i); |
823 $chunk1 = substr($pt, 0, $i); |
824 $chunk2 = substr($pt, $i, $len); |
824 $chunk2 = substr($pt, $i, $len); |
825 $chunk3 = substr($pt, ( $i + $len )); |
825 $chunk3 = substr($pt, ( $i + $len )); |
826 $pt = $chunk1 . $chunk2 . $chunk3; |
826 $pt = $chunk1 . $chunk2 . $chunk3; |
827 $ptlen = strlen($pt); |
827 $ptlen = strlen($pt); |
828 // Cut off text to 150 chars or so |
828 // Cut off text to 150 chars or so |
829 if ( !$cut_off ) |
829 if ( !$cut_off ) |
830 { |
830 { |
831 $cut_off = true; |
831 $cut_off = true; |
832 if ( $i - 75 > 0 ) |
832 if ( $i - 75 > 0 ) |
833 { |
833 { |
834 // Navigate backwards until a space character is found |
834 // Navigate backwards until a space character is found |
835 $chunk = substr($pt, 0, ( $i - 75 )); |
835 $chunk = substr($pt, 0, ( $i - 75 )); |
836 $final_chunk = $chunk; |
836 $final_chunk = $chunk; |
837 for ( $j = strlen($chunk) - 1; $j > 0; $j = $j - 1 ) |
837 for ( $j = strlen($chunk) - 1; $j > 0; $j = $j - 1 ) |
838 { |
838 { |
839 if ( in_array($chunk{$j}, $space_chars) ) |
839 if ( in_array($chunk{$j}, $space_chars) ) |
840 { |
840 { |
841 $final_chunk = substr($chunk, $j + 1); |
841 $final_chunk = substr($chunk, $j + 1); |
842 break; |
842 break; |
843 } |
843 } |
844 } |
844 } |
845 $mid_chunk = substr($pt, ( $i - 75 ), 75); |
845 $mid_chunk = substr($pt, ( $i - 75 ), 75); |
846 |
846 |
847 $clipped = '...' . $final_chunk . $mid_chunk . $chunk2; |
847 $clipped = '...' . $final_chunk . $mid_chunk . $chunk2; |
848 |
848 |
849 $chunk = substr($pt, ( $i + strlen($chunk2) + 75 )); |
849 $chunk = substr($pt, ( $i + strlen($chunk2) + 75 )); |
850 $final_chunk = $chunk; |
850 $final_chunk = $chunk; |
851 for ( $j = 0; $j < strlen($chunk); $j++ ) |
851 for ( $j = 0; $j < strlen($chunk); $j++ ) |
852 { |
852 { |
853 if ( in_array($chunk{$j}, $space_chars) ) |
853 if ( in_array($chunk{$j}, $space_chars) ) |
854 { |
854 { |
855 $final_chunk = substr($chunk, 0, $j); |
855 $final_chunk = substr($chunk, 0, $j); |
856 break; |
856 break; |
857 } |
857 } |
858 } |
858 } |
859 |
859 |
860 $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 ); |
860 $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 ); |
861 |
861 |
862 $clipped .= $end_chunk . $final_chunk . '...'; |
862 $clipped .= $end_chunk . $final_chunk . '...'; |
863 |
863 |
864 $pt = $clipped; |
864 $pt = $clipped; |
865 } |
865 } |
866 else if ( strlen($pt) > 200 ) |
866 else if ( strlen($pt) > 200 ) |
867 { |
867 { |
868 $mid_chunk = substr($pt, ( $i - 75 ), 75); |
868 $mid_chunk = substr($pt, ( $i - 75 ), 75); |
869 |
869 |
870 $clipped = $chunk1 . $chunk2; |
870 $clipped = $chunk1 . $chunk2; |
871 |
871 |
872 $chunk = substr($pt, ( $i + strlen($chunk2) + 75 )); |
872 $chunk = substr($pt, ( $i + strlen($chunk2) + 75 )); |
873 $final_chunk = $chunk; |
873 $final_chunk = $chunk; |
874 for ( $j = 0; $j < strlen($chunk); $j++ ) |
874 for ( $j = 0; $j < strlen($chunk); $j++ ) |
875 { |
875 { |
876 if ( in_array($chunk{$j}, $space_chars) ) |
876 if ( in_array($chunk{$j}, $space_chars) ) |
877 { |
877 { |
878 $final_chunk = substr($chunk, 0, $j); |
878 $final_chunk = substr($chunk, 0, $j); |
879 break; |
879 break; |
880 } |
880 } |
881 } |
881 } |
882 |
882 |
883 $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 ); |
883 $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 ); |
884 |
884 |
885 $clipped .= $end_chunk . $final_chunk . '...'; |
885 $clipped .= $end_chunk . $final_chunk . '...'; |
886 |
886 |
887 $pt = $clipped; |
887 $pt = $clipped; |
888 |
888 |
889 } |
889 } |
890 break 2; |
890 break 2; |
891 } |
891 } |
892 } |
892 } |
893 } |
893 } |
894 $cut_off = false; |
894 $cut_off = false; |
895 } |
895 } |
896 return $pt; |
896 return $pt; |
897 } |
897 } |
898 |
898 |
899 /** |
899 /** |
900 * Returns a list of words that shouldn't under most circumstances be indexed for searching. |
900 * Returns a list of words that shouldn't under most circumstances be indexed for searching. |
901 * @return array |
901 * @return array |
902 */ |
902 */ |
903 |
903 |
904 function get_stopwords() |
904 function get_stopwords() |
905 { |
905 { |
906 static $stopwords; |
906 static $stopwords; |
907 if ( is_array($stopwords) ) |
907 if ( is_array($stopwords) ) |
908 return $stopwords; |
908 return $stopwords; |
909 |
909 |
910 $stopwords = array('I', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'de', 'en', 'for', 'from', 'how', 'in', 'is', 'it', |
910 $stopwords = array('I', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'de', 'en', 'for', 'from', 'how', 'in', 'is', 'it', |
911 'la', 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'what', 'when', 'where', 'who', 'will', 'with', 'and', |
911 'la', 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'what', 'when', 'where', 'who', 'will', 'with', 'and', |
912 'the'); |
912 'the'); |
913 |
913 |
914 return $stopwords; |
914 return $stopwords; |
915 } |
915 } |
916 |
916 |
917 /** |
917 /** |
918 * Private function to inject custom results into a search. |
918 * Private function to inject custom results into a search. |
919 */ |
919 */ |
920 |
920 |
921 function inject_custom_search_results(&$query, &$query_phrase, &$scores, &$page_data, &$case_sensitive, &$word_list) |
921 function inject_custom_search_results(&$query, &$query_phrase, &$scores, &$page_data, &$case_sensitive, &$word_list) |
922 { |
922 { |
923 global $db, $session, $paths, $template, $plugins; // Common objects |
923 global $db, $session, $paths, $template, $plugins; // Common objects |
924 global $lang; |
924 global $lang; |
925 |
925 |
926 global $search_handlers; |
926 global $search_handlers; |
927 |
927 |
928 // global functions |
928 // global functions |
929 $terms = array( |
929 $terms = array( |
930 'any' => array_merge($query['any'], $query_phrase['any']), |
930 'any' => array_merge($query['any'], $query_phrase['any']), |
931 'req' => array_merge($query['req'], $query_phrase['req']), |
931 'req' => array_merge($query['req'], $query_phrase['req']), |
932 'not' => $query['not'] |
932 'not' => $query['not'] |
933 ); |
933 ); |
934 |
934 |
935 foreach ( $search_handlers as &$options ) |
935 foreach ( $search_handlers as &$options ) |
936 { |
936 { |
937 $where = array('any' => array(), 'req' => array(), 'not' => array()); |
937 $where = array('any' => array(), 'req' => array(), 'not' => array()); |
938 $where_any =& $where['any']; |
938 $where_any =& $where['any']; |
939 $where_req =& $where['req']; |
939 $where_req =& $where['req']; |
940 $where_not =& $where['not']; |
940 $where_not =& $where['not']; |
941 $title_col = ( $case_sensitive ) ? $options['titlecolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['titlecolumn'] . ')'; |
941 $title_col = ( $case_sensitive ) ? $options['titlecolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['titlecolumn'] . ')'; |
942 if ( isset($options['datacolumn']) ) |
942 if ( isset($options['datacolumn']) ) |
943 $desc_col = ( $case_sensitive ) ? $options['datacolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['datacolumn'] . ')'; |
943 $desc_col = ( $case_sensitive ) ? $options['datacolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['datacolumn'] . ')'; |
944 else |
944 else |
945 $desc_col = "''"; |
945 $desc_col = "''"; |
946 foreach ( $terms['any'] as $term ) |
946 foreach ( $terms['any'] as $term ) |
947 { |
947 { |
948 $term = escape_string_like($term); |
948 $term = escape_string_like($term); |
949 if ( !$case_sensitive ) |
949 if ( !$case_sensitive ) |
950 $term = strtolower($term); |
950 $term = strtolower($term); |
951 $where_any[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )"; |
951 $where_any[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )"; |
952 } |
952 } |
953 foreach ( $terms['req'] as $term ) |
953 foreach ( $terms['req'] as $term ) |
954 { |
954 { |
955 $term = escape_string_like($term); |
955 $term = escape_string_like($term); |
956 if ( !$case_sensitive ) |
956 if ( !$case_sensitive ) |
957 $term = strtolower($term); |
957 $term = strtolower($term); |
958 $where_req[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )"; |
958 $where_req[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )"; |
959 } |
959 } |
960 foreach ( $terms['not'] as $term ) |
960 foreach ( $terms['not'] as $term ) |
961 { |
961 { |
962 $term = escape_string_like($term); |
962 $term = escape_string_like($term); |
963 if ( !$case_sensitive ) |
963 if ( !$case_sensitive ) |
964 $term = strtolower($term); |
964 $term = strtolower($term); |
965 $where_not[] = "$title_col NOT LIKE '%{$term}%' AND $desc_col NOT LIKE '%{$term}%'"; |
965 $where_not[] = "$title_col NOT LIKE '%{$term}%' AND $desc_col NOT LIKE '%{$term}%'"; |
966 } |
966 } |
967 if ( empty($where_any) ) |
967 if ( empty($where_any) ) |
968 unset($where_any, $where['any']); |
968 unset($where_any, $where['any']); |
969 if ( empty($where_req) ) |
969 if ( empty($where_req) ) |
970 unset($where_req, $where['req']); |
970 unset($where_req, $where['req']); |
971 if ( empty($where_not) ) |
971 if ( empty($where_not) ) |
972 unset($where_not, $where['not']); |
972 unset($where_not, $where['not']); |
973 |
973 |
974 $where_any = '(' . implode(' OR ', $where_any) . '' . ( isset($where['req']) || isset($where['not']) ? ' OR 1 = 1' : '' ) . ')'; |
974 $where_any = '(' . implode(' OR ', $where_any) . '' . ( isset($where['req']) || isset($where['not']) ? ' OR 1 = 1' : '' ) . ')'; |
975 |
975 |
976 if ( isset($where_req) ) |
976 if ( isset($where_req) ) |
977 $where_req = implode(' AND ', $where_req); |
977 $where_req = implode(' AND ', $where_req); |
978 if ( isset($where_not) ) |
978 if ( isset($where_not) ) |
979 $where_not = implode( 'AND ', $where_not); |
979 $where_not = implode( 'AND ', $where_not); |
980 |
980 |
981 $where = implode(' AND ', $where); |
981 $where = implode(' AND ', $where); |
982 |
982 |
983 $columns = $options['titlecolumn']; |
983 $columns = $options['titlecolumn']; |
984 if ( isset($options['datacolumn']) ) |
984 if ( isset($options['datacolumn']) ) |
985 $columns .= ", {$options['datacolumn']}"; |
985 $columns .= ", {$options['datacolumn']}"; |
986 if ( isset($options['additionalcolumns']) ) |
986 if ( isset($options['additionalcolumns']) ) |
987 $columns .= ', ' . implode(', ', $options['additionalcolumns']); |
987 $columns .= ', ' . implode(', ', $options['additionalcolumns']); |
988 |
988 |
989 $additionalwhere = ( isset($options['additionalwhere']) ) ? $options['additionalwhere'] : ''; |
989 $additionalwhere = ( isset($options['additionalwhere']) ) ? $options['additionalwhere'] : ''; |
990 |
990 |
991 $sql = "SELECT $columns FROM " . table_prefix . "{$options['table']} WHERE ( $where ) $additionalwhere;"; |
991 $sql = "SELECT $columns FROM " . table_prefix . "{$options['table']} WHERE ( $where ) $additionalwhere;"; |
992 |
992 |
993 if ( !($q = $db->sql_unbuffered_query($sql)) ) |
993 if ( !($q = $db->sql_unbuffered_query($sql)) ) |
994 { |
994 { |
995 $db->_die('Automatically generated search query'); |
995 $db->_die('Automatically generated search query'); |
996 } |
996 } |
997 |
997 |
998 if ( $row = $db->fetchrow() ) |
998 if ( $row = $db->fetchrow() ) |
999 { |
999 { |
1000 do |
1000 do |
1001 { |
1001 { |
1002 $parser = $template->makeParserText($options['uniqueid']); |
1002 $parser = $template->makeParserText($options['uniqueid']); |
1003 $parser->assign_vars($row); |
1003 $parser->assign_vars($row); |
1004 $idstring = $parser->run(); |
1004 $idstring = $parser->run(); |
1005 |
1005 |
1006 // Score this result |
1006 // Score this result |
1007 foreach ( $word_list as $term ) |
1007 foreach ( $word_list as $term ) |
1008 { |
1008 { |
1009 if ( $case_sensitive ) |
1009 if ( $case_sensitive ) |
1010 { |
1010 { |
1011 if ( strstr($row[$options['titlecolumn']], $term) ) |
1011 if ( strstr($row[$options['titlecolumn']], $term) ) |
1012 { |
1012 { |
1013 ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5; |
1013 ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5; |
1014 } |
1014 } |
1015 else if ( isset($options['datacolumn']) && strstr($row[$options['datacolumn']], $term) ) |
1015 else if ( isset($options['datacolumn']) && strstr($row[$options['datacolumn']], $term) ) |
1016 { |
1016 { |
1017 ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1; |
1017 ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1; |
1018 } |
1018 } |
1019 } |
1019 } |
1020 else |
1020 else |
1021 { |
1021 { |
1022 if ( stristr($row[$options['titlecolumn']], $term) ) |
1022 if ( stristr($row[$options['titlecolumn']], $term) ) |
1023 { |
1023 { |
1024 ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5; |
1024 ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5; |
1025 } |
1025 } |
1026 else if ( isset($options['datacolumn']) && stristr($row[$options['datacolumn']], $term) ) |
1026 else if ( isset($options['datacolumn']) && stristr($row[$options['datacolumn']], $term) ) |
1027 { |
1027 { |
1028 ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1; |
1028 ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1; |
1029 } |
1029 } |
1030 } |
1030 } |
1031 } |
1031 } |
1032 // Generate text... |
1032 // Generate text... |
1033 $text = ''; |
1033 $text = ''; |
1034 if ( isset($options['datacolumn']) && !isset($options['formatcallback']) ) |
1034 if ( isset($options['datacolumn']) && !isset($options['formatcallback']) ) |
1035 { |
1035 { |
1036 $text = highlight_and_clip_search_result(htmlspecialchars($row[$options['datacolumn']]), $word_list); |
1036 $text = highlight_and_clip_search_result(htmlspecialchars($row[$options['datacolumn']]), $word_list); |
1037 } |
1037 } |
1038 else if ( isset($options['formatcallback']) ) |
1038 else if ( isset($options['formatcallback']) ) |
1039 { |
1039 { |
1040 if ( is_callable($options['formatcallback']) ) |
1040 if ( is_callable($options['formatcallback']) ) |
1041 { |
1041 { |
1042 $text = call_user_func($options['formatcallback'], $row, $word_list); |
1042 $text = call_user_func($options['formatcallback'], $row, $word_list); |
1043 } |
1043 } |
1044 else |
1044 else |
1045 { |
1045 { |
1046 $parser = $template->makeParserText($options['formatcallback']); |
1046 $parser = $template->makeParserText($options['formatcallback']); |
1047 $parser->assign_vars($row); |
1047 $parser->assign_vars($row); |
1048 $text = $parser->run(); |
1048 $text = $parser->run(); |
1049 } |
1049 } |
1050 } |
1050 } |
1051 |
1051 |
1052 // Inject result |
1052 // Inject result |
1053 |
1053 |
1054 if ( isset($scores[$idstring]) ) |
1054 if ( isset($scores[$idstring]) ) |
1055 { |
1055 { |
1056 $parser = $template->makeParserText($options['linkformat']['page_id']); |
1056 $parser = $template->makeParserText($options['linkformat']['page_id']); |
1057 $parser->assign_vars($row); |
1057 $parser->assign_vars($row); |
1058 $page_id = $parser->run(); |
1058 $page_id = $parser->run(); |
1059 |
1059 |
1060 $parser = $template->makeParserText($options['linkformat']['namespace']); |
1060 $parser = $template->makeParserText($options['linkformat']['namespace']); |
1061 $parser->assign_vars($row); |
1061 $parser->assign_vars($row); |
1062 $namespace = $parser->run(); |
1062 $namespace = $parser->run(); |
1063 |
1063 |
1064 $page_data[$idstring] = array( |
1064 $page_data[$idstring] = array( |
1065 'page_name' => highlight_search_result(htmlspecialchars($row[$options['titlecolumn']]), $word_list), |
1065 'page_name' => highlight_search_result(htmlspecialchars($row[$options['titlecolumn']]), $word_list), |
1066 'page_text' => $text, |
1066 'page_text' => $text, |
1067 'score' => $scores[$idstring], |
1067 'score' => $scores[$idstring], |
1068 'page_id' => $page_id, |
1068 'page_id' => $page_id, |
1069 'namespace' => $namespace, |
1069 'namespace' => $namespace, |
1070 ); |
1070 ); |
1071 |
1071 |
1072 // Any additional flags that need to be added to the result? |
1072 // Any additional flags that need to be added to the result? |
1073 // The small usually-bracketed text to the left of the title |
1073 // The small usually-bracketed text to the left of the title |
1074 if ( isset($options['resultnote']) ) |
1074 if ( isset($options['resultnote']) ) |
1075 { |
1075 { |
1076 $page_data[$idstring]['page_note'] = $options['resultnote']; |
1076 $page_data[$idstring]['page_note'] = $options['resultnote']; |
1077 } |
1077 } |
1078 // Should we include the length? |
1078 // Should we include the length? |
1079 if ( isset($options['datacolumn']) ) |
1079 if ( isset($options['datacolumn']) ) |
1080 { |
1080 { |
1081 $page_data[$idstring]['page_length'] = strlen($row[$options['datacolumn']]); |
1081 $page_data[$idstring]['page_length'] = strlen($row[$options['datacolumn']]); |
1082 } |
1082 } |
1083 else |
1083 else |
1084 { |
1084 { |
1085 $page_data[$idstring]['page_length'] = 0; |
1085 $page_data[$idstring]['page_length'] = 0; |
1086 $page_data[$idstring]['zero_length'] = true; |
1086 $page_data[$idstring]['zero_length'] = true; |
1087 } |
1087 } |
1088 // Anything to append to result links? |
1088 // Anything to append to result links? |
1089 if ( isset($options['linkformat']['append']) ) |
1089 if ( isset($options['linkformat']['append']) ) |
1090 { |
1090 { |
1091 $page_data[$idstring]['url_append'] = $options['linkformat']['append']; |
1091 $page_data[$idstring]['url_append'] = $options['linkformat']['append']; |
1092 } |
1092 } |
1093 } |
1093 } |
1094 } |
1094 } |
1095 while ( $row = $db->fetchrow($q) ); |
1095 while ( $row = $db->fetchrow($q) ); |
1096 $db->free_result($q); |
1096 $db->free_result($q); |
1097 } |
1097 } |
1098 } |
1098 } |
1099 } |
1099 } |
1100 |
1100 |
1101 ?> |
1101 ?> |