1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
// Tests where the QueryOptimizerCursor enters takeover mode during a query rather than a get more.
t = db.jstests_finda;
t.drop();
numDocs = 200;
function clearQueryPlanCache() {
t.ensureIndex({c: 1});
t.dropIndex({c: 1});
}
function assertAllFound(matches) {
// printjson( matches );
found = new Array(numDocs);
for (var i = 0; i < numDocs; ++i) {
found[i] = false;
}
for (var i in matches) {
m = matches[i];
found[m._id] = true;
}
for (var i = 0; i < numDocs; ++i) {
assert(found[i], i.toString());
}
}
function makeCursor(query, projection, sort, batchSize, returnKey) {
print("\n*** query:");
printjson(query);
print("proj:");
printjson(projection);
cursor = t.find(query, projection);
if (sort) {
cursor.sort(sort);
print("sort:");
printjson(sort);
}
if (batchSize) {
cursor.batchSize(batchSize);
print("bs: " + batchSize);
}
if (returnKey) {
cursor.returnKey();
}
return cursor;
}
function checkCursorWithBatchSizeProjection(
query, projection, sort, batchSize, expectedLeftInBatch) {
clearQueryPlanCache();
cursor = makeCursor(query, projection, sort, batchSize);
// XXX: this
assert.eq(expectedLeftInBatch, cursor.objsLeftInBatch());
assertAllFound(cursor.toArray());
}
function checkCursorWithBatchSize(query, sort, batchSize, expectedLeftInBatch) {
checkCursorWithBatchSizeProjection(query, {}, sort, batchSize, expectedLeftInBatch);
checkCursorWithBatchSizeProjection(query, {a: 1, _id: 1}, sort, batchSize, expectedLeftInBatch);
// In the cases tested, when expectedLeftInBatch is high enough takeover will occur during
// the query operation rather than getMore and the last few matches should properly return keys
// from the a,_id index.
clearQueryPlanCache();
if (expectedLeftInBatch > 110) {
cursor = makeCursor(query, {}, sort, batchSize, true);
lastNonAIndexResult = -1;
for (var i = 0; i < expectedLeftInBatch; ++i) {
next = cursor.next();
// Identify the query plan used by checking the fields of a returnKey query.
if (!friendlyEqual(['a', '_id'], Object.keySet(next))) {
lastNonAIndexResult = i;
}
}
// The last results should come from the a,_id index.
assert.lt(lastNonAIndexResult, expectedLeftInBatch - 5);
}
}
function queryWithPlanTypes(withDups) {
t.drop();
for (var i = 1; i < numDocs; ++i) {
t.save({_id: i, a: i, b: 0});
}
if (withDups) {
t.save({_id: 0, a: [0, numDocs], b: 0}); // Add a dup on a:1 index.
} else {
t.save({_id: 0, a: 0, b: 0});
}
t.ensureIndex({a: 1, _id: 1}); // Include _id for a covered index projection.
// All plans in order.
checkCursorWithBatchSize({a: {$gte: 0}}, null, 150, 150);
// All plans out of order.
checkCursorWithBatchSize({a: {$gte: 0}}, {c: 1}, null, 101);
// Some plans in order, some out of order.
checkCursorWithBatchSize({a: {$gte: 0}, b: 0}, {a: 1}, 150, 150);
checkCursorWithBatchSize({a: {$gte: 0}, b: 0}, {a: 1}, null, 101);
}
queryWithPlanTypes(false);
queryWithPlanTypes(true);
|