Работа с Facet API и Apache Solr. Часть 3

Привет.

Продолжаю свои заметки про работу с поисковиком Solr. В прошлых постах я писал о том, как добавлять в индекс дополнительные поля, как управлять процессом индексации определенного поля и как научить solr искать в этих полях. Сегодня расскажу о том, как можно договориться с solr’ом о том, что индексировать, а что нет. Оговорюсь, что нижеприведенный способ работает адекватно только начиная с версии apachesolr-1.x-beta19, раньше этого не было. Итак, рассказываю.

На самом деле рассказывать то особо и нечего, всё потрясающе просто. Представим, что по определенным признакам мы не хотим индексировать ноду, для этого у apachesolr есть status callback. В моем случае у типа материалов “публикация” есть чекбокс “Индексируемая”, по умолчанию отмеченный, но если галочку снимут, то публикация не будет ни индексироваться, ни, само собой, выводиться в результатах поиска. Сделано это следующим образом:

<?php
// Для начала мы должны сообщить модулю apachesolr о использовании кастомного коллбэка. Делается это с помощью
// реализации хука <strong>hook_apachesolr_entity_info_alter()</strong>, описанного в файле apachesolr.api.php.
// Таких коллбэков может быть несколько.
function inti_apachesolr_entity_info_alter(&$entity_info) {
  // Способ, как видите, работает не только для нод, а для любых сущностей.
  $entity_info['node']['status callback'][] = 'inti_index_node_status_callback';
}

// Эта функция должна возвращать TRUE, если публикация индексируемая, иначе FALSE.
// Коллбэк по умолчанию проверяет, опубликована ли нода. Учитывая, что коллбэков может быть
// несколько, как я написал выше, здесь мне это проверять нет нужды. 
function inti_index_node_status_callback($entity_id, $entity_type) {
  $node = node_load($entity_id, NULL, TRUE);
  $status = TRUE;
  if (($node->type == 'publication') && !empty($node->field_do_search)) {
    $status = ($node->field_do_search['und'][0]['value']) ? TRUE : FALSE;
  }
  return $status;
}
?>

Вот и всё. Важное примечание: как я понял, после изменения функции, реализующей hook_apachesolr_entity_info_alter(), нужно очистить кэш.

В следующий раз будет пост (тоже небольшой) о том, как научить Apache Solr искать с использованием wildcard(*). Как ни странно, но по умолчанию, поддержки поиска по частям слов нет.

Ссылки:
Часть 1. О добавлении полей в индекс и фасетах.
Часть 2. О подмене коллбэка для индексации определенного полям и о том, как искать по дополнительным полям.
Часть 4. Установка Solr 3.x и поиск с использованием *

Источник: http://www.drupal.ru/node/80497

Работа с Facet API и Apache Solr. Часть 2

Всем привет. В предыдущем посте я рассказал, как можно “повлиять” на процесс индексации, добавляя дополнительные поля в индекс, и создавать свои фасеты (фильтры) с помощью Facet API. Сегодня я хочу рассказать об альтернативном способе индексирования полей и возможности поиска по дополнительным полям. Дело в том, что Solr не ищет по всем полям, а только по тем, о которых его просят. Итак, поехали.

Представим задачу, когда нам нужно не добавить какое-то поле из связанных материалов, а изменить способ индексации какого-то текущего поля. Например, есть такой модуль Field Collection, позволяющий сделать поле-контейнер, содержащее в себе несколько полей. В моей структуре с помощью него у публикации указывается автор, а к автору – организация, в которой он работает:

В базе данных у меня есть таблица field_data_field_author_org, в которой есть поле value, которое указывает на entity_id поле таблиц field_data_field_author и field_data_field_jobs. Таким образом в индексированном документе публикации я получаю поле im_field_author_org, значение которого мне абсолютно не нужно. Вот для того, чтобы указать, как будет индексироваться поле, мне нужно определить indexing_callback для этого поля в функции, реализующей хук hook_apachesolr_field_mappings(), находящийся в файле apachesolr.api.inc:

<?php

function inti_apachesolr_field_mappings() {
  // Функция должна вернуть массив, в котором ключем может быть либо тип поля, либо значение 'per_field'. 
  // В первом случае мы указываем, как будут индексироваться поля этого типа, во втором - значением будет массив,
  // у которого ключем будет имя поля. Настоятельно рекомендую ознакомиться с документацией ко всем хукам, которые
  // я указываю в заметках, для этого я пишу, в каких файлах они находятся. Дело в том, что в документации
  // это описано довольно подробно, а я лишь хочу описать сам принцип.
  $mappings = array();
  $mappings['per-field']['field_author_org'] = array(
    'indexing_callback' => 'inti_field_author_org_indexing_callback',
    // В это поле я хочу записать имена авторов и организаций, поэтому меняю тип с int на text,
    // это повлияет на имя поля (см. мой предыдущий пост), которое в данном случае генерируется 
    // автоматически
    'index_type' => 'text',
    // Фасеты по этому полю мне не нужны, потому что их я формирую вручную (описано также в предыдщем посте)
    'facets' => FALSE,  
  );
  return $mappings;
}

