diff options
author | Timour Katchaounov <timour.katchaounov@mongodb.com> | 2021-11-22 11:41:21 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-11-22 12:25:57 +0000 |
commit | 90699509e15b33fda10832e79efcd158aee1f0eb (patch) | |
tree | 06b9188ed370cb2e971ff1891bddc95223099c8b /jstests/core/match_numeric_components.js | |
parent | b4aca2363c2e59789643c9132e764e72e86b3996 (diff) | |
download | mongo-90699509e15b33fda10832e79efcd158aee1f0eb.tar.gz |
SERVER-57588 Inconsistent query results when an array position is indexed whose value is an array
Diffstat (limited to 'jstests/core/match_numeric_components.js')
-rw-r--r-- | jstests/core/match_numeric_components.js | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/jstests/core/match_numeric_components.js b/jstests/core/match_numeric_components.js index 07a58507b8e..7f20b3710ab 100644 --- a/jstests/core/match_numeric_components.js +++ b/jstests/core/match_numeric_components.js @@ -350,4 +350,100 @@ assert.commandWorked(coll2.insert(kRegexDocs)); const expected = [{_id: 1, "b": "hello"}]; assert.sameMembers(res, expected); } + +// The tests below indirectly make sure that an index scan is not chosen when $elemMatch is +// against a indexed positional path component because it is not possible to generate index +// bounds from the $elemMatch conditions. If an index scan is chosen, then the corresponding +// queries would produce a wrong result. +// More precisely for an index like {"a.0": 1} and a document {a: [[1, 2, 3]]}, the nested array is +// not unwound during index key generation. That is, there is a single index key {"": [1, 2, 3]} +// rather than three separate index keys, {"": 1}, {"": 2}, {"": 3}. This precludes the ability to +// generate index bounds for $elemMatch predicates on "a.0" because "a.0" refers to the whole array +// [1, 2, 3], and not its individual members. +{ + // Test with $in. + assert(coll.drop()); + const expectedDoc = {_id: 42, "a": [["b"], ["c"]]}; + assert.commandWorked(coll.insert(expectedDoc)); + const query = {"a.0": {$elemMatch: {$in: ["b"]}}}; + + const res1 = coll.find(query).toArray(); + assert.commandWorked(coll.createIndex({"a.0": 1})); + const res2 = coll.find(query).toArray(); + assert.sameMembers([expectedDoc], res1); + assert.sameMembers([expectedDoc], res2); +} + +// Tests with equality. Add some data for the next few tests. +coll.drop(); +assert.commandWorked(coll.insert({_id: 0, f0: 'zz', f1: 5})); +assert.commandWorked(coll.insert({_id: 1, f0: 'zz', f1: [3, 5]})); +assert.commandWorked(coll.insert({_id: 4, f0: 'zz', f1: [3, 5, [7, 9]]})); +assert.commandWorked(coll.insert({_id: 2, f0: 'zz', f1: [[3, 5], [5, 7]]})); +assert.commandWorked(coll.insert({_id: 3, f0: 'zz', f1: [[[0], [3, 5]], [[0], [5, 7]]]})); + +{ + const res1 = coll.find({'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + const res2 = coll.find({'f1.0': {$elemMatch: {$eq: [3, 5]}}}).toArray(); + assert.commandWorked(coll.createIndex({'f1.0': 1})); + const res3 = coll.find({'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + const res4 = coll.find({'f1.0': {$elemMatch: {$eq: [3, 5]}}}).toArray(); + const expected1 = [{_id: 2, f0: 'zz', f1: [[3, 5], [5, 7]]}]; + const expected2 = [{_id: 3, f0: 'zz', f1: [[[0], [3, 5]], [[0], [5, 7]]]}]; + assert.sameMembers(expected1, res1); + assert.sameMembers(expected1, res3); + assert.sameMembers(expected2, res2); + assert.sameMembers(expected2, res4); + assert.commandWorked(coll.dropIndex({'f1.0': 1})); +} + +{ + // Compound index. + const res1 = coll.find({'f0': 'zz', 'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + assert.commandWorked(coll.createIndex({'f0': 1, 'f1.0': 1})); + const res2 = coll.find({'f0': 'zz', 'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + const expected = [{_id: 2, f0: 'zz', f1: [[3, 5], [5, 7]]}]; + assert.sameMembers(expected, res1); + assert.sameMembers(expected, res2); + assert.commandWorked(coll.dropIndex({'f0': 1, 'f1.0': 1})); +} + +{ + // Two-levels of array nesting. + const res1 = coll.find({'f1.0.1': {$elemMatch: {$eq: 3}}}).toArray(); + assert.commandWorked(coll.createIndex({'f1.0.1': 1})); + const res2 = coll.find({'f1.0.1': {$elemMatch: {$eq: 3}}}).toArray(); + const expected = [{_id: 3, f0: 'zz', f1: [[[0], [3, 5]], [[0], [5, 7]]]}]; + assert.sameMembers(expected, res1); + assert.sameMembers(expected, res2); +} + +{ + assert(coll.drop()); + assert.commandWorked(coll.createIndex({'f1.0': 1})); + assert.commandWorked(coll.insert({_id: 1, f1: [[42, 5], [77, 99]]})); + + const res1 = coll.find({'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + assert.sameMembers([{_id: 1, f1: [[42, 5], [77, 99]]}], res1); + + // Object with numeric field component, and no nested arrays. + assert.commandWorked(coll.insert({_id: 2, f1: {0: [42, 5], 1: [77, 99]}})); + const res2 = coll.find({'f1.0': {$elemMatch: {$eq: 5}}}).toArray(); + assert.sameMembers( + [{_id: 1, f1: [[42, 5], [77, 99]]}, {_id: 2, f1: {'0': [42, 5], '1': [77, 99]}}], res2); +} + +{ + assert(coll.drop()); + assert.commandWorked(coll.createIndex({'0': 1})); + + assert.commandWorked(coll.insert({_id: 1, '0': [42, 5]})); + const res1 = coll.find({'0': {$elemMatch: {$eq: 5}}}).toArray(); + assert.sameMembers([{'0': [42, 5], _id: 1}], res1); + + assert.commandWorked(coll.createIndex({'0.1': 1})); + assert.commandWorked(coll.insert({_id: 2, '0': {0: [42], 1: [5]}})); + const res2 = coll.find({'0.1': {$elemMatch: {$eq: 5}}}).toArray(); + assert.sameMembers([{'0': {'0': [42], '1': [5]}, _id: 2}], res2); +} })(); |