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
|
// @tags: [
// assumes_balancer_off,
// assumes_read_concern_local,
// ]
(function() {
"use strict";
load("jstests/aggregation/extras/utils.js"); // For arrayEq().
load("jstests/libs/analyze_plan.js"); // For getPlanStages().
load("jstests/libs/fixture_helpers.js"); // For numberOfShardsForCollection().
const coll = db.wildcard_nonblocking_sort;
coll.drop();
assert.commandWorked(coll.createIndex({"$**": 1}, {wildcardProjection: {"excludedField": 0}}));
for (let i = 0; i < 50; i++) {
assert.commandWorked(coll.insert({a: i, b: -i, x: [123], excludedField: i}));
}
function checkQueryHasSameResultsWhenUsingIdIndex(query, sort, projection) {
const l = coll.find(query, projection).sort(sort).toArray();
const r = coll.find(query, projection).sort(sort).hint({$natural: 1}).toArray();
assert(arrayEq(l, r));
}
function checkQueryUsesSortType(query, sort, projection, isBlocking) {
const explain = assert.commandWorked(coll.find(query, projection).sort(sort).explain());
const plan = getWinningPlan(explain.queryPlanner);
const ixScans = getPlanStages(plan, "IXSCAN");
const sorts = getPlanStages(plan, "SORT");
if (isBlocking) {
assert.eq(sorts.length, FixtureHelpers.numberOfShardsForCollection(coll), explain);
assert.eq(sorts[0].sortPattern, sort, explain);
// A blocking sort may or may not use the index, so we don't check the length of
// 'ixScans'.
} else {
assert.eq(sorts.length, 0, explain);
assert.eq(ixScans.length, FixtureHelpers.numberOfShardsForCollection(coll), explain);
const sortKey = Object.keys(sort)[0];
assert.docEq({$_path: 1, [sortKey]: 1}, ixScans[0].keyPattern);
}
}
function checkQueryUsesNonBlockingSortAndGetsCorrectResults(query, sort, projection) {
checkQueryUsesSortType(query, sort, projection, false);
checkQueryHasSameResultsWhenUsingIdIndex(query, sort, projection);
}
function checkQueryUsesBlockingSortAndGetsCorrectResults(query, sort, projection) {
checkQueryUsesSortType(query, sort, projection, true);
checkQueryHasSameResultsWhenUsingIdIndex(query, sort, projection);
}
function runSortTests(dir, proj) {
// Test that the $** index can provide a non-blocking sort where appropriate.
checkQueryUsesNonBlockingSortAndGetsCorrectResults({a: {$gte: 0}}, {a: dir}, proj);
checkQueryUsesNonBlockingSortAndGetsCorrectResults({a: {$gte: 0}, x: 123}, {a: dir}, proj);
// Test that the $** index can produce a solution with a blocking sort where appropriate.
checkQueryUsesBlockingSortAndGetsCorrectResults({a: {$gte: 0}}, {a: dir, b: dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({a: {$gte: 0}}, {a: dir, b: -dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({a: {$gte: 0}}, {a: -dir, b: dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({a: {$exists: true}}, {a: dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({}, {a: dir}, proj);
// Test sorted queries on a field that is excluded by the $** index's wildcardProjection.
checkQueryUsesBlockingSortAndGetsCorrectResults(
{excludedField: {$gte: 0}}, {excludedField: dir}, proj);
// Test sorted queries on a multikey field, with and without $elemMatch.
checkQueryUsesBlockingSortAndGetsCorrectResults({x: 123}, {a: dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({x: {$elemMatch: {$eq: 123}}}, {x: dir}, proj);
checkQueryUsesBlockingSortAndGetsCorrectResults({x: {$elemMatch: {$eq: 123}}}, {a: dir}, proj);
}
// Run each test for both ascending and descending sorts, with and without a projection.
for (let dir of [1, -1]) {
for (let proj of [{}, {_id: 0, a: 1}]) {
runSortTests(dir, proj);
}
}
})();
|