function inti_field_author_org_indexing_callback($entity, $field_name, $index_key, $field_info) {
  $fields = array();

  // Получаем все значения нашего поля
  $field_values = array_map(function($n) { return $n['value']; }, $entity->{$field_name}['und']);  

  // По этим значениям достаем всех авторов и организации прямо из базы данных
  // В момент написания заметки я предположил, что можно было бы воспользоваться функционалом
  // самого модуля Field Collection, но это предположение я проверю позже. :)
  $select = db_select('node', 'n');
  $select->join('field_data_field_author', 'fdfa', 'fdfa.field_author_nid = n.nid');
  $select->condition('fdfa.entity_id', $field_values, 'IN');
  $select->fields('n', array('title'));
  $authors = $select->execute()->fetchCol();
  
  $select = db_select('node', 'n');
  $select->join('field_data_field_jobs', 'fdfj', 'fdfj.field_jobs_nid = n.nid');
  $select->condition('fdfj.entity_id', $field_values, 'IN');
  $select->fields('n', array('title'));
  $orgs = $select->execute()->fetchCol();

  // Формируем массив с $index_key в кач-ве ключей, и 
  // именами авторов и названиями всех организаций в кач-ве значений.
  $fields[] = array(
    'key' => $index_key,
    'value' => implode(' ', $authors),
  );
  $fields[] = array(
      'key' => $index_key,
      'value' => implode(' ', $orgs),
  );
  return $fields;
}

?>

Таким образом в результате индексирования поле у меня выглядит следующим образом:

