summaryrefslogtreecommitdiff
path: root/jstests/core/elemmatch_index.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/elemmatch_index.js')
-rw-r--r--jstests/core/elemmatch_index.js115
1 files changed, 115 insertions, 0 deletions
diff --git a/jstests/core/elemmatch_index.js b/jstests/core/elemmatch_index.js
new file mode 100644
index 00000000000..a1941620a48
--- /dev/null
+++ b/jstests/core/elemmatch_index.js
@@ -0,0 +1,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);
+})();
+})();