You are here

facetapi_apachesolr.module in Facet API 6

The Apache Solr Search Integration module's implementation of the the Facet API.

File

contrib/facetapi_apachesolr/facetapi_apachesolr.module
View source
<?php

/**
 * @file
 * The Apache Solr Search Integration module's implementation of the the Facet
 * API.
 */

/**
 * Implementation of hook_menu().
 */
function facetapi_apachesolr_menu() {
  $items = array();
  $items['admin/settings/apachesolr/facets'] = array(
    'title' => 'Facets',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'facetapi_admin_settings_form',
      'apachesolr_search',
    ),
    'access arguments' => array(
      'administer search',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'facetapi.admin.inc',
    'file path' => drupal_get_path('module', 'facetapi'),
    'weight' => -9,
  );
  return $items;
}

/**
 * Implementation of hook_menu_alter().
 */
function facetapi_apachesolr_menu_alter(&$items) {
  $items['admin/settings/apachesolr/enabled-filters']['access callback'] = FALSE;
}

/**
 * Implementation of hook_facetapi_adapter_info().
 */
function facetapi_apachesolr_facetapi_adapter_info() {
  return array(
    'apachesolr_search' => array(
      'class' => 'FacetapiApachesolrAdapter',
      'type' => 'node',
      'file' => 'facetapi_apachesolr.adapter.inc',
    ),
  );
}

/**
 * Implementation of hook_facetapi_facet_info_alter().
 *
 * Modifies fields for vocabulary facets.
 */
function facetapi_apachesolr_facetapi_facet_info_alter(array &$facets, $searcher, $type) {
  if ('apachesolr_search' == $searcher) {
    foreach ($facets as &$facet) {
      if (preg_match('/^vocabulary_(\\d+)$/', $facet['name'], $matches)) {
        $facet['field'] = 'im_vid_' . $matches[1];
        $facet['field alias'] = 'tid';
      }
    }
    unset($facet);
  }
}

/**
 * Implementation of hook_apachesolr_prepare_query().
 *
 * Invokes type hooks, adds filters.
 */
function facetapi_apachesolr_apachesolr_prepare_query($query, &$params, $caller) {
  facetapi_query_type_hooks_invoke('apachesolr_search', $params, $query);

  // Gets enabled facets, adds filter queries to $params.
  $adapter = facetapi_adapter_load('apachesolr_search');
  foreach (facetapi_enabled_facets_get('apachesolr_search') as $facet) {
    $queries = array();
    foreach ($adapter
      ->getActiveItems($facet) as $value => $item) {
      $queries[] = $facet['field alias'] . ':' . $value;
    }
    if (!empty($queries)) {
      $params['fq'][$facet['field alias']] = $queries;
    }
  }
}

/**
 * Implementation of hook_facetapi_query_QUERY_TYPE_prepare().
 */
function facetapi_apachesolr_facetapi_query_term_prepare(FacetapiAdapter $adapter, array $facet, &$params, $query) {
  $searcher = $adapter
    ->getSearcher();

  // Adds the operator parameter.
  $operator = facetapi_setting_get('operator', $searcher, '', $facet['name']);
  $ex = FACETAPI_OPERATOR_OR != $operator ? '' : "{!ex={$facet['field']}}";
  $params['facet.field'][] = $ex . $facet['field'];

  // Adds "hard limit" parameter to prevent too many return values.
  $limit = facetapi_setting_get('hard_limit', $searcher, '', $facet['name']);
  $params['f.' . $facet['field'] . '.facet.limit'] = $limit !== NULL ? (int) $limit : 20;
}

/**
 * Implementation of hook_facetapi_query_QUERY_TYPE_prepare().
 */
function facetapi_apachesolr_facetapi_query_date_prepare(FacetapiAdapter $adapter, array $facet, &$params, $query) {
  $searcher = $adapter
    ->getSearcher();

  // Gets the data range in formats that Solr understands.
  list($start, $end, $gap) = facetapi_apachesolr_date_range($query, $facet['field']);
  $params['facet.date'][] = $facet['field'];
  $params['f.' . $facet['field'] . '.facet.date.start'] = $start;
  $params['f.' . $facet['field'] . '.facet.date.end'] = $end;
  $params['f.' . $facet['field'] . '.facet.date.gap'] = $gap;

  // Adds "hard limit" parameter to prevent too many return values.
  $limit = facetapi_setting_get('hard_limit', $searcher, '', $facet['name']);
  $params['f.' . $facet['field'] . '.facet.limit'] = $limit !== NULL ? (int) $limit : 20;
}

/**
 * Gets the range of dates we are using.
 *
 * @param $query
 *   A Solr_Base_Query object.
 * @param $facet_field
 *   A string containing the name of the facet field.
 *
 * @return
 *   An array containing the gap and range information.
 *
 * @todo integrate this into facetapi_apachesolr_facetapi_date_range_prepare()
 *       once we have a "range limit callback".
 */
