В предыдущих частях серии автозаполнения мы представили два метода запросов автозаполнения. Чем мы расширили один из тех, у кого была возможность определять возвращаемую информацию . В сегодняшней записи мы вернулись к автозаполнению с помощью facet и ngram.
Требования
Наш механизм автозаполнения имеет следующие требования:
- Мы возвращаем целую фразу, а не только одно слово
- Возвращенная фраза может присутствовать несколько раз в индексе.
- Мы хотим знать количество результатов для возвращенной фразы
- Общие фразы должны быть показаны выше, чем менее распространенные
- Порядок слов, введенных пользователем, не имеет значения
Решение
Решение, данное в первой части серии, не будет соответствовать требованиям из-за первого требования. Конечно, мы можем изменить тип анализа, но мы не вернем всю фразу.
Решением вышеуказанных требований является модифицированный метод огранки. Вместо того, чтобы искать все элементы и сужать результаты с помощью параметра facet.prefix , мы можем искать только те элементы, которые имеют искомый фрагмент слова. Мы не хотим, чтобы использовался подстановочный запрос (из-за производительности), который мы вызываем ngram для спасения Это означает, что нам нужно записать нграммы в индекс (конечно, Solr сделает это за нас). Очевидным недостатком является рост размера индекса, но в этом случае мы можем с этим смириться.
Schema.xml
Мы определяем дополнительный тип:
<fieldType name="text_autocomplete" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter minGramSize="1" maxGramSize="25" /> </analyzer> <analyzer type="query"> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
Мы также определяем дополнительные поля: поле, значение которого мы планируем вернуть, и поле, которое будет использоваться для поиска:
<field name="tag_autocomplete" type="text_autocomplete" indexed="true" stored="true" omitNorms="true" omitTermFreqAndPositions="true"/> <field name="tag" type="string" indexed="true" stored="true" />
И один copyField, чтобы сделать вещи проще:
<copyField source="tag" dest="tag_autocomplete"/>
запрос
После индексации мы готовы протестировать наши запросы:
- Мы сужаем результаты только для тех, у которых есть интересный фрагмент слова в поле tag_autocomplete , с: q = tag_autocomplete: (PHRASE)
- Нам нужны все фрагменты, введенные пользователем, поэтому мы используем AND в качестве логического оператора: q.op = AND
- Нас не интересуют фактические результаты запроса, мы будем использовать данные, возвращаемые фасетированием, поэтому мы говорим: rows = 0
- Нам нужна огранка: facet = true
- Нам нужно огранить поле, в котором мы храним исходную фразу: facet.field = tag
- Нас не интересуют пустые теги: facet.mincount = 1
- Нас интересуют только 5 значений автозаполнения: facet.limit = 5
И последний запрос:
?q=tag_autocomplete:(PHRASE)&q.op=AND&rows=0&facet=true&facet.field=tag&facet.mincount=1&facet.limit=5
Если мы настроим обработчик поиска для включения всех постоянных параметров, у нас будет следующий запрос:
?q=tag_autocomplete:(PHRASE)
В конце
Основным достоинством представленного метода является возможность использовать одно поле для поиска, а другое — для возврата результатов. Благодаря этому мы смогли вернуть целую фразу вместо одного слова.