Ruby 如何重新构造Mongo文档以实际命中索引?

Ruby 如何重新构造Mongo文档以实际命中索引?,ruby,mongodb,mongoid,Ruby,Mongodb,Mongoid,理想情况下,我有一个如下所示的Mongo文档。我希望能够查询任意两个属性,然后按第三个属性排序 文件: { "tags" => ["ads", "shopping", "web20", "newspaper", "others..."], "reachable_via" => ["email", "twitter", "facebook", "contact_form", "phone"], "keywords" => ["keyword1", "keyword2",

理想情况下,我有一个如下所示的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秒才能返回

那么,我应该如何重新构造以保持一组数组字段的灵活性,同时获得指数化的好处呢

仅供参考,关键字可能有数百个长度(由用户添加),但标记和可通过元素访问的元素是固定的(7个选项将增加),标记大约有20个选项将增加,并由应用程序的代码控制


谢谢

您需要建立您想要使用的索引

复合键部分就是您想要的

如果您认为索引已正确建立,则可以为查询提供提示


问题在于$in与排序的组合

如果您可以删除其中一个,它将大大加快您的查询速度

由于不能有多个具有数组值键(多键,他们称之为多键)的索引,因此需要从查询中选择最细粒度的数组作为索引。在您的示例查询中,可能是关键字

因此,为了使查询更快一些,您可以在{keywords:1,score:-1}上放置一个索引。这将扫描关键字索引,过滤掉标签和可访问_via上的其他查询要求,然后按分数递减排序。我收集了500万个与您类似的文档,对其进行了测试,它使用了对值的索引,这些值实际上在过滤方面做得很好

下面是一个来自mongo shell的查询示例(对不起,我不是mongoid专家):

如果您可以将查询更改为只查询一个关键字,那么它可以更有效地使用索引,在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,那就很理想了

我的测试脚本位于此处:


测试脚本有一些缺点,例如几乎每个文档都有一个关键字1,因此结果表明,在查询关键字1时,虽然它有一个索引,但执行集合扫描的速度更快。不管怎么说,我只是有点懒于随机选择关键词,但在现实生活中这不会是一个问题。

虽然这是一个好的开始,但这并不能回答问题。问题是Mongo只支持索引中的单个“范围”。因此,您不能简单地为此构建一个有效的复合索引。我很想让它这么做,但事实并非如此。事实上,在Mongoid 2.2.6 migrations中,如果你尝试的话,你会呕吐。顺便说一句(我注意到你在DC),你应该在周三晚上去MongoDB会议室:那里至少会有两名10gen员工,让你去了解索引。
> 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
        ]
    ]
}
}