MongoDB официально предложил возможности полнотектового поиска, начиная с 2.6 версии. Она входит в число Top 5 наиболее востребованных возможностей для серверных компонент и модулей в этой текущей версии наравне с многочисленными модулями и парсерами, фразовыми соответствиями, отрицаниями и весом каждого поля. Так что самое время дать ему немного любви и поделиться печеньками в Spring Data кухне для поддержки этой возможности.
Индексация текста и поиск MongoDB по умолчанию выполняется для Английского языка, нормализации текста по разделителям, удаления общих стоп-слов и сокращение слов на их основе. Также присутствует поддержка для нескольких других языков.
Ручное построение текстовых индексов
@Document
class CookingRecipe {
String title;
String content;
}
Здесь мы получили очень простой объект CookingRecipe
и иметь текстовый индекс на основе title
и content
полей, устанавливая вес равный 2 поисковым словам в title
. Назначение веса полей позволяет вам влиять на релевантность документа, который будет просмотрен. Это определяет значение поля относительно других, повышая стоимость документов. В этом случае, это дублирует актуальные документы, когда они попадают в соответствие с title
. Сырое определение MongoDB индекса выглядит примерно так:
{
title : "text",
content : "text"
},
{
weights: { title : 2 }
}
Начиная с версии 1.5 M1 Spring Data MongoDB мы можем создавать индекс текста, охватывая поля, которые мы хотим включить в полнотекстовый поиск, вручную.
TextIndexDefinition textIndex = new TextIndexDefinitionBuilder()
.onField("title", 2F)
.onField("content")
.build();
MongoTemplate template = … // получение MongoTemplate
template.indexOps(CookingRecipe.class).ensureIndex(textIndex);
С другой стороны, можно позволить создавать индекс автоматически на основе аннотаций. Все, что мы можем сделать для этого, это добавить несколько изменений.
@Document
class CookingRecipe {
@TextIndexed(weight=2) String title;
@TextIndexed String content;
}
Обратите внимание, что это возможно только для одного полнотекстового индекса на коллекцию. Теперь, когда мы создали индекс, мы запросим 5 первых рецептов с соответствием «coffee» или «cake».
TextCriteria criteria = TextCriteria.forDefaultLanguage()
.matchingAny("coffee", "cake");
Query query = TextQuery.queryText(criteria)
.sortByScore()
.with(new PageRequest(0, 5));
List<CookingRecipe> recipes = template.find(query, CookingRecipe);
Обратите внимание, что мы предоставляем отдельные типы TextCriteria
и TextQuery
для экспресс-поиска с подробным описанием.
Подсчет
Как упоминалось ранее, документы оцениваются в процессе поиска. score
значение не возвращается по умолчанию, но поскольку эта информация полезна, мы можем включить её в вывод, добавив { score : { $meta : "textScore" } }
в план, который неявно делается путем вызова query.sortByScore()
. Для доступа к оценке в итоговых документах мы добавим свойство с аннотацией @TextScore
в CookingRecipe
.
@Document
class CookingRecipe {
@TextIndexed(weight=2) String title;
@TextIndexed String content;
@TextScore Float score;
}
@TextScore
аннотация неявно превращает свойство score
в свойство только для чтения, соответствующее @ReadOnlyProperty
аннотации. Последнее может быть использовано в других контекстах, а также там, где вы хотели бы быть уверены, что поля в документах только для чтения, а не для записи.
Дополнительные ресурсы по поведению и ограничениям, а также какие поддерживаются языки вы можете найти в MongoDB справочнике.