You are here

apachesolr_search.module in Apache Solr Search 5

Provides a content search implementation for node content for use with the Apache Solr search application.

File

apachesolr_search.module
View source
<?php

/**
 * @file
 *   Provides a content search implementation for node content for use with the
 *   Apache Solr search application.
 */

/**
 * Implementation of hook_update_index().
 */
function apachesolr_update_index() {
  ApacheSolrUpdate::update_index('apachesolr');
}

/**
 * Implementation of hook_search().
 */
function apachesolr_search_search($op = 'search', $keys = NULL) {
  switch ($op) {
    case 'name':
      return t('Search');
    case 'reset':
      ApacheSolrUpdate::reset('apachesolr');
      return;
    case 'status':
      $change = ApacheSolrUpdate::get_change('apachesolr');
      $last = ApacheSolrUpdate::get_last('apachesolr');
      $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1'));
      $remaining = db_result(db_query('SELECT COUNT(*) FROM {node} n ' . 'LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid ' . 'WHERE n.status = 1 AND ((GREATEST(n.created, n.changed, c.last_comment_timestamp) = %d AND n.nid > %d ) OR (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d))', $change, $last, $change, $change, $change));
      return array(
        'remaining' => $remaining,
        'total' => $total,
      );
    case 'search':
      global $pager_total;

      // This is the object that does the communication with the solr server.
      $solr =& apachesolr_get_solr(variable_get('apachesolr_host', 'localhost'), variable_get('apachesolr_port', 8983), variable_get('apachesolr_path', '/solr'));

      // This is the object that knows about the query coming from the user.
      $query =& apachesolr_drupal_query($keys);
      $results = array();
      try {
        $params = array(
          //'qt' => 'standard',
          'fl' => '*,score',
          'rows' => variable_get('apachesolr_rows', 10),
          'facet' => 'true',
          'facet.mincount' => 1,
          'facet.sort' => 'true',
        );

        // TODO: This adds all of the possible facets to the query. Not all
        // of these facets have their blocks enabled, so the list should be
        // filtered by the actual enabled blocks, otherwise we're putting
        // unneeded strain on the Solr server.
        foreach (module_implements('apachesolr_facets') as $module) {
          $function = $module . '_apachesolr_facets';
          $result = call_user_func_array($function, array());
          if (isset($result) && is_array($result)) {
            foreach ($result as $facet) {
              $params['facet.field'][] = $facet;
            }
          }
        }

        // Facet limits
        $facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
        foreach ($facet_query_limits as $fieldname => $limit) {
          $params['f.' . $fieldname . '.facet.limit'] = $limit;
        }
        if (isset($_GET['solrsort'])) {
          $sort = check_plain($_GET['solrsort']);
        }

        // Validate sort parameter
        if ($sort && preg_match('/^([a-z0-9_]+ (asc|desc)(,)?)+$/i', $sort)) {
          $params['sort'] = $sort;
        }
        if ($fields = apachesolr_cck_fields()) {
          foreach ($fields as $name => $field) {
            $index_key = apachesolr_index_key($field);
            $params['facet.field'][] = $index_key;
          }
        }
        $page = isset($_GET['page']) ? $_GET['page'] : 0;
        $params['start'] = $page * $params['rows'];

        /**
         * This hook allows modules to modify the query are params objects.
         *
         * Example:
         *
         *<code>
         * function my_module_apachesolr_modify_query(&$query, &$params) {
         *   // I only want to see articles by the admin!
         *   $query->add_field("uid", 1);
         *
         * }
         * </code>
         */
        foreach (module_implements('apachesolr_modify_query') as $module) {
          $function_name = "{$module}_apachesolr_modify_query";
          $function_name($query, $params);
        }
        if (!$query) {
          return array();
        }
        $hash = md5(url(NULL, NULL, NULL, TRUE));
        $query
          ->add_field('hash', $hash);
        $response = $solr
          ->search($query
          ->get_query(), $params['start'], $params['rows'], $params);

        // The response is cached so that it is accessible to the blocks and anything
        // else that needs it beyond the initial search.
        apachesolr_static_response_cache($response);
        apachesolr_has_searched(TRUE);
        $query
          ->remove_field('hash', $hash);
        $total = $response->response->numFound;
        pager_query("SELECT %d", $params['rows'], 0, NULL, $total);
        if ($total > 0) {
          $extra = array();
          foreach ($response->response->docs as $doc) {
            $extra += node_invoke_nodeapi($doc, 'search result');
            $extra['score'] = $doc->score;
            $snippet = search_excerpt($keys, $doc->body);
            if (trim($snippet) == '...') {
              $snippet = '';
            }
            $results[] = array(
              'link' => $doc->url,
              'type' => node_get_types('name', $doc),
              'title' => $doc->title,
              'user' => theme('username', $doc),
              'date' => $doc->changed,
              'node' => $doc,
              'extra' => $extra,
              'score' => $doc->score,
              'snippet' => $snippet,
            );
          }

          // Hook to allow modifications of the retrieved results
          foreach (module_implements('apachesolr_process_results') as $module) {
            $function = $module . '_apachesolr_process_results';
            call_user_func_array($function, array(
              &$results,
            ));
          }
        }

        // Set breadcrumb
        drupal_set_breadcrumb($query
          ->get_breadcrumb());
        return $results;
      } catch (Exception $e) {
        watchdog('Apache Solr', $e
          ->getMessage(), WATCHDOG_ERROR);
        apachesolr_failure(t('Search'), $query
          ->get_query());
      }
      break;
  }

  // switch
}
function apachesolr_search_apachesolr_facets() {
  return array_keys(apachesolr_search_block());
}

