理想情况下,我有一个Mongo文档,如下所示。我希望能够查询任何两个属性,然后按第三个排序。
文件:
{
"tags" => ["ads", "shopping", "web20", "newspaper", "others..."],
"reachable_via" => ["email", "twitter", "facebook", "contact_form", "phone"],
"keywords" => ["keyword1", "keyword2", "keyword3"],
"score" => 4 #scalar of 0 - 10,
"read_in_project_ids => [124, 433,556]
}
示例查询,使用Mongoid语法:
Document.any_in(:keywords => ["keyword1", "keyword2"]).where(:tags.in => ["ads", "shopping"], :reachable_via.in => ["email"]).order_by([:presence_score, :desc]).limit(10)
此查询有效,但它们不使用索引。另外,我试图对这个东西进行重组,使它以三种不同的方式工作,没有任何运气。
现在,我有380万个文档,这个查询可能需要45-60秒才能返回。
那么,我应该如何进行重组以保持一组数组字段的灵活性,同时获得索引优势?
仅供参考,关键字可能长达数百(并由用户添加),但标签和reachable_via元素是固定的(7个选项会增长),标签大约有20个选项会增长,并由应用程序的代码控制。
谢谢!
答案 0 :(得分:1)
问题是$ in加上排序。
如果你可以删除其中一个,那么它会显着加快你的查询速度。
由于您不能拥有多个具有数组值键的索引(多键,因为它们会调用它们),因此您希望从查询中选择最精细的数组进行索引。在您的示例查询中,可能是关键字。
因此,为了使您的查询更快一点,您可以在{keywords:1,得分:-1}上放置一个索引。这将扫描关键字索引,过滤掉标签和reachable_via上的其他查询要求,然后按降序排序。我用500万个类似文档的集合测试了这个,并且它使用了实际上做得很好的过滤值的索引。
这是来自mongo shell的示例查询(对不起,我不是一个mongoid专家):
> db.test.find({keywords:{$in:["keyword15", "keyword18"]}, tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}}).sort({score:-1}).limit(10).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1 multi",
"nscanned" : 1750873,
"nscannedObjects" : 1750872,
"n" : 10,
"scanAndOrder" : true,
"millis" : 11999,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
],
[
"keyword18",
"keyword18"
]
],
"score" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
]
}
}
如果您可以将查询更改为仅查询一个关键字,则可以更有效地使用索引,在0毫秒内获得特定关键字的前10个分数。
> db.test.find({keywords:"keyword15", tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}}).sort({score:-1}).limit(10).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1",
"nscanned" : 14,
"nscannedObjects" : 14,
"n" : 10,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
]
],
"score" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
]
}
}
这是另一个例子。我将分数移出排序,并进入查询(查询确切的分数,没有限制)。如果您只是寻找最高分或类似的东西,这可以很好地加快查询速度。
> db.test.find({keywords:{$in:["keyword15", "keyword18"]}, tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}, score:9}).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1 multi",
"nscanned" : 175583,
"nscannedObjects" : 175581,
"n" : 82345,
"millis" : 999,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
],
[
"keyword18",
"keyword18"
]
],
"score" : [
[
9,
9
]
]
}
}
冲洗,重复其他查询组合。在查询中选择最高粒度数组字段,将其与排序字段一起索引。如果您可以将查询限制为不在索引数组上使用$ in,那么这是理想的。
我的测试脚本位于: https://gist.github.com/2091880
测试脚本有一些缺点,例如几乎每个文档都有一个关键字1,所以事实证明查询关键字1,虽然它有一个索引,但是进行集合扫描的速度更快。无论如何,关于随机选择关键词,我只是有点懒,但在现实生活中这不会是一个问题。
答案 1 :(得分:0)
您需要建立您想要使用的索引。
http://www.mongodb.org/display/DOCS/Indexes
复合键部分就是您想要的。
如果您认为您的索引已正确建立,则可以为查询提供提示。
http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint