summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/query_planner_array_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/query_planner_array_test.cpp')
-rw-r--r--src/mongo/db/query/query_planner_array_test.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/mongo/db/query/query_planner_array_test.cpp b/src/mongo/db/query/query_planner_array_test.cpp
index 68521862a55..77829966187 100644
--- a/src/mongo/db/query/query_planner_array_test.cpp
+++ b/src/mongo/db/query/query_planner_array_test.cpp
@@ -2252,4 +2252,211 @@ TEST_F(QueryPlannerTest, CanExplodeMultikeyIndexScanForSortWhenSortFieldsAreNotM
"{ixscan: {pattern: {a: 1, 'b.c': 1}, filter: null,"
"bounds: {a: [[2,2,true,true]], 'b.c': [['MinKey','MaxKey',true,true]]}}}]}}}}");
}
+
+TEST_F(QueryPlannerTest, ElemMatchValueNENull) {
+ addIndex(BSON("a" << 1));
+ runQuery(fromjson("{a: {$elemMatch: {$ne: null}}}"));
+
+ // We can't use the index because we would exclude {a: []} which should match.
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {filter: {a: {$elemMatch: {$ne: null}}}, node: {"
+ " ixscan: {pattern: {a:1}, bounds: {"
+ " a: [['MinKey',undefined,true,false], [null,'MaxKey',false,true]]"
+ "}}}}}");
+}
+
+TEST_F(QueryPlannerTest, ElemMatchObjectNENull) {
+ addIndex(BSON("a.b" << 1));
+ runQuery(fromjson("{a: {$elemMatch: {b: {$ne: null}}}}"));
+
+ // We can't use the index because we would exclude {"a.b": []} which should match.
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {filter: {a: {$elemMatch: {b: {$ne: null}}}}, node: {"
+ " ixscan: {pattern: {'a.b':1}, bounds: {"
+ " 'a.b': [['MinKey',undefined,true,false], [null,'MaxKey',false,true]]"
+ "}}}}}");
+}
+
+TEST_F(QueryPlannerTest, NENullOnMultikeyIndex) {
+ // true means multikey
+ addIndex(BSON("a" << 1), true);
+ runQuery(fromjson("{a: {$ne: null}}"));
+
+ // We can't use the index because we would exclude {a: []} which should match.
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+}
+
+TEST_F(QueryPlannerTest, ElemMatchValueNENullOnMultikeyIndex) {
+ // true means multikey
+ addIndex(BSON("a" << 1), true);
+ runQuery(fromjson("{a: {$elemMatch: {$ne: null}}}"));
+
+ // We should be able to use the index because of the value $elemMatch.
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {filter: {a: {$elemMatch: {$ne: null}}}, node: {"
+ " ixscan: {pattern: {a: 1}, bounds: {"
+ " a: [['MinKey',undefined,true,false], [null,'MaxKey',false,true]]"
+ "}}}}}");
+}
+
+TEST_F(QueryPlannerTest, ElemMatchObjectNENullOnMultikeyIndex) {
+ // true means multikey
+ addIndex(BSON("a.b" << 1), true);
+ runQuery(fromjson("{a: {$elemMatch: {b: {$ne: null}}}}"));
+
+ // We can't use the index because we would exclude {a: [{b: []}]} which should match.
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+}
+
+TEST_F(QueryPlannerTest, ElemMatchObjectNENullWithSuffixOfElemMatchMultiKey) {
+ MultikeyPaths multikeyPaths{{0U, 1U}};
+ addIndex(BSON("a.b" << 1), multikeyPaths);
+ runQuery(fromjson("{a: {$elemMatch: {b: {$ne: null}}}}"));
+
+ // We can't use the index because we would exclude {a: [{b: []}]} which should match.
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+}
+
+TEST_F(QueryPlannerTest, ElemMatchObjectNENullWithPrefixOfElemMatchMultiKey) {
+ MultikeyPaths multikeyPaths{{0U}};
+ addIndex(BSON("a.b" << 1), multikeyPaths);
+ runQuery(fromjson("{a: {$elemMatch: {b: {$ne: null}}}}"));
+
+ // We should be able to use the index since only 'a' is multikey.
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {filter: {a: {$elemMatch: {b: {$ne: null}}}}, node: {"
+ " ixscan: {pattern: {'a.b': 1}, bounds: {"
+ " 'a.b': [['MinKey',undefined,true,false], [null,'MaxKey',false,true]]"
+ "}}}}}");
+}
+
+TEST_F(QueryPlannerTest, CompoundIndexBoundsDottedNotEqualsNullWithProjectionMultiKeyOnOtherPath) {
+ MultikeyPaths multikeyPaths{{0U}, {}};
+ addIndex(BSON("a" << 1 << "c.d" << 1), multikeyPaths);
+ runQuerySortProj(fromjson("{'a': {$gt: 'foo'}, 'c.d': {$ne: null}}"),
+ BSONObj(),
+ fromjson("{_id: 0, 'c.d': 1}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{proj: {spec: {_id: 0, 'c.d': 1}, node: {cscan: {dir: 1}}}}");
+ assertSolutionExists(
+ "{proj: {spec: {_id: 0, 'c.d': 1}, node: {"
+ " ixscan: {filter: null, pattern: {'a': 1, 'c.d': 1}, bounds: {"
+ " 'a': [['foo',{},false,false]], "
+ " 'c.d':[['MinKey',undefined,true,false],[null,'MaxKey',false,true]]"
+ "}}}}}");
+}
+
+TEST_F(QueryPlannerTest,
+ CompoundIndexBoundsElemMatchObjectEqualsNullWithProjectionMultiKeyOnOtherPath) {
+ MultikeyPaths multikeyPaths{{0U}, {}};
+ addIndex(BSON("a" << 1 << "c.d" << 1), multikeyPaths);
+ runQuerySortProj(fromjson("{'a': {$gt: 'foo'}, c: {$elemMatch: {d: {$ne: null}}}}"),
+ BSONObj(),
+ fromjson("{_id: 0, 'c.d': 1}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{proj: {spec: {_id: 0, 'c.d': 1}, node: {cscan: {dir: 1}}}}");
+ assertSolutionExists(
+ "{proj: {spec: {_id: 0, 'c.d': 1}, node: {"
+ " fetch: {filter: {c: {$elemMatch: {d: {$ne: null}}}}, node: {"
+ " ixscan: {filter: null, pattern: {'a': 1, 'c.d': 1}, bounds: {"
+ " a: [['foo',{},false,false]], "
+ " 'c.d':[['MinKey',undefined,true,false],[null,'MaxKey',false,true]]"
+ "}}}}}}}");
+}
+
+TEST_F(QueryPlannerTest, CompoundIndexBoundsDottedNotEqualsNullMultiKey) {
+ const bool isMultiKey = true;
+ addIndex(BSON("a.b" << 1 << "c.d" << 1), isMultiKey);
+ runQuery(fromjson("{'a.b': {$gt: 'foo'}, 'c.d': {$ne: null}}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {"
+ " filter: {'c.d': {$ne: null}},"
+ " node: {ixscan: {"
+ " filter: null,"
+ " pattern: {'a.b': 1, 'c.d': 1},"
+ " bounds: {"
+ " 'a.b': [['foo',{},false,false]], "
+ " 'c.d':[['MinKey','MaxKey',true,true]]"
+ " }"
+ " }}"
+ "}}");
+}
+
+TEST_F(QueryPlannerTest, CompoundIndexBoundsDottedNotEqualsNullMultiKeyPaths) {
+ const MultikeyPaths multikeyPaths = {{}, {0}}; // 'c' is multikey.
+ addIndex(BSON("a.b" << 1 << "c.d" << 1), multikeyPaths);
+ runQuery(fromjson("{'a.b': {$gt: 'foo'}, 'c.d': {$ne: null}}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {"
+ " filter: {'c.d': {$ne: null}},"
+ " node: {ixscan: {"
+ " filter: null,"
+ " pattern: {'a.b': 1, 'c.d': 1},"
+ " bounds: {"
+ " 'a.b': [['foo',{},false,false]], "
+ " 'c.d':[['MinKey','MaxKey',true,true]]"
+ " }"
+ " }}"
+ "}}");
+}
+
+TEST_F(QueryPlannerTest, CompoundIndexBoundsDottedNotEqualsNullMultiKeyWithProjection) {
+ const bool isMultiKey = true;
+ addIndex(BSON("a.b" << 1 << "c.d" << 1), isMultiKey);
+ runQuerySortProj(fromjson("{'a.b': {$gt: 'foo'}, 'c.d': {$ne: null}}"),
+ BSONObj(),
+ fromjson("{_id: 0, 'c.d': 1}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{proj: {spec: {_id: 0, 'c.d': 1}, node: {cscan: {dir: 1}}}}");
+ assertSolutionExists(
+ "{proj: {spec: {_id: 0, 'c.d': 1}, node: {"
+ "fetch: {filter: {'c.d': {$ne: null}}, node: {ixscan: {filter: null, pattern: "
+ "{'a.b': 1, 'c.d': 1}, bounds: {'a.b': [['foo',{},false,false]], "
+ "'c.d':[['MinKey','MaxKey',true,true]]}}}}}}}");
+}
+
+TEST_F(QueryPlannerTest, CompoundIndexBoundsNotEqualsNullReverseIndex) {
+ addIndex(BSON("a" << 1 << "b" << -1 << "c" << 1));
+ runQuery(fromjson("{a: {$gt: 'foo'}, b: {$ne: null}, c: {$ne: null}}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists(
+ "{fetch: {"
+ " filter: null,"
+ " node: {"
+ " ixscan: {"
+ " filter: null, "
+ " pattern: {a: 1, b: -1, c: 1},"
+ " bounds: {"
+ " a: [['foo', {}, false, false]],"
+ " b: [['MaxKey', null, true, false], [undefined, 'MinKey', false, true]],"
+ " c: [['MinKey', undefined, true, false], [null, 'MaxKey', false, true]]"
+ " },"
+ " dir: 1"
+ " }"
+ " }"
+ "}}");
+}
+
} // namespace