diff options
Diffstat (limited to 'jstests/core/query/exists/existsa.js')
-rw-r--r-- | jstests/core/query/exists/existsa.js | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/jstests/core/query/exists/existsa.js b/jstests/core/query/exists/existsa.js new file mode 100644 index 00000000000..2b5f70dd77f --- /dev/null +++ b/jstests/core/query/exists/existsa.js @@ -0,0 +1,111 @@ +/** + * Tests that sparse indexes are disallowed for $exists:false queries. + */ +(function() { +"use strict"; + +const coll = db.jstests_existsa; +coll.drop(); + +assert.commandWorked(coll.insert({})); +assert.commandWorked(coll.insert({a: 1})); +assert.commandWorked(coll.insert({a: {x: 1}, b: 1})); + +let indexKeySpec = {}; +let indexKeyField = ''; + +/** Configure testing of an index { <indexKeyField>:1 }. */ +function setIndex(_indexKeyField) { + indexKeyField = _indexKeyField; + indexKeySpec = {}; + indexKeySpec[indexKeyField] = 1; + coll.createIndex(indexKeySpec, {sparse: true}); +} +setIndex('a'); + +/** @return count when hinting the index to use. */ +function hintedCount(query) { + return coll.find(query).hint(indexKeySpec).itcount(); +} + +/** The query field does not exist and the sparse index is not used without a hint. */ +function assertMissing(query, expectedMissing = 1, expectedIndexedMissing = 0) { + assert.eq(expectedMissing, coll.count(query)); + // We also shouldn't get a different count depending on whether + // an index is used or not. + assert.eq(expectedIndexedMissing, hintedCount(query)); +} + +/** The query field exists and the sparse index is used without a hint. */ +function assertExists(query, expectedExists = 2) { + assert.eq(expectedExists, coll.count(query)); + // An $exists:true predicate generates no index filters. Add another predicate on the index + // key to trigger use of the index. + let andClause = {}; + andClause[indexKeyField] = {$ne: null}; + Object.extend(query, {$and: [andClause]}); + assert.eq(expectedExists, coll.count(query)); + assert.eq(expectedExists, hintedCount(query)); +} + +/** The query field exists and the sparse index is not used without a hint. */ +function assertExistsUnindexed(query, expectedExists = 2) { + assert.eq(expectedExists, coll.count(query)); + // Even with another predicate on the index key, the sparse index is disallowed. + let andClause = {}; + andClause[indexKeyField] = {$ne: null}; + Object.extend(query, {$and: [andClause]}); + assert.eq(expectedExists, coll.count(query)); + assert.eq(expectedExists, hintedCount(query)); +} + +// $exists:false queries match the proper number of documents and disallow the sparse index. +assertMissing({a: {$exists: false}}); +assertMissing({a: {$not: {$exists: true}}}); +assertMissing({$and: [{a: {$exists: false}}]}); +assertMissing({$or: [{a: {$exists: false}}]}); +assertMissing({$nor: [{a: {$exists: true}}]}); +assertMissing({'a.x': {$exists: false}}, 2, 1); + +// Currently a sparse index is disallowed even if the $exists:false query is on a different +// field. +assertMissing({b: {$exists: false}}, 2, 1); +assertMissing({b: {$exists: false}, a: {$ne: 6}}, 2, 1); +assertMissing({b: {$not: {$exists: true}}}, 2, 1); + +// Top level $exists:true queries match the proper number of documents +// and use the sparse index on { a : 1 }. +assertExists({a: {$exists: true}}); + +// Nested $exists queries match the proper number of documents and disallow the sparse index. +assertExistsUnindexed({$nor: [{a: {$exists: false}}]}); +assertExistsUnindexed({$nor: [{'a.x': {$exists: false}}]}, 1); +assertExistsUnindexed({a: {$not: {$exists: false}}}); + +// Nested $exists queries disallow the sparse index in some cases where it is not strictly +// necessary to do so. (Descriptive tests.) +assertExistsUnindexed({$nor: [{b: {$exists: false}}]}, 1); // Unindexed field. +assertExists({$or: [{a: {$exists: true}}]}); // $exists:true not $exists:false. + +// Behavior is similar with $elemMatch. +coll.drop(); +assert.commandWorked(coll.insert({a: [{}]})); +assert.commandWorked(coll.insert({a: [{b: 1}]})); +assert.commandWorked(coll.insert({a: [{b: [1]}]})); +setIndex('a.b'); + +assertMissing({a: {$elemMatch: {b: {$exists: false}}}}); + +// A $elemMatch predicate is treated as nested, and the index should be used for $exists:true. +assertExists({a: {$elemMatch: {b: {$exists: true}}}}); + +// A $not within $elemMatch should not attempt to use a sparse index for $exists:false. +assertExistsUnindexed({'a.b': {$elemMatch: {$not: {$exists: false}}}}, 1); +assertExistsUnindexed({'a.b': {$elemMatch: {$gt: 0, $not: {$exists: false}}}}, 1); + +// A non sparse index will not be disallowed. +coll.drop(); +assert.commandWorked(coll.insert({})); +coll.createIndex({a: 1}); +assert.eq(1, coll.find({a: {$exists: false}}).itcount()); +})(); |