You are here

search_api_facetapi.module in Search API 7

Integrates the Search API with the Facet API.

File

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

/**
 * @file
 * Integrates the Search API with the Facet API.
 */

/**
 * Implements hook_menu().
 */
function search_api_facetapi_menu() {

  // We need to handle our own menu paths for facets because we need a facet
  // configuration page per index.
  $first = TRUE;
  foreach (facetapi_get_realm_info() as $realm_name => $realm) {
    if ($first) {
      $first = FALSE;
      $items['admin/config/search/search_api/index/%search_api_index/facets'] = array(
        'title' => 'Facets',
        'page callback' => 'search_api_facetapi_settings',
        'page arguments' => array(
          $realm_name,
          5,
        ),
        'weight' => -1,
        'access arguments' => array(
          'administer search_api',
        ),
        'type' => MENU_LOCAL_TASK,
        'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
      );
      $items['admin/config/search/search_api/index/%search_api_index/facets/' . $realm_name] = array(
        'title' => $realm['label'],
        'type' => MENU_DEFAULT_LOCAL_TASK,
        'weight' => $realm['weight'],
      );
    }
    else {
      $items['admin/config/search/search_api/index/%search_api_index/facets/' . $realm_name] = array(
        'title' => $realm['label'],
        'page callback' => 'search_api_facetapi_settings',
        'page arguments' => array(
          $realm_name,
          5,
        ),
        'access arguments' => array(
          'administer search_api',
        ),
        'type' => MENU_LOCAL_TASK,
        'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
        'weight' => $realm['weight'],
      );
    }
  }
  return $items;
}

/**
 * Implements hook_facetapi_searcher_info().
 */
function search_api_facetapi_facetapi_searcher_info() {
  $info = array();
  $indexes = search_api_index_load_multiple(FALSE);
  foreach ($indexes as $index) {
    if (_search_api_facetapi_index_support_feature($index)) {
      $searcher_name = 'search_api@' . $index->machine_name;
      $info[$searcher_name] = array(
        'label' => t('Search service: @name', array(
          '@name' => $index->name,
        )),
        'adapter' => 'search_api',
        'instance' => $index->machine_name,
        'types' => array(
          $index->item_type,
        ),
        'path' => '',
        'supports facet missing' => TRUE,
        'supports facet mincount' => TRUE,
        'include default facets' => FALSE,
      );
      if (($entity_type = $index
        ->getEntityType()) && $entity_type !== $index->item_type) {
        $info[$searcher_name]['types'][] = $entity_type;
      }
    }
  }
  return $info;
}

/**
 * Implements hook_facetapi_facet_info().
 */
function search_api_facetapi_facetapi_facet_info(array $searcher_info) {
  $facet_info = array();
  if ('search_api' == $searcher_info['adapter']) {
    $index = search_api_index_load($searcher_info['instance']);
    if (!empty($index->options['fields'])) {
      $wrapper = $index
        ->entityWrapper();
      $bundle_key = NULL;
      if ($index
        ->getEntityType() && ($entity_info = entity_get_info($index
        ->getEntityType())) && !empty($entity_info['bundle keys']['bundle'])) {
        $bundle_key = $entity_info['bundle keys']['bundle'];
      }

      // Some type-specific settings. Allowing to set some additional callbacks
      // (and other settings) in the map options allows for easier overriding by
      // other modules.
      $type_settings = array(
        'taxonomy_term' => array(
          'hierarchy callback' => 'search_api_facetapi_get_taxonomy_hierarchy',
        ),
        'date' => array(
          'query type' => 'date',
          'map options' => array(
            'map callback' => 'search_api_facetapi_map_date',
          ),
        ),
      );

      // Iterate through the indexed fields to set the facetapi settings for
      // each one.
      foreach ($index
        ->getFields() as $key => $field) {
        $field['key'] = $key;

        // Determine which, if any, of the field type-specific options will be
        // used for this field.
        if (isset($field['entity_type'])) {
          $type = $inner_type = $field['entity_type'];
        }
        else {
          $type = $field['type'];
          $inner_type = search_api_extract_inner_type($type);
        }
        $type_settings += array(
          $inner_type => array(),
        );
        $facet_info[$key] = $type_settings[$inner_type] + array(
          'label' => $field['name'],
          'description' => t('Filter by @type.', array(
            '@type' => $field['name'],
          )),
          'allowed operators' => array(
            FACETAPI_OPERATOR_AND => TRUE,
            FACETAPI_OPERATOR_OR => _search_api_facetapi_index_support_feature($index, 'search_api_facets_operator_or'),
          ),
          'dependency plugins' => array(
            'role',
          ),
          'facet missing allowed' => TRUE,
          'facet mincount allowed' => TRUE,
          'map callback' => 'search_api_facetapi_facet_map_callback',
          'map options' => array(),
          'field type' => $type,
        );
        if ($inner_type === 'date') {
          $facet_info[$key]['description'] .= ' ' . t('(Caution: This may perform very poorly for large result sets.)');
        }
        $facet_info[$key]['map options'] += array(
          'field' => $field,
          'index id' => $index->machine_name,
          'value callback' => '_search_api_facetapi_facet_create_label',
        );

        // Find out whether this property is a Field API field.
        if (strpos($key, ':') === FALSE) {
          if (isset($wrapper->{$key})) {
            $property_info = $wrapper->{$key}
              ->info();
            if (!empty($property_info['field'])) {
              $facet_info[$key]['field api name'] = $key;
            }
          }
        }

        // Add bundle information, if applicable.
        if ($bundle_key) {
          if ($key === $bundle_key) {

            // Set entity type this field contains bundle information for.
            $facet_info[$key]['field api bundles'][] = $index
              ->getEntityType();
          }
          else {

            // Add "bundle" as possible dependency plugin.
            $facet_info[$key]['dependency plugins'][] = 'bundle';
          }
        }
      }
    }
  }
  return $facet_info;
}

