用部分字符串进行Mongoose文本搜索

Car*_*ten 13 text-search mongoose mongodb node.js

嗨,我正在使用猫鼬来搜索我的收藏中的人物.

/*Person model*/
{
    name: {
       first: String,
       last: String
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想搜索有查询的人:

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

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

如果我搜索约翰,我会得到结果(如果我搜索Jo,则为事件).但如果我搜索John Doe,我显然没有得到任何结果.

如果我将QUERY更改为John | Doe,我会得到结果,但它会返回所有在姓氏或名字中都有JohnDoe的人.

接下来就是尝试使用mongoose textsearch:

首先将字段添加到索引:

PersonSchema.index({
   name: {
      first: 'text',
      last: 'text'
   }
},{
   name: 'Personsearch index',
   weights: {
      name: {
          first : 10,
          last: 10
   }
}
});
Run Code Online (Sandbox Code Playgroud)

然后修改Person查询:

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

这很好用!现在它只返回与整个名/姓相匹配的人:

- > John返回值

- > Jo没有返回任何值

有办法解决这个问题吗?

没有外部插件的答案是首选,但其他人也希望.

Kri*_*lye 10

正则表达式可以帮助你.

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


Joh*_*yHK 6

您可以使用将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);
});
Run Code Online (Sandbox Code Playgroud)

但是,性能是一个问题,因为它不能使用索引,因此需要进行完整的集合扫描.

您可以通过在$project阶段之前使用可以使用索引来减少管道其余部分需要查看的文档集的$match查询来缓解这种情况.

因此,如果您单独编制索引name.first,name.last然后将搜索字符串的第一个单词作为锚定查询(例如/^John/i),则可以将以下内容添加到管道的开头:

{$match: $or: [
  {'name.first': /^John/i},
  {'name.last': /^John/i}
]}
Run Code Online (Sandbox Code Playgroud)

显然你需要以编程方式生成"第一个单词"正则表达式,但希望它能为你提供这个想法.