В предыдущих статьях я рассказывал, как устанавливать и настраивать Apache Solr для организации поиска на Drupal. Кроме этого мы научились добавлять поля и настройки поискового индекса. В этой статье я расскажу о том, как добавить собственные поля и настройки, если стандартных недостаточно.
Для тех кто не читал предыдущие части, я рекомендую прочесть их для лучшего понимания материала.
- Поиск на Drupal 7 с помощью Apache Solr ч.1 — базовая настройка
- Поиск на Drupal 7 с помощью Apache Solr ч.2 — учимся настраивать индекс
- Поиск на Drupal 7 с помощью Apache Solr ч.3 — учимся добавлять собственные поля и опции в индекс
- Поиск на Drupal 7 с помощью Apache Solr ч.4 — фасетные фильтры
- Поиск на Drupal 7 с помощью Apache Solr ч.5 — виджеты для фасетных фильтров
- Поиск на Drupal 7 с помощью Apache Solr ч.6 — настраиваем apache solr + tomcat
- Поиск на Drupal 7 с помощью Apache Solr ч.7 — полнотекстовый поиск на русском языке
Поля
Итак начнем с добавления полей. Допустим у нод, которые вы индексируете есть референс на словарь с данными о геолокации. Его структура выглядит так:
Задача — индексировать только значения первого уровня, т.е. области к которым относится нода. Если мы добавим в индекс соответствующее поле, то индексироваться будут все термы, независимо от уровня. Поэтому нам потребуется добавить свое собственное поле. Для этого нам понадобятся две функции. Первая это хук entity_property_info_alter. В этом хуке можно добавить новые поля для сущности. Итак, добавим новое поля для ноды.
/**
* Implements hook_entity_property_info_alter.
*/
function test_search_entity_property_info_alter(&$info) {
$info['node']['properties']['geo_first_level'] = array(
'type' => 'text',
'label' => t('Geo 1 level'),
'getter callback' => 'test_search_geo_first_level_getter_callback',
);
}
Вторая функция — getter callback, в данном случае test_search_geo_first_level_getter_callback. Она должна возвращать значение для нашего поля в момент индексирования ноды. Это значение будет сохраняться в индексе.
/**
* Getter callback.
*/
function test_search_geo_first_level_getter_callback($item) {
if ($geo = field_get_items('node', $item, 'field_geo')) {
$parents = taxonomy_get_parents($geo[0]['tid']);
if (empty($parents)) {
if ($term = taxonomy_term_load($geo[0]['tid'])) {
return $term->name;
}
}
}
return NULL;
}
В качестве $item будет передаваться индексируемая сущность, в нашем случае это объект ноды. Делаем небольшую проверку на то, содержит ли эта нода, терм 1го уровня или нет и возвращаем имя терма в качестве индексируемого значения.
Теперь заходим в настройки индекса и выбираем вкладку Fields.
Наше новое поле уже доступно для индексирования. Не забудьте добавить его в список полей, по которым можно искать в настройках view.
Иногда может потребоваться индексировать несколько значений для поля. Для этого в хуке entity_property_info_alter укажите 'type' => 'list', а в геттер колбеке нужно будет возвращать массив значений.
Например
return array('Москва', 'Санкт-Петербург', 'Новосибирск');
Фильтры
Теперь попробуем добавить фильтр, по которому можно будет отбирать ноды для индексирования. Фильтр будет выполнять аналогичную функцию — позволять индексировать только те ноды, у которых есть референс на терм первого уровня из словаря geo. Для этого в хуке search_api_alter_callback_info объявим наш фильтр.
/**
* Implements hook_search_api_alter_callback_info().
*/
function test_search_search_api_alter_callback_info() {
$callbacks['search_api_alter_geo_level'] = array(
'name' => t('Filter by level of geo'),
'description' => t('Index only nodes with first level of term from vocabulary geo'),
'class' => 'SearchApiAlterGeoLevelFilter',
// Filters should be executed first.
'weight' => -10,
);
return $callbacks;
}
Файл с классом фильтра я расположил в папке includes модуля test_search. Не забудьте подключить его в .info файле вашего модуля. Например так:
files[] = includes/callback_geo_level.inc
Ниже приведен код самого фильтра
<?php
/**
* Search API data alteration callback that adds an URL field for all items.
*/
class SearchApiAlterGeoLevelFilter extends SearchApiAbstractAlterCallback {
public function alterItems(array &$items) {
foreach ($items as $id => $item) {
if ($geo = field_get_items('node', $item, 'field_geo')) {
$parents = taxonomy_get_parents($geo[0]['tid']);
// If term has parents.
if (!empty($parents)) {
unset($items[$id]);
}
}
}
}
public function supportsIndex(SearchApiIndex $index) {
return $index->item_type === 'node';
}
}
В функции alterItems мы просто исключаем элементы, которые имеют термы не первого уровня. Эти элементы не будут проиндексированы.
Чтобы включить фильтр, нужно почистить кеш и перейти на вкладке workflow в настройках индекса.
После того включения фильтра необходимо переиндексировать контент заново. Теперь в индекс попадут лишь те ноды, у которых есть терм словаря первого уровня.