/**
 * Implements hook_facetapi_adapters().
 */
function search_api_facetapi_facetapi_adapters() {
  return array(
    'search_api' => array(
      'handler' => array(
        'class' => 'SearchApiFacetapiAdapter',
      ),
    ),
  );
}

/**
 * Implements hook_facetapi_query_types().
 */
function search_api_facetapi_facetapi_query_types() {
  return array(
    'search_api_term' => array(
      'handler' => array(
        'class' => 'SearchApiFacetapiTerm',
        'adapter' => 'search_api',
      ),
    ),
    'search_api_date' => array(
      'handler' => array(
        'class' => 'SearchApiFacetapiDate',
        'adapter' => 'search_api',
      ),
    ),
  );
}

/**
 * Implements hook_search_api_query_alter().
 *
 * Adds Facet API support to the query.
 */
function search_api_facetapi_search_api_query_alter($query) {
  $index = $query
    ->getIndex();
  if ($index
    ->server()
    ->supportsFeature('search_api_facets')) {

    // This is the main point of communication between the facet system and the
    // search back-end - it makes the query respond to active facets.
    search_api_facetapi_current_search_path($query);
    $searcher = 'search_api@' . $index->machine_name;
    $adapter = facetapi_adapter_load($searcher);
    if ($adapter) {
      $adapter
        ->addActiveFilters($query);
    }
  }
}

/**
 * Implements hook_date_formats().
 */
function search_api_facetapi_date_formats() {
  return array(
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_YEAR,
      'format' => 'Y',
      'locales' => array(),
    ),
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_MONTH,
      'format' => 'F Y',
      'locales' => array(),
    ),
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_DAY,
      'format' => 'F j, Y',
      'locales' => array(),
    ),
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_HOUR,
      'format' => 'H:__',
      'locales' => array(),
    ),
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_MINUTE,
      'format' => 'H:i',
      'locales' => array(),
    ),
    array(
      'type' => 'search_api_facetapi_' . FACETAPI_DATE_SECOND,
      'format' => 'H:i:s',
      'locales' => array(),
    ),
  );
}

/**
 * Implements hook_date_format_types().
 */
function search_api_facetapi_date_format_types() {
  return array(
    'search_api_facetapi_' . FACETAPI_DATE_YEAR => t('Search facets - Years'),
    'search_api_facetapi_' . FACETAPI_DATE_MONTH => t('Search facets - Months'),
    'search_api_facetapi_' . FACETAPI_DATE_DAY => t('Search facets - Days'),
    'search_api_facetapi_' . FACETAPI_DATE_HOUR => t('Search facets - Hours'),
    'search_api_facetapi_' . FACETAPI_DATE_MINUTE => t('Search facets - Minutes'),
    'search_api_facetapi_' . FACETAPI_DATE_SECOND => t('Search facets - Seconds'),
  );
}

