MongoDB:如何进行文本搜索并按日期排序

Jul*_*ara 1 sorting mongodb

上下文:我有一个填充了大量电子邮件的MongoDB.我想在以下任何字段中搜索包含给定电子邮件地址的所有电子邮件:收件人,收件人,CC和BCC.结果需要按字段Date排序.我们目前正在尝试以下查询:

db.collection.find({ $text : {$search: "\"email@domain.com\""}}).sort({Date:1})
Run Code Online (Sandbox Code Playgroud)

我已经尝试过复合索引,包括日期,但它不起作用.

有了这个指数......

db.collection.createIndex({Date: 1, From:"text", To:"text", CC:"text", BCC:"text"})
Run Code Online (Sandbox Code Playgroud)

它给出了错误17007,因为Date应该具有相等匹配,因为它是一个前缀.这不是一个选项,因为我们喜欢所有电子邮件,无论日期如何.

还有这个其他指数......

db.collection.createIndex({From:"text", To:"text", CC:"text", BCC:"text", Date:1})
Run Code Online (Sandbox Code Playgroud)

然后,当它超过排序的内部限制时,它会给出错误17144.

我们已经阅读了以下内容:

Stackoverflow参考

Stackoverflow参考

关于复合索引的mongoDB文档

在这些参考文献和其他参考文献中,我认为这是不可能的,但我不认为我们想要做的是非典型或开箱即用.

我们做错了吗?有没有办法用复合索引或任何其他MongoDB功能进行此查询?

谢谢!

Bla*_*ven 5

无论其他复合索引键如何,您都需要包含$meta"textScore"以获得正确的排序:

db.collection.find(
    { "$text": { "$search": "\"email@domain.com\""}},
    { "score": { "$meta": "textScore" } }
).sort({
    "score": { "$meta": "textScore" }, "Date": 1
})
Run Code Online (Sandbox Code Playgroud)

所以你自然希望首先对"得分"进行排序,然后按"日期"进行排序,以便根据搜索的相关性对事物进行正确排序.

索引的顺序无关紧要,但当然你可以使用"一个"文本索引.因此,请确保在创建之前删除所有其他内容:

db.collection.createIndex({ 
   "From": "text",
   "To": "text",
   "CC":"text", 
   "BCC": "text", 
   "Date":1
})
Run Code Online (Sandbox Code Playgroud)

查找当前的索引:

db.collection.getIndicies()
Run Code Online (Sandbox Code Playgroud)

或者只是丢弃一切并重新开始:

db.collection.dropIndexes()
Run Code Online (Sandbox Code Playgroud)

对于您似乎正在搜索的数据,我原本认为每个字段上的常规复合索引应该更适合您.寻找"电子邮件"地址应该是"完全匹配",如果您希望每个字段有多个项目,那么它们应该是字符串数组,如下所示:

{
    "TO": ["bill@example.com"],
    "FROM": ["ted@example.com"],
    "CC": ["marty@example.com","sarah@example.com"],
    "BCC": [],
    "Date": ISODate("2015-07-27T13:42:05.535Z")
}
Run Code Online (Sandbox Code Playgroud)

那么你需要在每个字段上使用单独的索引,可能在复合中使用"Date",如下所示:

db.email.createIndex({ "TO": 1, "Date": 1 })
db.email.createIndex({ "FROM": 1, "Date": 1 })
db.email.createIndex({ "CC": 1, "Date": 1 })
db.email.createIndex({ "BCC": 1, "Date": 1 })
Run Code Online (Sandbox Code Playgroud)

并查询$or条件:

db.email.find({
    "$or": [
        { "TO": "sarah@example.com" },
        { "FROM": "sarah@example.com" },
        { "CC": "sarah@example.com" },
        { "BCC": "sarah@example.com" }
    ],
    "Date": { "$lt": new Date() }
})
Run Code Online (Sandbox Code Playgroud)

如果您查看.explain(true)(详细)输出,您应该看到获胜计划是所有指定索引的"索引交集".由于每个字段(和所选索引)具有完全匹配值,并且索引日期的范围匹配,因此效率非常高.

对于你来说,这比文本搜索的"模糊匹配"要好得多.即使正则表达式在这里也应该更好(对于电子邮件地址),特别是如果它们"锚定" ^到字符串的开头.

文本索引用于匹配"类似标记",但这不应该是您的数据.在$or不看不错,但它应该做一个更好的工作.

  • @JulioEndara:通过`sort({ score: { $meta: "textScore" }, Date: 1 })` 中的`Date` 字段添加二级排序几乎不会产生任何影响(在一般模糊匹配情况下) ,因为`textScore` 的值在大多数情况下彼此不同,并且只有对于具有相同`textScore` 的文档才会考虑按日期排序,这种情况很少见。如果您想通过模糊匹配分数和另一个字段对“查找”结果进行排名,则需要查看更复杂的搜索解决方案。 (3认同)