На сайте имеется поиск созданный модулями Views и Search API Solr Search. Задача - автоматически исправлять ошибки в искомых словах и показывать результаты для исправленных слов.

Для решения напишем небольшой модуль, который будет зависить от модуля Search API Spellcheck. Устанавливаем его и переходим к написанию собственного модуля. Для примера я назову модуль "search_api_misspell". Создаем файл "search_api_misspell.info":
name = Search misspell
description = Spelling suggestions from Search API services.
core = 7.x
package = Custom
dependencies[] = search_api_spellcheck
files[] = views/views_handler_area_misspell.inc
Далее создаем файл "search_api_misspell.module". Его содержимое:
<?php
/**
* @file
* Main module file.
*/
/**
* Implements hook_views_api().
*/
function search_api_misspell_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'search_api_misspell') . '/views',
);
}
/**
* Implements hook_theme().
*/
function search_api_misspell_theme() {
return array(
'search_api_misspell' => array(
'variables' => array('spellcheck' => NULL, 'options' => array()),
'file' => 'search_api_misspell.theme.inc',
),
);
}
/**
* Implements hook_search_api_solr_query_alter().
*/
function search_api_misspell_search_api_solr_query_alter(array &$call_args, SearchApiQueryInterface $query) {
if ($query->getOption('search_api_spellcheck') && (!isset($_GET['spell']) || $_GET['spell'] != 0)) {
$call_args['params']['spellcheck.accuracy'] = 0.7;
$call_args['params']['spellcheck.collate'] = 'true';
$call_args['params']['spellcheck.collateExtendedResults'] = 'true';
$call_args['params']['spellcheck.onlyMorePopular'] = 'false';
$call_args['params']['spellcheck.maxCollationTries'] = 1;
}
}
/**
* Implements hook_search_api_solr_search_results_alter().
*/
function search_api_misspell_search_api_solr_search_results_alter(array &$results, SearchApiQueryInterface $query, $response) {
if ($query->getOption('search_api_spellcheck') && (!isset($_GET['spell']) || $_GET['spell'] != 0)) {
if (!empty($response->spellcheck->suggestions->collation->collationQuery)) {
$query->setOption('parse mode', 'direct');
$query->keys($response->spellcheck->suggestions->collation->collationQuery);
$collate_results = $query->execute();
$results['results'] = $collate_results['results'];
$results['result count'] = $collate_results['result count'];
$results['miss_spell'] = TRUE;
}
}
}
В данном файле имеется два основных хука:
-
hook_search_api_solr_query_alter - используется для того, что бы изменить запрос или добавить некоторые параметры перед отправкой в Solr. В данном случае он используется для добавления параметров необходимых для работы SpellCheck компонента. Ознакомиться с данным компонентом и посмотреть полный список параметров и настроек можно здесь Solr Spell Checking;
-
hook_search_api_solr_search_results_alter - используется для модификации результатов полученных от Solr. В данном случае мы анализируем ответ и делаем еще один запрос с исправленными словами если пользователем была допущена ошибка.
Далее создаем Views handler который будет включать опцию search_api_spellcheck и выводить данные об исправленных словах. Внутри нашего модуля создаем 2 файла:
"views/search_api_misspell.views.inc":
<?php
/**
* @file
* Keeping views hooks in an include reduces the size of Drupal's bootstrap.
*/
/**
* Implements hook_views_data_alter().
*/
function search_api_misspell_views_data_alter(&$data) {
foreach (search_api_index_load_multiple(FALSE) as $index) {
$key = "search_api_index_{$index->machine_name}";
try {
if ($index->server() && $index->server()->supportsFeature('search_api_spellcheck')) {
$data[$key]['search_api_misspell'] = array(
'title' => t('Misspell'),
'group' => t('Search'),
'help' => t('Suggestions for spellings.'),
'area' => array('handler' => 'views_handler_area_misspell'),
);
}
}
catch (SearchApiException $e) {
}
}
}
"views/views_handler_area_misspell.inc":
<?php
/**
* @file
* Views Search API area handler.
*/
/**
* Area handlers are available to be placed in a views header and footer.
*/
class views_handler_area_misspell extends views_handler_area {
/**
* Overrides views_handler::pre_query().
*/
function pre_query() {
$this->query->setOption('search_api_spellcheck', TRUE);
}
/**
* Overrides views_handler_area::render().
*/
function render($empty = FALSE) {
$results = $this->query->getSearchApiResults();
if (!isset($results['search_api_spellcheck']) || empty($results['results']) || empty($results['miss_spell'])) {
return '';
}
$options = array();
foreach ($this->view->filter as $key => $filter) {
if ($filter instanceof SearchApiViewsHandlerFilterFulltext) {
$options['get'][] = $filter->options['expose']['identifier'];
}
}
$variables = array(
'spellcheck' => $results['search_api_spellcheck'],
'options' => $options,
);
return theme('search_api_misspell', $variables);
}
}
Теперь создаем файл "search_api_misspell.theme.inc":
<?php
/**
* @file
* Contains theme functions.
*/
/**
* Returns HTML for a search API spellcheck.
*
* @param array $variables
* An associative array contains:
* - spellcheck: SearchApiSpellcheckInterface instance
* - options: An associative array containing:
* - get: (optional) An array of query keys which should be spellchecked
*
* @return string
* HTML for spelling suggestions.
*
* @throws \Exception
*/
function theme_search_api_misspell($variables) {
$options = $variables['options'];
$spellcheck = $variables['spellcheck'];
$suggestion_links = array();
if (isset($options['get'])) {
foreach ($options['get'] as $get) {
if ($link = $spellcheck->getSuggestionLinkForGet($get)) {
$suggestion_links[] = $link;
}
}
}
$output = '';
if (count($suggestion_links)) {
$query = drupal_get_query_parameters();
$query['spell'] = 0;
$output .= '<div>' . t('Showing results for "!link"', array('!link' => $suggestion_links[0]->link)) . '</div>';
$output .= '<div>' . t('Search results for "!link"', array(
'!link' => l($suggestion_links[0]->original, current_path(), array(
'query' => $query,
)),
)) . '</div>';
}
return $output;
}
Включаем модуль и добавляем включенный handler в хедер вьюса:

Spell Checking может использовать разные словари или сразу несколько. Словари могут отличаться настройками и должны быть описаны в файле "solrconfig_extra.xml":
<lst name="spellchecker">
<str name="name">wordbreak</str>
<str name="field">spell</str>
<str name="classname">solr.WordBreakSolrSpellChecker</str>
<str name="combineWords">true</str>
<str name="breakWords">true</str>
<int name="maxChanges">10</int>
</lst>
Для того, что бы указать, какой словарь использовать, необходимо в hook_search_api_solr_query_alter добавить параметр spellcheck.dictionary
, например:
$call_args['params']['spellcheck.dictionary'] = array('default', 'wordbreak');
В данном примере будет использовано два словаря. Если этот параметр не передать, то будет использоваться default словарь.