/**
 * Helper function that statically stores the Search API's base path. Can
 * either be used to store or retrieve the base path.
 *
 * This static store is used in SearchApiFacetapiAdapter::getSearchPath() in
 * order to retrieve the current search base path. Just relying on
 * search_api_current_search() to retrieve the current query is not enough, as
 * the path might be requested before the query is finally executed and stored.
 *
 * @param SearchApiQueryInterface $query
 *   When storing the base base, the query that is executed. Else NULL.
 *
 * @return
 *   The Search API's base path.
 */
function search_api_facetapi_current_search_path(SearchApiQueryInterface $query = NULL) {
  $path =& drupal_static(__FUNCTION__, '');
  if ($query && $query
    ->getOption('search_api_base_path')) {
    $path = $query
      ->getOption('search_api_base_path');
  }
  return $path;
}

/**
 * Menu callback for the facet settings page.
 */
function search_api_facetapi_settings($realm_name, SearchApiIndex $index) {
  if (!$index->enabled) {
    return array(
      '#markup' => t('Since this index is at the moment disabled, no facets can be activated.'),
    );
  }
  if (!_search_api_facetapi_index_support_feature($index)) {
    return array(
      '#markup' => t('This index uses a server that does not support facet functionality.'),
    );
  }
  $searcher_name = 'search_api@' . $index->machine_name;
  module_load_include('inc', 'facetapi', 'facetapi.admin');
  return drupal_get_form('facetapi_realm_settings_form', $searcher_name, $realm_name);
}

/**
 * Checks whether a certain feature is supported for an index.
 *
 * @param SearchApiIndex $index
 *   The search index which should be checked.
 * @param string $feature
 *   (optional) The feature to check for. Defaults to "search_api_facets".
 *
 * @return bool
 *   TRUE if the feature is supported by the index's server (and the index is
 *   currently enabled), FALSE otherwise.
 */
function _search_api_facetapi_index_support_feature(SearchApiIndex $index, $feature = 'search_api_facets') {
  try {
    $server = $index
      ->server();
    return $server && $server
      ->supportsFeature($feature);
  } catch (SearchApiException $e) {
    return FALSE;
  }
}

/**
 * Gets hierarchy information for taxonomy terms.
 *
 * Used as a hierarchy callback in search_api_facetapi_facetapi_facet_info().
 *
 * Internally just uses facetapi_get_taxonomy_hierarchy(), but makes sure that
 * our special "!" value is not passed.
 *
 * @param array $values
 *   An array containing the term IDs.
 *
 * @return array
 *   An associative array mapping term IDs to parent IDs (where parents could be
 *   found).
 */
function search_api_facetapi_get_taxonomy_hierarchy(array $values) {
  $values = array_filter($values, 'is_numeric');
  return $values ? facetapi_get_taxonomy_hierarchy($values) : array();
}

/**
 * Map callback for all search_api facet fields.
 *
 * @param array $values
 *   The values to map.
 * @param array $options
 *   An associative array containing:
 *   - field: Field information, as stored in the index, but with an additional
 *     "key" property set to the field's internal name.
 *   - index id: The machine name of the index for this facet.
 *   - map callback: (optional) A callback that will be called at the beginning,
 *     which allows initial mapping of filters. Only values not mapped by that
 *     callback will be processed by this method.
 *   - value callback: A callback used to map single values and the limits of
 *     ranges. The signature is the same as for this function, but all values
 *     will be single values.
 *   - missing label: (optional) The label used for the "missing" facet.
 *
 * @return array
 *   An array mapping raw filter values to their labels.
 */