/**
 * Implementation of hook_block().
 */
function apachesolr_search_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks['uid'] = array(
        'info' => t('ApacheSolr Search: Filter by author'),
      );

      // Get taxonomy vocabulary facets.
      if (module_exists('taxonomy')) {
        $vocabs = taxonomy_get_vocabularies();
        foreach ($vocabs as $vid => $vocab) {
          $blocks['imfield_vid' . $vid] = array(
            'info' => t('ApacheSolr Search: Filter by @name', array(
              '@name' => $vocab->name,
            )),
          );
        }
      }

      // Get CCK field facets.
      if ($fields = apachesolr_cck_fields()) {
        foreach ($fields as $name => $field) {
          $index_key = apachesolr_index_key($field);
          $label = db_result(db_query("SELECT label FROM {node_field_instance} WHERE field_name = '%s'", $name));

          // TODO: $index_key must be wrong here.
          $blocks[$index_key] = array(
            'info' => t('ApacheSolr Search: Filter by @field', array(
              '@field' => $label,
            )),
          );
        }
      }
      return $blocks;
    case 'view':
      if (arg(1) == 'apachesolr_search' && apachesolr_has_searched()) {

        // Get the query and response. Without these no blocks make sense.
        $response =& apachesolr_static_response_cache();
        if (empty($response)) {
          return;
        }
        $query =& apachesolr_drupal_query();

        // Get information needed by the rest of the blocks about limits.
        $facet_display_limits = variable_get('apachesolr_facet_query_limits', array());

        // Handle taxonomy vocabulary facets
        if (strpos($delta, 'imfield_vid') === 0 && module_exists('taxonomy')) {
          if (is_object($response->facet_counts->facet_fields->{$delta})) {
            $contains_active = FALSE;
            $terms = array();
            foreach ($response->facet_counts->facet_fields->{$delta} as $tid => $count) {
              $unclick_link = '';
              unset($active);
              $term = taxonomy_get_term($tid);
              $new_query = clone $query;
              if ($active = $query
                ->has_field('tid', $tid)) {
                $contains_active = TRUE;
                $new_query
                  ->remove_field('tid', $term->tid);
                $path = 'search/' . arg(1) . '/' . $new_query
                  ->get_query();
                $unclick_link = theme('apachesolr_unclick_link', $path);
              }
              else {
                $new_query
                  ->add_field('tid', $term->tid);
                $path = 'search/' . arg(1) . '/' . $new_query
                  ->get_query();
              }
              $countsort = $count == 0 ? '' : 1 / $count;

              // if numdocs == 1 and !active, don't add.
              if ($response->numFound == 1 && !$active) {

                // skip
              }
              else {
                $terms[$term->vid][$active ? $countsort . $term->name : 1 + $countsort . $term->name] = theme('apachesolr_facet_item', $term->name, $count, $path, $active, $unclick_link, $response->numFound);
              }
            }
          }
          $vid = substr($delta, 11);
          $vocab = taxonomy_get_vocabulary($vid);
          if (is_numeric($vid) && is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) {
            ksort($terms[$vid]);
            $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10;
            $terms[$vid] = array_slice($terms[$vid], 0, $facet_display_limit == -1 ? NULL : $facet_display_limit);
            return array(
              'subject' => t('Filter by @name', array(
                '@name' => $vocab->name,
              )),
              'content' => theme('apachesolr_facet_list', $terms[$vid]),
            );
          }
          else {
            return;
          }
        }
        switch ($delta) {
          case 'uid':
            $filter_by = t('Filter by author');
            return apachesolr_facet_block($response, $query, $delta, $filter_by, 'apachesolr_search_get_username');
          default:
            if ($fields = apachesolr_cck_fields()) {
              foreach ($fields as $name => $field) {
                $index_key = apachesolr_index_key($field);
                if ($index_key == $delta) {
                  if (!empty($response->facet_counts->facet_fields->{$index_key})) {
                    $contains_active = FALSE;
                    foreach ($response->facet_counts->facet_fields->{$index_key} as $facet => $count) {
                      $unclick_link = '';
                      unset($active);
                      $new_query = clone $query;
                      if ($active = $query
                        ->has_field($index_key, $facet)) {
                        $contains_active = TRUE;
                        $new_query
                          ->remove_field($index_key, $facet);
                        $path = 'search/' . arg(1) . '/' . $new_query
                          ->get_query();
                        $unclick_link = theme('apachesolr_unclick_link', $path);
                      }
                      else {
                        $new_query
                          ->add_field($index_key, $facet);
                        $path = 'search/' . arg(1) . '/' . $new_query
                          ->get_query();
                      }
                      $countsort = $count == 0 ? '' : 1 / $count;

                      // if numdocs == 1 and !active, don't add.
                      if ($response->numFound == 1 && !$active) {

                        // skip
                      }
                      else {
                        $facets[$active ? $countsort . $facet : 1 + $countsort . $facet] = theme('apachesolr_facet_item', $facet, $count, $path, $active, $unclick_link, $response->numFound);
                      }
                    }
                    if (is_array($facets)) {
                      ksort($facets);
                      $facet_display_limit = isset($facet_display_limits[$delta]) ? $facet_display_limits[$delta] : 10;
                      $facets = array_slice($facets, 0, $facet_display_limit == -1 ? NULL : $facet_display_limit);
                      $output = theme('apachesolr_facet_list', $facets);
                      $label = db_result(db_query("SELECT label FROM {node_field_instance} WHERE field_name = '%s'", $name));
                      return array(
                        'subject' => t('Filter by @field', array(
                          '@field' => $label,
                        )),
                        'content' => $output,
                      );
                    }
                  }
                }
              }
            }
        }
        break;
      }
      break;
    case 'configure':
      if ($delta != 'sort') {
        return apachesolr_facetcount_form($delta);
      }
      break;
    case 'save':
      if ($delta != 'sort') {
        apachesolr_facetcount_save($delta, $edit);
      }
      break;
  }
}
function apachesolr_search_get_username($facet) {
  if ($facet == 0) {
    return variable_get('anonymous', t('Anonymous'));
  }
  else {
    return db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $facet));
  }
}

/**
 * Return the username from $uid
 */
function theme_apachesolr_breadcrumb_uid($uid) {
  $user = user_load(array(
    'uid' => $uid,
  ));
  return $user->name;
}

/**
 * Return the term name from $tid.
 */
function theme_apachesolr_breadcrumb_tid($tid) {
  $term = taxonomy_get_term($tid);
  return $term->name;
}