2014년 12월 16일 화요일

$or doesn't use compound index where $in does

Given an index on (a,b) and a fixed choice for a,
$or:[{b:1},{b:2}]
doesn't use the index but
b:{$in:[1,2]}
does, showing that I'm not asking for anything that is fundamentally impossible with the given index.

I confirmed this in 2.6.6 but could not find an open ticket about it – is this a known issue?

Test code:
use test;
db
.createCollection('indexTest');
for (var i=1; i<=10; i++) { for (var j=1; j<=10; j++) {db.indexTest.insert({ a:i, b:}); } }
db
.indexTest.ensureIndex({ a:1, b:1 });
db
.indexTest.find({ a:4, $or:[{b:1},{b:2}] }).explain(); // uses index(a)
db
.indexTest.find({ a:4, b:{$in:[1,2]} }).explain(); // uses index(a,b)

Output of the explain()s:
> db.indexTest.find({ a:4, $or:[{b:1},{b:3}] }).explain();
{
        
"cursor" : "BtreeCursor a_1_b_1",
        
"isMultiKey" : false,
        
"n" : 2,
        
"nscannedObjects" : 10,
        
"nscanned" : 10,
        
"nscannedObjectsAllPlans" : 10,
        
"nscannedAllPlans" : 10,
        
"scanAndOrder" : false,
        
"indexOnly" : false,
        
"nYields" : 0,
        
"nChunkSkips" : 0,
        
"millis" : 0,
        
"indexBounds" : {
                
"a" : [
                        
[
                                
4,
                                
4
                        
]
                
],
                
"b" : [
                        
[
                                
{
                                        
"$minElement" : 1
                                
},
                                
{
                                        
"$maxElement" : 1
                                
}
                        
]
                
]
        
},
        
"server" : "...",
        
"filterSet" : false
}
> db.indexTest.find({ a:4, b:{$in:[1,3]} }).explain();
{
        
"cursor" : "BtreeCursor a_1_b_1",
        
"isMultiKey" : false,
        
"n" : 2,
        
"nscannedObjects" : 2,
        
"nscanned" : 3,
        
"nscannedObjectsAllPlans" : 2,
        
"nscannedAllPlans" : 3,
        
"scanAndOrder" : false,
        
"indexOnly" : false,
        
"nYields" : 0,
        
"nChunkSkips" : 0,
        
"millis" : 0,
        
"indexBounds" : {
                
"a" : [
                        
[
                                
4,
                                
4
                        
]
                
],
                
"b" : [
                        
[
                                
1,
                                
1
                        
],
                        
[
                                
3,
                                
3
                        
]
                
]
        
},
        
"server" : "...",
        
"filterSet" : false
}







The key is "given a fixed choice for a".

If you rewrite your query as {$or: [ {a:4, b:1}, {a:4,b:3} ]} I
believe you will see the index be used again.

The server ticket tracking the fact the optimizer doesn't rewrite the
query this way is SERVER-13732



Thanks for pointing me to the right ticket! One could argue that in addition to the options discussed there, the query could be rewritten to use $in where possible – if there were a rewrite engine. But obviously, the option of rephrasing the query is already available to the user, so it's not that big a deal.


댓글 없음:

댓글 쓰기