function search_api_facetapi_facet_map_callback(array $values, array $options = array()) {
  $map = array();

  // See if we have an additional map callback.
  if (isset($options['map callback']) && is_callable($options['map callback'])) {
    $map = call_user_func($options['map callback'], $values, $options);
  }

  // Then look at all unmapped values and save information for them.
  $mappable_values = array();
  $ranges = array();
  foreach ($values as $value) {
    $value = (string) $value;
    if (isset($map[$value])) {
      continue;
    }
    if ($value == '!') {

      // The "missing" filter is usually always the same, but we allow an easy
      // override via the "missing label" map option.
      $map['!'] = isset($options['missing label']) ? $options['missing label'] : '(' . t('none') . ')';
      continue;
    }
    $length = strlen($value);
    if ($length > 5 && $value[0] == '[' && $value[$length - 1] == ']' && ($pos = strpos($value, ' TO '))) {

      // This is a range filter.
      $lower = trim(substr($value, 1, $pos));
      $upper = trim(substr($value, $pos + 4, -1));
      if ($lower != '*') {
        $mappable_values[$lower] = TRUE;
      }
      if ($upper != '*') {
        $mappable_values[$upper] = TRUE;
      }
      $ranges[$value] = array(
        'lower' => $lower,
        'upper' => $upper,
      );
    }
    else {

      // A normal, single-value filter.
      $mappable_values[$value] = TRUE;
    }
  }
  if ($mappable_values) {
    $map += call_user_func($options['value callback'], array_keys($mappable_values), $options);
  }
  foreach ($ranges as $value => $range) {
    $lower = isset($map[$range['lower']]) ? $map[$range['lower']] : $range['lower'];
    $upper = isset($map[$range['upper']]) ? $map[$range['upper']] : $range['upper'];
    if ($lower == '*' && $upper == '*') {
      $map[$value] = t('any');
    }
    elseif ($lower == '*') {
      $map[$value] = "≤ {$upper}";
    }
    elseif ($upper == '*') {
      $map[$value] = "≥ {$lower}";
    }
    else {
      $map[$value] = "{$lower} – {$upper}";
    }
  }
  return $map;
}

/**
 * Creates a human-readable label for single facet filter values.
 *
 * @param array $values
 *   The values for which labels should be returned.
 * @param array $options
 *   An associative array containing the following information about the facet:
 *   - field: Field information, as stored in the index, but with an additional
 *     "key" property set to the field's internal name.
 *   - index id: The machine name of the index for this facet.
 *   - map callback: (optional) A callback that will be called at the beginning,
 *     which allows initial mapping of filters. Only values not mapped by that
 *     callback will be processed by this method.
 *   - value callback: A callback used to map single values and the limits of
 *     ranges. The signature is the same as for this function, but all values
 *     will be single values.
 *   - missing label: (optional) The label used for the "missing" facet.
 *
 * @return array
 *   An array mapping raw facet values to their labels.
 */