[tm_field_author_org] => Array ( [0] => Иванов Е. С. Петрова Р. Ш. // Авторы [1] => Рога и копыта Министерство образования и науки // Организации )

Теперь мне нужно сообщить Solr’у, что по этому полю тоже необходимо производить поиск. Делается это с помощью реализации хука hook_apachesolr_query_alter(), описанном всё в том же чудесном файле apachesolr.api.php:

<?php

function inti_apachesolr_query_alter($query) {
  // Поля, в которых нужно искать, должны быть добавлены в параметр 'qf' поискового запроса.
  // Формат параметров - обычный массив, значения которого выглядят как fieldname^boost, т.е. тут же поисковик
  // будет уведомлен о приоритетах. В моём случае поле достаточно важное, поэтому приоритет делаю 
  // высоким. Напомню, что имя поля должно быть указано не то, которое в Drupal'е, а то, которое
  // в индексированном документе.
  
  $params = array('tm_field_author_org^25.0');
  $query->addParam('qf', $params);
}

?>

Кстати, $query – это интерфейс DrupalSolrQueryInterface, описанный в файле apachesolr.interface.inc, с которым рекомендую ознакомиться, если вы хотите узнать, как еще можно работать с запросом перед его отправкой.

Собственно, вот и всё. В следующем посте расскажу о status_callback и индексировании определенных нод.

Примечание: вышенаписанное актуально для текущих версий модулей apachesolr (7.x-1.0-beta19) и facetapi (7.x-1.0-rc4). Они активно развиваются, поэтому если у вас другая версия и что-то не работает, читайте release notes.

Ссылки:
Часть 1. О добавлении полей в индекс и фасетах.
Часть 3. О том, как не индексировать, если не хочется.
Часть 4. Установка Solr 3.x и поиск с использованием *

Источник: http://www.drupal.ru/node/79948

Работа с Facet API и Apache Solr. Часть 1

Всем привет.

Так сложилось, что передо мной встала задача создать библиографическую систему для хранения научных публикаций в большом количестве. Ну и как в любой нормальной библиографической системе, мне нужен поиск. На момент начала работы я уже был знаком с модулями Apachesolr search integration и Facet API, работающими в связке, и у которых довольно много возможностей из коробки, а помимо них есть ведь и API. Так вот некоторые мои задачи из коробки не решались, пришлось пообщаться и с разработчиками, и поковыряться в коде, поэтому я таки решил написать о решении некоторых задачек здесь. Возможно, кому-то будет полезно, возможно кто-то предложит, как сделать лучше.

Итак, поехали. Расписывать модули и что они делают, я не буду, это неинтересно, можно почитать на drupal.org и в интернете, благо, такой информации достаточно. В этом посте я хочу рассказать о том, как можно создавать свои собственные фильтры (их еще называет фасетами).

Дело в том, что в моём проекте достаточно непростая структура. Так, например, у меня есть тип материала “Публикация”, у которого есть поле – ссылка на материал типа “Издание”. В свою очередь, у издания есть поле – ссылка на материал “Издательство”. Таким образом, когда я ищу публикации, в списке доступных фильтров я вижу “Издания”, но, что если я хочу фильтровать результаты поиска по издательствам, когда они привязаны к публикациям не напрямую?

Один из предложенных вариантов решения этой задачки было ручное индексирование публикаций. Добавив в индексированную публикацию поле издательства мы легко получим нужный фильтр. Добавить поле можно с помощью реализации хука hook_apachesolr_index_document_build() (если вы такого хука не нашли, значит у вас старая версия модуля apachesolr, где он называется hook_apachesolr_update_index()), описанного в файле apachesolr.api.php:

<?php
function mymodule_apachesolr_index_document_build($document, $node, $namespace) {
  if ($node->type == 'publication') {
    if (!empty($node->field_edition)) {

      // Примечание: $node->field_edition - это ассоциативный массив, в котором ключами являются языки, это важно понимать при индексировании. 
      // В данном случае у меня материалы не привязаны к языку, поэтому ключ массива я захардкодил.
      foreach ($node->field_edition['und'] as $edition) {
        if (!empty($edition['node']->field_publishing_house))    {
          foreach ($edition['node']->field_publishing_house['und'] as $pubhouse) {

            // Ну а таким образом я добавляю новое поле в наш документ. Здесь хочу добавить два примечания.
            // Первое: В качестве значения не надо пихать весь термин, или всю ноду, а достаточно проиндексировать идентификатор.
            // Второе: У Apachesolr есть правила именования поля. Название всегда должно быть формата xy_fieldname, где
            // x - тип поля (s - строка, t - текст, i - тип long, а f - float), а y - количество значений (s - одно, m - несколько).
            // В моем примере в поле хранится идентификатор, а издательство у издания может быть только одно.
            // fieldname может быть любым, но для себя я решил, что разделяю его на две части, где первая хранит информацию о том, чье это поле,
            // а вторая - о том, что это поле содержит. pub - публикация, pubhouse - издательство.
            $document->addField('is_pub_pubhouse', $pubhouse['nid']);          
          }
        }
      }
    }
  }
}
?>

Таким образом в самом друпале у нас публикация не хранит информацию о своём издательстве, а в индексе хранит. И помимо этого, может хранить любую другую информацию, какую разработчик пожелает туда “запихнуть”.
Но этого недостаточно. Теперь мы должны рассказать модулю Facet API, что мы хотим по этому полю получить фильтр. Это, в свою очередь, решается с помощью реализации хука hook_facetapi_facet_info(), описанного в файле facetapi.api.php:

<?php
function mymodule_facetapi_facet_info($searcher_info) {
  // Данная функция должна возвращать ассоциативный массив с описаниями полей, где
  // ключом является само название поля.
  $facets = array();
  $facets['is_pub_pubhouse'] = array(

    // Название фильтра на странице настройки.
    // Учитывая, что каждый фильтр отображается в блоке, а заголовок блока я всегда успею переопределить,
    // метку я тоже решил делать понятной
    'label' => t('Publication->Pubhouse'),
    'description' => t('Pubhouse facet for publications'),

    // Здесь я должен указать название коллбэка, который будет вместо идентификаторов подставлять нормальные 
    // значения. Для полей - ссылок на таксономию рекомендую использовать уже готовый коллбэк
    // facetapi_map_taxonomy_terms, для ссылок на ноды я создал свой коллбэк, для полей со списком значений
    // я создавал отдельные коллбэки, хотя возможно, можно это сделать изящнее.
    // Помимо этого, здесь можно указать параметры фильтра такие, как, например, древовидность (hierarchy_callback).
    // Подробнее можно посмотреть в вышеуказанном файле facetapi.api.php в коде описания хука.
    'map callback' => 'mymodule_map_node_references',
  );
}

function mymodule_map_node_references(array $values) {
  // Здесь все очень просто, коллбэк возвращает ассоциативный массив, где ключ массива - это значение, хранимое в поле,
  // а значение в массиве - это то, что должен увидеть пользователь.
  $map = array();
  if ($values) {
    $map = db_select('node', 'n')->fields('n', array('nid', 'title'))->condition('n.nid', $values, 'IN')->execute()->fetchAllKeyed();
  }
  return $map;
}
?>

Вот, собственно, и всё. Теперь после включения модуля и переиндексации содержимого на сайте (еще может потребоваться очистка кэша) на странице фильтров я могу увидеть свой фильтр, включить его, настроить и, собственно, пользоваться.

Примечание: вышенаписанное актуально для текущих версий модулей apachesolr (7.x-1.0-beta16) и facetapi (7.x-1.0-rc4). Они активно развиваются, поэтому если у вас другая версия и что-то не работает, читайте release notes.

Ссылки:
Часть 2. О подмене коллбэка для индексации определенного полям и о том, как искать по дополнительным полям.
Часть 3. О том, как не индексировать, если не хочется.
Часть 4. Установка Solr 3.x и поиск с использованием *

Источник: http://www.drupal.ru/node/78426

Резервныe ApacheSolr серверa для Drupal’a

Во первых, что такое Apache Solr?
Apache Solr – это расширяемая поисковая платформа с открытым исходным кодом для от проекта Apache Lucene.

Почему Apache Solr?
Основными критериями выбора этой платформы для нас стало то, что Apache Solr оптимизирован для работы с большой нагрузкой и что репликация уже в составе ядра платформы, а это дает большие возможности для масштабирования.

На одном с проектов мы столкнулись с вопросом «А что будет если наш основной Solr сервер вдруг перестанет отвечать? Вся система поиска на сайте умирает?».

Полная статья »

Источник: http://www.drupal.ru/node/57760

© 2009 Обзор CMS