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

Допустим, у меня есть коллекция монго с text index в поле itemName с этими 3 документами:

{
    _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', ...}
]

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

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

ИЗМЕНИТЬ:

Кроме того, иногда поведение $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.

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

лучший, П


person Patrick DaVader    schedule 26.02.2017    source источник
comment
Какой эффект это имеет, когда вы сортируете по textScore?   -  person Vince Bowdren    schedule 02.03.2017
comment
пожалуйста, проверьте мое редактирование! :-) Спасибо!   -  person Patrick DaVader    schedule 03.03.2017
comment
@PatrickDaVader посмотри мою правку   -  person felix    schedule 03.03.2017
comment
Полнотекстовый поиск @PatrickDaVader не предназначен для сопоставления подстрок - если Zwei и Zweibel - совершенно разные слова, то поиск Zwei должен не сопоставлять записи с Zweibel, т. е. то, что вы видите, является правильным поведением.   -  person Vince Bowdren    schedule 03.03.2017


Ответы (1)


Решение состоит в том, чтобы использовать $indexOfCP введен в MongoDB 3.4

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

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

  1. отфильтровать все документы, не содержащие «автомобиль», с помощью регулярного выражения: /car/gi (без учета регистра)
  2. создайте поле с именем index, в котором хранится индекс «автомобиль» в itemName
  3. сортировать документы по полю 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 }

попробуйте онлайн: mongoplayground.net/p/FqqCUQI3D-E

Изменить:

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

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

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

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

person felix    schedule 02.03.2017
comment
Спасибо за ваш ответ! Пожалуйста, также проверьте мое редактирование в OP! Спасибо! - person Patrick DaVader; 03.03.2017