function _search_api_facetapi_facet_create_label(array $values, array $options) {
  $field = $options['field'];
  $map = array();
  $n = count($values);

  // For entities, we can simply use the entity labels.
  if (isset($field['entity_type'])) {
    $type = $field['entity_type'];
    $entities = entity_load($type, $values);
    foreach ($entities as $id => $entity) {
      $label = entity_label($type, $entity);
      if ($label !== FALSE) {
        $map[$id] = $label;
      }
    }
    if (count($map) == $n) {
      return $map;
    }
  }

  // Then, we check whether there is an options list for the field.
  $index = search_api_index_load($options['index id']);
  $wrapper = $index
    ->entityWrapper();
  $values = drupal_map_assoc($values);
  foreach (explode(':', $field['key']) as $part) {
    if (!isset($wrapper->{$part})) {
      $wrapper = NULL;
      break;
    }
    $wrapper = $wrapper->{$part};
    while (($info = $wrapper
      ->info()) && search_api_is_list_type($info['type'])) {
      $wrapper = $wrapper[0];
    }
  }
  if ($wrapper && ($options_list = $wrapper
    ->optionsList('view'))) {

    // We have no use for empty strings, as then the facet links would be
    // invisible.
    $map += array_intersect_key(array_filter($options_list, 'strlen'), $values);
    if (count($map) == $n) {
      return $map;
    }
  }

  // As a "last resort" we try to create a label based on the field type, for
  // all values that haven't got a mapping yet.
  foreach (array_diff_key($values, $map) as $value) {
    switch ($field['type']) {
      case 'boolean':
        $map[$value] = $value ? t('true') : t('false');
        break;
      case 'date':
        $v = is_numeric($value) ? $value : strtotime($value);
        $map[$value] = format_date($v, 'short');
        break;
      case 'duration':
        $map[$value] = format_interval($value);
        break;
    }
  }
  return $map;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function search_api_facetapi_form_search_api_admin_index_fields_alter(&$form, &$form_state) {
  $form['#submit'][] = 'search_api_facetapi_search_api_admin_index_fields_submit';
}

/**
 * Form submission handler for search_api_admin_index_fields().
 */
function search_api_facetapi_search_api_admin_index_fields_submit($form, &$form_state) {

  // Clears this searcher's cached facet definitions.
  $cid = 'facetapi:facet_info:search_api@' . $form_state['index']->machine_name . ':';
  cache_clear_all($cid, 'cache', TRUE);
}

/**
 * Implements hook_form_FORM_ID_alter() for views_exposed_form().
 *
 * Custom integration for facets. When a Views exposed filter is modified on a
 * search results page, any facets which have been already selected will be
 * removed. This (optionally) adds hidden fields for each facet so their values
 * are retained.
 */
function search_api_facetapi_form_views_exposed_form_alter(array &$form, array &$form_state) {
  if (empty($form_state['view'])) {
    return;
  }
  $view = $form_state['view'];

  // Check if this is a Search API-based view and if the "Preserve facets"
  // option is enabled. ("search_api_multi" would be the exact base table name,
  // not just a prefix, but since it's just 16 characters long, we can still use
  // this check to make the condition less complex.)
  $base_table_prefix = substr($view->base_table, 0, 17);
  if (in_array($base_table_prefix, array(
    'search_api_index_',
    'search_api_multi',
  )) && _search_api_preserve_views_facets($view)) {

    // Get query parameters.
    $query_parameters = drupal_get_query_parameters();

    // Check if any facet query parameters are provided.
    if (!empty($query_parameters['f'])) {

      // Iterate through facet query parameters.
      foreach ($query_parameters['f'] as $key => $value) {

        // Add hidden form field for facet parameter.
        $form['f[' . $key . ']'] = array(
          '#type' => 'hidden',
          '#value' => $value,
          '#weight' => -1,
        );
      }
    }
  }
}

/**
 * Checks whether "Preserve facets" option is enabled on the given view.
 *
 * If the view display is overridden, use its configuration. Otherwise, use the
 * default configuration.
 *
 * @param view $view
 *   The search view.
 *
* @return bool
*   TRUE if "Preserve facets" is enabled, FALSE otherwise.
*/
function _search_api_preserve_views_facets(view $view) {
  $query_options = $view->display_handler
    ->get_option('query');
  return !empty($query_options['options']['preserve_facet_query_args']);
}

/**
 * Computes the granularity of a date facet filter.
 *
 * @param $filter
 *   The filter value to examine.
 *
 * @return string|null
 *   Either one of the FACETAPI_DATE_* constants corresponding to the
 *   granularity of the filter, or NULL if it couldn't be computed.
 */
function search_api_facetapi_date_get_granularity($filter) {

  // Granularity corresponds to number of dashes in filter value.
  $units = array(
    FACETAPI_DATE_YEAR,
    FACETAPI_DATE_MONTH,
    FACETAPI_DATE_DAY,
    FACETAPI_DATE_HOUR,
    FACETAPI_DATE_MINUTE,
    FACETAPI_DATE_SECOND,
  );
  $count = substr_count($filter, '-');
  return isset($units[$count]) ? $units[$count] : NULL;
}

/**
 * Returns the date format used for a given granularity.
 *
 * @param $granularity
 *   One of the FACETAPI_DATE_* constants.
 *
 * @return string
 *   The date format used for the given granularity.
 */
function search_api_facetapi_date_get_granularity_format($granularity) {
  $formats = array(
    FACETAPI_DATE_YEAR => 'Y',
    FACETAPI_DATE_MONTH => 'Y-m',
    FACETAPI_DATE_DAY => 'Y-m-d',
    FACETAPI_DATE_HOUR => 'Y-m-d-H',
    FACETAPI_DATE_MINUTE => 'Y-m-d-H-i',
    FACETAPI_DATE_SECOND => 'Y-m-d-H-i-s',
  );
  return $formats[$granularity];
}

/**
 * Constructs labels for date facet filter values.
 *
 * @param array $values
 *   The date facet filter values, as used in URL parameters.
 * @param array $options
 *   (optional) Options for creating the mapping. The following options are
 *   recognized:
 *   - format callback: A callback for creating a label for a timestamp. The
 *     function signature is like search_api_facetapi_format_timestamp(),
 *     receiving a timestamp and one of the FACETAPI_DATE_* constants as the
 *     parameters and returning a human-readable label.
 *
 * @return array
 *   An array of labels for the given facet filters.
 */
function search_api_facetapi_map_date(array $values, array $options = array()) {
  $map = array();
  foreach ($values as $value) {

    // Ignore any filters passed directly from the server (range or missing). We
    // always create filters starting with a year.
    $value = "{$value}";
    if (!$value || !ctype_digit($value[0])) {
      continue;
    }

    // Get the granularity of the filter.
    $granularity = search_api_facetapi_date_get_granularity($value);
    if (!$granularity) {
      continue;
    }

    // Otherwise, parse the timestamp from the known format and format it as a
    // label.
    $format = search_api_facetapi_date_get_granularity_format($granularity);

    // Use the "!" modifier to make the date parsing independent of the current
    // date/time. (See #2678856.)
    $date = DateTime::createFromFormat('!' . $format, $value);
    if (!$date) {
      continue;
    }
    $format_callback = 'search_api_facetapi_format_timestamp';
    if (!empty($options['format callback']) && is_callable($options['format callback'])) {
      $format_callback = $options['format callback'];
    }
    $map[$value] = call_user_func($format_callback, $date
      ->format('U'), $granularity);
  }
  return $map;
}

/**
 * Format a date according to the default timezone and the given precision.
 *
 * @param int $timestamp
 *   An integer containing the Unix timestamp being formated.
 * @param string $precision
 *   A string containing the formatting precision. See the FACETAPI_DATE_*
 *   constants for valid values.
 *
 * @return string
 *   A human-readable representation of the timestamp.
 */
function search_api_facetapi_format_timestamp($timestamp, $precision = FACETAPI_DATE_YEAR) {
  $formats = array(
    FACETAPI_DATE_YEAR,
    FACETAPI_DATE_MONTH,
    FACETAPI_DATE_DAY,
    FACETAPI_DATE_HOUR,
    FACETAPI_DATE_MINUTE,
    FACETAPI_DATE_SECOND,
  );
  if (!in_array($precision, $formats)) {
    $precision = FACETAPI_DATE_YEAR;
  }
  return format_date($timestamp, 'search_api_facetapi_' . $precision);
}

Functions

Namesort descending Description
search_api_facetapi_current_search_path Helper function that statically stores the Search API's base path. Can either be used to store or retrieve the base path.
search_api_facetapi_date_formats Implements hook_date_formats().
search_api_facetapi_date_format_types Implements hook_date_format_types().
search_api_facetapi_date_get_granularity Computes the granularity of a date facet filter.
search_api_facetapi_date_get_granularity_format Returns the date format used for a given granularity.
search_api_facetapi_facetapi_adapters Implements hook_facetapi_adapters().
search_api_facetapi_facetapi_facet_info Implements hook_facetapi_facet_info().
search_api_facetapi_facetapi_query_types Implements hook_facetapi_query_types().
search_api_facetapi_facetapi_searcher_info Implements hook_facetapi_searcher_info().
search_api_facetapi_facet_map_callback Map callback for all search_api facet fields.
search_api_facetapi_format_timestamp Format a date according to the default timezone and the given precision.
search_api_facetapi_form_search_api_admin_index_fields_alter Implements hook_form_FORM_ID_alter().
search_api_facetapi_form_views_exposed_form_alter Implements hook_form_FORM_ID_alter() for views_exposed_form().
search_api_facetapi_get_taxonomy_hierarchy Gets hierarchy information for taxonomy terms.
search_api_facetapi_map_date Constructs labels for date facet filter values.
search_api_facetapi_menu Implements hook_menu().
search_api_facetapi_search_api_admin_index_fields_submit Form submission handler for search_api_admin_index_fields().
search_api_facetapi_search_api_query_alter Implements hook_search_api_query_alter().
search_api_facetapi_settings Menu callback for the facet settings page.
_search_api_facetapi_facet_create_label Creates a human-readable label for single facet filter values.
_search_api_facetapi_index_support_feature Checks whether a certain feature is supported for an index.
_search_api_preserve_views_facets Checks whether "Preserve facets" option is enabled on the given view.