我的集合中有两个数组(一个是嵌入式文档,另一个只是一个简单的字符串集合)。例如文件:
{
"_id" : ObjectId("534fb7b4f9591329d5ea3d0c"),
"_class" : "discussion",
"title" : "A",
"owner" : "1",
"tags" : ["tag-1", "tag-2", "tag-3"],
"creation_time" : ISODate("2014-04-17T11:14:59.777Z"),
"modification_time" : ISODate("2014-04-17T11:14:59.777Z"),
"policies" : [
{
"participant_id" : "2",
"action" : "CREATE"
}, {
"participant_id" : "1",
"action" : "READ"
}
]
}
由于某些查询仅包含策略,而某些查询将包含标记和参与者数组,并且考虑到我无法使用两个数组创建多键索引,我认为它将是一个经典场景使用Index Intersection。
我正在执行查询,但我无法看到交叉点开始。
以下是索引:
db.discussion.getIndexes()
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test-fw.discussion"
},
{
"v" : 1,
"key" : {
"tags" : 1,
"creation_time" : 1
},
"name" : "tags",
"ns" : "test-fw.discussion",
"dropDups" : false,
"background" : false
},
{
"v" : 1,
"key" : {
"policies.participant_id" : 1,
"policies.action" : 1
},
"name" : "policies",
"ns" : "test-fw.discussion"
}
以下是查询:
db.discussion.find({
"$and" : [
{ "tags" : { "$in" : [ "tag-1" , "tag-2" , "tag-3"] }},
{ "policies" : { "$elemMatch" : {
"$and" : [
{ "participant_id" : { "$in" : [
"participant-1",
"participant-2",
"participant-3"
]}},
{ "action" : "READ"}
]
}}}
]
})
.limit(20000).sort({ "creation_time" : 1 }).explain();
以下是解释的结果:
"clauses" : [
{
"cursor" : "BtreeCursor tags",
"isMultiKey" : true,
"n" : 10000,
"nscannedObjects" : 10000,
"nscanned" : 10000,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"tags" : [
[
"tag-1",
"tag-1"
]
],
"creation_time" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
}
},
{
"cursor" : "BtreeCursor tags",
"isMultiKey" : true,
"n" : 10000,
"nscannedObjects" : 10000,
"nscanned" : 10000,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"tags" : [
[
"tag-2",
"tag-2"
]
],
"creation_time" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
}
},
{
"cursor" : "BtreeCursor tags",
"isMultiKey" : true,
"n" : 10000,
"nscannedObjects" : 10000,
"nscanned" : 10000,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"tags" : [
[
"tag-3",
"tag-3"
]
],
"creation_time" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
"n" : 20000,
"nscannedObjects" : 30000,
"nscanned" : 30000,
"nscannedObjectsAllPlans" : 30203,
"nscannedAllPlans" : 30409,
"scanAndOrder" : false,
"nYields" : 471,
"nChunkSkips" : 0,
"millis" : 165,
"server" : "User-PC:27017",
"filterSet" : false
查询中的每个标记(tag1,tag-2和tag-3)都有10K文档。 每个策略({participant-1,READ},{participant-2,READ},{participant-3,READ})都有10K文档。
AND运算符产生20K文档。
正如我之前所说,我不明白为什么两个索引的交叉点(我的意思是政策和标签索引)并没有开始。
有人可以对我失踪的事物说些什么吗?
答案 0 :(得分:0)
有两件事对你理解这一点非常重要。
第一点是查询优化器在解析查询计划时只能使用一个索引,并且不能使用您指定的两个索引。因此,除非您使用hint
明确指定,否则它会根据自己的决定选择最适合的那个。交叉点有点适合,但现在是下一点:
第二点记录在limitations复合索引中。这实际上指出,即使你“尝试”创建一个包含你想要的两个数组字段的复合索引,你也不能。这里的问题是,作为一个数组,这为边界键引入了太多的可能性,并且当在具有标准字段的复合中使用时,多键索引已经引入了相当复杂的复杂性。
组合两个多键索引的限制是这里的主要问题,就像它在创建时一样,“组合”两者的复杂性会产生两个很多排列,使其成为一个可行的选择。
可能只是policies
索引实际上是用于此类搜索的更好的索引,你可以通过首先在查询中指定该字段来修改它:
db.discussion.find({
{
"policies" : { "$elemMatch" : {
"participant_id" : { "$in" : [
"participant-1",
"participant-2",
"participant-3"
]},
"action" : "READ"
}},
"tags" : { "$in" : [ "tag-1" , "tag-2" , "tag-3"] }
}
)
那就是如果它会选择较小范围的数据,它可能会做。否则,请使用前面提到的hint
修饰符。
如果这实际上没有直接帮助结果,那么可能值得重新考虑模式,而不需要在数组字段或其他类型的“元”字段中使用这些值可以轻松查找索引。
另请注意,在编辑后的表单中,不应要求所有包装 $and
语句,因为“和”是MongoDB查询中隐含的。作为修饰符,只有在同一字段中需要两个 不同的条件时才需要它。
答案 1 :(得分:0)
经过一些测试后,我相信Mongo实际上可以在交叉路口使用两个多键索引。我创建了一个具有以下结构的集合:
{
"_id" : ObjectId("54e129c90ab3dc0006000001"),
"bar" : [
"hgcmdflitt",
...
"nuzjqxmzot"
],
"foo" : [
"bxzvqzlpwy",
...
"xcwrwluxbd"
]
}
我在foo和bar上创建了索引,然后运行以下查询。注意" true"传入解释。这样可以启用详细模式。
db.col.find({"bar":"hgcmdflitt", "foo":"bxzvqzlpwy"}).explain(true)
在详细的结果中,您可以找到" allPlans"响应部分,它将显示mongo考虑的所有查询计划。
"allPlans" : [
{
"cursor" : "BtreeCursor bar_1",
...
},
{
"cursor" : "BtreeCursor foo_1",
...
},
{
"cursor" : "Complex Plan"
...
}
]
如果您看到带有"光标的计划" :"复杂计划"这意味着mongo considered using an index intersection。要查找mongo可能未决定实际使用该查询计划的原因,请参阅以下答案:Why doesn't MongoDB use index intersection?