MongoDB搜索和排序,具有匹配数和完全匹配

时间:2015-07-20 14:28:30

标签: regex mongodb sorting

我想创建一个小的MongoDB搜索查询,我想根据完全匹配对结果集进行排序,然后是否。比赛

例如。如果我有以下标签

Physics
11th-Physics
JEE-IIT-Physics
Physics-Physics

然后,如果我搜索"物理"它应排序为

Physics
Physics-Physics
11th-Physics
JEE-IIT-Physics

1 个答案:

答案 0 :(得分:5)

寻找那种"得分"你在这里谈论的是一个不完美的解决方案"的练习。在这种情况下,最合适的"这里以"文本搜索"和"不完美"开头。是在使用MongoDB的文本搜索功能时首先要考虑的术语。

MongoDB是"不是"专门的"文本搜索"产品,也不是(像大多数数据库一样)试图成为一体。完整功能"文本搜索"专门用于专业产品的专业产品。所以也许不是最合适的,但"文本搜索"对于那些能够忍受限制并且不想实现其他引擎的人来说,它是一个选项。或者还是!至少。

话虽如此,让我们看一下你可以对所提供的数据样本做些什么。首先在集合中设置一些数据:

db.junk.insert([
    { "data": "Physics" },
    { "data": "11th-Physics" },
    { "data": "JEE-IIT-Physics" },
    { "data": "Physics-Physics" },
    { "data": "Something Unrelated" }
])

然后当然要#34;启用"文本搜索功能,然后您需要使用"文本"索引文档中的至少一个字段。索引类型:

db.junk.createIndex({ "data": "text" })

现在已经准备好了#34;让我们来看看第一个基本查询:

db.junk.find(
   { "$text": { "$search": "\"Physics\"" } },
   { "score": { "$meta": "textScore" } }
).sort({ "score": { "$meta": "textScore" } })

这将得到这样的结果:

{
    "_id" : ObjectId("55af83b964876554be823f33"),
    "data" : "Physics-Physics",
    "score" : 1.5
}
{
    "_id" : ObjectId("55af83b964876554be823f30"),
    "data" : "Physics",
    "score" : 1
}
{
    "_id" : ObjectId("55af83b964876554be823f31"),
    "data" : "11th-Physics",
    "score" : 0.75
}
{
    "_id" : ObjectId("55af83b964876554be823f32"),
    "data" : "JEE-IIT-Physics",
    "score" : 0.6666666666666666
}

这就是"关闭"达到你想要的结果,但当然没有"完全匹配"零件。此外,文本搜索功能与$text运算符一起使用的逻辑意味着"物理 - 物理"是这里的首选匹配。

这是因为引擎无法识别"非单词"例如"连字符"之间。对它来说,"物理"在文档的索引内容中多次出现,因此得分较高。

现在,你的其余逻辑取决于"完全匹配"的应用。你的意思是什么如果您正在寻找"物理学"在字符串中"不是"用连字符包围"连字符"或其他字符,然后以下不适合。但你可以匹配一个字段" value"那就是"确切地说"只是"物理":

db.junk.aggregate([
    { "$match": { 
        "$text": { "$search": "Physics" } 
    }},
    { "$project": {
        "data": 1,
        "score": {
           "$add": [
               { "$meta": "textScore" },
               { "$cond": [
                   { "$eq": [ "$data", "Physics" ] },
                   10,
                   0
               ]}
           ]
        }
    }},
    { "$sort": { "score": -1 } }
])

这会给你一个结果,看看" textScore"由引擎生成,然后通过逻辑测试应用一些数学。在这种情况下,"数据"完全等于"物理学"那么我们"体重"使用$add的其他因素得分:

{
    "_id": ObjectId("55af83b964876554be823f30"),
    "data" : "Physics",
    "score" : 11
}
{
    "_id" : ObjectId("55af83b964876554be823f33"),
    "data" : "Physics-Physics",
    "score" : 1.5
}
{
    "_id" : ObjectId("55af83b964876554be823f31"),
    "data" : "11th-Physics",
    "score" : 0.75
}
{
    "_id" : ObjectId("55af83b964876554be823f32"),
    "data" : "JEE-IIT-Physics",
    "score" : 0.6666666666666666
}

这是aggregation framework可以为您做的事情,允许通过附加条件操纵返回的数据。最终结果传递到$sort阶段(注意它按降序颠倒),以允许新值为排序键。

但聚合框架实际上只能处理"完全匹配"在字符串上这样。目前没有工具来处理正则表达式匹配或字符串中的索引位置,这些位置返回有意义的投影值。甚至不是逻辑匹配。并且$regex操作仅用于"过滤"在查询中,所以不在这里使用。

所以,如果你正在寻找一个"短语"多数民众赞成比一个"字符串等于"完全匹配,然后另一个选项是使用mapReduce

这是另一个"不完美"方法作为mapReduce命令的限制意味着" textScore"来自引擎的这种查询是完全消失了#34;虽然实际文件将被正确选择,但是继承"排名数据"引擎无法使用。这是MongoDB"投射"的一个副产品。 "得分"首先进入文档,"投影"不是mapReduce可用的功能。

但是你可以和#34;一起玩。使用JavaScript的字符串,如我的"不完美"样品:

db.junk.mapReduce(
    function() {
        var _id = this._id,
            score = 0;

        delete this._id;

        score += this.data.indexOf(search);
        score += this.data.lastIndexOf(search);

        emit({ "score": score, "id": _id }, this);
    },
    function() {},
    { 
        "out": { "inline": 1 },
        "query": { "$text": { "$search": "Physics" } },
        "scope": { "search": "Physics" }
    }
)

结果如下:

{
    "_id" : {
        "score" : 0,
        "id" : ObjectId("55af83b964876554be823f30")
    },
    "value" : {
        "data" : "Physics"
    }
},
{
    "_id" : {
        "score" : 8,
        "id" : ObjectId("55af83b964876554be823f33")
    },
    "value" : {
        "data" : "Physics-Physics"
    }
},
{
    "_id" : {
        "score" : 10,
        "id" : ObjectId("55af83b964876554be823f31")
    },
    "value" : {
        "data" : "11th-Physics"
    }
},
{
    "_id" : {
        "score" : 16,
        "id" : ObjectId("55af83b964876554be823f32")
    },
    "value" : {
        "data" : "JEE-IIT-Physics"
   }
}

我自己的"愚蠢的小算法"这基本上是采取"第一"和"最后"此处匹配字符串的索引位置并将它们相加以产生分数。它可能不是你真正想要的,但重点是如果你可以用JavaScript编写你的逻辑代码,那么你可以把它扔到引擎上以产生所需的"排名"。

唯一真正的"技巧"这里要记住的是"得分" 必须成为"之前的"分组的一部分" key"在这里,如果包含原始文档_id值,则必须重命名复合关键部分 ,否则_id将优先于顺序。

这只是mapReduce的一部分,其中"优化"所有输出"键"值以"升序排序"在由减速机处理之前。这当然没有什么,因为我们不是"聚合",而只是使用JavaScript运行器和文件整形mapReduce

总的来说,这些是可用的选项。它们都不是完美的,但你可以和它们一起生活,甚至只是接受"默认引擎结果。

如果你想要更多,那么请看外部"专用"文本搜索产品,这将更适合。

旁注:此处$text搜索优先于$regex,因为他们可以使用索引。 A"非锚定"正则表达式(没有插入符^)不能与MongoDB最佳地使用索引。因此,$text搜索通常会成为查找"单词"的更好基础。在短语中。

相关问题