diff options
author | Joel Redman <joel.redman@mongodb.com> | 2022-09-27 16:19:20 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-03-15 22:28:04 +0000 |
commit | 21ee6003fe704534e63ea20d5aa0cf563da0df2f (patch) | |
tree | 0e08c4b7d8ff9311075babf73ef857de5b83fe87 | |
parent | 3f23a831ecd8d03cc6ed73202bbfe238b5a21857 (diff) | |
download | mongo-21ee6003fe704534e63ea20d5aa0cf563da0df2f.tar.gz |
SERVER-66469 Fix correctness when filtering on dates before the epoch
(cherry picked from commit aedaf14d6dcc60253f3944252eb5cfc588e08ecb)
-rw-r--r-- | jstests/core/timeseries/timeseries_filter_extended_range.js | 190 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp | 243 |
2 files changed, 347 insertions, 86 deletions
diff --git a/jstests/core/timeseries/timeseries_filter_extended_range.js b/jstests/core/timeseries/timeseries_filter_extended_range.js new file mode 100644 index 00000000000..5285a2ea47a --- /dev/null +++ b/jstests/core/timeseries/timeseries_filter_extended_range.js @@ -0,0 +1,190 @@ +/** + * Test that find/match type queries work properly on dates ouside the 32 bit epoch range, + * [1970-01-01 00:00:00 UTC - 2038-01-29 03:13:07 UTC]. + * + * @tags: [ + * # We need a timeseries collection. + * requires_timeseries, + * # Timeseries collections require FCV 5.0. + * requires_fcv_50, + * # Cannot insert into a time-series collection in a multi-document transaction + * does_not_support_transactions, + * # TODO SERVER-73641 Re-enable this test once the bug is fixed in the sharded case. + * assumes_unsharded_collection, + * ] + */ + +(function() { +"use strict"; +const timeFieldName = "time"; + +/* + * Creates a collection, populates it, runs the `query` and ensures that the result set + * is equal to `results`. + * + * If overflow is set we create a document with dates above the 32 bit range (year 2040) + * If underflow is set, we create a document with dates below the 32 bit range (year 1965) + */ +function runTest(underflow, overflow, query, results) { + // Setup our DB & our collections. + const tsColl = db.getCollection(jsTestName()); + tsColl.drop(); + + assert.commandWorked( + db.createCollection(tsColl.getName(), {timeseries: {timeField: timeFieldName}})); + + const dates = [ + // If underflow, we want to insert a date that would fall below the epoch + // i.e. 1970-01-01 00:00:00 UTC. Otherwise we use a date within the epoch. + {[timeFieldName]: underflow ? new Date("1965-01-01") : new Date("1971-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + {[timeFieldName]: new Date("1980-01-01")}, + {[timeFieldName]: new Date("1995-01-01")}, + // If overflow, we want to insert a date that would use more than 32 bit milliseconds after + // the epoch. This overflow will occur 2038-01-29 03:13:07 UTC. Otherwise we go slightly + // before the end of the 32 bit epoch. + {[timeFieldName]: overflow ? new Date("2040-01-01") : new Date("2030-01-01")} + ]; + assert.commandWorked(tsColl.insert(dates)); + + // Make sure the expected results are in the correct order for comparison below. + function cmpTimeFields(a, b) { + return (b[timeFieldName].getTime() - a[timeFieldName].getTime()); + } + results.sort(cmpTimeFields); + + const pipeline = [{$match: query}, {$project: {_id: 0, [timeFieldName]: 1}}]; + + // Verify agg pipeline. We don't want to go through a plan that encourages a sort order to + // avoid BUS and index selection, so we sort after gathering the results. + const aggActuals = tsColl.aggregate(pipeline).toArray(); + aggActuals.sort(cmpTimeFields); + assert.docEq(aggActuals, results); + + // Verify the equivalent find command. We again don't want to go through a plan that + // encourages a sort order to avoid BUS and index selection, so we sort after gathering the + // results. + let findActuals = tsColl.find(query, {_id: 0, [timeFieldName]: 1}).toArray(); + findActuals.sort(cmpTimeFields); + assert.docEq(findActuals, results); +} + +runTest(false, + false, + {[timeFieldName]: {$eq: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1980-01-01")}]); +runTest(false, + true, + {[timeFieldName]: {$eq: new Date("2040-01-01")}}, + [{[timeFieldName]: new Date("2040-01-01")}]); +runTest(true, + false, + {[timeFieldName]: {$eq: new Date("1965-01-01")}}, + [{[timeFieldName]: new Date("1965-01-01")}]); + +runTest(false, + false, + {[timeFieldName]: {$lt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1971-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]); +runTest(false, + true, + {[timeFieldName]: {$lt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1971-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]); +runTest(true, + false, + {[timeFieldName]: {$lt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1965-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]); +runTest(true, + true, + {[timeFieldName]: {$lt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1965-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]); + +runTest(false, + false, + {[timeFieldName]: {$gt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2030-01-01")}]); +runTest(false, + true, + {[timeFieldName]: {$gt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2040-01-01")}]); +runTest(true, + false, + {[timeFieldName]: {$gt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2030-01-01")}]); +runTest(true, + true, + {[timeFieldName]: {$gt: new Date("1980-01-01")}}, + [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2040-01-01")}]); + +runTest(false, false, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1971-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + {[timeFieldName]: new Date("1980-01-01")} +]); +runTest(false, true, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1971-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + {[timeFieldName]: new Date("1980-01-01")} +]); +runTest(true, false, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1965-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + {[timeFieldName]: new Date("1980-01-01")} +]); +runTest(true, true, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1965-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + {[timeFieldName]: new Date("1980-01-01")} +]); + +runTest(false, false, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1980-01-01")}, + {[timeFieldName]: new Date("1995-01-01")}, + {[timeFieldName]: new Date("2030-01-01")} +]); +runTest(false, true, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1980-01-01")}, + {[timeFieldName]: new Date("1995-01-01")}, + {[timeFieldName]: new Date("2040-01-01")} +]); +runTest(true, false, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1980-01-01")}, + {[timeFieldName]: new Date("1995-01-01")}, + {[timeFieldName]: new Date("2030-01-01")} +]); +runTest(true, true, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1980-01-01")}, + {[timeFieldName]: new Date("1995-01-01")}, + {[timeFieldName]: new Date("2040-01-01")} +]); + +// Verify ranges that straddle the lower epoch work properly +runTest( + true, false, {[timeFieldName]: {$gt: new Date("1920-01-01"), $lt: new Date("1980-01-01")}}, [ + {[timeFieldName]: new Date("1965-01-01")}, + {[timeFieldName]: new Date("1975-01-01")}, + ]); + +runTest( + false, true, {[timeFieldName]: {$gt: new Date("1980-01-01"), $lt: new Date("2050-01-01")}}, [ + {[timeFieldName]: new Date("1995-01-01")}, + {[timeFieldName]: new Date("2040-01-01")}, + ]); + +// TODO: SERVER-69952 Literals outside the epoch are currently compared to _id, generally, +// so we cannot match against them. This will have to be fixed in a similar manner by determining +// whether the compared dates can be outside the epoch range and not relying on _id in that case. +// +// The following scenarios fail: +// runTest( +// false, false, {[timeFieldName]: {$gt: new Date("1920-01-01"), $lt: new Date("1980-01-01")}}, [ +// {[timeFieldName]: new Date("1971-01-01")}, +// {[timeFieldName]: new Date("1975-01-01")}, +// ]); +// runTest( +// false, false, {[timeFieldName]: {$gt: new Date("1980-01-01"), $lt: new Date("2050-01-01")}}, +// [ +// {[timeFieldName]: new Date("1995-01-01")}, +// {[timeFieldName]: new Date("2030-01-01")}, +// ]); +})(); 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 9a333ca711f..16d0ec9517f 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp @@ -586,6 +586,27 @@ std::pair<BSONObj, bool> DocumentSourceInternalUnpackBucket::extractOrBuildProje return {BSONObj{}, false}; } +/** + * Makes a disjunction of the given predicates. + * + * - The result is non-null; it may be an OrMatchExpression with zero children. + * - Any trivially-false arguments are omitted. + * - If only one argument is nontrivial, returns that argument rather than adding an extra + * OrMatchExpression around it. + */ +std::unique_ptr<MatchExpression> makeOr(std::vector<std::unique_ptr<MatchExpression>> predicates) { + std::vector<std::unique_ptr<MatchExpression>> nontrivial; + for (auto&& p : predicates) { + if (!p->isTriviallyFalse()) + nontrivial.push_back(std::move(p)); + } + + if (nontrivial.size() == 1) + return std::move(nontrivial[0]); + + return std::make_unique<OrMatchExpression>(std::move(nontrivial)); +} + /* * Creates a predicate that ensures that if there exists a subpath of matchExprPath such that the * type of `control.min.subpath` is not the same as `control.max.subpath` then we will match that @@ -700,120 +721,170 @@ std::unique_ptr<MatchExpression> createComparisonPredicate( // For $eq, make both a $lte against 'control.min' and a $gte predicate against // 'control.max'. // - // If the comparison is against the 'time' field, include a predicate against the _id - // field which is converted to the maximum for the corresponding range of ObjectIds and - // is adjusted by the max range for a bucket to approximate the max bucket value given + // If the comparison is against the 'time' field and we haven't stored a time outside of + // the 32 bit range, include a predicate against the _id field which is converted to + // the maximum for the corresponding range of ObjectIds and // the min. Also include a predicate against the _id field which is converted to the // minimum for the range of ObjectIds corresponding to the given date. In - // addition, we include a {'control.min' : {$gte: 'time - bucketMaxSpanSeconds'}} and - // a {'control.max' : {$lte: 'time + bucketMaxSpanSeconds'}} predicate which will be - // helpful in reducing bounds for index scans on 'time' field and routing on mongos. - return isTimeField - ? makePredicate( - MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), - MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, - minTime.firstElement()), - MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), - MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, - maxTime.firstElement()), - MatchExprPredicate<LTEMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<LTEMatchExpression>(matchExprData, - bucketMaxSpanSeconds)), - MatchExprPredicate<GTEMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<GTEMatchExpression>(matchExprData, - bucketMaxSpanSeconds))) - : std::make_unique<OrMatchExpression>(makeVector<std::unique_ptr<MatchExpression>>( - makePredicate(MatchExprPredicate<InternalExprLTEMatchExpression>( - minPath, matchExprData), - MatchExprPredicate<InternalExprGTEMatchExpression>( - maxPath, matchExprData)), - createTypeEqualityPredicate(pExpCtx, matchExprPath))); + // addition, we include a {'control.min' : {$gte: 'time - bucketMaxSpanSeconds'}} + // predicate which will be helpful in reducing bounds for index scans on 'time' field + // and routing on mongos. + if (!isTimeField) { + return makeOr(makeVector<std::unique_ptr<MatchExpression>>( + makePredicate( + MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData)), + createTypeEqualityPredicate(pExpCtx, matchExprPath))); + } else if (bucketSpec.usesExtendedRange()) { + return makePredicate( + MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, + minTime.firstElement()), + MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, + maxTime.firstElement())); + } else { + return makePredicate( + MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, + minTime.firstElement()), + MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, + maxTime.firstElement()), + MatchExprPredicate<LTEMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<LTEMatchExpression>(matchExprData, + bucketMaxSpanSeconds)), + MatchExprPredicate<GTEMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<GTEMatchExpression>(matchExprData, + bucketMaxSpanSeconds))); + } + MONGO_UNREACHABLE_TASSERT(6646903); case MatchExpression::GT: // For $gt, make a $gt predicate against 'control.max'. In addition, if the comparison - // is against the 'time' field, include a predicate against the _id field which is - // converted to the maximum for the corresponding range of ObjectIds and is adjusted - // by the max range for a bucket to approximate the max bucket value given the min. In - // addition, we include a {'control.min' : {$gt: 'time - bucketMaxSpanSeconds'}} + // is against the 'time' field, and the collection doesn't contain times outside the + // 32 bit range, include a predicate against the _id field which is converted to the + // maximum for the corresponding range of ObjectIds and is adjusted by the max range + // for a bucket to approximate the max bucket value given the min. + // + // In addition, we include a {'control.min' : {$gt: 'time - bucketMaxSpanSeconds'}} // predicate which will be helpful in reducing bounds for index scans on 'time' field // and routing on mongos. - return isTimeField - ? makePredicate( - - MatchExprPredicate<InternalExprGTMatchExpression>(maxPath, matchExprData), - MatchExprPredicate<InternalExprGTMatchExpression>(minPath, - minTime.firstElement()), - MatchExprPredicate<GTMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<GTMatchExpression>(matchExprData, - bucketMaxSpanSeconds))) - : std::make_unique<OrMatchExpression>(makeVector<std::unique_ptr<MatchExpression>>( - std::make_unique<InternalExprGTMatchExpression>(maxPath, matchExprData), - createTypeEqualityPredicate(pExpCtx, matchExprPath))); + // + // The same procedure applies to aggregation expressions of the form + // {$expr: {$gt: [...]}} that can be rewritten to use $_internalExprGt. + if (!isTimeField) { + return makeOr(makeVector<std::unique_ptr<MatchExpression>>( + std::make_unique<InternalExprGTMatchExpression>(maxPath, matchExprData), + createTypeEqualityPredicate(pExpCtx, matchExprPath))); + } else if (bucketSpec.usesExtendedRange()) { + return makePredicate( + MatchExprPredicate<InternalExprGTMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprGTMatchExpression>(minPath, + minTime.firstElement())); + } else { + return makePredicate( + MatchExprPredicate<InternalExprGTMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprGTMatchExpression>(minPath, + minTime.firstElement()), + MatchExprPredicate<GTMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<GTMatchExpression>(matchExprData, + bucketMaxSpanSeconds))); + } + MONGO_UNREACHABLE_TASSERT(6646904); case MatchExpression::GTE: // For $gte, make a $gte predicate against 'control.max'. In addition, if the comparison - // is against the 'time' field, include a predicate against the _id field which is + // is against the 'time' field, and the collection doesn't contain times outside the + // 32 bit range, include a predicate against the _id field which is // converted to the minimum for the corresponding range of ObjectIds and is adjusted // by the max range for a bucket to approximate the max bucket value given the min. In // addition, we include a {'control.min' : {$gte: 'time - bucketMaxSpanSeconds'}} // predicate which will be helpful in reducing bounds for index scans on 'time' field // and routing on mongos. - return isTimeField - ? makePredicate( - - MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), - MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, - minTime.firstElement()), - MatchExprPredicate<GTEMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<GTEMatchExpression>(matchExprData, - bucketMaxSpanSeconds))) - : std::make_unique<OrMatchExpression>(makeVector<std::unique_ptr<MatchExpression>>( - std::make_unique<InternalExprGTEMatchExpression>(maxPath, matchExprData), - createTypeEqualityPredicate(pExpCtx, matchExprPath))); + if (!isTimeField) { + return makeOr(makeVector<std::unique_ptr<MatchExpression>>( + std::make_unique<InternalExprGTEMatchExpression>(maxPath, matchExprData), + createTypeEqualityPredicate(pExpCtx, matchExprPath))); + } else if (bucketSpec.usesExtendedRange()) { + return makePredicate( + MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, + minTime.firstElement())); + } else { + return makePredicate( + MatchExprPredicate<InternalExprGTEMatchExpression>(maxPath, matchExprData), + MatchExprPredicate<InternalExprGTEMatchExpression>(minPath, + minTime.firstElement()), + MatchExprPredicate<GTEMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<GTEMatchExpression>(matchExprData, + bucketMaxSpanSeconds))); + } + MONGO_UNREACHABLE_TASSERT(6646905); case MatchExpression::LT: // For $lt, make a $lt predicate against 'control.min'. In addition, if the comparison // is against the 'time' field, include a predicate against the _id field which is - // converted to the minimum for the corresponding range of ObjectIds. In - // addition, we include a {'control.max' : {$lt: 'time + bucketMaxSpanSeconds'}} + // converted to the minimum for the corresponding range of ObjectIds, unless the + // collection contain extended range dates which won't fit int the 32 bits allocated + // for _id. + // + // In addition, we include a {'control.max' : {$lt: 'time + bucketMaxSpanSeconds'}} // predicate which will be helpful in reducing bounds for index scans on 'time' field // and routing on mongos. - return isTimeField - ? makePredicate( - MatchExprPredicate<InternalExprLTMatchExpression>(minPath, matchExprData), - MatchExprPredicate<InternalExprLTMatchExpression>(maxPath, - maxTime.firstElement()), - MatchExprPredicate<LTMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<LTMatchExpression>(matchExprData, - bucketMaxSpanSeconds))) - : std::make_unique<OrMatchExpression>(makeVector<std::unique_ptr<MatchExpression>>( - std::make_unique<InternalExprLTMatchExpression>(minPath, matchExprData), - createTypeEqualityPredicate(pExpCtx, matchExprPath))); + if (!isTimeField) { + return makeOr(makeVector<std::unique_ptr<MatchExpression>>( + std::make_unique<InternalExprLTMatchExpression>(minPath, matchExprData), + createTypeEqualityPredicate(pExpCtx, matchExprPath))); + } else if (bucketSpec.usesExtendedRange()) { + return makePredicate( + MatchExprPredicate<InternalExprLTMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprLTMatchExpression>(maxPath, + maxTime.firstElement())); + } else { + return makePredicate( + MatchExprPredicate<InternalExprLTMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprLTMatchExpression>(maxPath, + maxTime.firstElement()), + MatchExprPredicate<LTMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<LTMatchExpression>(matchExprData, + bucketMaxSpanSeconds))); + } + MONGO_UNREACHABLE_TASSERT(6646906); case MatchExpression::LTE: // For $lte, make a $lte predicate against 'control.min'. In addition, if the comparison - // is against the 'time' field, include a predicate against the _id field which is + // is against the 'time' field, and the collection doesn't contain times outside the + // 32 bit range, include a predicate against the _id field which is // converted to the maximum for the corresponding range of ObjectIds. In // addition, we include a {'control.max' : {$lte: 'time + bucketMaxSpanSeconds'}} // predicate which will be helpful in reducing bounds for index scans on 'time' field // and routing on mongos. - return isTimeField - ? makePredicate( - MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), - MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, - maxTime.firstElement()), - MatchExprPredicate<LTEMatchExpression, Value>( - kBucketIdFieldName, - constructObjectIdValue<LTEMatchExpression>(matchExprData, - bucketMaxSpanSeconds))) - : std::make_unique<OrMatchExpression>(makeVector<std::unique_ptr<MatchExpression>>( - std::make_unique<InternalExprLTEMatchExpression>(minPath, matchExprData), - createTypeEqualityPredicate(pExpCtx, matchExprPath))); + if (!isTimeField) { + return makeOr(makeVector<std::unique_ptr<MatchExpression>>( + std::make_unique<InternalExprLTEMatchExpression>(minPath, matchExprData), + createTypeEqualityPredicate(pExpCtx, matchExprPath))); + } else if (bucketSpec.usesExtendedRange()) { + return makePredicate( + MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, + maxTime.firstElement())); + } else { + return makePredicate( + MatchExprPredicate<InternalExprLTEMatchExpression>(minPath, matchExprData), + MatchExprPredicate<InternalExprLTEMatchExpression>(maxPath, + maxTime.firstElement()), + MatchExprPredicate<LTEMatchExpression, Value>( + kBucketIdFieldName, + constructObjectIdValue<LTEMatchExpression>(matchExprData, + bucketMaxSpanSeconds))); + } + MONGO_UNREACHABLE_TASSERT(6646907); default: MONGO_UNREACHABLE_TASSERT(5348302); |