Mongoose text-поиск с частичной строкой

Привет, я использую мангуста поиск людей в моей коллекции.

/*Person model*/
{
    name: {
       first: String,
       last: String
    }
}

теперь я хочу искать людей с запросом:

let regex = new RegExp(QUERY,'i');

Person.find({
   $or: [
      {'name.first': regex},
      {'name.last': regex}
   ]
}).exec(function(err,persons){
  console.log(persons);
});

если я ищу Джон Я получаю результаты (событие, если я ищу Жо). Но если я буду искать Вася Пупкин Я не получаю никаких результатов очевидно.

если я изменю запрос to Вася|Пупкин Я получаю результаты, но он возвращает все лица, которые либо Джон или Пупкин в их последнем - / firstname.

следующее, что нужно было попробовать с Мангуст textsearch:

Сначала добавьте поля для индекса:

PersonSchema.index({
   name: {
      first: 'text',
      last: 'text'
   }
},{
   name: 'Personsearch index',
   weights: {
      name: {
          first : 10,
          last: 10
   }
}
});

затем измените запрос Person:

Person.find({ 
    $text : { 
        $search : QUERY
    } 
},
{ score:{$meta:'textScore'} })
.sort({ score : { $meta : 'textScore' } })
.exec(function(err,persons){
    console.log(persons);
});

это работает просто отлично! но теперь это только возвращающиеся лица, которые соответствуют всему первому - / lastname:

->Джон возвращает значение

->Жо возвращает no value

есть ли способ решить эту проблему?

ответы без внешних плагинов предпочтительнее, но другие тоже желают.

2 ответов


вы можете сделать это с помощью aggregate конвейер, который объединяет имена и фамилии вместе, используя $concat и затем ищет против этого:

let regex = new RegExp(QUERY,'i');

Person.aggregate([
    // Project the concatenated full name along with the original doc
    {$project: {fullname: {$concat: ['$name.first', ' ', '$name.last']}, doc: '$$ROOT'}},
    {$match: {fullname: regex}}
], function(err, persons) {
    // Extract the original doc from each item
    persons = persons.map(function(item) { return item.doc; });
    console.log(persons);
});

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

вы можете смягчить это, предшествуя $project этап с $match запрос можете используйте индекс, чтобы уменьшить набор документов, которые должны выглядеть в остальной части конвейера на.

Итак, если вы отдельно индексируете name.first и name.last а затем возьмите первое слово строки поиска в качестве привязанного запроса (например,/^John/i), вы можете добавить следующее К началу вашего конвейера:

{$match: $or: [
  {'name.first': /^John/i},
  {'name.last': /^John/i}
]}

очевидно, вам нужно будет программно генерировать это регулярное выражение "первое слово", но, надеюсь, это даст вам идею.


regex может помочь вам в этом.

Person.find({ "name": { "$regex": "Alex", "$options": "i" } },
function(err,docs) { 
});