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) {
});