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
|
/*
* Tests that a sparse index cannot be used to answer a $expr query unless the sparse index is
* explicitly hinted. If a sparse index is hinted to answer a $expr query, incomplete results could
* be returned.
*
* @tags: [
* multiversion_incompatible,
* does_not_support_transaction,
* ]
*/
(function() {
"use strict";
load("jstests/libs/analyze_plan.js");
const coll = db.sparse_index_internal_expr;
coll.drop();
coll.insert({a: 1});
const exprQuery = {
$expr: {$lt: ["$missing", "r"]}
};
// Run a query with $expr on a missing field. This query will use a COLLSCAN plan and return
// document '{a: 1}' because $expr expression does not apply type bracketing, specifically, the
// missing field is evaluated to 'null'. The expression returns "true" because 'null' < "r".
let res = coll.find(exprQuery, {_id: 0}).toArray();
assert.eq(res.length, 1);
assert.docEq(res[0], {a: 1});
// Tests that a non-sparse index {missing: 1} can be used to answer the $expr query.
assert.commandWorked(coll.createIndex({"missing": 1}));
// Explain the query, and determine whether an indexed solution is available.
let ixScans = getPlanStages(getWinningPlan(coll.find(exprQuery).explain().queryPlanner), "IXSCAN");
// Verify that the winning plan uses the $** index with the expected bounds.
assert.gt(ixScans.length, 0, ixScans);
assert.eq("missing_1", ixScans[0].indexName, ixScans);
// Run the same query. A complete result will be returned.
res = coll.find(exprQuery, {_id: 0}).toArray();
assert.eq(res.length, 1);
assert.docEq(res[0], {a: 1});
// Drop the non-sparse index and create a sparse index with the same key pattern.
assert.commandWorked(coll.dropIndex("missing_1"));
assert.commandWorked(
coll.createIndex({'missing': 1}, {'sparse': true, 'name': 'missing_1_sparse'}));
// Run the same query to test that a COLLSCAN plan is used rather than an indexed plan.
const collScans =
getPlanStages(getWinningPlan(coll.find(exprQuery).explain().queryPlanner), "COLLSCAN");
// Verify that the winning plan uses the $** index with the expected bounds.
assert.gt(collScans.length, 0, collScans);
// Test that a sparse index can be hinted to answer $expr query but incomplete results in returned,
// because the document is not indexed by the sparse index.
res = coll.find(exprQuery, {_id: 0}).hint("missing_1_sparse").toArray();
assert.eq(res.length, 0);
ixScans = getPlanStages(
getWinningPlan(coll.find(exprQuery).hint("missing_1_sparse").explain().queryPlanner), "IXSCAN");
assert.gt(ixScans.length, 0, ixScans);
assert.eq("missing_1_sparse", ixScans[0].indexName, ixScans);
assert.eq(true, ixScans[0].isSparse, ixScans);
}());
|