Mongodb 部分匹配

Mongodb partial matching

如何以一个编辑距离获取 mongodb 中的所有文档。

我有 collection 足球队。

{
    name: 'Real Madrir',
    nicknames: ['Real', 'Madrid', 'Real Madrir' ... ]
}

并且用户搜索了 Real MadidMaddrid 或其他。

我想 return 所有包含昵称的文档与给定搜索字符串的编辑距离为 0 或 1。

我认为有两种方法,mongodb全文搜索或正则表达式。

那么我可以写这样的正则表达式或查询吗?

谢谢。

对于全文搜索,首先您必须在 nicknames 字段上创建一个 Text Index。在创建索引之前插入的文档将不可搜索。搜索仅适用于 创建索引后插入的文档。然后,当您使用 MongoDb 的 $text$search 运算符执行搜索时,MongoDb 将 return 其 nicknames 字段对应于搜索文本。对于正则表达式匹配,MongoDb 有一个可以使用的 $regex 运算符。

这里有几个简短的例子:

全文搜索

  1. 将此脚本另存为 football.js。它将创建一个 teams 集合,其中包含一个文本索引和两个文档供我们搜索。
// create football database
var db = connect("localhost:27017/football");

/* 
   note:
   You may also create indexes from your console
   using the MongoDb shell. Actually each of these
   statements may be run from the shell. I'm using
   a script file for convenience.
*/

// create Text Index on the 'nicknames' field 
// so full-text search works
db.teams.createIndex({"nicknames":"text"});

// insert two teams to search for
db.teams.insert({
    name: 'Real Madrir',
    nicknames: ['Real', 'Madrid', 'Real Madrir' ]
})

db.teams.insert({
    name: 'Fake Madrir',
    nicknames: ['Fake']
})
  1. 打开终端并导航到保存 football.js 的目录,然后通过键入 mongo football.js 针对本地 MongoDb 实例 运行 此脚本].

  2. 从您的终端输入 mongo 以打开 MongoDb Shell 并通过输入 use football 切换到 football 数据库。

  3. 进入足球数据库后,使用 db.teams.find({"$text":{"$search":"<search-text>"}})

  4. 搜索您的文档之一
> use football

// find Real Madrir
> db.teams.find({"$text":{"$search":"Real"}})

// find Fake Madrir
> db.teams.find({"$text":{"$search":"Fake"}})

正则表达式

如果您想使用正则表达式进行搜索,则无需创建索引。只需使用 mongodb 的 $regex 运算符进行搜索:

//find Real Madrir
db.teams.find({"nicknames": {"$regex": /Real/}})

db.teams.find({"nicknames": {"$regex": /Real Madrir/}})

//find Fake Madrir
db.teams.find({"nicknames": {"$regex": /Fa/}})

db.teams.find({"nicknames": {"$regex": /ke/}})

猫鼬

这就是每个搜索在 NodeJS 中的工作方式 mongoose:

var searchText = "Madrir"; // or some value from request.body

var searchRegex = new RegExp(searchText);

var fullTextSearchOptions = {
  "$text":{
    "$search": searchText
  }
};

var regexSearchOptions = {
  "nicknames": {
    "$regex": searchRegex
  }
};

// full-text search
Team.find(fullTextSearchOptions, function(err, teams){

  if(err){
    // ...
  }else if(teams){
    // ...
  }

})

// regex search
Team.find(regexSearchOptions, function(err, teams){

  if(err){
    // ...
  }else if(teams){
    // ...
  }

})

来晚了,但希望能帮助其他搜索此内容的人。

唯一的选择是不使用正则表达式(因此不使用索引,对于大型数据集会非常慢)或使用正常的 $text 搜索(快速索引搜索但不部分匹配)。还有第三个选项使用更多的索引内存,但都支持部分匹配并使用索引(所以速度很快)。

您可以通过从字符串字段生成字符串数组(例如 name)并将生成的数组存储在索引数组字段中来创建自己的“索引”(我们称之为 _nameSearch).像这样

const getSearchArray: (str) => string[] = _str => {
  const str = _str.toLowerCase();
  const output = [];
  let acc = "";
  let accTotal = "";
  str.split("").forEach(char => {
    // Reset accumulator when space is encountered
    // Otherwise, add the new phrase to the array
    accTotal += char;
    output.push(accTotal);
    if (char === " ") {
      acc = "";
    } else {
      acc += char;
      output.push(acc);
    }
  });
  return Array.from(new Set(output));
};

因此,如果 name 值为“选项”,_nameSearch 将是 ["o", "op", "opt", "opti", "option"] 那么您可以索引 _nameSearch。所以你的架构看起来像这样:

const schema = new Schema(
  {
    name: String,
    _nameSearch: { type: [String], index: true },
    ...
  }
);

查询 name 字段就像 db.collection.find({ _nameSearch: SEARCH_STRING }) 一样简单。而且您将能够找到部分匹配项并且还可以使用索引(因此搜索速度非常快)。但是,您将为 name 字段使用更大一点的索引,因此这是一种权衡,但也是一个值得考虑的可行选择。