Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.708 diff -u -p -r1.708 comment.module --- modules/comment/comment.module 3 May 2009 10:11:34 -0000 1.708 +++ modules/comment/comment.module 11 May 2009 20:22:26 -0000 @@ -2236,10 +2236,15 @@ function comment_ranking() { return array( 'comments' => array( 'title' => t('Number of comments'), - 'join' => 'LEFT JOIN {node_comment_statistics} node_comment_statistics ON node_comment_statistics.nid = i.sid', + 'join' => array( + 'type' => 'LEFT OUTER', + 'table' => 'node_comment_statistics', + 'alias' => 'node_comment_statistics', + 'on' => 'node_comment_statistics.nid = i.sid', + ), // Inverse law that maps the highest reply count on the site to 1 and 0 to 0. - 'score' => '2.0 - 2.0 / (1.0 + node_comment_statistics.comment_count * CAST(%f AS DECIMAL))', - 'arguments' => array(variable_get('node_cron_comments_scale', 0)), + 'score' => '2.0 - 2.0 / (1.0 + node_comment_statistics.comment_count * CAST(:scale AS DECIMAL))', + 'arguments' => array(':scale' => variable_get('node_cron_comments_scale', 0)), ), ); } Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1045 diff -u -p -r1.1045 node.module --- modules/node/node.module 9 May 2009 18:28:12 -0000 1.1045 +++ modules/node/node.module 11 May 2009 20:22:34 -0000 @@ -1369,20 +1369,22 @@ function _node_rankings() { $rankings = array( 'total' => 0, 'join' => array(), 'score' => array(), 'args' => array(), ); + $i = 0; if ($ranking = module_invoke_all('ranking')) { foreach ($ranking as $rank => $values) { if ($node_rank = variable_get('node_rank_'. $rank, 0)) { // If the table defined in the ranking isn't already joined, then add it. - if (isset($values['join']) && !isset($rankings['join'][$values['join']])) { - $rankings['join'][$values['join']] = $values['join']; + if (isset($values['join']) && !isset($rankings['join'][$values['join']['alias']])) { + $rankings['join'][$values['join']['alias']] = $values['join']; } // Add the rankings weighted score multiplier value, handling NULL gracefully. - $rankings['score'][] = 'CAST(%f AS DECIMAL) * COALESCE(('. $values['score'] .'), 0)'; + $rankings['score'][] = "CAST(:node_rank_$i AS DECIMAL) * COALESCE(( " . $values['score'] .'), 0)'; // Add the the administrator's weighted score multiplier value for this ranking. $rankings['total'] += $node_rank; - $rankings['arguments'][] = $node_rank; + $rankings['arguments'][':node_rank_' . $i] = $node_rank; + $i++; // Add any additional arguments used by this ranking. if (isset($values['arguments'])) { @@ -1441,38 +1443,37 @@ function node_search($op = 'search', $ke case 'search': // Build matching conditions - list($join1, $where1) = _db_rewrite_sql(); - $arguments1 = array(); - $conditions1 = 'n.status = 1'; + $query = db_select('search_index', 'i'); + $query->join('node', 'n', 'n.nid = i.sid'); + $query + ->condition('n.status', 1) + ->addTag('node_access'); if ($type = search_query_extract($keys, 'type')) { - $types = array(); + $types_or = db_or(); foreach (explode(',', $type) as $t) { - $types[] = "n.type = '%s'"; - $arguments1[] = $t; + $types_or->condition('n.type', $t); } - $conditions1 .= ' AND (' . implode(' OR ', $types) . ')'; + $query->condition($types_or); $keys = search_query_insert($keys, 'type'); } if ($term = search_query_extract($keys, 'term')) { - $terms = array(); + $term_or = db_or(); foreach (explode(',', $term) as $c) { - $terms[] = "tn.tid = %d"; - $arguments1[] = $c; + $term_or->condition('tn.nid', $c); } - $conditions1 .= ' AND (' . implode(' OR ', $terms) . ')'; - $join1 .= ' INNER JOIN {taxonomy_term_node} tn ON n.vid = tn.vid'; + $query->condition($term_or); + $query->join('taxonomy_term_node', 'tn', 'n.vid = tn.vid'); $keys = search_query_insert($keys, 'term'); } if ($languages = search_query_extract($keys, 'language')) { - $terms = array(); + $lang_or = db_or(); foreach (explode(',', $languages) as $l) { - $terms[] = "n.language = '%s'"; - $arguments1[] = $l; + $lang_or->condition('n.language', $l); } - $conditions1 .= ' AND (' . implode(' OR ', $terms) . ')'; + $query->condition($lang_or); $keys = search_query_insert($keys, 'language'); } @@ -1481,21 +1482,17 @@ function node_search($op = 'search', $ke // When all search factors are disabled (ie they have a weight of zero), // The default score is based only on keyword relevance. - if ($rankings['total'] == 0) { - $total = 1; - $arguments2 = array(); - $join2 = ''; - $select2 = 'SUM(i.relevance) AS calculated_score'; - } - else { + $total = 0; + if ($rankings['total'] != 0) { $total = $rankings['total']; - $arguments2 = $rankings['arguments']; - $join2 = implode(' ', $rankings['join']); - $select2 = 'SUM('. implode(' + ', $rankings['score']) .') AS calculated_score'; + foreach ($rankings['join'] as $join) { + $query->addJoin($join['type'], $join['table'], $join['alias'], $join['on']); + } + $query->addExpression('SUM('. implode(' + ', $rankings['score']) . ')', 'calculated_score', $rankings['arguments']); } // Do search. - $find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid ' . $join1, $conditions1 . (empty($where1) ? '' : ' AND ' . $where1), $arguments1, $select2, $join2, $arguments2); + $find = do_search($keys, 'node', $query); // Load results. $results = array(); @@ -1557,8 +1554,8 @@ function node_ranking() { $ranking['recent'] = array( 'title' => t('Recently posted'), // Exponential decay with half-life of 6 months, starting at last indexed node - 'score' => 'POW(2.0, (GREATEST(n.created, n.changed) - %d) * 6.43e-8)', - 'arguments' => array($node_cron_last), + 'score' => 'POW(2.0, (GREATEST(n.created, n.changed) - :node_cron_last) * 6.43e-8)', + 'arguments' => array(':node_cron_last' => $node_cron_last), ); } return $ranking; Index: modules/search/search.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.api.php,v retrieving revision 1.7 diff -u -p -r1.7 search.api.php --- modules/search/search.api.php 9 May 2009 10:45:57 -0000 1.7 +++ modules/search/search.api.php 11 May 2009 20:22:35 -0000 @@ -76,12 +76,15 @@ function hook_search($op = 'search', $ke return t('Content'); case 'reset': - db_query("UPDATE {search_dataset} SET reindex = %d WHERE type = 'node'", REQUEST_TIME); + db_update('search_dataset') + ->fields(array('reindex' => REQUEST_TIME)) + ->condition('type', 'node') + ->execute(); return; case 'status': - $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')); - $remaining = db_result(db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")); + $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField(); + $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField(); return array('remaining' => $remaining, 'total' => $total); case 'admin': @@ -248,7 +251,11 @@ function hook_update_index() { $last = variable_get('node_cron_last', 0); $limit = (int)variable_get('search_cron_limit', 100); - $result = db_query_range('SELECT n.nid, c.last_comment_timestamp FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND n.moderate = 0 AND (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d) ORDER BY GREATEST(n.created, n.changed, c.last_comment_timestamp) ASC', $last, $last, $last, 0, $limit); + $result = db_query_range('SELECT n.nid, c.last_comment_timestamp FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND n.moderate = 0 AND (n.created > :created OR n.changed > :changed OR c.last_comment_timestamp > :comment) ORDER BY GREATEST(n.created, n.changed, c.last_comment_timestamp) ASC', array( + ':created' => $last, + ':changed' => $last, + ':comment' => $last, + ), 0, $limit); while ($node = db_fetch_object($result)) { $last_comment = $node->last_comment_timestamp; Index: modules/search/search.module =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.module,v retrieving revision 1.289 diff -u -p -r1.289 search.module --- modules/search/search.module 6 May 2009 10:48:01 -0000 1.289 +++ modules/search/search.module 11 May 2009 20:22:39 -0000 @@ -248,11 +248,20 @@ function search_wipe($sid = NULL, $type module_invoke_all('search', 'reset'); } else { - db_query("DELETE FROM {search_dataset} WHERE sid = %d AND type = '%s'", $sid, $type); - db_query("DELETE FROM {search_index} WHERE sid = %d AND type = '%s'", $sid, $type); + db_delete('search_dataset') + ->condition('sid', $sid) + ->condition('type', $type) + ->execute(); + db_delete('search_index') + ->condition('sid', $sid) + ->condition('type', $type) + ->execute(); // Don't remove links if re-indexing. if (!$reindex) { - db_query("DELETE FROM {search_node_links} WHERE sid = %d AND type = '%s'", $sid, $type); + db_delete('search_node_links') + ->condition('sid', $sid) + ->condition('type', $type) + ->execute(); } } } @@ -295,7 +304,7 @@ function search_update_totals() { // Update word IDF (Inverse Document Frequency) counts for new/changed words foreach (search_dirty() as $word => $dummy) { // Get total count - $total = db_result(db_query("SELECT SUM(score) FROM {search_index} WHERE word = '%s'", $word)); + $total = db_query("SELECT SUM(score) FROM {search_index} WHERE word = :word", array(':word' => $word))->fetchField(); // Apply Zipf's law to equalize the probability distribution $total = log10(1 + 1/(max(1, $total))); db_merge('search_total')->key(array('word' => $word))->fields(array('count' => $total))->execute(); @@ -304,8 +313,14 @@ function search_update_totals() { // search_total. We use a LEFT JOIN between the two tables and keep only the // rows which fail to join. $result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL"); - while ($word = db_fetch_object($result)) { - db_query("DELETE FROM {search_total} WHERE word = '%s'", $word->realword); + $or = db_or(); + foreach ($result as $word) { + $or->condition('word', $word->realword); + } + if (count($or) > 0) { + db_delete('search_total') + ->condition($or) + ->execute(); } } @@ -512,7 +527,7 @@ function search_index($sid, $type, $text $linknid = $match[1]; if ($linknid > 0) { // Note: ignore links to uncachable nodes to avoid redirect bugs. - $node = db_fetch_object(db_query('SELECT n.title, n.nid, n.vid, r.format FROM {node} n INNER JOIN {node_revision} r ON n.vid = r.vid WHERE n.nid = %d', $linknid)); + $node = db_query('SELECT n.title, n.nid, n.vid, r.format FROM {node} n INNER JOIN {node_revision} r ON n.vid = r.vid WHERE n.nid = :nid', array(':nid' => $linknid))->fetchAssoc(); if (filter_format_allowcache($node->format)) { $link = TRUE; $linktitle = $node->title; @@ -580,7 +595,14 @@ function search_index($sid, $type, $text search_wipe($sid, $type, TRUE); // Insert cleaned up data into dataset - db_query("INSERT INTO {search_dataset} (sid, type, data, reindex) VALUES (%d, '%s', '%s', %d)", $sid, $type, $accum, 0); + db_insert('search_dataset') + ->fields(array( + 'sid' => $sid, + 'type' => $type, + 'data' => $accum, + 'reindex' => 0, + )) + ->execute(); // Insert results into search index foreach ($results[0] as $word => $score) { @@ -588,19 +610,24 @@ function search_index($sid, $type, $text // appropriately. If not, we create a new record with the appropriate // starting score. db_merge('search_index')->key(array( - 'word' => $word, - 'sid' => $sid, - 'type' => $type, - ))->fields(array('score' => $score))->expression('score', 'score + :score', array(':score' => $score)) - ->execute(); + 'word' => $word, + 'sid' => $sid, + 'type' => $type, + )) + ->fields(array('score' => $score)) + ->expression('score', 'score + :score', array(':score' => $score)) + ->execute(); search_dirty($word); } unset($results[0]); // Get all previous links from this item. - $result = db_query("SELECT nid, caption FROM {search_node_links} WHERE sid = %d AND type = '%s'", $sid, $type); + $result = db_query("SELECT nid, caption FROM {search_node_links} WHERE sid = :sid AND type = :type", array( + ':sid' => $sid, + ':type' => $type + )); $links = array(); - while ($link = db_fetch_object($result)) { + foreach ($result as $link) { $links[$link->nid] = $link->caption; } @@ -610,7 +637,12 @@ function search_index($sid, $type, $text if (isset($links[$nid])) { if ($links[$nid] != $caption) { // Update the existing link and mark the node for reindexing. - db_query("UPDATE {search_node_links} SET caption = '%s' WHERE sid = %d AND type = '%s' AND nid = %d", $caption, $sid, $type, $nid); + db_update('search_node_links') + ->fields(array('caption' => $caption)) + ->condition('sid', $sid) + ->condition('type', $type) + ->condition('nid', $nid) + ->execute(); search_touch_node($nid); } // Unset the link to mark it as processed. @@ -618,13 +650,24 @@ function search_index($sid, $type, $text } else { // Insert the existing link and mark the node for reindexing. - db_query("INSERT INTO {search_node_links} (caption, sid, type, nid) VALUES ('%s', %d, '%s', %d)", $caption, $sid, $type, $nid); + db_insert('search_node_links') + ->fields(array( + 'caption' => $caption, + 'sid' => $sid, + 'type' => $type, + 'nid' => $nid, + )) + ->execute(); search_touch_node($nid); } } // Any left-over links in $links no longer exist. Delete them and mark the nodes for reindexing. foreach ($links as $nid => $caption) { - db_query("DELETE FROM {search_node_links} WHERE sid = %d AND type = '%s' AND nid = %d", $sid, $type, $nid); + db_delete('search_node_links') + ->condition('sid', $sid) + ->condition('type', $type) + ->condition('nid', $nid) + ->execute(); search_touch_node($nid); } } @@ -636,7 +679,11 @@ function search_index($sid, $type, $text * The nid of the node that needs reindexing. */ function search_touch_node($nid) { - db_query("UPDATE {search_dataset} SET reindex = %d WHERE sid = %d AND type = 'node'", REQUEST_TIME, $nid); + db_update('search_dataset') + ->fields(array('reindex' => REQUEST_TIME)) + ->condition('type', 'node') + ->condition('sid', $nid) + ->execute(); } /** @@ -644,9 +691,9 @@ function search_touch_node($nid) { */ function search_node_update_index($node) { // Transplant links to a node into the target node. - $result = db_query("SELECT caption FROM {search_node_links} WHERE nid = %d", $node->nid); + $result = db_query("SELECT caption FROM {search_node_links} WHERE nid = :nid", array(':nid' => $node->nid)); $output = array(); - while ($link = db_fetch_object($result)) { + foreach ($result as $link) { $output[] = $link->caption; } if (count($output)) { @@ -811,10 +858,8 @@ function search_parse_query($text) { } // Convert keywords into SQL statements. - $query = array(); - $query2 = array(); - $arguments = array(); - $arguments2 = array(); + $words = array(); + $conditions = db_and(); $matches = 0; $simple_and = FALSE; $simple_or = FALSE; @@ -823,18 +868,15 @@ function search_parse_query($text) { // Group of ORed terms if (is_array($key) && count($key)) { $simple_or = TRUE; - $queryor = array(); $any = FALSE; + $queryor = db_or(); foreach ($key as $or) { - list($q, $num_new_scores) = _search_parse_query($or, $arguments2); + list($num_new_scores) = _search_parse_query($or, $words); $any |= $num_new_scores; - if ($q) { - $queryor[] = $q; - $arguments[] = "% $or %"; - } + $queryor->condition('d.data', "% $or %", 'LIKE'); } if (count($queryor)) { - $query[] = '(' . implode(' OR ', $queryor) . ')'; + $conditions->condition($queryor); // A group of OR keywords only needs to match once $matches += ($any > 0); } @@ -842,16 +884,13 @@ 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); - if ($q) { - $query[] = $q; - $arguments[] = "% $key %"; - if (!$num_valid_words) { - $simple = FALSE; - } - // Each AND keyword needs to match at least once - $matches += $num_new_scores; + list($num_new_scores, $num_valid_words) = _search_parse_query($key, $words); + $conditions->condition('d.data', "% $key %", 'LIKE'); + if (!$num_valid_words) { + $simple = FALSE; } + // Each AND keyword needs to match at least once + $matches += $num_new_scores; } } if ($simple_and && $simple_or) { @@ -859,19 +898,12 @@ function search_parse_query($text) { } // Negative matches foreach ($keys['negative'] as $key) { - list($q) = _search_parse_query($key, $arguments2, TRUE); - if ($q) { - $query[] = $q; - $arguments[] = "% $key %"; - $simple = FALSE; - } + $conditions->condition('d.data', "% $key %", 'NOT LIKE'); + $simple = FALSE; } - $query = implode(' AND ', $query); // Build word-index conditions for the first pass - $query2 = substr(str_repeat("i.word = '%s' OR ", count($arguments2)), 0, -4); - - return array($query, $arguments, $query2, $arguments2, $matches, $simple, $warning); + return array($conditions, $words, $matches, $simple, $warning); } /** @@ -896,7 +928,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($num_new_scores, $num_valid_words); } /** @@ -913,7 +945,10 @@ function _search_parse_query(&$word, &$s * of also providing the exact result set for simple "AND" or "OR" searches. * * The second portion of the query further refines this set by verifying - * advanced text conditions (such negative or phrase matches) + * advanced text conditions (such negative or phrase matches). + * + * The used query object has the tag 'search_$type' and can be further extend + * with hook_query_alter(). * * @param $keywords * A search string as entered by the user. @@ -921,84 +956,111 @@ function _search_parse_query(&$word, &$s * @param $type * A string identifying the calling module. * - * @param $join1 - * (optional) Inserted into the JOIN part of the first SQL query. - * For example "INNER JOIN {node} n ON n.nid = i.sid". - * - * @param $where1 - * (optional) Inserted into the WHERE part of the first SQL query. - * For example "(n.status > %d)". - * - * @param $arguments1 - * (optional) Extra SQL arguments belonging to the first query. - * - * @param $columns2 - * (optional) Inserted into the SELECT pat of the second query. Must contain - * a column selected as 'calculated_score'. - * defaults to 'SUM(i.relevance) AS calculated_score' - * - * @param $join2 - * (optional) Inserted into the JOIN par of the second SQL query. - * For example "INNER JOIN {node_comment_statistics} n ON n.nid = i.sid" - * - * @param $arguments2 - * (optional) Extra SQL arguments belonging to the second query parameter. - * - * @param $sort_parameters - * (optional) SQL arguments for sorting the final results. - * Default: 'ORDER BY calculated_score DESC' + * @param $query + * A SelectQuery object that is used instead of the default value. + * + * This will be cloned and used for both steps of the search passes. + * However, expressions will be not be used in the first pass, they only apply + * for the second pass. + * + * Default value: + * $query = db_select('search_index', 'i'); * * @return * An array of SIDs for the search results. * * @ingroup search */ -function do_search($keywords, $type, $join1 = '', $where1 = '1 = 1', $arguments1 = array(), $columns2 = 'SUM(i.relevance) AS calculated_score', $join2 = '', $arguments2 = array(), $sort_parameters = 'ORDER BY calculated_score DESC') { - $query = search_parse_query($keywords); +function do_search($keywords, $type, SelectQueryInterface $query = NULL) { + if (is_null($query)) { + $query = db_select('search_index', 'i'); + } + list($conditions, $words, $matches, $simple, $warning) = search_parse_query($keywords); - if ($query[2] == '') { + if (count($words) == 0) { form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.')); } - 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 ($warning == 'or') { + drupal_set_message(t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.')); } - if ($query === NULL || $query[0] == '' || $query[2] == '') { + if (count($conditions) == 0 || count($words) == 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"; + if (!empty($words)) { + $or = db_or(); + foreach ($words as $word) { + $or->condition('i.word', $word); + } + $query->condition($or); } + // Build query for keyword normalization. + $query->join('search_total', 't', 'i.word = t.word'); + $query + ->condition('i.type', $type) + ->groupBy('i.type') + ->groupBy('i.sid') + ->having('COUNT(*) >= :matches', array(':matches' => $matches)); + if (!$simple) { + $query->condition($conditions); + $query->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); + } + + // Create the second query object before adding normalization specific information. + $query2 = clone $query; + + // Reset the expressions, for the count query, only the default SUM is necessary + $expressions =& $query->getExpressions(); + $expressions = array(); // Calculate maximum keyword relevance, to normalize it. - $select = "SELECT SUM(i.score * t.count) AS calculated_score FROM {search_index} i $join WHERE $conditions GROUP BY i.type, i.sid HAVING COUNT(*) >= %d ORDER BY calculated_score DESC"; - $arguments = array_merge($arguments1, array($query[4])); - $normalize = db_result(db_query_range($select, $arguments, 0, 1)); + $query->addExpression('SUM(i.score * t.count)', 'calculated_score'); + $normalize = $query + ->range(0, 1) + ->orderBy('calculated_score', 'DESC') + ->execute() + ->fetchField(); if (!$normalize) { return array(); } - $columns2 = str_replace('i.relevance', '(' . (1.0 / $normalize) . ' * 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_values(array_merge($arguments2, $arguments1, array($query[4]))); - - // Do actual search query - $result = pager_query("$select $sort_parameters", 10, 0, $count_select, $arguments); - $results = array(); - while ($item = db_fetch_object($result)) { - $results[] = $item; + // If the search query is simple, add conditions now. + // For a complex query, they have been added already. + if ($simple) { + $query2->condition($conditions); + $query2->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); + } + + // Either add default score expression or replace pseudo field i.relevance + // with the actual normalization value. + if (count($query2->getExpressions()) == 0) { + $query2->addExpression('SUM(' . (1.0 / $normalize) . ' * i.score * t.count)', 'calculated_score'); + } else { + $expressions =& $query2->getExpressions(); + foreach ($expressions as &$expression) { + $expression['expression'] = str_replace('i.relevance', '(' . (1.0 / $normalize) . ' * i.score * t.count)', $expression['expression']); + } } - return $results; + + // Add tag and useful metadata. + $query2 + ->addTag('search_' . $type) + ->addMetaData('normalize', $normalize); + + // Create count query. + $count_query = db_select(clone $query2, 'subquery'); + $count_query->addExpression('COUNT(*)'); + + // Extend the query with the pager and add the count query. + $query2 = $query2->extend('PagerDefault'); + $query2->setCountQuery($count_query); + + return $query2 + ->fields('i', array('type', 'sid')) + ->orderBy('calculated_score', 'DESC') + ->limit(10) + ->execute() + ->fetchAll(); } /** Index: modules/statistics/statistics.module =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.module,v retrieving revision 1.303 diff -u -p -r1.303 statistics.module --- modules/statistics/statistics.module 3 May 2009 10:11:35 -0000 1.303 +++ modules/statistics/statistics.module 11 May 2009 20:22:49 -0000 @@ -380,10 +380,15 @@ function statistics_ranking() { return array( 'views' => array( 'title' => t('Number of views'), - 'join' => 'LEFT JOIN {node_counter} node_counter ON node_counter.nid = i.sid', + 'join' => array( + 'type' => 'LEFT OUTER', + 'table' => 'node_counter', + 'alias' => 'node_counter', + 'on' => 'node_counter.nid = i.sid', + ), // Inverse law that maps the highest view count on the site to 1 and 0 to 0. - 'score' => '2.0 - 2.0 / (1.0 + node_counter.totalcount * CAST(%f AS DECIMAL))', - 'arguments' => array(variable_get('node_cron_views_scale', 0)), + 'score' => '2.0 - 2.0 / (1.0 + node_counter.totalcount * CAST(:scale AS DECIMAL))', + 'arguments' => array(':scale' => variable_get('node_cron_views_scale', 0)), ), ); }