Текстовый запрос Mongo $: возвращать документы "начиная с строки" перед другими

Скажем, у меня есть коллекция mongo с text index в поле itemName с этими тремя документами:

{
    _id: ...,
    itemName: 'Mashed carrots with big carrot pieces',
    price: 1.29
},
{
    _id: ...,
    itemName: 'Carrot juice',
    price: 0.79
},
{
    _id: ...,
    itemName: 'Apple juice',
    price: 1.49
}

Затем я выполняю такой запрос:

db.items.find({ $text: { $search: 'Car' } }, { score: { $meta: "textScore" } }).sort( { score: { $meta: "textScore" } } );

Как заставить mongo на возвращать документы, начинающиеся с "Car" (без учета регистра) до, возвращая любые другие документы, содержащие "Car" где-то в itemName строка?

Итак, я хочу получить документы в следующем порядке:

[
    {..., itemName: 'Carrot Juice', ...},
    {..., itemName: 'Mashed carrots with big carrot pieces', ...}
]

Конечно, это предназначено для использования в функциональности поиска, поэтому имеет смысл показать пользователю элементы начиная с своей строки поиска, прежде чем показывать какие-либо другие элементы после этого.

До сих пор я использовал стандартное регулярное выражение, но производительность здесь, конечно, намного хуже! +, так как я должен искать регистр, нечувствительный, согласно документам, нормальное регулярное выражение вообще не использует никаких индексов?!

EDIT:

Кроме того, иногда поведение $text очень странно. Например, у меня около 10-15 предметов, где itemName начинается со слова "Zwiebel". Этот запрос

db.items.find({ $text: { $search: "Zwiebel" }, supplier_id: 'iNTJHEf5YgBPicTrJ' }, { score: { $meta: "textScore" } }).sort( { score: { $meta: "textScore" } } );

работает как шарм и возвращает все эти документы, а этот запрос

db.items.find({ $text: { $search: "Zwie" }, supplier_id: 'iNTJHEf5YgBPicTrJ' }, { score: { $meta: "textScore" } }).sort( { score: { $meta: "textScore" } } );

ничего не возвращает! Только изменив "Zwiebel" на "Zwie" в $search.

Я действительно не понимаю, как это возможно?!

лучший, P

Ответ 1

Решение состоит в использовании оператора $indexOfCP, встроенного в MongoDB 3.4

Этот оператор возвращает индекс вхождения String в другой String и -1, если не существует события

как это работает:

  • отфильтровать все документы, не содержащие "автомобиль" с регулярным выражением: /car/gi (нечувствительный к регистру)
  • создайте поле с именем index, в котором хранится индекс "автомобиль" в itemName
  • сортировать документы в поле index

запрос будет выглядеть так:

db.items.aggregate([
   {
      $match:{
         itemName:/car/gi
      }
   },
   {
      $project:{
         index:{
            $indexOfCP:[
               {
                  $toLower:"$itemName"
               },
               "car"
            ]
         },
         price:1,
         itemName:1
      }
   },
   {
      $sort:{
         index:1
      }
   }
])

и это возвращает:

{ "_id" : 2, "itemName" : "Carrot juice", "price" : 0.79, "index" : 0 }
{ "_id" : 1, "itemName" : "Mashed carrots with big carrot pieces", "price" : 1.29, "index" : 7 }

Edit:

Для поведения индекса $text это вполне нормально

Текстовый индекс tokenize текст с использованием разделителей (разделители по умолчанию - это пробел и пунктуация). Его можно использовать только для поиска целых миров, и поэтому он не будет работать для части слов

из текстовая документация mongodb

$text будет токенизировать строку поиска, используя пробелы и большинство пунктуации в качестве разделителей и выполнять логическое ИЛИ всех таких токенов в строке поиска.