function facetapi_apachesolr_date_range(Solr_Base_Query $query, $facet_field) {

  // Captures adapter, facet.
  // @todo Do we need some defensive coding here?
  // @todo Is facet field the facet name or the field alias?
  $adapter = facetapi_adapter_load('apachesolr_search');
  $enabled_facets = facetapi_enabled_facets_get('apachesolr_search');
  $facet = $enabled_facets[$facet_field];

  // Attempts to get next gap from passed date filters.
  $return = NULL;
  foreach ($adapter
    ->getActiveItems($facet) as $value => $item) {
    if ($gap = facetapi_date_gap_get($item['start'], $item['end'])) {
      $next_gap = facetapi_next_date_gap_get($gap, FACETAPI_DATE_MINUTE);
      if ($next_gap == $gap) {
        $next_gap = NULL;
      }
      $return = array(
        "{$item['start']}/{$next_gap}",
        "{$item['end']}+1{$next_gap}/{$next_gap}",
        "+1{$next_gap}",
      );
    }
  }

  // If no filters were passed, get default range.
  if (NULL === $return) {

    // Builds SQL that gets minimum and maximum values from node table.
    $minimum = $maximum = FALSE;
    if (!empty($facet['min callback']) && function_exists($facet['min callback'])) {
      $minimum = $facet['min callback']($facet);
    }
    if (!empty($facet['max callback']) && function_exists($facet['max callback'])) {
      $maximum = $facet['max callback']($facet);
    }

    // Gets the default gap.
    $gap = FACETAPI_DATE_YEAR;
    if ($minimum && $maximum) {
      $gap = facetapi_timestamp_gap_get($minimum, $maximum);
      $minimum = facetapi_isodate($minimum, $gap);
      $maximum = facetapi_isodate($maximum, $gap);
      $return = array(
        "{$minimum}/{$gap}",
        "{$maximum}+1{$gap}/{$gap}",
        "+1{$gap}",
      );
    }
  }

  // Returns the range information.
  return $return;
}

/**
 * Implementation of hook_facetapi_facet_QUERY_TYPE_build().
 */
function facetapi_apachesolr_facetapi_facet_term_build(FacetapiAdapter $adapter, array $facet) {
  $build = array();
  if ($response = apachesolr_static_response_cache()) {
    $values = (array) $response->facet_counts->facet_fields->{$facet['field']};
    foreach ($values as $value => $count) {
      $build[$value] = array(
        '#count' => $count,
      );
    }
  }
  return $build;
}

/**
 * Implementation of hook_facetapi_facet_QUERY_TYPE_build().
 */
function facetapi_apachesolr_facetapi_facet_date_build(FacetapiAdapter $adapter, array $facet) {
  $build = array();
  if (!($response = apachesolr_static_response_cache())) {
    return array();
  }

  // Gets total number of documents matched in search.
  // NOTE: We need a Solr_Base_Query::get_solr() method.
  static $total;
  if (NULL === $total) {
    if ($raw_response = json_decode($response
      ->getRawResponse())) {
      $total = $raw_response->response->numFound;
    }
    else {
      $total = 0;
    }
  }

  // Gets the active date facets, starts to builds the "parent - child"
  // relationships.
  $parent = NULL;
  foreach ($adapter
    ->getActiveItems($facet) as $value => $item) {

    // Builds the raw facet "value", the count for selected items will be the
    // total number of rows returned in the query.
    $build[$value] = array(
      '#count' => $total,
    );

    // If there is a previous item, there is a parent, uses a reference so the
    // arrays are populated when they are updated.
    if (NULL !== $parent) {
      $build[$parent]['#item_children'][$value] =& $build[$value];
      $build[$value]['#item_parents'][$parent] = $parent;
    }

    // Stores the last value iterated over.
    $parent = $value;
  }

  // Gets raw facet data from the Solr server.
  if (isset($response->facet_counts->facet_dates)) {
    $raw_data = (array) $response->facet_counts->facet_dates->{$facet['field']};
  }
  else {
    $raw_data = array();
  }
  $end = !empty($raw_data['end']) ? $raw_data['end'] : '';
  $gap = !empty($raw_data['gap']) ? $raw_data['gap'] : '';
  unset($raw_data['end']);
  unset($raw_data['gap']);

  // Treat each date facet as a range start, and use the next date facet
  // as range end.  Use 'end' for the final end.
  $range_end = array();
  $previous = NULL;
  foreach ($raw_data as $value => $count) {
    if (isset($previous)) {
      $range_end[$previous] = $value;
    }
    $previous = $value;
  }
  $range_end[$previous] = $end;

  // Builds facet counts object used by the server.
  foreach ($raw_data as $value => $count) {
    if ($count) {
      $new_value = '[' . $value . ' TO ' . $range_end[$value] . ']';
      $build[$new_value] = array(
        '#count' => $count,
        '#active' => 0,
      );
      if (NULL !== $parent) {
        $build[$parent]['#item_children'][$new_value] =& $build[$new_value];
        $build[$new_value]['#item_parents'][$parent] = $parent;
      }
    }
  }
  return $build;
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Adds breadcrumb trail for Apache Solr administrative pages.
 */
function facetapi_apachesolr_form_facetapi_facet_settings_form_alter(&$form, &$form_state) {
  if ('apachesolr_search' == arg(2)) {
    $breadcrumb = drupal_get_breadcrumb();
    $breadcrumb[] = l(t('Apache Solr'), 'admin/settings/apachesolr');
    $breadcrumb[] = l(t('Facets'), 'admin/settings/apachesolr/facets');
    drupal_set_breadcrumb($breadcrumb);
  }
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Hides Apache Solr core facet settings.
 */
function facetapi_apachesolr_form_apachesolr_settings_alter(&$form, &$form_state) {
  $form['apachesolr_facetstyle'] = array(
    '#type' => 'value',
    '#value' => $form['apachesolr_facetstyle']['#default_value'],
  );
  $form['apachesolr_search_browse'] = array(
    '#type' => 'value',
    '#value' => $form['apachesolr_search_browse']['#default_value'],
  );
}