summaryrefslogtreecommitdiff
path: root/jstests/core/elemmatch_index.js
blob: a1941620a484a25c54a6da388cd7ac852d0e214d (plain)
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
105
106
107
108
109
110
111
112
113
114
115
/**
 * Test that queries containing $elemMatch correctly use an index if each child expression is
 * compatible with the index.
 * @tags: [
 *   assumes_balancer_off,
 *   assumes_read_concern_local,
 * ]
 */
(function() {
"use strict";

load("jstests/libs/analyze_plan.js");

const coll = db.elemMatch_index;
coll.drop();

assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.insert({a: [{}]}));
assert.commandWorked(coll.insert({a: [1, null]}));
assert.commandWorked(coll.insert({a: [{type: "Point", coordinates: [0, 0]}]}));

assert.commandWorked(coll.createIndex({a: 1}, {sparse: true}));

function assertIndexResults(coll, query, useIndex, nReturned) {
    const explainPlan = coll.find(query).explain("executionStats");
    assert.eq(isIxscan(db, getWinningPlan(explainPlan.queryPlanner)), useIndex);
    assert.eq(explainPlan.executionStats.nReturned, nReturned);
}

assertIndexResults(coll, {a: {$elemMatch: {$exists: false}}}, false, 0);

// An $elemMatch predicate is treated as nested, and the index should be used for $exists:true.
assertIndexResults(coll, {a: {$elemMatch: {$exists: true}}}, true, 3);

// $not within $elemMatch should not attempt to use a sparse index for $exists:false.
assertIndexResults(coll, {a: {$elemMatch: {$not: {$exists: false}}}}, false, 3);
assertIndexResults(coll, {a: {$elemMatch: {$gt: 0, $not: {$exists: false}}}}, false, 1);

// $geo within $elemMatch should not attempt to use a non-geo index.
assertIndexResults(coll,
                   {
                       a: {
                           $elemMatch: {
                               $geoWithin: {
                                   $geometry: {
                                       type: "Polygon",
                                       coordinates: [[[0, 0], [0, 1], [1, 0], [0, 0]]]
                                   }
                               }
                           }
                       }
                   },
                   false,
                   1);

// $in with a null value within $elemMatch should use a sparse index.
assertIndexResults(coll, {a: {$elemMatch: {$in: [null]}}}, true, 1);

// $eq with a null value within $elemMatch should use a sparse index.
assertIndexResults(coll, {a: {$elemMatch: {$eq: null}}}, true, 1);

// A negated regex within $elemMatch should not use an index, sparse or not.
assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3);

coll.dropIndexes();
assert.commandWorked(coll.createIndex({a: 1}));
assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3);

(function() {
assert(coll.drop());
assert.commandWorked(coll.insert({a: [{b: {c: "x"}}]}));
assert.commandWorked(coll.createIndex({"a.b.c": 1}));

// Tests $elemMatch with path components that are empty strings. The system should not attempt to
// use the index for these queries.
assertIndexResults(coll, {"": {$elemMatch: {"a.b.c": "x"}}}, false, 0);
assertIndexResults(coll, {"": {$all: [{$elemMatch: {"a.b.c": "x"}}]}}, false, 0);
assertIndexResults(coll, {a: {$elemMatch: {"": {$elemMatch: {"b.c": "x"}}}}}, false, 0);

// Tests $elemMatch with supporting index and no path components that are empty strings.
assertIndexResults(coll, {a: {$elemMatch: {"b.c": "x"}}}, true, 1);
assertIndexResults(coll, {a: {$all: [{$elemMatch: {"b.c": "x"}}]}}, true, 1);
})();

(function() {
const coll = db.index_elemmatch1;
coll.drop();

let x = 0;
let y = 0;
const bulk = coll.initializeUnorderedBulkOp();
for (let a = 0; a < 10; a++) {
    for (let b = 0; b < 10; b++) {
        bulk.insert({a: a, b: b % 10, arr: [{x: x++ % 10, y: y++ % 10}]});
    }
}
assert.commandWorked(bulk.execute());

assert.commandWorked(coll.createIndex({a: 1, b: 1}));
assert.commandWorked(coll.createIndex({"arr.x": 1, a: 1}));

const query = {
    a: 5,
    b: {$in: [1, 3, 5]},
    arr: {$elemMatch: {x: 5, y: 5}}
};

const count = coll.find(query).itcount();
assert.eq(count, 1);

const explain = coll.find(query).hint({"arr.x": 1, a: 1}).explain("executionStats");
assert.commandWorked(explain);
assert.eq(count, explain.executionStats.totalKeysExamined, explain);
})();
})();