Текстовый индекс Mongodb против regex для поиска текста

В этом посте я расскажу о разнице между двумя методами поиска текста в коллекции mongodb и сравню их сложности, плюсы и минусы.

Пример

Рассмотрим коллекцию постов, где каждый объект состоит из заголовка и содержания:

"_id": ObjectId(""),
"title": "PostA",
"content": "This is the content for the first post."
},
{
"_id": ObjectId(""),
"title": "PostB",
"content": "This is a different content for the second post."
}
Войти в полноэкранный режим Выход из полноэкранного режима

Нашей целью является поиск некоторого текста и возвращение соответствующих документов.

Использование REGEX

В случае, если мы хотим найти заголовок, будет просто использовать обычный фильтр find

db.posts.find({title: "PostA"})

если мы точно знаем заголовок или использовать regex, если мы знаем его часть.

db.posts.find({title: 'pattern', $options: '<options>'})

Но если мы хотим искать в поле содержимого, то использование этого кода

db.posts.find({content: "first"})

ничего не даст, так как будет искать точное совпадение.
Поэтому здесь мы можем использовать regex

db.posts.find({content: {$regex: /first/}})

этот запрос вернет документ PostA, поскольку его содержимое содержит слово first.
Но при этом будет выполняться сканирование всей коллекции за O(n), и это будет иметь низкую производительность на больших наборах данных.

Использование текстового индекса

Текстовые индексы: преобразуют текст в массив отдельных слов и удаляют все стоп-слова (is, a, an и т.д.).
Давайте создадим текстовый индекс для нашего поля содержимого

db.posts.createIndex({content: "text"})

и не забудьте указать «text», чтобы удалить ненужные слова и сохранить ключевые слова.
для поиска слова

db.posts.find({$text: {$search: "first"}})

Это вернет сообщение PostA.

Почему мы не искали внутри нашего контента в вышеприведенном запросе? 🤔
Поскольку mongo рассматривает этот индекс как массив слов по порядку, если вы хотите добавить другое поле для этого текстового индекса, например, мы можем добавить в индекс заголовок и содержание, и он будет рассматривать их как один текст.

Пример

db.posts.createIndex({title: "text", content: "text"})

Примечание: мы не можем добавить еще один текстовый индекс, пока уже существует другой, поэтому следующие строки являются незаконными и мы должны добавить их сразу,

 db.posts.createIndex({content: "text"})
 db.posts.createIndex({title: "text"})
Войти в полноэкранный режим Выйти из полноэкранного режима

Итак, теперь у нас есть комбинированный индекс как по заголовку, так и по содержанию, поэтому при поиске по любому ключевому слову, будь то в заголовке или в содержании, будет возвращен соответствующий документ.
Этот подход будет очень эффективным с точки зрения сложности, так как он использует indexScan (O(log(n))), а также с точки зрения удобства использования, вместо поиска по определенному полю будет поиск по нескольким полям, что более практично.

Исключение слов с помощью текстового индекса

Давайте попробуем найти пост, содержимое которого содержит ключевое слово ‘post’

db.posts.find({$text: {$search: "post"}})

Этот запрос вернет документы PostA и PostB, но мы можем вернуть только PostB, если исключим ключевое слово ‘first’:

db.posts.find({$text: {$search: "post -first"}})

Это исключит документы с содержанием, содержащим слово ‘first’.

Заключение

Наконец мы увидели, что использование текстового индекса быстрее, проще и предпочтительнее и поддерживает исключение ключевых слов, но у нас есть и другие случаи, когда мы хотим искать подстроки или частичные совпадения слов, например, слово Post в PostB, в этом случае мы должны использовать regex.

Оцените статью
devanswers.ru
Добавить комментарий