$text $search ваших документов с Spring Data MongoDB

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 справочнике.

comments powered by Disqus