? 258998_0.patch ? d7search.kpf ? do_search.patch ? sites/default/files ? sites/default/settings.php Index: includes/database/database.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database/database.inc,v retrieving revision 1.2 diff -u -p -r1.2 database.inc --- includes/database/database.inc 31 Aug 2008 09:12:35 -0000 1.2 +++ includes/database/database.inc 1 Sep 2008 08:29:09 -0000 @@ -1847,6 +1847,7 @@ function _db_query_process_args($query, foreach ($m[1] as $idx => $char) { switch ($char) { case 'd': +// print_r($idx); print_r($args); $args[$idx] = (int) $args[$idx]; break; case 'f': Index: modules/search/search.module =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.module,v retrieving revision 1.263 diff -u -p -r1.263 search.module --- modules/search/search.module 21 Aug 2008 19:36:38 -0000 1.263 +++ modules/search/search.module 1 Sep 2008 08:29:09 -0000 @@ -770,10 +770,10 @@ function search_parse_query($text) { } // Convert keywords into SQL statements. - $query = array(); - $query2 = array(); - $arguments = array(); - $arguments2 = array(); + $phrase_query = array(); + $keyword_query = array(); + $phrase_arguments = array(); + $keyword_arguments = array(); $matches = 0; $simple_and = FALSE; $simple_or = FALSE; @@ -785,15 +785,15 @@ function search_parse_query($text) { $queryor = array(); $any = FALSE; foreach ($key as $or) { - list($q, $num_new_scores) = _search_parse_query($or, $arguments2); + list($q, $num_new_scores) = _search_parse_query($or, $keyword_arguments); $any |= $num_new_scores; if ($q) { $queryor[] = $q; - $arguments[] = "% $or %"; + $phrase_arguments[] = $or; } } if (count($queryor)) { - $query[] = '(' . implode(' OR ', $queryor) . ')'; + $phrase_query[] = '(' . implode(' OR ', $queryor) . ')'; // A group of OR keywords only needs to match once $matches += ($any > 0); } @@ -801,10 +801,10 @@ function search_parse_query($text) { // Single ANDed term else { $simple_and = TRUE; - list($q, $num_new_scores, $num_valid_words) = _search_parse_query($key, $arguments2); + list($q, $num_new_scores, $num_valid_words) = _search_parse_query($key, $keyword_arguments); if ($q) { - $query[] = $q; - $arguments[] = "% $key %"; + $phrase_query[] = $q; + $phrase_arguments[] = $key; if (!$num_valid_words) { $simple = FALSE; } @@ -818,19 +818,19 @@ function search_parse_query($text) { } // Negative matches foreach ($keys['negative'] as $key) { - list($q) = _search_parse_query($key, $arguments2, TRUE); + list($q) = _search_parse_query($key, $keyword_arguments, TRUE); if ($q) { - $query[] = $q; - $arguments[] = "% $key %"; + $phrase_query[] = $q; + $phrase_arguments[] = $key; $simple = FALSE; } } - $query = implode(' AND ', $query); + $phrase_query = implode(' AND ', $phrase_query); // Build word-index conditions for the first pass - $query2 = substr(str_repeat("i.word = '%s' OR ", count($arguments2)), 0, -4); + $keyword_query = substr(str_repeat("i.word = '%s' OR ", count($keyword_arguments)), 0, -4); - return array($query, $arguments, $query2, $arguments2, $matches, $simple, $warning); + return compact('phrase_query', 'phrase_arguments', 'keyword_query', 'keyword_arguments', 'matches', 'simple', 'warning'); } /** @@ -855,7 +855,7 @@ function _search_parse_query(&$word, &$s } } // Return matching snippet and number of added words - return array("d.data " . ($not ? 'NOT ' : '') . "LIKE '%s'", $num_new_scores, $num_valid_words); + return array("d.data " . ($not ? 'NOT ' : '') . "LIKE '%% %s %%'", $num_new_scores, $num_valid_words); } /** @@ -912,47 +912,75 @@ function _search_parse_query(&$word, &$s * * @ingroup search */ -function do_search($keywords, $type, $join1 = '', $where1 = '1 = 1', $arguments1 = array(), $columns2 = 'i.relevance AS score', $join2 = '', $arguments2 = array(), $sort_parameters = 'ORDER BY score DESC') { +function do_search($keywords, $type, $join1 = '', $where1 = null, $arguments1 = array(), $columns2 = 'i.relevance AS score', $join2 = '', $arguments2 = array(), $sort_parameters = 'ORDER BY score DESC') { $query = search_parse_query($keywords); - - if ($query[2] == '') { - form_set_error('keys', t('You must include at least one positive keyword with @count characters or more.', array('@count' => variable_get('minimum_word_size', 3)))); - } - if ($query[6]) { - if ($query[6] == 'or') { - drupal_set_message(t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.')); - } + // If nothing is being searched for, get out of here. + if ($query['phrase_query'] == '' || $query['keyword_query'] == '') { + return array(); } - if ($query === NULL || $query[0] == '' || $query[2] == '') { + + // Calculate maximum keyword relevance, to normalize it. + // Start with a COUNT query. + $count_arguments = $arguments1; + $count_joins = array($join1); + $count_wheres = $where1 ? array($where1) : array(); + + if ($query['keyword_query']) { + $count_wheres[] = "($query[keyword_query])"; + $count_arguments = array_merge($count_arguments, $query['keyword_arguments']); + } + $count_wheres[] = "i.type = '%s'"; + $count_arguments[] = $type; + if (!$query['simple']) { + // Complex queries have a JOIN and WHERE for phrases plus extra arguments. + $count_joins[] = 'INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type'; + $count_arguments = array_merge($count_arguments, $query['phrase_arguments']); + $count_wheres[] = "({$query['phrase_query']})"; + } + + $count_arguments[] = $query['matches']; + + $count_join = implode(' ', $count_joins); + $count_where = implode(' AND ', $count_wheres); + // Execute the count query before the main query. + $count = db_result(db_query("SELECT COUNT(*) FROM (SELECT 1 FROM {search_index} i $count_join WHERE $count_where GROUP BY i.type, i.sid HAVING COUNT(*) >= %d) n1", $count_arguments)); + // If there aren't any results, return empty. + if ($count == 0) { return array(); } - // Build query for keyword normalization. - $conditions = "$where1 AND ($query[2]) AND i.type = '%s'"; - $arguments1 = array_merge($arguments1, $query[3], array($type)); - $join = "INNER JOIN {search_total} t ON i.word = t.word $join1"; - if (!$query[5]) { - $conditions .= " AND ($query[0])"; - $arguments1 = array_merge($arguments1, $query[1]); - $join .= " INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type"; - } + // Build the simplest of all queries for db_pager(). + $count_select = "SELECT $count"; - // Calculate maximum keyword relevance, to normalize it. - $select = "SELECT SUM(i.score * t.count) AS score FROM {search_index} i $join WHERE $conditions GROUP BY i.type, i.sid HAVING COUNT(*) >= %d ORDER BY score DESC"; - $arguments = array_merge($arguments1, array($query[4])); - $normalize = db_result(db_query_range($select, $arguments, 0, 1)); + // Build query for keyword normalization. + $query_arguments = array_merge($arguments1, $query['keyword_arguments'], array($type)); + $query_joins = array("INNER JOIN {search_total} t ON i.word = t.word", $join1); + $query_wheres = $where1 ? array($where1) : array(); + $query_wheres[] = "($query[keyword_query])"; + $query_wheres[] = "i.type = '%s'"; + + if (!$query['simple']) { + $query_wheres[] = "($query[phrase_query])"; + $query_arguments = array_merge($query_arguments, $query['phrase_arguments']); + $query_joins[] = "INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type"; + } + + $query_arguments[] = $query['matches']; + $query_join = implode(' ', $query_joins); + $query_where = implode(' AND ', $query_wheres); + + $select = "SELECT SUM(i.score * t.count) AS score FROM {search_index} i $query_join WHERE $query_where GROUP BY i.type, i.sid HAVING COUNT(*) >= %d ORDER BY score DESC"; + $normalize = db_result(db_query_range($select, $query_arguments, 0, 1)); if (!$normalize) { return array(); } - $columns2 = str_replace('i.relevance', '(' . (1.0 / $normalize) . ' * SUM(i.score * t.count))', $columns2); // Build query to retrieve results. - $select = "SELECT i.type, i.sid, $columns2 FROM {search_index} i $join $join2 WHERE $conditions GROUP BY i.type, i.sid HAVING COUNT(*) >= %d"; - $count_select = "SELECT COUNT(*) FROM ($select) n1"; - $arguments = array_merge($arguments2, $arguments1, array($query[4])); - + $columns2 = str_replace('i.relevance', '(' . (1.0 / $normalize) . ' * SUM(i.score * t.count))', $columns2); + $select = "SELECT i.type, i.sid, $columns2 FROM {search_index} i $query_join $join2 WHERE $query_where GROUP BY i.type, i.sid HAVING COUNT(*) >= %d"; + // Do actual search query - $result = pager_query("$select $sort_parameters", 10, 0, $count_select, $arguments); + $result = pager_query("$select $sort_parameters", 10, 0, $count_select, array_merge($arguments2, $query_arguments)); $results = array(); while ($item = db_fetch_object($result)) { $results[] = $item; @@ -1069,6 +1097,7 @@ function search_box(&$form_state, $form_ ); $form['submit'] = array('#type' => 'submit', '#value' => t('Search')); $form['#submit'][] = 'search_box_form_submit'; + $form['#submit'][] = 'search_form_submit'; return $form; } @@ -1078,7 +1107,8 @@ function search_box(&$form_state, $form_ */ function search_box_form_submit($form, &$form_state) { $form_id = $form['form_id']['#value']; - $form_state['redirect'] = 'search/node/' . trim($form_state['values'][$form_id]); + $form_state['values']['processed_keys'] = trim($form_state['values'][$form_id]); + $form_state['values']['module'] = 'node'; } /** Index: modules/search/search.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.pages.inc,v retrieving revision 1.5 diff -u -p -r1.5 search.pages.inc --- modules/search/search.pages.inc 14 Apr 2008 17:48:41 -0000 1.5 +++ modules/search/search.pages.inc 1 Sep 2008 08:29:09 -0000 @@ -124,6 +124,17 @@ function search_form_submit($form, &$for // Fall through to the drupal_goto() call. } + $query = search_parse_query($keys); + if ($query['keyword_query'] == '') { + form_set_error('keys', t('You must include at least one keyword with @count characters or more.', array('@count' => variable_get('minimum_word_size', 3)))); + } + + // Warn users if they have the word 'or' in their query that only uppercase + // OR is recognized as a boolean modifier. + if ($query['warning'] == 'or') { + drupal_set_message(t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.')); + } + $type = $form_state['values']['module'] ? $form_state['values']['module'] : 'node'; $form_state['redirect'] = 'search/' . $type . '/' . $keys; return; Index: modules/search/search.test =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.test,v retrieving revision 1.6 diff -u -p -r1.6 search.test --- modules/search/search.test 5 Jun 2008 21:55:45 -0000 1.6 +++ modules/search/search.test 1 Sep 2008 08:29:09 -0000 @@ -196,6 +196,63 @@ class SearchBikeShed extends DrupalWebTe } } +class SearchAdvancedSearchForm extends DrupalWebTestCase { + protected $settings; + + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('Advanced search form'), + 'description' => t('Indexes content and tests advanced search form searches.'), + 'group' => t('Search'), + ); + } + + /** + * Implementation of setUp(). + */ + function setUp() { + parent::setUp('search'); + // Create and login user. + $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes')); + $this->drupalLogin($test_user); + + // Create initial node. + $node = $this->drupalCreateNode(); + $this->settings = get_object_vars($node); + + // First update the index. This does the initial processing + node_update_index(); + + // Then, run the shutdown function. Testing is a unique case where indexing + // and searching has to happen in the same request, so running the shutdown + // function manually is needed to finsh the indexing process. + search_update_totals(); + } + + function testNodeType() { + $this->assertTrue($this->settings, t('Node type is page.')); + + // Search for the title of the node with a GET query + $this->drupalGet('search/node/' . drupal_urlencode($this->settings['title'])); + $this->assertText($this->settings['title'], t('Page node is found with GET query.')); + + // Search for the title of the node with a POST query + $edit = array('or' => $this->settings['title']); + $this->drupalPost('search/node', $edit, t('Advanced search')); + $this->assertText($this->settings['title'], t('Page node is found with POST query.')); + + $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search')); + $this->assertText($this->settings['title'], t('Page node is found with POST query and type:page.')); + + $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search')); + $this->assertText('bike shed', t('Article node is not found with POST query and type:article.')); + + } +} + class SearchRankingTestCase extends DrupalWebTestCase { /** * Implementation of getInfo().