Hackery, Math & Design

Steven Wittens i

Spotlightish search result grouping

Here's a fun, albeit hackish search theme snippet for Drupal.

It takes advantage of the fact that the temporary tables for search stay around, to present results grouped by node type à la Apple Spotlight. Note that it does not simply reformat a single search result page, but regroups results across all pages.

In fact, it's really just a hackish way of doing N separate queries that contain a type:something restriction, one for each node type. To see more results, it presents you with a link to a normal type:something query.

It's a bit inefficient in that the presentational work for the normal results is thrown away (loading nodes, filtering content, extracting a summary). But it's a start ;).


define('SEARCH_TYPE_ITEMS', 5);

function _node_search_result($nid) {
  $node = node_load($nid);

  // Get node output (filtered and with module-specific fields).
  if (node_hook($node, 'view')) {
    node_invoke($node, 'view', false, false);
  else {
    $node = node_prepare($node, false);
  // Allow modules to change $node->body before viewing.
  node_invoke_nodeapi($node, 'view', false, false);

  // Fetch comments for snippet
  $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
  // Fetch terms for snippet
  $node->body .= module_invoke('taxonomy', 'nodeapi', $node, 'update index');

  $extra = node_invoke_nodeapi($node, 'search result');

  return array('link' => url('node/'. $nid),
               'type' => node_get_name($node),
               'title' => $node->title,
               'user' => theme('username', $node),
               'date' => $node->changed,
               'node' => $node,
               'extra' => $extra,
               'snippet' => search_excerpt($keys, $node->body));

function phptemplate_search_page($results, $type) {
  // Only kick in for type-agnostic node searches
  if ($type == 'node' && !search_query_extract($keys = search_get_keys(), 'type')) {

      // Get all types used in results
      $result = db_query("SELECT DISTINCT(n.type) FROM temp_search_results s LEFT JOIN node n ON s.sid = n.nid");
      while ($node = db_fetch_object($result)) {
        $types[] = $node->type;

      $output = '';

      foreach ($types as $type) {
        // Fetch results of one particular type from the 2nd search pass results
        $result = db_query_range("SELECT n.nid FROM temp_search_results s LEFT JOIN node n ON s.sid = n.nid WHERE n.type = '%s'", $type, 0, SEARCH_TYPE_ITEMS + 1);

        $output .= '<h2>'. check_plain($type) .'</h2>';

        $i = 0;
        $output .= '<dl class="search-results">';
        while (($node = db_fetch_object($result)) && $i++ < SEARCH_TYPE_ITEMS) {
          $output .= theme('search_item', _node_search_result($node->nid), $type);
        $output .= '</dl>';

        if (db_num_rows($result) > SEARCH_TYPE_ITEMS) {
          $output .= l(t('More results'), 'search/node/'. search_query_insert(search_get_keys(), 'type', $type));

      return $output;

  // Otherwise present the default page
  return theme_search_page($results, $type);

Drupal  Search
This article contains graphics made with WebGL, which your browser does not seem to support.
Try Google Chrome or Mozilla Firefox. ×