summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKatherine Wu <katherine.wu@mongodb.com>2021-02-23 13:42:03 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-03-03 20:36:27 +0000
commit8a04ca462a9e478a7ce19a1db368858c2056a32c (patch)
tree01304e12ad103924da6dd436bd467a66f5377c58 /src
parent7ee383575e877eea74e910efba52f46159abafbf (diff)
downloadmongo-8a04ca462a9e478a7ce19a1db368858c2056a32c.tar.gz
SERVER-53758 Map predicates on min time to a portion of _id
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/pipeline/SConscript2
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp78
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket.h18
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp (renamed from src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/map_predicates_on_control_field_test.cpp)237
4 files changed, 291 insertions, 44 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index aaed2a2b3b6..4914a74c8aa 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -392,8 +392,8 @@ env.CppUnitTest(
'document_source_sort_test.cpp',
'document_source_union_with_test.cpp',
'document_source_internal_unpack_bucket_test/extract_or_build_project_to_internalize_test.cpp',
+ 'document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp',
'document_source_internal_unpack_bucket_test/internalize_project_test.cpp',
- 'document_source_internal_unpack_bucket_test/map_predicates_on_control_field_test.cpp',
'document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp',
'document_source_unwind_test.cpp',
'expression_and_test.cpp',
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
index dfb3e67185a..beba31ecc55 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
@@ -108,6 +108,39 @@ void optimizeEndOfPipeline(Pipeline::SourceContainer::iterator itr,
container->erase(std::next(itr), container->end());
container->splice(std::next(itr), endOfPipeline);
}
+
+/**
+ * Creates an ObjectId initialized with an appropriate timestamp corresponding to 'matchExpr' and
+ * returns it as a Value.
+ */
+Value constructObjectIdValue(const ComparisonMatchExpression* matchExpr) {
+ // An ObjectId consists of a 4-byte timestamp, as well as a unique value and a counter, thus
+ // two ObjectIds initialized with the same date will have different values. To ensure that we
+ // do not incorrectly include or exclude any buckets, depending on the operator we will
+ // construct either the largest or the smallest ObjectId possible with the corresponding date.
+ OID oid;
+ if (matchExpr->getData().type() == BSONType::Date) {
+ switch (matchExpr->matchType()) {
+ case MatchExpression::LT: {
+ oid.init(matchExpr->getData().date(), false /* min */);
+ break;
+ }
+ case MatchExpression::LTE:
+ case MatchExpression::EQ: {
+ oid.init(matchExpr->getData().date(), true /* max */);
+ break;
+ }
+ default:
+ // We will only perform this optimization with query operators $lt, $lte and $eq.
+ MONGO_UNREACHABLE_TASSERT(5375801);
+ }
+ }
+ // If the query operand is not of type Date, the original query will not match on any documents
+ // because documents in a time-series collection must have a timeField of type Date. We will
+ // make this case faster by keeping the ObjectId as the lowest possible value so as to
+ // eliminate all buckets.
+ return Value(oid);
+}
} // namespace
void BucketUnpacker::reset(BSONObj&& bucket) {
@@ -365,7 +398,7 @@ std::pair<BSONObj, bool> DocumentSourceInternalUnpackBucket::extractOrBuildProje
}
std::unique_ptr<MatchExpression> createComparisonPredicate(
- const ComparisonMatchExpression* matchExpr, const boost::optional<std::string>& metaField) {
+ const ComparisonMatchExpression* matchExpr, const BucketSpec& bucketSpec) {
auto path = matchExpr->path();
auto rhs = matchExpr->getData();
@@ -385,8 +418,9 @@ std::unique_ptr<MatchExpression> createComparisonPredicate(
}
// We must avoid mapping predicates on the meta field onto the control field.
- if (metaField &&
- (path == metaField.get() || expression::isPathPrefixOf(metaField.get(), path))) {
+ if (bucketSpec.metaField &&
+ (path == bucketSpec.metaField.get() ||
+ expression::isPathPrefixOf(bucketSpec.metaField.get(), path))) {
return nullptr;
}
@@ -400,6 +434,11 @@ std::unique_ptr<MatchExpression> createComparisonPredicate(
andMatchExpr->add(std::make_unique<InternalExprGTEMatchExpression>(
str::stream() << DocumentSourceInternalUnpackBucket::kControlMaxFieldName << path,
rhs));
+
+ if (path == bucketSpec.timeField) {
+ andMatchExpr->add(std::make_unique<LTEMatchExpression>(
+ BucketUnpacker::kBucketIdFieldName, constructObjectIdValue(matchExpr)));
+ }
return andMatchExpr;
}
case MatchExpression::GT: {
@@ -413,14 +452,34 @@ std::unique_ptr<MatchExpression> createComparisonPredicate(
rhs);
}
case MatchExpression::LT: {
- return std::make_unique<InternalExprLTMatchExpression>(
+ auto controlPred = std::make_unique<InternalExprLTMatchExpression>(
str::stream() << DocumentSourceInternalUnpackBucket::kControlMinFieldName << path,
rhs);
+ if (path == bucketSpec.timeField) {
+ auto andMatchExpr = std::make_unique<AndMatchExpression>();
+
+ andMatchExpr->add(std::make_unique<LTMatchExpression>(
+ BucketUnpacker::kBucketIdFieldName, constructObjectIdValue(matchExpr)));
+ andMatchExpr->add(controlPred.release());
+
+ return andMatchExpr;
+ }
+ return controlPred;
}
case MatchExpression::LTE: {
- return std::make_unique<InternalExprLTEMatchExpression>(
+ auto controlPred = std::make_unique<InternalExprLTEMatchExpression>(
str::stream() << DocumentSourceInternalUnpackBucket::kControlMinFieldName << path,
rhs);
+ if (path == bucketSpec.timeField) {
+ auto andMatchExpr = std::make_unique<AndMatchExpression>();
+
+ andMatchExpr->add(std::make_unique<LTEMatchExpression>(
+ BucketUnpacker::kBucketIdFieldName, constructObjectIdValue(matchExpr)));
+ andMatchExpr->add(controlPred.release());
+
+ return andMatchExpr;
+ }
+ return controlPred;
}
default:
MONGO_UNREACHABLE_TASSERT(5348302);
@@ -429,14 +488,15 @@ std::unique_ptr<MatchExpression> createComparisonPredicate(
MONGO_UNREACHABLE_TASSERT(5348303);
}
-std::unique_ptr<MatchExpression> DocumentSourceInternalUnpackBucket::createPredicatesOnControlField(
+std::unique_ptr<MatchExpression>
+DocumentSourceInternalUnpackBucket::createPredicatesOnBucketLevelField(
const MatchExpression* matchExpr) const {
if (matchExpr->matchType() == MatchExpression::AND) {
auto nextAnd = static_cast<const AndMatchExpression*>(matchExpr);
auto andMatchExpr = std::make_unique<AndMatchExpression>();
for (size_t i = 0; i < nextAnd->numChildren(); i++) {
- if (auto child = createPredicatesOnControlField(nextAnd->getChild(i))) {
+ if (auto child = createPredicatesOnBucketLevelField(nextAnd->getChild(i))) {
andMatchExpr->add(std::move(child));
}
}
@@ -445,7 +505,7 @@ std::unique_ptr<MatchExpression> DocumentSourceInternalUnpackBucket::createPredi
}
} else if (ComparisonMatchExpression::isComparisonMatchExpression(matchExpr)) {
return createComparisonPredicate(static_cast<const ComparisonMatchExpression*>(matchExpr),
- _bucketUnpacker.bucketSpec().metaField);
+ _bucketUnpacker.bucketSpec());
}
return nullptr;
@@ -464,7 +524,7 @@ Pipeline::SourceContainer::iterator DocumentSourceInternalUnpackBucket::doOptimi
// Attempt to map predicates on bucketed fields to predicates on the control field.
if (auto nextMatch = dynamic_cast<DocumentSourceMatch*>((*std::next(itr)).get())) {
- if (auto match = createPredicatesOnControlField(nextMatch->getMatchExpression())) {
+ if (auto match = createPredicatesOnBucketLevelField(nextMatch->getMatchExpression())) {
// Optimize the newly created MatchExpression.
auto optimized = MatchExpression::optimize(std::move(match));
BSONObjBuilder bob;
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h
index 763771d9f2f..69988aa5549 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h
@@ -206,14 +206,22 @@ public:
Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container) const;
/**
- * Takes a predicate after $_internalUnpackBucket on a bucketed field as an argument, and
- * attempts to map it to a new predicate on the control field. For example, the predicate {a:
- * {$gt: 5}} will generate the predicate {control.max.a: {$_internalExprGt: 5}}, which will be
- * added before the $_internalUnpackBucket stage.
+ * Takes a predicate after $_internalUnpackBucket on a bucketed field as an argument and
+ * attempts to map it to a new predicate on the 'control' field. For example, the predicate
+ * {a: {$gt: 5}} will generate the predicate {control.max.a: {$_internalExprGt: 5}}, which will
+ * be added before the $_internalUnpackBucket stage.
+ *
+ * If the original predicate is on the bucket's timeField we may also create a new predicate
+ * on the '_id' field to assist in index utilization. For example, the predicate
+ * {time: {$lt: new Date(...)}} will generate the following predicate:
+ * {$and: [
+ * {_id: {$lt: ObjectId(...)}},
+ * {control.min.time: {$_internalExprLt: new Date(...)}}
+ * ]}
*
* If the provided predicate is ineligible for this mapping, the function will return a nullptr.
*/
- std::unique_ptr<MatchExpression> createPredicatesOnControlField(
+ std::unique_ptr<MatchExpression> createPredicatesOnBucketLevelField(
const MatchExpression* matchExpr) const;
private:
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/map_predicates_on_control_field_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp
index bd4360ac2f0..88e5a2a2109 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/map_predicates_on_control_field_test.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp
@@ -52,7 +52,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{'control.max.a': {$_internalExprGt: 1}}"));
@@ -70,7 +70,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{'control.max.a': {$_internalExprGte: 1}}"));
@@ -88,7 +88,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{'control.min.a': {$_internalExprLt: 1}}"));
@@ -106,7 +106,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{'control.min.a': {$_internalExprLte: 1}}"));
@@ -124,7 +124,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{$and: [{'control.min.a': {$_internalExprLte: 1}}, "
@@ -135,7 +135,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
OptimizeMapsAndWithPushableChildrenOnControlField) {
auto pipeline = Pipeline::parse(
makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
- fromjson("{$match: {$and: [{time: {$gt: 1}}, {a: {$lt: 5}}]}}")),
+ fromjson("{$match: {$and: [{b: {$gt: 1}}, {a: {$lt: 5}}]}}")),
getExpCtx());
auto& container = pipeline->getSources();
@@ -143,10 +143,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
- fromjson("{$and: [{'control.max.time': {$_internalExprGt: 1}}, "
+ fromjson("{$and: [{'control.max.b': {$_internalExprGt: 1}}, "
"{'control.min.a': {$_internalExprLt: 5}}]}"));
}
@@ -154,7 +154,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
OptimizeDoesNotMapAndWithUnpushableChildrenOnControlField) {
auto pipeline = Pipeline::parse(
makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
- fromjson("{$match: {$and: [{time: {$ne: 1}}, {a: {$ne: 5}}]}}")),
+ fromjson("{$match: {$and: [{b: {$ne: 1}}, {a: {$ne: 5}}]}}")),
getExpCtx());
auto& container = pipeline->getSources();
@@ -162,7 +162,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -171,7 +171,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
OptimizeMapsAndWithPushableAndUnpushableChildrenOnControlField) {
auto pipeline = Pipeline::parse(
makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
- fromjson("{$match: {$and: [{time: {$gt: 1}}, {a: {$ne: 5}}]}}")),
+ fromjson("{$match: {$and: [{b: {$gt: 1}}, {a: {$ne: 5}}]}}")),
getExpCtx());
auto& container = pipeline->getSources();
@@ -179,10 +179,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
- fromjson("{$and: [{'control.max.time': {$_internalExprGt: 1}}]}"));
+ fromjson("{$and: [{'control.max.b': {$_internalExprGt: 1}}]}"));
}
TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
@@ -190,8 +190,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto pipeline = Pipeline::parse(
makeVector(
fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
- fromjson(
- "{$match: {$and: [{b: {$gte: 2}}, {$and: [{time: {$gt: 1}}, {a: {$lt: 5}}]}]}}")),
+ fromjson("{$match: {$and: [{b: {$gte: 2}}, {$and: [{b: {$gt: 1}}, {a: {$lt: 5}}]}]}}")),
getExpCtx());
auto& container = pipeline->getSources();
@@ -199,18 +198,18 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
fromjson("{$and: [{'control.max.b': {$_internalExprGte: 2}}, {$and: "
- "[{'control.max.time': {$_internalExprGt: 1}}, "
+ "[{'control.max.b': {$_internalExprGt: 1}}, "
"{'control.min.a': {$_internalExprLt: 5}}]}]}"));
}
TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
OptimizeFurtherOptimizesNewlyAddedMatchWithSingletonAndNode) {
auto unpackBucketObj = fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}");
- auto matchObj = fromjson("{$match: {$and: [{time: {$gt: 1}}, {a: {$ne: 5}}]}}");
+ auto matchObj = fromjson("{$match: {$and: [{b: {$gt: 1}}, {a: {$ne: 5}}]}}");
auto pipeline = Pipeline::parse(makeVector(unpackBucketObj, matchObj), getExpCtx());
ASSERT_EQ(pipeline->getSources().size(), 2U);
@@ -220,7 +219,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto stages = pipeline->serializeToBson();
ASSERT_EQ(stages.size(), 3U);
- ASSERT_BSONOBJ_EQ(stages[0], fromjson("{$match: {'control.max.time': {$_internalExprGt: 1}}}"));
+ ASSERT_BSONOBJ_EQ(stages[0], fromjson("{$match: {'control.max.b': {$_internalExprGt: 1}}}"));
ASSERT_BSONOBJ_EQ(stages[1], unpackBucketObj);
ASSERT_BSONOBJ_EQ(stages[2], matchObj);
}
@@ -229,7 +228,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
OptimizeFurtherOptimizesNewlyAddedMatchWithNestedAndNodes) {
auto unpackBucketObj = fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}");
auto matchObj =
- fromjson("{$match: {$and: [{b: {$gte: 2}}, {$and: [{time: {$gt: 1}}, {a: {$lt: 5}}]}]}}");
+ fromjson("{$match: {$and: [{b: {$gte: 2}}, {$and: [{c: {$gt: 1}}, {a: {$lt: 5}}]}]}}");
auto pipeline = Pipeline::parse(makeVector(unpackBucketObj, matchObj), getExpCtx());
ASSERT_EQ(pipeline->getSources().size(), 2U);
@@ -241,7 +240,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
ASSERT_BSONOBJ_EQ(
stages[0],
- fromjson("{$match: {$and: [{'control.max.b': {$_internalExprGte: 2}}, {'control.max.time': "
+ fromjson("{$match: {$and: [{'control.max.b': {$_internalExprGte: 2}}, {'control.max.c': "
"{$_internalExprGt: 1}}, {'control.min.a': {$_internalExprLt: 5}}]}}"));
ASSERT_BSONOBJ_EQ(stages[1], unpackBucketObj);
ASSERT_BSONOBJ_EQ(stages[2], matchObj);
@@ -259,7 +258,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -276,7 +275,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -293,7 +292,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -312,7 +311,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -331,7 +330,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate == nullptr);
}
@@ -342,7 +341,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
makeVector(
fromjson(
"{$_internalUnpackBucket: {exclude: [], timeField: 'time', metaField: 'myMeta'}}"),
- fromjson("{$match: {$and: [{time: {$gt: 1}}, {myMeta: {$eq: 5}}]}}")),
+ fromjson("{$match: {$and: [{a: {$gt: 1}}, {myMeta: {$eq: 5}}]}}")),
getExpCtx());
auto& container = pipeline->getSources();
@@ -350,10 +349,190 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
- ->createPredicatesOnControlField(original->getMatchExpression());
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT_BSONOBJ_EQ(predicate->serialize(true),
- fromjson("{$and: [{'control.max.time': {$_internalExprGt: 1}}]}"));
+ fromjson("{$and: [{'control.max.a': {$_internalExprGt: 1}}]}"));
+}
+
+TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePredicatesOnId) {
+ auto date = Date_t::now();
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$lt" << date)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[0]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(date);
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) == 0);
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ BSON("control.min.time" << BSON("$_internalExprLt" << date)));
+ }
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$lte" << date)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[0]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(date);
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) < 0);
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ BSON("control.min.time" << BSON("$_internalExprLte" << date)));
+ }
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$eq" << date)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+
+ ASSERT_BSONOBJ_EQ(children.get()[0]->serialize(true),
+ BSON("control.min.time" << BSON("$_internalExprLte" << date)));
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ BSON("control.max.time" << BSON("$_internalExprGte" << date)));
+
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[2]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(date);
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) < 0);
+ }
+}
+
+TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
+ OptimizeMapsTimePredicatesWithNonDateTypeOnId) {
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$lt" << 1)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[0]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(Date_t::min());
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) > 0);
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ fromjson("{'control.min.time': {$_internalExprLt: 1}}"));
+ }
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$lte" << 1)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[0]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(Date_t::min());
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) > 0);
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ fromjson("{'control.min.time': {$_internalExprLte: 1}}"));
+ }
+ {
+ auto timePred = BSON("$match" << BSON("time" << BSON("$eq" << 1)));
+ auto pipeline = Pipeline::parse(
+ makeVector(fromjson("{$_internalUnpackBucket: {exclude: [], timeField: 'time'}}"),
+ timePred),
+ getExpCtx());
+ auto& container = pipeline->getSources();
+
+ ASSERT_EQ(pipeline->getSources().size(), 2U);
+
+ auto original = dynamic_cast<DocumentSourceMatch*>(container.back().get());
+ auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
+ ->createPredicatesOnBucketLevelField(original->getMatchExpression());
+ auto andExpr = dynamic_cast<AndMatchExpression*>(predicate.get());
+ auto children = andExpr->getChildVector();
+
+ ASSERT_BSONOBJ_EQ(children.get()[0]->serialize(true),
+ fromjson("{'control.min.time': {$_internalExprLte: 1}}"));
+
+ ASSERT_BSONOBJ_EQ(children.get()[1]->serialize(true),
+ fromjson("{'control.max.time': {$_internalExprGte: 1}}"));
+
+ auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>(children.get()[2]);
+
+ ASSERT_EQ(idPred->path(), "_id"_sd);
+ ASSERT_EQ(idPred->getData().type(), BSONType::jstOID);
+
+ OID oid;
+ oid.init(Date_t::min());
+ ASSERT_TRUE(oid.compare(idPred->getData().OID()) > 0);
+ }
}
} // namespace