summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/exec/SConscript2
-rw-r--r--src/mongo/db/exec/and_common.h8
-rw-r--r--src/mongo/db/exec/change_stream_proxy.cpp4
-rw-r--r--src/mongo/db/exec/distinct_scan.cpp1
-rw-r--r--src/mongo/db/exec/ensure_sorted.cpp9
-rw-r--r--src/mongo/db/exec/geo_near.cpp12
-rw-r--r--src/mongo/db/exec/idhack.cpp4
-rw-r--r--src/mongo/db/exec/index_scan.cpp4
-rw-r--r--src/mongo/db/exec/projection.cpp40
-rw-r--r--src/mongo/db/exec/projection_exec.cpp15
-rw-r--r--src/mongo/db/exec/projection_exec.h7
-rw-r--r--src/mongo/db/exec/projection_exec_test.cpp5
-rw-r--r--src/mongo/db/exec/sort.cpp9
-rw-r--r--src/mongo/db/exec/sort_key_generator.cpp5
-rw-r--r--src/mongo/db/exec/sort_key_generator.h2
-rw-r--r--src/mongo/db/exec/text_or.cpp5
-rw-r--r--src/mongo/db/exec/working_set.cpp20
-rw-r--r--src/mongo/db/exec/working_set.h72
-rw-r--r--src/mongo/db/exec/working_set_computed_data.cpp53
-rw-r--r--src/mongo/db/exec/working_set_computed_data.h125
-rw-r--r--src/mongo/db/index/sort_key_generator.cpp7
-rw-r--r--src/mongo/db/index/sort_key_generator_test.cpp5
-rw-r--r--src/mongo/db/pipeline/SConscript2
-rw-r--r--src/mongo/db/pipeline/document.cpp130
-rw-r--r--src/mongo/db/pipeline/document.h95
-rw-r--r--src/mongo/db/pipeline/document_internal.h236
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields.cpp200
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields.h281
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields_test.cpp242
-rw-r--r--src/mongo/db/pipeline/document_source_change_stream_test.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_change_stream_transform.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_check_invalidate.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_geo_near_cursor.cpp10
-rw-r--r--src/mongo/db/pipeline/document_source_sample.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp4
-rw-r--r--src/mongo/db/pipeline/document_source_sample_test.cpp24
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp10
-rw-r--r--src/mongo/db/pipeline/document_source_sort_test.cpp8
-rw-r--r--src/mongo/db/pipeline/document_value_test.cpp205
-rw-r--r--src/mongo/db/pipeline/expression.cpp9
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp4
-rw-r--r--src/mongo/db/pipeline/parsed_add_fields_test.cpp4
-rw-r--r--src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp4
-rw-r--r--src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp4
-rw-r--r--src/mongo/db/query/planner_analysis.cpp2
-rw-r--r--src/mongo/db/storage/index_entry_comparison.h19
-rw-r--r--src/mongo/dbtests/query_stage_sort_key_generator.cpp5
-rw-r--r--src/mongo/s/query/router_stage_pipeline.cpp2
48 files changed, 1065 insertions, 857 deletions
diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript
index 7ee2806711e..29ff41d620c 100644
--- a/src/mongo/db/exec/SConscript
+++ b/src/mongo/db/exec/SConscript
@@ -9,11 +9,11 @@ env.Library(
target = "working_set",
source = [
"working_set.cpp",
- "working_set_computed_data.cpp"
],
LIBDEPS = [
"$BUILD_DIR/mongo/base",
"$BUILD_DIR/mongo/db/bson/dotted_path_support",
+ "$BUILD_DIR/mongo/db/pipeline/document_value",
"$BUILD_DIR/mongo/db/service_context",
],
)
diff --git a/src/mongo/db/exec/and_common.h b/src/mongo/db/exec/and_common.h
index beb84e5751f..fe7a0573694 100644
--- a/src/mongo/db/exec/and_common.h
+++ b/src/mongo/db/exec/and_common.h
@@ -52,13 +52,7 @@ public:
verify(src.hasRecordId());
verify(dest->recordId == src.recordId);
- // Merge computed data.
- typedef WorkingSetComputedDataType WSCD;
- for (WSCD i = WSCD(0); i < WSM_COMPUTED_NUM_TYPES; i = WSCD(i + 1)) {
- if (!dest->hasComputed(i) && src.hasComputed(i)) {
- dest->addComputed(src.getComputed(i)->clone());
- }
- }
+ dest->metadata().mergeWith(src.metadata());
if (dest->hasObj()) {
// The merged WSM that we're creating already has the full document, so there's
diff --git a/src/mongo/db/exec/change_stream_proxy.cpp b/src/mongo/db/exec/change_stream_proxy.cpp
index 62e6de4aba6..d4f5ae39c6f 100644
--- a/src/mongo/db/exec/change_stream_proxy.cpp
+++ b/src/mongo/db/exec/change_stream_proxy.cpp
@@ -56,7 +56,7 @@ boost::optional<BSONObj> ChangeStreamProxyStage::getNextBson() {
// the latest event observed in the oplog, the latter via its sort key metadata field.
auto nextBSON = _validateAndConvertToBSON(*next);
_latestOplogTimestamp = PipelineD::getLatestOplogTimestamp(_pipeline.get());
- _postBatchResumeToken = next->getSortKeyMetaField();
+ _postBatchResumeToken = next->metadata().getSortKey();
_setSpeculativeReadTimestamp();
return nextBSON;
}
@@ -83,7 +83,7 @@ BSONObj ChangeStreamProxyStage::_validateAndConvertToBSON(const Document& event)
}
// Confirm that the document _id field matches the original resume token in the sort key field.
auto eventBSON = event.toBson();
- auto resumeToken = event.getSortKeyMetaField();
+ auto resumeToken = event.metadata().getSortKey();
auto idField = eventBSON.getObjectField("_id");
invariant(!resumeToken.isEmpty());
uassert(ErrorCodes::ChangeStreamFatalError,
diff --git a/src/mongo/db/exec/distinct_scan.cpp b/src/mongo/db/exec/distinct_scan.cpp
index af7d3c49cfa..22af764672f 100644
--- a/src/mongo/db/exec/distinct_scan.cpp
+++ b/src/mongo/db/exec/distinct_scan.cpp
@@ -35,7 +35,6 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/exec/filter.h"
#include "mongo/db/exec/scoped_timer.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index/index_descriptor.h"
diff --git a/src/mongo/db/exec/ensure_sorted.cpp b/src/mongo/db/exec/ensure_sorted.cpp
index d10e7e95308..611835aaba6 100644
--- a/src/mongo/db/exec/ensure_sorted.cpp
+++ b/src/mongo/db/exec/ensure_sorted.cpp
@@ -34,7 +34,6 @@
#include <memory>
#include "mongo/db/exec/scoped_timer.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/query/find_common.h"
namespace mongo {
@@ -60,12 +59,10 @@ PlanStage::StageState EnsureSortedStage::doWork(WorkingSetID* out) {
StageState stageState = child()->work(out);
if (PlanStage::ADVANCED == stageState) {
- // We extract the sort key from the WSM's computed data. This must have been generated
- // by a SortKeyGeneratorStage descendent in the execution tree.
+ // We extract the sort key from the WSM's metadata. This must have been generated by a
+ // SortKeyGeneratorStage descendent in the execution tree.
WorkingSetMember* member = _ws->get(*out);
- auto sortKeyComputedData =
- static_cast<const SortKeyComputedData*>(member->getComputed(WSM_SORT_KEY));
- BSONObj curSortKey = sortKeyComputedData->getSortKey();
+ auto curSortKey = member->metadata().getSortKey();
invariant(!curSortKey.isEmpty());
if (!_prevSortKey.isEmpty() && !isInOrder(_prevSortKey, curSortKey)) {
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp
index f3f08bd3be8..df28f3edcdb 100644
--- a/src/mongo/db/exec/geo_near.cpp
+++ b/src/mongo/db/exec/geo_near.cpp
@@ -41,12 +41,12 @@
#include "mongo/db/bson/dotted_path_support.h"
#include "mongo/db/exec/fetch.h"
#include "mongo/db/exec/index_scan.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/geo/geoconstants.h"
#include "mongo/db/geo/geoparser.h"
#include "mongo/db/geo/hash.h"
#include "mongo/db/index/expression_params.h"
#include "mongo/db/matcher/expression.h"
+#include "mongo/db/pipeline/value.h"
#include "mongo/db/query/expression_index.h"
#include "mongo/db/query/expression_index_knobs_gen.h"
#include "mongo/util/log.h"
@@ -155,7 +155,7 @@ static StatusWith<double> computeGeoNearDistance(const GeoNearParams& nearParams
// Compute the minimum distance of all the geometries in the document
double minDistance = -1;
- BSONObj minDistanceObj;
+ Value minDistanceMetadata;
for (auto it = geometries.begin(); it != geometries.end(); ++it) {
StoredGeometry& stored = **it;
@@ -175,7 +175,7 @@ static StatusWith<double> computeGeoNearDistance(const GeoNearParams& nearParams
if (minDistance < 0 || nextDistance < minDistance) {
minDistance = nextDistance;
- minDistanceObj = stored.element.Obj();
+ minDistanceMetadata = Value{stored.element};
}
}
@@ -189,14 +189,14 @@ static StatusWith<double> computeGeoNearDistance(const GeoNearParams& nearParams
// Hack for nearSphere
// TODO: Remove nearSphere?
invariant(SPHERE == queryCRS);
- member->addComputed(new GeoDistanceComputedData(minDistance / kRadiusOfEarthInMeters));
+ member->metadata().setGeoNearDistance(minDistance / kRadiusOfEarthInMeters);
} else {
- member->addComputed(new GeoDistanceComputedData(minDistance));
+ member->metadata().setGeoNearDistance(minDistance);
}
}
if (nearParams.addPointMeta) {
- member->addComputed(new GeoNearPointComputedData(minDistanceObj));
+ member->metadata().setGeoNearPoint(minDistanceMetadata);
}
return StatusWith<double>(minDistance);
diff --git a/src/mongo/db/exec/idhack.cpp b/src/mongo/db/exec/idhack.cpp
index b287dc12531..13a5837b53d 100644
--- a/src/mongo/db/exec/idhack.cpp
+++ b/src/mongo/db/exec/idhack.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/exec/projection.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_common.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/btree_access_method.h"
namespace mongo {
@@ -133,8 +132,7 @@ PlanStage::StageState IDHackStage::advance(WorkingSetID id,
if (_addKeyMetadata) {
BSONObj ownedKeyObj = member->obj.value()["_id"].wrap().getOwned();
- member->addComputed(
- new IndexKeyComputedData(IndexKeyComputedData::rehydrateKey(_key, ownedKeyObj)));
+ member->metadata().setIndexKey(IndexKeyEntry::rehydrateKey(_key, ownedKeyObj));
}
_done = true;
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index f74743629bb..7e5c9c19152 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/exec/filter.h"
#include "mongo/db/exec/scoped_timer.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index_names.h"
#include "mongo/db/query/index_bounds_builder.h"
@@ -217,8 +216,7 @@ PlanStage::StageState IndexScan::doWork(WorkingSetID* out) {
_workingSet->transitionToRecordIdAndIdx(id);
if (_addKeyMetadata) {
- member->addComputed(
- new IndexKeyComputedData(IndexKeyComputedData::rehydrateKey(_keyPattern, kv->key)));
+ member->metadata().setIndexKey(IndexKeyEntry::rehydrateKey(_keyPattern, kv->key));
}
*out = id;
diff --git a/src/mongo/db/exec/projection.cpp b/src/mongo/db/exec/projection.cpp
index eb2933ffa63..f48a09ffb02 100644
--- a/src/mongo/db/exec/projection.cpp
+++ b/src/mongo/db/exec/projection.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/exec/plan_stage.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_common.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/record_id.h"
@@ -51,33 +50,30 @@ static const char* kIdField = "_id";
namespace {
BSONObj indexKey(const WorkingSetMember& member) {
- return static_cast<const IndexKeyComputedData*>(member.getComputed(WSM_INDEX_KEY))->getKey();
+ return member.metadata().getIndexKey();
}
BSONObj sortKey(const WorkingSetMember& member) {
- return static_cast<const SortKeyComputedData*>(member.getComputed(WSM_SORT_KEY))->getSortKey();
+ return member.metadata().getSortKey();
}
double geoDistance(const WorkingSetMember& member) {
- return static_cast<const GeoDistanceComputedData*>(
- member.getComputed(WSM_COMPUTED_GEO_DISTANCE))
- ->getDist();
+ return member.metadata().getGeoNearDistance();
}
-BSONObj geoPoint(const WorkingSetMember& member) {
- return static_cast<const GeoNearPointComputedData*>(member.getComputed(WSM_GEO_NEAR_POINT))
- ->getPoint();
+Value geoPoint(const WorkingSetMember& member) {
+ return member.metadata().getGeoNearPoint();
}
double textScore(const WorkingSetMember& member) {
- if (member.hasComputed(WSM_COMPUTED_TEXT_SCORE))
- return static_cast<const TextScoreComputedData*>(
- member.getComputed(WSM_COMPUTED_TEXT_SCORE))
- ->getScore();
- // It is permitted to request a text score when none has been computed. Zero is returned as an
- // empty value in this case.
- else
+ auto&& metadata = member.metadata();
+ if (metadata.hasTextScore()) {
+ return metadata.getTextScore();
+ } else {
+ // It is permitted to request a text score when none has been computed. Zero is returned as
+ // an empty value in this case.
return 0.0;
+ }
}
void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
@@ -89,10 +85,10 @@ void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
const WorkingSetMember& member) {
- if (exec.needsGeoNearDistance() && !member.hasComputed(WSM_COMPUTED_GEO_DISTANCE))
+ if (exec.needsGeoNearDistance() && !member.metadata().hasGeoNearDistance())
return Status(ErrorCodes::InternalError, "near loc dist requested but no data available");
- if (exec.needsGeoNearPoint() && !member.hasComputed(WSM_GEO_NEAR_POINT))
+ if (exec.needsGeoNearPoint() && !member.metadata().hasGeoNearPoint())
return Status(ErrorCodes::InternalError, "near loc proj requested but no data available");
return member.hasObj()
@@ -100,7 +96,7 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
exec.needsGeoNearDistance()
? boost::optional<const double>(geoDistance(member))
: boost::none,
- exec.needsGeoNearPoint() ? geoPoint(member) : BSONObj(),
+ exec.needsGeoNearPoint() ? geoPoint(member) : Value{},
exec.needsSortKey() ? sortKey(member) : BSONObj(),
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
: boost::none,
@@ -109,7 +105,7 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
member.keyData,
exec.needsGeoNearDistance() ? boost::optional<const double>(geoDistance(member))
: boost::none,
- exec.needsGeoNearPoint() ? geoPoint(member) : BSONObj(),
+ exec.needsGeoNearPoint() ? geoPoint(member) : Value{},
exec.needsSortKey() ? sortKey(member) : BSONObj(),
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
: boost::none,
@@ -204,13 +200,13 @@ ProjectionStageDefault::ProjectionStageDefault(OperationContext* opCtx,
Status ProjectionStageDefault::transform(WorkingSetMember* member) const {
// The default no-fast-path case.
- if (_exec.needsSortKey() && !member->hasComputed(WSM_SORT_KEY))
+ if (_exec.needsSortKey() && !member->metadata().hasSortKey())
return Status(ErrorCodes::InternalError,
"sortKey meta-projection requested but no data available");
if (_exec.returnKey()) {
auto keys = _exec.computeReturnKeyProjection(
- member->hasComputed(WSM_INDEX_KEY) ? indexKey(*member) : BSONObj(),
+ member->metadata().hasIndexKey() ? indexKey(*member) : BSONObj(),
_exec.needsSortKey() ? sortKey(*member) : BSONObj());
if (!keys.isOK())
return keys.getStatus();
diff --git a/src/mongo/db/exec/projection_exec.cpp b/src/mongo/db/exec/projection_exec.cpp
index aae286bb318..736dd900545 100644
--- a/src/mongo/db/exec/projection_exec.cpp
+++ b/src/mongo/db/exec/projection_exec.cpp
@@ -218,7 +218,7 @@ StatusWith<BSONObj> ProjectionExec::computeReturnKeyProjection(const BSONObj& in
StatusWith<BSONObj> ProjectionExec::project(const BSONObj& in,
const boost::optional<const double> geoDistance,
- const BSONObj& geoNearPoint,
+ Value geoNearPoint,
const BSONObj& sortKey,
const boost::optional<const double> textScore,
const int64_t recordId) const {
@@ -241,7 +241,7 @@ StatusWith<BSONObj> ProjectionExec::project(const BSONObj& in,
StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDatum>& keyData,
const boost::optional<const double> geoDistance,
- const BSONObj& geoNearPoint,
+ Value geoNearPoint,
const BSONObj& sortKey,
const boost::optional<const double> textScore,
const int64_t recordId) const {
@@ -294,7 +294,7 @@ StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDat
BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
const boost::optional<const double> geoDistance,
- const BSONObj& geoNearPoint,
+ Value geoNearPoint,
const BSONObj& sortKey,
const boost::optional<const double> textScore,
const int64_t recordId) const {
@@ -305,13 +305,8 @@ BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
bob.append(it->first, geoDistance.get());
break;
case META_GEONEAR_POINT: {
- invariant(!geoNearPoint.isEmpty());
- auto& ptObj = geoNearPoint;
- if (ptObj.couldBeArray()) {
- bob.appendArray(it->first, ptObj);
- } else {
- bob.append(it->first, ptObj);
- }
+ invariant(!geoNearPoint.missing());
+ geoNearPoint.addToBsonObj(&bob, it->first);
break;
}
case META_TEXT_SCORE:
diff --git a/src/mongo/db/exec/projection_exec.h b/src/mongo/db/exec/projection_exec.h
index ab4dc5f48dc..a6ce7b1317c 100644
--- a/src/mongo/db/exec/projection_exec.h
+++ b/src/mongo/db/exec/projection_exec.h
@@ -35,6 +35,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/db/pipeline/value.h"
#include "mongo/util/string_map.h"
namespace mongo {
@@ -134,7 +135,7 @@ public:
*/
StatusWith<BSONObj> project(const BSONObj& in,
const boost::optional<const double> geoDistance = boost::none,
- const BSONObj& geoNearPoint = BSONObj(),
+ Value geoNearPoint = Value{},
const BSONObj& sortKey = BSONObj(),
const boost::optional<const double> textScore = boost::none,
const int64_t recordId = 0) const;
@@ -147,7 +148,7 @@ public:
StatusWith<BSONObj> projectCovered(
const std::vector<IndexKeyDatum>& keyData,
const boost::optional<const double> geoDistance = boost::none,
- const BSONObj& geoNearPoint = BSONObj(),
+ Value geoNearPoint = Value{},
const BSONObj& sortKey = BSONObj(),
const boost::optional<const double> textScore = boost::none,
const int64_t recordId = 0) const;
@@ -167,7 +168,7 @@ private:
*/
BSONObj addMeta(BSONObjBuilder bob,
const boost::optional<const double> geoDistance,
- const BSONObj& geoNearPoint,
+ Value geoNearPoint,
const BSONObj& sortKey,
const boost::optional<const double> textScore,
const int64_t recordId) const;
diff --git a/src/mongo/db/exec/projection_exec_test.cpp b/src/mongo/db/exec/projection_exec_test.cpp
index 9c04e20c2d0..33e14d01406 100644
--- a/src/mongo/db/exec/projection_exec_test.cpp
+++ b/src/mongo/db/exec/projection_exec_test.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/exec/projection_exec.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
@@ -81,10 +80,10 @@ boost::optional<std::string> project(
auto objStr = stdx::get_if<const char*>(&objStrOrDatum);
auto projected = objStr
- ? exec.project(fromjson(*objStr), boost::none, BSONObj(), sortKey, textScore)
+ ? exec.project(fromjson(*objStr), boost::none, Value{}, sortKey, textScore)
: exec.projectCovered({stdx::get<const IndexKeyDatum>(objStrOrDatum)},
boost::none,
- BSONObj(),
+ Value{},
sortKey,
textScore);
diff --git a/src/mongo/db/exec/sort.cpp b/src/mongo/db/exec/sort.cpp
index 9bf39c08773..13f1615ee11 100644
--- a/src/mongo/db/exec/sort.cpp
+++ b/src/mongo/db/exec/sort.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/catalog/collection.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_common.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/btree_key_generator.h"
#include "mongo/db/index_names.h"
#include "mongo/db/query/find_common.h"
@@ -126,11 +125,9 @@ PlanStage::StageState SortStage::doWork(WorkingSetID* out) {
SortableDataItem item;
item.wsid = id;
- // We extract the sort key from the WSM's computed data. This must have been generated
- // by a SortKeyGeneratorStage descendent in the execution tree.
- auto sortKeyComputedData =
- static_cast<const SortKeyComputedData*>(member->getComputed(WSM_SORT_KEY));
- item.sortKey = sortKeyComputedData->getSortKey();
+ // We extract the sort key from the WSM's metadata. This must have been generated by a
+ // SortKeyGeneratorStage descendent in the execution tree.
+ item.sortKey = member->metadata().getSortKey();
if (member->hasRecordId()) {
// The RecordId breaks ties when sorting two WSMs with the same sort key.
diff --git a/src/mongo/db/exec/sort_key_generator.cpp b/src/mongo/db/exec/sort_key_generator.cpp
index 1aa3abf8660..7b846f18bad 100644
--- a/src/mongo/db/exec/sort_key_generator.cpp
+++ b/src/mongo/db/exec/sort_key_generator.cpp
@@ -41,7 +41,6 @@
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/exec/working_set_common.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/matcher/extensions_callback_noop.h"
#include "mongo/db/query/collation/collator_interface.h"
#include "mongo/util/log.h"
@@ -74,8 +73,8 @@ PlanStage::StageState SortKeyGeneratorStage::doWork(WorkingSetID* out) {
return PlanStage::FAILURE;
}
- // Add the sort key to the WSM as computed data.
- member->addComputed(new SortKeyComputedData(sortKey.getValue()));
+ // Add the sort key to the WSM as metadata.
+ member->metadata().setSortKey(sortKey.getValue());
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/db/exec/sort_key_generator.h b/src/mongo/db/exec/sort_key_generator.h
index b5208ee78fb..79cb5a6fe49 100644
--- a/src/mongo/db/exec/sort_key_generator.h
+++ b/src/mongo/db/exec/sort_key_generator.h
@@ -45,7 +45,7 @@ class WorkingSetMember;
/**
* Passes results from the child through after adding the sort key for each result as
- * WorkingSetMember computed data.
+ * WorkingSetMember metadata.
*/
class SortKeyGeneratorStage final : public PlanStage {
public:
diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp
index 2e3eb79d5a0..c945ccd2fcd 100644
--- a/src/mongo/db/exec/text_or.cpp
+++ b/src/mongo/db/exec/text_or.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/exec/working_set_common.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/record_id.h"
@@ -236,8 +235,8 @@ PlanStage::StageState TextOrStage::returnResults(WorkingSetID* out) {
WorkingSetMember* wsm = _ws->get(textRecordData.wsid);
- // Populate the working set member with the text score and return it.
- wsm->addComputed(new TextScoreComputedData(textRecordData.score));
+ // Populate the working set member with the text score metadata and return it.
+ wsm->metadata().setTextScore(textRecordData.score);
*out = textRecordData.wsid;
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/db/exec/working_set.cpp b/src/mongo/db/exec/working_set.cpp
index 79a0fbbcaa6..400be5acb83 100644
--- a/src/mongo/db/exec/working_set.cpp
+++ b/src/mongo/db/exec/working_set.cpp
@@ -125,10 +125,7 @@ WorkingSetMember::WorkingSetMember() {}
WorkingSetMember::~WorkingSetMember() {}
void WorkingSetMember::clear() {
- for (size_t i = 0; i < WSM_COMPUTED_NUM_TYPES; i++) {
- _computed[i].reset();
- }
-
+ _metadata = DocumentMetadataFields{};
keyData.clear();
obj.reset();
_state = WorkingSetMember::INVALID;
@@ -162,21 +159,6 @@ void WorkingSetMember::makeObjOwnedIfNeeded() {
}
}
-bool WorkingSetMember::hasComputed(const WorkingSetComputedDataType type) const {
- return _computed[type].get();
-}
-
-const WorkingSetComputedData* WorkingSetMember::getComputed(
- const WorkingSetComputedDataType type) const {
- verify(_computed[type]);
- return _computed[type].get();
-}
-
-void WorkingSetMember::addComputed(WorkingSetComputedData* data) {
- verify(!hasComputed(data->type()));
- _computed[data->type()].reset(data);
-}
-
bool WorkingSetMember::getFieldDotted(const string& field, BSONElement* out) const {
// If our state is such that we have an object, use it.
if (hasObj()) {
diff --git a/src/mongo/db/exec/working_set.h b/src/mongo/db/exec/working_set.h
index fc6a228036d..43814158fb5 100644
--- a/src/mongo/db/exec/working_set.h
+++ b/src/mongo/db/exec/working_set.h
@@ -33,6 +33,7 @@
#include <vector>
#include "mongo/db/jsobj.h"
+#include "mongo/db/pipeline/document_metadata_fields.h"
#include "mongo/db/record_id.h"
#include "mongo/db/storage/snapshot.h"
#include "mongo/stdx/unordered_set.h"
@@ -181,51 +182,6 @@ struct IndexKeyDatum {
};
/**
- * What types of computed data can we have?
- */
-enum WorkingSetComputedDataType {
- // What's the score of the document retrieved from a $text query?
- WSM_COMPUTED_TEXT_SCORE = 0,
-
- // What's the distance from a geoNear query point to the document?
- WSM_COMPUTED_GEO_DISTANCE = 1,
-
- // The index key used to retrieve the document, for returnKey query option.
- WSM_INDEX_KEY = 2,
-
- // What point (of several possible points) was used to compute the distance to the document
- // via geoNear?
- WSM_GEO_NEAR_POINT = 3,
-
- // Comparison key for sorting.
- WSM_SORT_KEY = 4,
-
- // Must be last.
- WSM_COMPUTED_NUM_TYPES,
-};
-
-/**
- * Data that is a computed function of a WSM.
- */
-class WorkingSetComputedData {
- WorkingSetComputedData(const WorkingSetComputedData&) = delete;
- WorkingSetComputedData& operator=(const WorkingSetComputedData&) = delete;
-
-public:
- WorkingSetComputedData(const WorkingSetComputedDataType type) : _type(type) {}
- virtual ~WorkingSetComputedData() {}
-
- WorkingSetComputedDataType type() const {
- return _type;
- }
-
- virtual WorkingSetComputedData* clone() const = 0;
-
-private:
- WorkingSetComputedDataType _type;
-};
-
-/**
* The type of the data passed between query stages. In particular:
*
* Index scan stages return a WorkingSetMember in the RID_AND_IDX state.
@@ -297,14 +253,6 @@ public:
*/
void makeObjOwnedIfNeeded();
- //
- // Computed data
- //
-
- bool hasComputed(const WorkingSetComputedDataType type) const;
- const WorkingSetComputedData* getComputed(const WorkingSetComputedDataType type) const;
- void addComputed(WorkingSetComputedData* data);
-
/**
* getFieldDotted uses its state (obj or index data) to produce the field with the provided
* name.
@@ -321,12 +269,28 @@ public:
*/
size_t getMemUsage() const;
+ /**
+ * Returns a const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ const DocumentMetadataFields& metadata() const {
+ return _metadata;
+ }
+
+ /**
+ * Returns a non-const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ DocumentMetadataFields& metadata() {
+ return _metadata;
+ }
+
private:
friend class WorkingSet;
MemberState _state = WorkingSetMember::INVALID;
- std::unique_ptr<WorkingSetComputedData> _computed[WSM_COMPUTED_NUM_TYPES];
+ DocumentMetadataFields _metadata;
};
} // namespace mongo
diff --git a/src/mongo/db/exec/working_set_computed_data.cpp b/src/mongo/db/exec/working_set_computed_data.cpp
deleted file mode 100644
index 5b8170778f8..00000000000
--- a/src/mongo/db/exec/working_set_computed_data.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
-
-#include "mongo/db/exec/working_set_computed_data.h"
-
-#include "mongo/util/log.h"
-
-namespace mongo {
-
-BSONObj IndexKeyComputedData::rehydrateKey(const BSONObj& keyPattern,
- const BSONObj& dehydratedKey) {
- BSONObjBuilder bob;
- BSONObjIterator keyIter(keyPattern);
- BSONObjIterator valueIter(dehydratedKey);
-
- while (keyIter.more() && valueIter.more()) {
- bob.appendAs(valueIter.next(), keyIter.next().fieldNameStringData());
- }
-
- invariant(!keyIter.more());
- invariant(!valueIter.more());
-
- return bob.obj();
-}
-} // namespace mongo
diff --git a/src/mongo/db/exec/working_set_computed_data.h b/src/mongo/db/exec/working_set_computed_data.h
deleted file mode 100644
index 3d71d4a815e..00000000000
--- a/src/mongo/db/exec/working_set_computed_data.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include "mongo/db/exec/working_set.h"
-
-namespace mongo {
-
-class TextScoreComputedData : public WorkingSetComputedData {
-public:
- TextScoreComputedData(double score)
- : WorkingSetComputedData(WSM_COMPUTED_TEXT_SCORE), _score(score) {}
-
- double getScore() const {
- return _score;
- }
-
- TextScoreComputedData* clone() const final {
- return new TextScoreComputedData(_score);
- }
-
-private:
- double _score;
-};
-
-class GeoDistanceComputedData : public WorkingSetComputedData {
-public:
- GeoDistanceComputedData(double dist)
- : WorkingSetComputedData(WSM_COMPUTED_GEO_DISTANCE), _dist(dist) {}
-
- double getDist() const {
- return _dist;
- }
-
- GeoDistanceComputedData* clone() const final {
- return new GeoDistanceComputedData(_dist);
- }
-
-private:
- double _dist;
-};
-
-class IndexKeyComputedData : public WorkingSetComputedData {
-public:
- // Given an index key 'dehydratedKey' with no field names, returns a new BSONObj after adding
- // field names according to 'keyPattern'.
- static BSONObj rehydrateKey(const BSONObj& keyPattern, const BSONObj& dehydratedKey);
-
- IndexKeyComputedData(BSONObj key)
- : WorkingSetComputedData(WSM_INDEX_KEY), _key(key.getOwned()) {}
-
- BSONObj getKey() const {
- return _key;
- }
-
- IndexKeyComputedData* clone() const final {
- return new IndexKeyComputedData(_key);
- }
-
-private:
- BSONObj _key;
-};
-
-class GeoNearPointComputedData : public WorkingSetComputedData {
-public:
- GeoNearPointComputedData(BSONObj point)
- : WorkingSetComputedData(WSM_GEO_NEAR_POINT), _point(point.getOwned()) {}
-
- BSONObj getPoint() const {
- return _point;
- }
-
- GeoNearPointComputedData* clone() const final {
- return new GeoNearPointComputedData(_point);
- }
-
-private:
- BSONObj _point;
-};
-
-class SortKeyComputedData : public WorkingSetComputedData {
-public:
- SortKeyComputedData(BSONObj sortKey)
- : WorkingSetComputedData(WSM_SORT_KEY), _sortKey(sortKey.getOwned()) {}
-
- BSONObj getSortKey() const {
- return _sortKey;
- }
-
- SortKeyComputedData* clone() const final {
- return new SortKeyComputedData(_sortKey);
- }
-
-private:
- BSONObj _sortKey;
-};
-
-} // namespace mongo
diff --git a/src/mongo/db/index/sort_key_generator.cpp b/src/mongo/db/index/sort_key_generator.cpp
index a9fd65038fb..c28caa0f47e 100644
--- a/src/mongo/db/index/sort_key_generator.cpp
+++ b/src/mongo/db/index/sort_key_generator.cpp
@@ -32,7 +32,6 @@
#include "mongo/db/index/sort_key_generator.h"
#include "mongo/bson/bsonobj_comparator.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/query/collation/collation_index_key.h"
namespace mongo {
@@ -87,10 +86,8 @@ SortKeyGenerator::SortKeyGenerator(const BSONObj& sortSpec, const CollatorInterf
StatusWith<BSONObj> SortKeyGenerator::getSortKey(const WorkingSetMember& wsm) const {
if (wsm.hasObj()) {
SortKeyGenerator::Metadata metadata;
- if (_sortHasMeta && wsm.hasComputed(WSM_COMPUTED_TEXT_SCORE)) {
- auto scoreData =
- static_cast<const TextScoreComputedData*>(wsm.getComputed(WSM_COMPUTED_TEXT_SCORE));
- metadata.textScore = scoreData->getScore();
+ if (_sortHasMeta && wsm.metadata().hasTextScore()) {
+ metadata.textScore = wsm.metadata().getTextScore();
}
return getSortKeyFromDocument(wsm.obj.value(), &metadata);
}
diff --git a/src/mongo/db/index/sort_key_generator_test.cpp b/src/mongo/db/index/sort_key_generator_test.cpp
index a533a6cfbff..7801c911bd9 100644
--- a/src/mongo/db/index/sort_key_generator_test.cpp
+++ b/src/mongo/db/index/sort_key_generator_test.cpp
@@ -32,7 +32,6 @@
#include <memory>
#include "mongo/bson/json.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/index/sort_key_generator.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/unittest/death_test.h"
@@ -279,7 +278,7 @@ TEST_F(SortKeyGeneratorWorkingSetTest, CanGenerateKeyFromWSMForTextScoreMetaSort
BSONObj pattern = fromjson("{a: 1, b: {$meta: 'textScore'}, c: -1}}");
auto sortKeyGen = std::make_unique<SortKeyGenerator>(pattern, nullptr);
setOwnedObj(BSON("x" << 1 << "a" << 2 << "y" << 3 << "c" << BSON_ARRAY(4 << 5 << 6)));
- member().addComputed(new TextScoreComputedData(9.9));
+ member().metadata().setTextScore(9.9);
auto sortKey = sortKeyGen->getSortKey(member());
ASSERT_OK(sortKey);
ASSERT_BSONOBJ_EQ(BSON("" << 2 << "" << 9.9 << "" << 6), sortKey.getValue());
@@ -314,7 +313,7 @@ DEATH_TEST_F(SortKeyGeneratorWorkingSetTest,
BSONObj pattern = fromjson("{z: {$meta: 'textScore'}}");
auto sortKeyGen = std::make_unique<SortKeyGenerator>(pattern, nullptr);
setRecordIdAndIdx(BSON("a" << 1 << "b" << 1), BSON("" << 2 << "" << 3));
- member().addComputed(new TextScoreComputedData(9.9));
+ member().metadata().setTextScore(9.9);
MONGO_COMPILER_VARIABLE_UNUSED auto ignored = sortKeyGen->getSortKey(member());
}
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 2d71d9dfadc..aae909141c1 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -31,6 +31,7 @@ env.Library(
source=[
'document.cpp',
'document_comparator.cpp',
+ 'document_metadata_fields.cpp',
'document_path_support.cpp',
'value.cpp',
'value_comparator.cpp',
@@ -421,6 +422,7 @@ env.CppUnitTest(
'aggregation_request_test.cpp',
'dependencies_test.cpp',
'document_comparator_test.cpp',
+ 'document_metadata_fields_test.cpp',
'document_path_support_test.cpp',
'document_source_add_fields_test.cpp',
'document_source_bucket_auto_test.cpp',
diff --git a/src/mongo/db/pipeline/document.cpp b/src/mongo/db/pipeline/document.cpp
index 4906a51dab4..9557d01a414 100644
--- a/src/mongo/db/pipeline/document.cpp
+++ b/src/mongo/db/pipeline/document.cpp
@@ -51,7 +51,8 @@ const StringDataSet Document::allMetadataFieldNames{Document::metaFieldTextScore
Document::metaFieldGeoNearDistance,
Document::metaFieldGeoNearPoint,
Document::metaFieldSearchScore,
- Document::metaFieldSearchHighlights};
+ Document::metaFieldSearchHighlights,
+ Document::metaFieldIndexKey};
DocumentStorageIterator::DocumentStorageIterator(DocumentStorage* storage, BSONObjIterator bsonIt)
: _bsonIt(std::move(bsonIt)),
@@ -322,46 +323,13 @@ intrusive_ptr<DocumentStorage> DocumentStorage::clone() const {
dassert(out->_numFields == _numFields);
}
- // Copy metadata
- if (_metadataFields) {
- out->_metadataFields = std::make_unique<MetadataFields>(*_metadataFields);
- }
+ out->_metadataFields = _metadataFields;
return out;
}
-MetadataFields::MetadataFields(const MetadataFields& other) {
- _metaFields = other._metaFields;
- _textScore = other._textScore;
- _randVal = other._randVal;
- _sortKey = other._sortKey.getOwned();
- _geoNearDistance = other._geoNearDistance;
- _geoNearPoint = other._geoNearPoint.getOwned();
- _searchScore = other._searchScore;
- _searchHighlights = other._searchHighlights;
-}
-
-size_t MetadataFields::getApproximateSize() const {
- size_t size = sizeof(MetadataFields);
-
- // Count the "deep" portion of the metadata values.
- size += _sortKey.objsize();
- size += _geoNearPoint.getApproximateSize();
- // Size of Value is double counted - once in sizeof(MetadataFields) and once in
- // getApproximateSize()
- size -= sizeof(_geoNearPoint);
- size += _searchHighlights.getApproximateSize();
- size -= sizeof(_searchHighlights);
-
- return size;
-}
-
size_t DocumentStorage::getMetadataApproximateSize() const {
- if (!_metadataFields) {
- return 0;
- }
-
- return _metadataFields->getApproximateSize();
+ return _metadataFields.getApproximateSize();
}
DocumentStorage::~DocumentStorage() {
@@ -377,25 +345,23 @@ void DocumentStorage::loadLazyMetadata() const {
return;
}
- _metadataFields = std::make_unique<MetadataFields>();
-
BSONObjIterator it(_bson);
while (it.more()) {
BSONElement elem(it.next());
auto fieldName = elem.fieldNameStringData();
if (fieldName[0] == '$') {
if (fieldName == Document::metaFieldTextScore) {
- _metadataFields->setTextScore(elem.Double());
+ _metadataFields.setTextScore(elem.Double());
} else if (fieldName == Document::metaFieldSearchScore) {
- _metadataFields->setSearchScore(elem.Double());
+ _metadataFields.setSearchScore(elem.Double());
} else if (fieldName == Document::metaFieldSearchHighlights) {
- _metadataFields->setSearchHighlights(Value(elem));
+ _metadataFields.setSearchHighlights(Value(elem));
} else if (fieldName == Document::metaFieldRandVal) {
- _metadataFields->setRandMetaField(elem.Double());
+ _metadataFields.setRandVal(elem.Double());
} else if (fieldName == Document::metaFieldSortKey) {
- _metadataFields->setSortKeyMetaField(elem.Obj());
+ _metadataFields.setSortKey(elem.Obj());
} else if (fieldName == Document::metaFieldGeoNearDistance) {
- _metadataFields->setGeoNearDistance(elem.Double());
+ _metadataFields.setGeoNearDistance(elem.Double());
} else if (fieldName == Document::metaFieldGeoNearPoint) {
Value val;
if (elem.type() == BSONType::Array) {
@@ -405,7 +371,9 @@ void DocumentStorage::loadLazyMetadata() const {
val = Value(elem.embeddedObject());
}
- _metadataFields->setGeoNearPoint(val);
+ _metadataFields.setGeoNearPoint(val);
+ } else if (fieldName == Document::metaFieldIndexKey) {
+ _metadataFields.setIndexKey(elem.Obj());
}
}
}
@@ -472,20 +440,22 @@ constexpr StringData Document::metaFieldSearchHighlights;
BSONObj Document::toBsonWithMetaData() const {
BSONObjBuilder bb;
toBson(&bb);
- if (hasTextScore())
- bb.append(metaFieldTextScore, getTextScore());
- if (hasRandMetaField())
- bb.append(metaFieldRandVal, getRandMetaField());
- if (hasSortKeyMetaField())
- bb.append(metaFieldSortKey, getSortKeyMetaField());
- if (hasGeoNearDistance())
- bb.append(metaFieldGeoNearDistance, getGeoNearDistance());
- if (hasGeoNearPoint())
- getGeoNearPoint().addToBsonObj(&bb, metaFieldGeoNearPoint);
- if (hasSearchScore())
- bb.append(metaFieldSearchScore, getSearchScore());
- if (hasSearchHighlights())
- getSearchHighlights().addToBsonObj(&bb, metaFieldSearchHighlights);
+ if (metadata().hasTextScore())
+ bb.append(metaFieldTextScore, metadata().getTextScore());
+ if (metadata().hasRandVal())
+ bb.append(metaFieldRandVal, metadata().getRandVal());
+ if (metadata().hasSortKey())
+ bb.append(metaFieldSortKey, metadata().getSortKey());
+ if (metadata().hasGeoNearDistance())
+ bb.append(metaFieldGeoNearDistance, metadata().getGeoNearDistance());
+ if (metadata().hasGeoNearPoint())
+ metadata().getGeoNearPoint().addToBsonObj(&bb, metaFieldGeoNearPoint);
+ if (metadata().hasSearchScore())
+ bb.append(metaFieldSearchScore, metadata().getSearchScore());
+ if (metadata().hasSearchHighlights())
+ metadata().getSearchHighlights().addToBsonObj(&bb, metaFieldSearchHighlights);
+ if (metadata().hasIndexKey())
+ bb.append(metaFieldIndexKey, metadata().getIndexKey());
return bb.obj();
}
@@ -573,7 +543,7 @@ size_t Document::getApproximateSize() const {
}
// The metadata also occupies space in the document storage that's pre-allocated.
- size += getMetadataApproximateSize();
+ size += storage().getMetadataApproximateSize();
size += storage().bsonObjSize();
return size;
@@ -661,27 +631,7 @@ void Document::serializeForSorter(BufBuilder& buf) const {
it->val.serializeForSorter(buf);
}
- if (hasTextScore()) {
- buf.appendNum(char(MetaType::TEXT_SCORE + 1));
- buf.appendNum(getTextScore());
- }
- if (hasRandMetaField()) {
- buf.appendNum(char(MetaType::RAND_VAL + 1));
- buf.appendNum(getRandMetaField());
- }
- if (hasSortKeyMetaField()) {
- buf.appendNum(char(MetaType::SORT_KEY + 1));
- getSortKeyMetaField().appendSelfToBufBuilder(buf);
- }
- if (hasSearchScore()) {
- buf.appendNum(char(MetaType::SEARCH_SCORE + 1));
- buf.appendNum(getSearchScore());
- }
- if (hasSearchHighlights()) {
- buf.appendNum(char(MetaType::SEARCH_HIGHLIGHTS + 1));
- getSearchHighlights().serializeForSorter(buf);
- }
- buf.appendNum(char(0));
+ metadata().serializeForSorter(buf);
}
Document Document::deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&) {
@@ -692,23 +642,7 @@ Document Document::deserializeForSorter(BufReader& buf, const SorterDeserializeS
doc.addField(name, Value::deserializeForSorter(buf, Value::SorterDeserializeSettings()));
}
- while (char marker = buf.read<char>()) {
- if (marker == char(MetaType::TEXT_SCORE) + 1) {
- doc.setTextScore(buf.read<LittleEndian<double>>());
- } else if (marker == char(MetaType::RAND_VAL) + 1) {
- doc.setRandMetaField(buf.read<LittleEndian<double>>());
- } else if (marker == char(MetaType::SORT_KEY) + 1) {
- doc.setSortKeyMetaField(
- BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
- } else if (marker == char(MetaType::SEARCH_SCORE) + 1) {
- doc.setSearchScore(buf.read<LittleEndian<double>>());
- } else if (marker == char(MetaType::SEARCH_HIGHLIGHTS) + 1) {
- doc.setSearchHighlights(
- Value::deserializeForSorter(buf, Value::SorterDeserializeSettings()));
- } else {
- uasserted(28744, "Unrecognized marker, unable to deserialize buffer");
- }
- }
+ DocumentMetadataFields::deserializeForSorter(buf, &doc.metadata());
return doc.freeze();
}
diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h
index 433466bf973..d89389dbabe 100644
--- a/src/mongo/db/pipeline/document.h
+++ b/src/mongo/db/pipeline/document.h
@@ -99,6 +99,7 @@ public:
static constexpr StringData metaFieldGeoNearPoint = "$pt"_sd;
static constexpr StringData metaFieldSearchScore = "$searchScore"_sd;
static constexpr StringData metaFieldSearchHighlights = "$searchHighlights"_sd;
+ static constexpr StringData metaFieldIndexKey = "$indexKey"_sd;
static const StringDataSet allMetadataFieldNames;
@@ -169,8 +170,7 @@ public:
size_t getApproximateSize() const;
/**
- * Return the approximate amount of space used by metadata. Note that documents may reserve
- * space for metadata even no metadata is used.
+ * Return the approximate amount of space used by metadata.
*/
size_t getMetadataApproximateSize() const {
return storage().getMetadataApproximateSize();
@@ -234,12 +234,6 @@ public:
*/
static Document fromBsonWithMetaData(const BSONObj& bson);
- /**
- * Given a BSON object that may have metadata fields added as part of toBsonWithMetadata(),
- * returns the same object without any of the metadata fields.
- */
- static BSONObj stripMetadataFields(const BSONObj& bsonWithMetadata);
-
// Support BSONObjBuilder and BSONArrayBuilder "stream" API
friend BSONObjBuilder& operator<<(BSONObjBuilderValueStream& builder, const Document& d);
@@ -262,53 +256,12 @@ public:
return Document(storage().clone().get());
}
- bool hasTextScore() const {
- return storage().hasTextScore();
- }
- double getTextScore() const {
- return storage().getTextScore();
- }
-
- bool hasRandMetaField() const {
- return storage().hasRandMetaField();
- }
- double getRandMetaField() const {
- return storage().getRandMetaField();
- }
-
- bool hasSortKeyMetaField() const {
- return storage().hasSortKeyMetaField();
- }
- BSONObj getSortKeyMetaField() const {
- return storage().getSortKeyMetaField();
- }
-
- bool hasGeoNearDistance() const {
- return storage().hasGeoNearDistance();
- }
- double getGeoNearDistance() const {
- return storage().getGeoNearDistance();
- }
-
- bool hasGeoNearPoint() const {
- return storage().hasGeoNearPoint();
- }
- Value getGeoNearPoint() const {
- return storage().getGeoNearPoint();
- }
-
- bool hasSearchScore() const {
- return storage().hasSearchScore();
- }
- double getSearchScore() const {
- return storage().getSearchScore();
- }
-
- bool hasSearchHighlights() const {
- return storage().hasSearchHighlights();
- }
- Value getSearchHighlights() const {
- return storage().getSearchHighlights();
+ /**
+ * Returns a const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ const DocumentMetadataFields& metadata() const {
+ return storage().metadata();
}
/// members for Sorter
@@ -553,32 +506,12 @@ public:
storage().copyMetaDataFrom(source.storage());
}
- void setTextScore(double score) {
- storage().setTextScore(score);
- }
-
- void setRandMetaField(double val) {
- storage().setRandMetaField(val);
- }
-
- void setSortKeyMetaField(BSONObj sortKey) {
- storage().setSortKeyMetaField(sortKey);
- }
-
- void setGeoNearDistance(double dist) {
- storage().setGeoNearDistance(dist);
- }
-
- void setGeoNearPoint(Value point) {
- storage().setGeoNearPoint(std::move(point));
- }
-
- void setSearchScore(double score) {
- storage().setSearchScore(score);
- }
-
- void setSearchHighlights(Value highlights) {
- storage().setSearchHighlights(highlights);
+ /**
+ * Returns a non-const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ DocumentMetadataFields& metadata() {
+ return storage().metadata();
}
/** Convert to a read-only document and release reference.
diff --git a/src/mongo/db/pipeline/document_internal.h b/src/mongo/db/pipeline/document_internal.h
index 6d7ecd7f675..f5dc33e0ae4 100644
--- a/src/mongo/db/pipeline/document_internal.h
+++ b/src/mongo/db/pipeline/document_internal.h
@@ -35,6 +35,7 @@
#include <boost/intrusive_ptr.hpp>
#include "mongo/base/static_assert.h"
+#include "mongo/db/pipeline/document_metadata_fields.h"
#include "mongo/db/pipeline/value.h"
#include "mongo/util/intrusive_counter.h"
@@ -261,118 +262,6 @@ private:
const ValueElement* _end;
};
-enum MetaType : char {
- TEXT_SCORE,
- RAND_VAL,
- SORT_KEY,
- GEONEAR_DIST,
- GEONEAR_POINT,
- SEARCH_SCORE,
- SEARCH_HIGHLIGHTS,
-
- // New fields must be added before the NUM_FIELDS sentinel.
- NUM_FIELDS
-};
-
-/**
- * A simple container of all metadata fields
- *
- */
-struct MetadataFields {
- std::bitset<MetaType::NUM_FIELDS> _metaFields;
- double _textScore{0.0};
- double _randVal{0.0};
- BSONObj _sortKey;
- double _geoNearDistance{0.0};
- Value _geoNearPoint;
- double _searchScore{0.0};
- Value _searchHighlights;
-
- MetadataFields() {}
- // When adding a field, make sure to update the copy constructor.
- MetadataFields(const MetadataFields& other);
-
- size_t getApproximateSize() const;
-
- bool hasTextScore() const {
- return _metaFields.test(MetaType::TEXT_SCORE);
- }
- double getTextScore() const {
- return _textScore;
- }
- void setTextScore(double score) {
- _metaFields.set(MetaType::TEXT_SCORE);
- _textScore = score;
- }
-
- bool hasRandMetaField() const {
- return _metaFields.test(MetaType::RAND_VAL);
- }
- double getRandMetaField() const {
- return _randVal;
- }
- void setRandMetaField(double val) {
- _metaFields.set(MetaType::RAND_VAL);
- _randVal = val;
- }
-
- bool hasSortKeyMetaField() const {
- return _metaFields.test(MetaType::SORT_KEY);
- }
- BSONObj getSortKeyMetaField() const {
- return _sortKey;
- }
- void setSortKeyMetaField(BSONObj sortKey) {
- _metaFields.set(MetaType::SORT_KEY);
- _sortKey = sortKey.getOwned();
- }
-
- bool hasGeoNearDistance() const {
- return _metaFields.test(MetaType::GEONEAR_DIST);
- }
- double getGeoNearDistance() const {
- return _geoNearDistance;
- }
- void setGeoNearDistance(double dist) {
- _metaFields.set(MetaType::GEONEAR_DIST);
- _geoNearDistance = dist;
- }
-
- bool hasGeoNearPoint() const {
- return _metaFields.test(MetaType::GEONEAR_POINT);
- }
- Value getGeoNearPoint() const {
- return _geoNearPoint;
- }
- void setGeoNearPoint(Value point) {
- _metaFields.set(MetaType::GEONEAR_POINT);
- _geoNearPoint = std::move(point);
- }
-
- bool hasSearchScore() const {
- return _metaFields.test(MetaType::SEARCH_SCORE);
- }
- double getSearchScore() const {
- return _searchScore;
- }
- void setSearchScore(double score) {
- _metaFields.set(MetaType::SEARCH_SCORE);
- _searchScore = score;
- }
-
- bool hasSearchHighlights() const {
- return _metaFields.test(MetaType::SEARCH_HIGHLIGHTS);
- }
- Value getSearchHighlights() const {
- return _searchHighlights;
- }
- void setSearchHighlights(Value highlights) {
- _metaFields.set(MetaType::SEARCH_HIGHLIGHTS);
- _searchHighlights = highlights;
- }
-};
-
-
/// Storage class used by both Document and MutableDocument
class DocumentStorage : public RefCountable {
public:
@@ -479,6 +368,7 @@ public:
auto bsonObjSize() const {
return _bson.objsize();
}
+
/**
* Compute the space allocated for the metadata fields. Will account for space allocated for
* unused metadata fields as well.
@@ -490,124 +380,32 @@ public:
* Note: does not clear metadata from this.
*/
void copyMetaDataFrom(const DocumentStorage& source) {
- // It the underlying BSON object is shared and the source does not have metadata then
+ // If the underlying BSON object is shared and the source does not have metadata then
// nothing needs to be copied. If the metadata is in the BSON then they are the same in
// this and source.
if (_bson.objdata() == source._bson.objdata() && !source._metadataFields) {
return;
}
- if (source.hasTextScore()) {
- setTextScore(source.getTextScore());
- }
- if (source.hasRandMetaField()) {
- setRandMetaField(source.getRandMetaField());
- }
- if (source.hasSortKeyMetaField()) {
- setSortKeyMetaField(source.getSortKeyMetaField());
- }
- if (source.hasGeoNearDistance()) {
- setGeoNearDistance(source.getGeoNearDistance());
- }
- if (source.hasGeoNearPoint()) {
- setGeoNearPoint(source.getGeoNearPoint());
- }
- if (source.hasSearchScore()) {
- setSearchScore(source.getSearchScore());
- }
- if (source.hasSearchHighlights()) {
- setSearchHighlights(source.getSearchHighlights());
- }
- }
-
- bool hasTextScore() const {
- loadLazyMetadata();
- return _metadataFields->hasTextScore();
- }
- double getTextScore() const {
- loadLazyMetadata();
- return _metadataFields->getTextScore();
- }
- void setTextScore(double score) {
- loadLazyMetadata();
- _metadataFields->setTextScore(score);
- }
-
- bool hasRandMetaField() const {
- loadLazyMetadata();
- return _metadataFields->hasRandMetaField();
- }
- double getRandMetaField() const {
- loadLazyMetadata();
- return _metadataFields->getRandMetaField();
- }
- void setRandMetaField(double val) {
- loadLazyMetadata();
- _metadataFields->setRandMetaField(val);
- }
-
- bool hasSortKeyMetaField() const {
- loadLazyMetadata();
- return _metadataFields->hasSortKeyMetaField();
- }
- BSONObj getSortKeyMetaField() const {
- loadLazyMetadata();
- return _metadataFields->getSortKeyMetaField();
- }
- void setSortKeyMetaField(BSONObj sortKey) {
- loadLazyMetadata();
- _metadataFields->setSortKeyMetaField(sortKey);
- }
-
- bool hasGeoNearDistance() const {
loadLazyMetadata();
- return _metadataFields->hasGeoNearDistance();
- }
- double getGeoNearDistance() const {
- loadLazyMetadata();
- return _metadataFields->getGeoNearDistance();
- }
- void setGeoNearDistance(double dist) {
- loadLazyMetadata();
- _metadataFields->setGeoNearDistance(dist);
- }
-
- bool hasGeoNearPoint() const {
- loadLazyMetadata();
- return _metadataFields->hasGeoNearPoint();
- }
- Value getGeoNearPoint() const {
- loadLazyMetadata();
- return _metadataFields->getGeoNearPoint();
- }
- void setGeoNearPoint(Value point) {
- loadLazyMetadata();
- _metadataFields->setGeoNearPoint(point);
+ metadata().copyFrom(source.metadata());
}
- bool hasSearchScore() const {
- loadLazyMetadata();
- return _metadataFields->hasSearchScore();
- }
- double getSearchScore() const {
- loadLazyMetadata();
- return _metadataFields->getSearchScore();
- }
- void setSearchScore(double score) {
+ /**
+ * Returns a const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ const DocumentMetadataFields& metadata() const {
loadLazyMetadata();
- _metadataFields->setSearchScore(score);
+ return _metadataFields;
}
- bool hasSearchHighlights() const {
- loadLazyMetadata();
- return _metadataFields->hasSearchHighlights();
- }
- Value getSearchHighlights() const {
- loadLazyMetadata();
- return _metadataFields->getSearchHighlights();
- }
- void setSearchHighlights(Value highlights) {
+ /**
+ * Returns a non-const reference to an object housing the metadata fields associated with this
+ * WorkingSetMember.
+ */
+ DocumentMetadataFields& metadata() {
loadLazyMetadata();
- _metadataFields->setSearchHighlights(highlights);
+ return _metadataFields;
}
static unsigned hashKey(StringData name) {
@@ -713,7 +511,7 @@ private:
BSONObj _bson;
mutable BSONObjIterator _bsonIt;
- mutable std::unique_ptr<MetadataFields> _metadataFields;
+ mutable DocumentMetadataFields _metadataFields;
// The storage constructed from a BSON value may contain metadata. When we process the BSON we
// have to move the metadata to the MetadataFields object. If we know that the BSON does not
diff --git a/src/mongo/db/pipeline/document_metadata_fields.cpp b/src/mongo/db/pipeline/document_metadata_fields.cpp
new file mode 100644
index 00000000000..bfca826cb8d
--- /dev/null
+++ b/src/mongo/db/pipeline/document_metadata_fields.cpp
@@ -0,0 +1,200 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/pipeline/document_metadata_fields.h"
+
+namespace mongo {
+
+DocumentMetadataFields::DocumentMetadataFields(const DocumentMetadataFields& other)
+ : _holder(other._holder ? std::make_unique<MetadataHolder>(*other._holder) : nullptr) {}
+
+DocumentMetadataFields& DocumentMetadataFields::operator=(const DocumentMetadataFields& other) {
+ _holder = other._holder ? std::make_unique<MetadataHolder>(*other._holder) : nullptr;
+ return *this;
+}
+
+DocumentMetadataFields::DocumentMetadataFields(DocumentMetadataFields&& other)
+ : _holder(std::move(other._holder)) {}
+
+DocumentMetadataFields& DocumentMetadataFields::operator=(DocumentMetadataFields&& other) {
+ _holder = std::move(other._holder);
+ return *this;
+}
+
+void DocumentMetadataFields::mergeWith(const DocumentMetadataFields& other) {
+ if (!hasTextScore() && other.hasTextScore()) {
+ setTextScore(other.getTextScore());
+ }
+ if (!hasRandVal() && other.hasRandVal()) {
+ setRandVal(other.getRandVal());
+ }
+ if (!hasSortKey() && other.hasSortKey()) {
+ setSortKey(other.getSortKey());
+ }
+ if (!hasGeoNearDistance() && other.hasGeoNearDistance()) {
+ setGeoNearDistance(other.getGeoNearDistance());
+ }
+ if (!hasGeoNearPoint() && other.hasGeoNearPoint()) {
+ setGeoNearPoint(other.getGeoNearPoint());
+ }
+ if (!hasSearchScore() && other.hasSearchScore()) {
+ setSearchScore(other.getSearchScore());
+ }
+ if (!hasSearchHighlights() && other.hasSearchHighlights()) {
+ setSearchHighlights(other.getSearchHighlights());
+ }
+ if (!hasIndexKey() && other.hasIndexKey()) {
+ setIndexKey(other.getIndexKey());
+ }
+}
+
+void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) {
+ if (other.hasTextScore()) {
+ setTextScore(other.getTextScore());
+ }
+ if (other.hasRandVal()) {
+ setRandVal(other.getRandVal());
+ }
+ if (other.hasSortKey()) {
+ setSortKey(other.getSortKey());
+ }
+ if (other.hasGeoNearDistance()) {
+ setGeoNearDistance(other.getGeoNearDistance());
+ }
+ if (other.hasGeoNearPoint()) {
+ setGeoNearPoint(other.getGeoNearPoint());
+ }
+ if (other.hasSearchScore()) {
+ setSearchScore(other.getSearchScore());
+ }
+ if (other.hasSearchHighlights()) {
+ setSearchHighlights(other.getSearchHighlights());
+ }
+ if (other.hasIndexKey()) {
+ setIndexKey(other.getIndexKey());
+ }
+}
+
+size_t DocumentMetadataFields::getApproximateSize() const {
+ if (!_holder) {
+ return 0;
+ }
+
+ // Purposefully exclude the size of the DocumentMetadataFields, as this is accounted for
+ // elsewhere. Here we only consider the "deep" size of the MetadataHolder.
+ size_t size = sizeof(MetadataHolder);
+
+ // Count the "deep" portion of the metadata values.
+ size += _holder->sortKey.objsize();
+ size += _holder->geoNearPoint.getApproximateSize();
+ // Size of Value is double counted - once in sizeof(MetadataFields) and once in
+ // getApproximateSize()
+ size -= sizeof(_holder->geoNearPoint);
+ size += _holder->searchHighlights.getApproximateSize();
+ size -= sizeof(_holder->searchHighlights);
+ size += _holder->indexKey.objsize();
+
+ return size;
+}
+
+void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const {
+ // If there is no metadata, all we need to do is write a zero byte.
+ if (!_holder) {
+ buf.appendNum(static_cast<char>(0));
+ return;
+ }
+
+ if (hasTextScore()) {
+ buf.appendNum(static_cast<char>(MetaType::TEXT_SCORE + 1));
+ buf.appendNum(getTextScore());
+ }
+ if (hasRandVal()) {
+ buf.appendNum(static_cast<char>(MetaType::RAND_VAL + 1));
+ buf.appendNum(getRandVal());
+ }
+ if (hasSortKey()) {
+ buf.appendNum(static_cast<char>(MetaType::SORT_KEY + 1));
+ getSortKey().appendSelfToBufBuilder(buf);
+ }
+ if (hasGeoNearDistance()) {
+ buf.appendNum(static_cast<char>(MetaType::GEONEAR_DIST + 1));
+ buf.appendNum(getGeoNearDistance());
+ }
+ if (hasGeoNearPoint()) {
+ buf.appendNum(static_cast<char>(MetaType::GEONEAR_POINT + 1));
+ getGeoNearPoint().serializeForSorter(buf);
+ }
+ if (hasSearchScore()) {
+ buf.appendNum(static_cast<char>(MetaType::SEARCH_SCORE + 1));
+ buf.appendNum(getSearchScore());
+ }
+ if (hasSearchHighlights()) {
+ buf.appendNum(static_cast<char>(MetaType::SEARCH_HIGHLIGHTS + 1));
+ getSearchHighlights().serializeForSorter(buf);
+ }
+ if (hasIndexKey()) {
+ buf.appendNum(static_cast<char>(MetaType::INDEX_KEY + 1));
+ getIndexKey().appendSelfToBufBuilder(buf);
+ }
+ buf.appendNum(static_cast<char>(0));
+}
+
+void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetadataFields* out) {
+ invariant(out);
+
+ while (char marker = buf.read<char>()) {
+ if (marker == static_cast<char>(MetaType::TEXT_SCORE) + 1) {
+ out->setTextScore(buf.read<LittleEndian<double>>());
+ } else if (marker == static_cast<char>(MetaType::RAND_VAL) + 1) {
+ out->setRandVal(buf.read<LittleEndian<double>>());
+ } else if (marker == static_cast<char>(MetaType::SORT_KEY) + 1) {
+ out->setSortKey(
+ BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
+ } else if (marker == static_cast<char>(MetaType::GEONEAR_DIST) + 1) {
+ out->setGeoNearDistance(buf.read<LittleEndian<double>>());
+ } else if (marker == static_cast<char>(MetaType::GEONEAR_POINT) + 1) {
+ out->setGeoNearPoint(
+ Value::deserializeForSorter(buf, Value::SorterDeserializeSettings()));
+ } else if (marker == static_cast<char>(MetaType::SEARCH_SCORE) + 1) {
+ out->setSearchScore(buf.read<LittleEndian<double>>());
+ } else if (marker == static_cast<char>(MetaType::SEARCH_HIGHLIGHTS) + 1) {
+ out->setSearchHighlights(
+ Value::deserializeForSorter(buf, Value::SorterDeserializeSettings()));
+ } else if (marker == static_cast<char>(MetaType::INDEX_KEY) + 1) {
+ out->setIndexKey(
+ BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
+ } else {
+ uasserted(28744, "Unrecognized marker, unable to deserialize buffer");
+ }
+ }
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_metadata_fields.h b/src/mongo/db/pipeline/document_metadata_fields.h
new file mode 100644
index 00000000000..95c11284580
--- /dev/null
+++ b/src/mongo/db/pipeline/document_metadata_fields.h
@@ -0,0 +1,281 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <bitset>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/pipeline/value.h"
+
+namespace mongo {
+
+/**
+ * This class represents the metadata that the query execution engine can associate with a
+ * particular intermediate result (either index key or document) passing between execution stages.
+ *
+ * Since most documents do not have metadata, this class can be left in an uninitialized state, in
+ * which case no memory is allocated to hold the metadata. The operator bool() overload can be used
+ * to determine whether or not a DocumentMetadataFields object is uninitialized (and thus has no
+ * metadata).
+ *
+ * Calling any of the setters is legal on an uninitialized object, and will cause the
+ * DocumentMetadataFields to transition to an initialized state.
+ *
+ * A DocumentMetadataFields is copy constructible, copy assignable, move constructible, and move
+ * assignable.
+ */
+class DocumentMetadataFields {
+public:
+ enum MetaType : char {
+ TEXT_SCORE,
+ RAND_VAL,
+ SORT_KEY,
+ GEONEAR_DIST,
+ GEONEAR_POINT,
+ SEARCH_SCORE,
+ SEARCH_HIGHLIGHTS,
+ INDEX_KEY,
+
+ // New fields must be added before the NUM_FIELDS sentinel.
+ NUM_FIELDS
+ };
+
+ /**
+ * Reads serialized metadata out of 'buf', and uses it to populate 'out'. Expects 'buf' to have
+ * been written to by a previous call to serializeForSorter(). It is illegal to pass a null
+ * pointer for 'out'.
+ */
+ static void deserializeForSorter(BufReader& buf, DocumentMetadataFields* out);
+
+ /**
+ * Constructs a new DocumentMetadataFields in an uninitialized state.
+ */
+ DocumentMetadataFields() = default;
+
+ DocumentMetadataFields(const DocumentMetadataFields& other);
+ DocumentMetadataFields& operator=(const DocumentMetadataFields& other);
+
+ DocumentMetadataFields(DocumentMetadataFields&& other);
+ DocumentMetadataFields& operator=(DocumentMetadataFields&& other);
+
+ /**
+ * For all metadata fields that 'other' has but 'this' does not have, copies these fields from
+ * 'other' to 'this'.
+ */
+ void mergeWith(const DocumentMetadataFields& other);
+
+ /**
+ * Copies all metadata fields that are present in 'other' from 'other' to 'this', overwriting
+ * values already present in 'this'.
+ *
+ * This differs slightly from the copy assignment operator. Copy-assignment will cause 'this' to
+ * equal 'other' exactly. This operation, on the other hand, leaves the metadata fields from
+ * 'this' which are not present in 'other' unmodified.
+ */
+ void copyFrom(const DocumentMetadataFields& other);
+
+ /**
+ * Returns an estimate in bytes of the size of the underlying metadata, which is held at a
+ * distance by this object. The size of this object is not incorporated in the estimate.
+ */
+ size_t getApproximateSize() const;
+
+ /**
+ * Returns true if this object is in an initialized state and may hold metadata.
+ */
+ operator bool() const {
+ return static_cast<bool>(_holder);
+ }
+
+ bool hasTextScore() const {
+ return _holder && _holder->metaFields.test(MetaType::TEXT_SCORE);
+ }
+
+ double getTextScore() const {
+ invariant(hasTextScore());
+ return _holder->textScore;
+ }
+
+ void setTextScore(double score) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::TEXT_SCORE);
+ _holder->textScore = score;
+ }
+
+ bool hasRandVal() const {
+ return _holder && _holder->metaFields.test(MetaType::RAND_VAL);
+ }
+
+ double getRandVal() const {
+ invariant(hasRandVal());
+ return _holder->randVal;
+ }
+
+ void setRandVal(double val) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::RAND_VAL);
+ _holder->randVal = val;
+ }
+
+ bool hasSortKey() const {
+ return _holder && _holder->metaFields.test(MetaType::SORT_KEY);
+ }
+
+ BSONObj getSortKey() const {
+ invariant(hasSortKey());
+ return _holder->sortKey;
+ }
+
+ void setSortKey(BSONObj sortKey) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::SORT_KEY);
+ _holder->sortKey = sortKey.getOwned();
+ }
+
+ bool hasGeoNearDistance() const {
+ return _holder && _holder->metaFields.test(MetaType::GEONEAR_DIST);
+ }
+
+ double getGeoNearDistance() const {
+ invariant(hasGeoNearDistance());
+ return _holder->geoNearDistance;
+ }
+
+ void setGeoNearDistance(double dist) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::GEONEAR_DIST);
+ _holder->geoNearDistance = dist;
+ }
+
+ bool hasGeoNearPoint() const {
+ return _holder && _holder->metaFields.test(MetaType::GEONEAR_POINT);
+ }
+
+ Value getGeoNearPoint() const {
+ invariant(hasGeoNearPoint());
+ return _holder->geoNearPoint;
+ }
+
+ void setGeoNearPoint(Value point) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::GEONEAR_POINT);
+ _holder->geoNearPoint = std::move(point);
+ }
+
+ bool hasSearchScore() const {
+ return _holder && _holder->metaFields.test(MetaType::SEARCH_SCORE);
+ }
+
+ double getSearchScore() const {
+ invariant(hasSearchScore());
+ return _holder->searchScore;
+ }
+
+ void setSearchScore(double score) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::SEARCH_SCORE);
+ _holder->searchScore = score;
+ }
+
+ bool hasSearchHighlights() const {
+ return _holder && _holder->metaFields.test(MetaType::SEARCH_HIGHLIGHTS);
+ }
+
+ Value getSearchHighlights() const {
+ invariant(hasSearchHighlights());
+ return _holder->searchHighlights;
+ }
+
+ void setSearchHighlights(Value highlights) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::SEARCH_HIGHLIGHTS);
+ _holder->searchHighlights = highlights;
+ }
+
+ bool hasIndexKey() const {
+ return _holder && _holder->metaFields.test(MetaType::INDEX_KEY);
+ }
+
+ BSONObj getIndexKey() const {
+ invariant(hasIndexKey());
+ return _holder->indexKey;
+ }
+
+ void setIndexKey(BSONObj indexKey) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+
+ _holder->metaFields.set(MetaType::INDEX_KEY);
+ _holder->indexKey = indexKey.getOwned();
+ }
+
+ void serializeForSorter(BufBuilder& buf) const;
+
+private:
+ // A simple data struct housing all possible metadata fields.
+ struct MetadataHolder {
+ std::bitset<MetaType::NUM_FIELDS> metaFields;
+ double textScore{0.0};
+ double randVal{0.0};
+ BSONObj sortKey;
+ double geoNearDistance{0.0};
+ Value geoNearPoint;
+ double searchScore{0.0};
+ Value searchHighlights;
+ BSONObj indexKey;
+ };
+
+ // Null until the first setter is called, at which point a MetadataHolder struct is allocated.
+ std::unique_ptr<MetadataHolder> _holder;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_metadata_fields_test.cpp b/src/mongo/db/pipeline/document_metadata_fields_test.cpp
new file mode 100644
index 00000000000..5e46ea7de7f
--- /dev/null
+++ b/src/mongo/db/pipeline/document_metadata_fields_test.cpp
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/pipeline/document_metadata_fields.h"
+#include "mongo/db/pipeline/document_value_test_util.h"
+#include "mongo/unittest/bson_test_util.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) {
+ DocumentMetadataFields metadata;
+ metadata.setTextScore(9.9);
+ metadata.setRandVal(42.0);
+ metadata.setSortKey(BSON("a" << 1));
+ metadata.setGeoNearDistance(3.2);
+ metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)});
+ metadata.setSearchScore(5.4);
+ metadata.setSearchHighlights(Value{"foo"_sd});
+ metadata.setIndexKey(BSON("b" << 1));
+
+ BufBuilder builder;
+ metadata.serializeForSorter(builder);
+ DocumentMetadataFields deserialized;
+ BufReader reader(builder.buf(), builder.len());
+ DocumentMetadataFields::deserializeForSorter(reader, &deserialized);
+
+ ASSERT_EQ(deserialized.getTextScore(), 9.9);
+ ASSERT_EQ(deserialized.getRandVal(), 42.0);
+ ASSERT_BSONOBJ_EQ(deserialized.getSortKey(), BSON("a" << 1));
+ ASSERT_EQ(deserialized.getGeoNearDistance(), 3.2);
+ ASSERT_VALUE_EQ(deserialized.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)});
+ ASSERT_EQ(deserialized.getSearchScore(), 5.4);
+ ASSERT_VALUE_EQ(deserialized.getSearchHighlights(), Value{"foo"_sd});
+ ASSERT_BSONOBJ_EQ(deserialized.getIndexKey(), BSON("b" << 1));
+}
+
+TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) {
+ DocumentMetadataFields metadata;
+ ASSERT_FALSE(metadata);
+ ASSERT_FALSE(metadata.hasTextScore());
+ ASSERT_FALSE(metadata.hasRandVal());
+ ASSERT_FALSE(metadata.hasSortKey());
+ ASSERT_FALSE(metadata.hasGeoNearPoint());
+ ASSERT_FALSE(metadata.hasGeoNearDistance());
+ ASSERT_FALSE(metadata.hasSearchScore());
+ ASSERT_FALSE(metadata.hasSearchHighlights());
+ ASSERT_FALSE(metadata.hasIndexKey());
+}
+
+TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) {
+ DocumentMetadataFields metadata;
+
+ ASSERT_FALSE(metadata.hasTextScore());
+ metadata.setTextScore(9.9);
+ ASSERT_TRUE(metadata.hasTextScore());
+
+ ASSERT_FALSE(metadata.hasRandVal());
+ metadata.setRandVal(42.0);
+ ASSERT_TRUE(metadata.hasRandVal());
+
+ ASSERT_FALSE(metadata.hasSortKey());
+ metadata.setSortKey(BSON("a" << 1));
+ ASSERT_TRUE(metadata.hasSortKey());
+
+ ASSERT_FALSE(metadata.hasGeoNearDistance());
+ metadata.setGeoNearDistance(3.2);
+ ASSERT_TRUE(metadata.hasGeoNearDistance());
+
+ ASSERT_FALSE(metadata.hasGeoNearPoint());
+ metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)});
+ ASSERT_TRUE(metadata.hasGeoNearPoint());
+
+ ASSERT_FALSE(metadata.hasSearchScore());
+ metadata.setSearchScore(5.4);
+ ASSERT_TRUE(metadata.hasSearchScore());
+
+ ASSERT_FALSE(metadata.hasSearchHighlights());
+ metadata.setSearchHighlights(Value{"foo"_sd});
+ ASSERT_TRUE(metadata.hasSearchHighlights());
+
+ ASSERT_FALSE(metadata.hasIndexKey());
+ metadata.setIndexKey(BSON("b" << 1));
+ ASSERT_TRUE(metadata.hasIndexKey());
+}
+
+TEST(DocumentMetadataFieldsTest, MoveConstructor) {
+ DocumentMetadataFields metadata;
+ metadata.setTextScore(9.9);
+ metadata.setRandVal(42.0);
+ metadata.setSortKey(BSON("a" << 1));
+ metadata.setGeoNearDistance(3.2);
+ metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)});
+ metadata.setSearchScore(5.4);
+ metadata.setSearchHighlights(Value{"foo"_sd});
+ metadata.setIndexKey(BSON("b" << 1));
+
+ DocumentMetadataFields moveConstructed(std::move(metadata));
+ ASSERT_TRUE(moveConstructed);
+ ASSERT_EQ(moveConstructed.getTextScore(), 9.9);
+ ASSERT_EQ(moveConstructed.getRandVal(), 42.0);
+ ASSERT_BSONOBJ_EQ(moveConstructed.getSortKey(), BSON("a" << 1));
+ ASSERT_EQ(moveConstructed.getGeoNearDistance(), 3.2);
+ ASSERT_VALUE_EQ(moveConstructed.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)});
+ ASSERT_EQ(moveConstructed.getSearchScore(), 5.4);
+ ASSERT_VALUE_EQ(moveConstructed.getSearchHighlights(), Value{"foo"_sd});
+ ASSERT_BSONOBJ_EQ(moveConstructed.getIndexKey(), BSON("b" << 1));
+
+ ASSERT_FALSE(metadata);
+}
+
+TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) {
+ DocumentMetadataFields metadata;
+ metadata.setTextScore(9.9);
+ metadata.setRandVal(42.0);
+ metadata.setSortKey(BSON("a" << 1));
+ metadata.setGeoNearDistance(3.2);
+ metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)});
+ metadata.setSearchScore(5.4);
+ metadata.setSearchHighlights(Value{"foo"_sd});
+ metadata.setIndexKey(BSON("b" << 1));
+
+ DocumentMetadataFields moveAssigned;
+ moveAssigned.setTextScore(12.3);
+ moveAssigned = std::move(metadata);
+ ASSERT_TRUE(moveAssigned);
+
+ ASSERT_EQ(moveAssigned.getTextScore(), 9.9);
+ ASSERT_EQ(moveAssigned.getRandVal(), 42.0);
+ ASSERT_BSONOBJ_EQ(moveAssigned.getSortKey(), BSON("a" << 1));
+ ASSERT_EQ(moveAssigned.getGeoNearDistance(), 3.2);
+ ASSERT_VALUE_EQ(moveAssigned.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)});
+ ASSERT_EQ(moveAssigned.getSearchScore(), 5.4);
+ ASSERT_VALUE_EQ(moveAssigned.getSearchHighlights(), Value{"foo"_sd});
+ ASSERT_BSONOBJ_EQ(moveAssigned.getIndexKey(), BSON("b" << 1));
+
+ ASSERT_FALSE(metadata);
+}
+
+TEST(DocumentMetadataFieldsTest, CopyConstructor) {
+ DocumentMetadataFields metadata;
+ metadata.setTextScore(9.9);
+
+ DocumentMetadataFields copied{metadata};
+
+ ASSERT_TRUE(metadata);
+ ASSERT_TRUE(copied);
+ ASSERT_EQ(metadata.getTextScore(), 9.9);
+ ASSERT_EQ(copied.getTextScore(), 9.9);
+}
+
+TEST(DocumentMetadataFieldsTest, CopyAssignmentOperator) {
+ DocumentMetadataFields metadata;
+ metadata.setTextScore(9.9);
+
+ DocumentMetadataFields copied;
+ copied.setTextScore(12.3);
+ copied = metadata;
+
+ ASSERT_TRUE(metadata);
+ ASSERT_TRUE(copied);
+ ASSERT_EQ(metadata.getTextScore(), 9.9);
+ ASSERT_EQ(copied.getTextScore(), 9.9);
+}
+
+TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesNotHave) {
+ DocumentMetadataFields source;
+ source.setTextScore(9.9);
+ source.setRandVal(42.0);
+ source.setSortKey(BSON("a" << 1));
+ source.setGeoNearDistance(3.2);
+
+ DocumentMetadataFields destination;
+ destination.setTextScore(12.3);
+ destination.setRandVal(84.0);
+
+ destination.mergeWith(source);
+
+ ASSERT_EQ(destination.getTextScore(), 12.3);
+ ASSERT_EQ(destination.getRandVal(), 84.0);
+ ASSERT_BSONOBJ_EQ(destination.getSortKey(), BSON("a" << 1));
+ ASSERT_EQ(destination.getGeoNearDistance(), 3.2);
+ ASSERT_FALSE(destination.hasGeoNearPoint());
+ ASSERT_FALSE(destination.hasSearchScore());
+ ASSERT_FALSE(destination.hasSearchHighlights());
+ ASSERT_FALSE(destination.hasIndexKey());
+}
+
+TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) {
+ DocumentMetadataFields source;
+ source.setTextScore(9.9);
+ source.setRandVal(42.0);
+ source.setSortKey(BSON("a" << 1));
+ source.setGeoNearDistance(3.2);
+
+ DocumentMetadataFields destination;
+ destination.setTextScore(12.3);
+ destination.setRandVal(84.0);
+
+ destination.copyFrom(source);
+
+ ASSERT_EQ(destination.getTextScore(), 9.9);
+ ASSERT_EQ(destination.getRandVal(), 42.0);
+ ASSERT_BSONOBJ_EQ(destination.getSortKey(), BSON("a" << 1));
+ ASSERT_EQ(destination.getGeoNearDistance(), 3.2);
+ ASSERT_FALSE(destination.hasGeoNearPoint());
+ ASSERT_FALSE(destination.hasSearchScore());
+ ASSERT_FALSE(destination.hasSearchHighlights());
+ ASSERT_FALSE(destination.hasIndexKey());
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_change_stream_test.cpp b/src/mongo/db/pipeline/document_source_change_stream_test.cpp
index f50808b97e9..65a2ed55821 100644
--- a/src/mongo/db/pipeline/document_source_change_stream_test.cpp
+++ b/src/mongo/db/pipeline/document_source_change_stream_test.cpp
@@ -1689,7 +1689,7 @@ TEST_F(ChangeStreamStageTest, UsesResumeTokenAsSortKeyIfNeedsMergeIsFalse) {
makeResumeToken(kDefaultTs, testUuid(), BSON("x" << 2 << "_id" << 1)).toBson();
ASSERT_TRUE(next.isAdvanced());
- ASSERT_BSONOBJ_EQ(next.releaseDocument().getSortKeyMetaField(), expectedSortKey);
+ ASSERT_BSONOBJ_EQ(next.releaseDocument().metadata().getSortKey(), expectedSortKey);
}
//
diff --git a/src/mongo/db/pipeline/document_source_change_stream_transform.cpp b/src/mongo/db/pipeline/document_source_change_stream_transform.cpp
index 2da744ae88e..1c6739b8497 100644
--- a/src/mongo/db/pipeline/document_source_change_stream_transform.cpp
+++ b/src/mongo/db/pipeline/document_source_change_stream_transform.cpp
@@ -328,7 +328,7 @@ Document DocumentSourceChangeStreamTransform::applyTransformation(const Document
// We set the resume token as the document's sort key in both the sharded and non-sharded cases,
// since we will subsequently rely upon it to generate a correct postBatchResumeToken.
- doc.setSortKeyMetaField(resumeToken.toBson());
+ doc.metadata().setSortKey(resumeToken.toBson());
// "invalidate" and "newShardDetected" entries have fewer fields.
if (operationType == DocumentSourceChangeStream::kInvalidateOpType ||
diff --git a/src/mongo/db/pipeline/document_source_check_invalidate.cpp b/src/mongo/db/pipeline/document_source_check_invalidate.cpp
index 276f7edb5b9..376e0dc97c9 100644
--- a/src/mongo/db/pipeline/document_source_check_invalidate.cpp
+++ b/src/mongo/db/pipeline/document_source_check_invalidate.cpp
@@ -107,7 +107,7 @@ DocumentSource::GetNextResult DocumentSourceCheckInvalidate::getNext() {
// We set the resume token as the document's sort key in both the sharded and non-sharded
// cases, since we will later rely upon it to generate a correct postBatchResumeToken. We
// must therefore update the sort key to match the new resume token that we generated above.
- result.setSortKeyMetaField(resumeTokenDoc.toBson());
+ result.metadata().setSortKey(resumeTokenDoc.toBson());
_queuedInvalidate = result.freeze();
}
diff --git a/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp b/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp
index 0a2d4b71e45..6a369e84a69 100644
--- a/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp
@@ -88,25 +88,25 @@ Document DocumentSourceGeoNearCursor::transformBSONObjToDocument(const BSONObj&
MutableDocument output(Document::fromBsonWithMetaData(obj));
// Scale the distance by the requested factor.
- invariant(output.peek().hasGeoNearDistance(),
+ invariant(output.peek().metadata().hasGeoNearDistance(),
str::stream()
<< "Query returned a document that is unexpectedly missing the geoNear distance: "
<< obj.jsonString());
- const auto distance = output.peek().getGeoNearDistance() * _distanceMultiplier;
+ const auto distance = output.peek().metadata().getGeoNearDistance() * _distanceMultiplier;
output.setNestedField(_distanceField, Value(distance));
if (_locationField) {
invariant(
- output.peek().hasGeoNearPoint(),
+ output.peek().metadata().hasGeoNearPoint(),
str::stream()
<< "Query returned a document that is unexpectedly missing the geoNear point: "
<< obj.jsonString());
- output.setNestedField(*_locationField, output.peek().getGeoNearPoint());
+ output.setNestedField(*_locationField, output.peek().metadata().getGeoNearPoint());
}
// In a cluster, $geoNear will be merged via $sort, so add the sort key.
if (pExpCtx->needsMerge) {
- output.setSortKeyMetaField(BSON("" << distance));
+ output.metadata().setSortKey(BSON("" << distance));
}
return output.freeze();
diff --git a/src/mongo/db/pipeline/document_source_sample.cpp b/src/mongo/db/pipeline/document_source_sample.cpp
index 23a71562a30..072b7a22d1c 100644
--- a/src/mongo/db/pipeline/document_source_sample.cpp
+++ b/src/mongo/db/pipeline/document_source_sample.cpp
@@ -62,7 +62,7 @@ DocumentSource::GetNextResult DocumentSourceSample::getNext() {
auto nextInput = pSource->getNext();
for (; nextInput.isAdvanced(); nextInput = pSource->getNext()) {
MutableDocument doc(nextInput.releaseDocument());
- doc.setRandMetaField(prng.nextCanonicalDouble());
+ doc.metadata().setRandVal(prng.nextCanonicalDouble());
_sortStage->loadDocument(doc.freeze());
}
switch (nextInput.getStatus()) {
diff --git a/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp b/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp
index ad84c24e9aa..a3a33ca6f4c 100644
--- a/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp
@@ -92,11 +92,11 @@ DocumentSource::GetNextResult DocumentSourceSampleFromRandomCursor::getNext() {
_randMetaFieldVal -= smallestFromSampleOfUniform(&prng, _nDocsInColl);
MutableDocument md(nextResult.releaseDocument());
- md.setRandMetaField(_randMetaFieldVal);
+ md.metadata().setRandVal(_randMetaFieldVal);
if (pExpCtx->needsMerge) {
// This stage will be merged by sorting results according to this random metadata field, but
// the merging logic expects to sort by the sort key metadata.
- md.setSortKeyMetaField(BSON("" << _randMetaFieldVal));
+ md.metadata().setSortKey(BSON("" << _randMetaFieldVal));
}
return md.freeze();
}
diff --git a/src/mongo/db/pipeline/document_source_sample_test.cpp b/src/mongo/db/pipeline/document_source_sample_test.cpp
index 039456ac440..0d38fa914a9 100644
--- a/src/mongo/db/pipeline/document_source_sample_test.cpp
+++ b/src/mongo/db/pipeline/document_source_sample_test.cpp
@@ -87,9 +87,9 @@ protected:
auto nextResult = sample()->getNext();
ASSERT_TRUE(nextResult.isAdvanced());
auto thisDoc = nextResult.releaseDocument();
- ASSERT_TRUE(thisDoc.hasRandMetaField());
+ ASSERT_TRUE(thisDoc.metadata().hasRandVal());
if (prevDoc) {
- ASSERT_LTE(thisDoc.getRandMetaField(), prevDoc->getRandMetaField());
+ ASSERT_LTE(thisDoc.metadata().getRandVal(), prevDoc->metadata().getRandVal());
}
prevDoc = std::move(thisDoc);
}
@@ -165,7 +165,7 @@ TEST_F(SampleBasics, DocsUnmodified) {
auto doc = next.releaseDocument();
ASSERT_EQUALS(1, doc["a"].getInt());
ASSERT_EQUALS(2, doc["b"]["c"].getInt());
- ASSERT_TRUE(doc.hasRandMetaField());
+ ASSERT_TRUE(doc.metadata().hasRandVal());
assertEOF();
}
@@ -281,7 +281,7 @@ TEST_F(SampleFromRandomCursorBasics, DocsUnmodified) {
auto doc = next.releaseDocument();
ASSERT_EQUALS(1, doc["_id"].getInt());
ASSERT_EQUALS(2, doc["b"]["c"].getInt());
- ASSERT_TRUE(doc.hasRandMetaField());
+ ASSERT_TRUE(doc.metadata().hasRandVal());
assertEOF();
}
@@ -298,16 +298,16 @@ TEST_F(SampleFromRandomCursorBasics, IgnoreDuplicates) {
ASSERT_TRUE(next.isAdvanced());
auto doc = next.releaseDocument();
ASSERT_EQUALS(1, doc["_id"].getInt());
- ASSERT_TRUE(doc.hasRandMetaField());
- double doc1Meta = doc.getRandMetaField();
+ ASSERT_TRUE(doc.metadata().hasRandVal());
+ double doc1Meta = doc.metadata().getRandVal();
// Should ignore the duplicate {_id: 1}, and return {_id: 2}.
next = sample()->getNext();
ASSERT_TRUE(next.isAdvanced());
doc = next.releaseDocument();
ASSERT_EQUALS(2, doc["_id"].getInt());
- ASSERT_TRUE(doc.hasRandMetaField());
- double doc2Meta = doc.getRandMetaField();
+ ASSERT_TRUE(doc.metadata().hasRandVal());
+ double doc2Meta = doc.metadata().getRandVal();
ASSERT_GTE(doc1Meta, doc2Meta);
// Both stages should be exhausted.
@@ -371,13 +371,13 @@ TEST_F(SampleFromRandomCursorBasics, MimicNonOptimized) {
auto doc = sample()->getNext();
ASSERT_TRUE(doc.isAdvanced());
- ASSERT_TRUE(doc.getDocument().hasRandMetaField());
- firstTotal += doc.getDocument().getRandMetaField();
+ ASSERT_TRUE(doc.getDocument().metadata().hasRandVal());
+ firstTotal += doc.getDocument().metadata().getRandVal();
doc = sample()->getNext();
ASSERT_TRUE(doc.isAdvanced());
- ASSERT_TRUE(doc.getDocument().hasRandMetaField());
- secondTotal += doc.getDocument().getRandMetaField();
+ ASSERT_TRUE(doc.getDocument().metadata().hasRandVal());
+ secondTotal += doc.getDocument().metadata().getRandVal();
}
// The average random meta value of the first document should be about 0.75. We assume that
// 10000 trials is sufficient for us to apply the Central Limit Theorem. Using an error
diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp
index 65f3d3bf8a4..dda569e4350 100644
--- a/src/mongo/db/pipeline/document_source_sort.cpp
+++ b/src/mongo/db/pipeline/document_source_sort.cpp
@@ -337,11 +337,11 @@ StatusWith<Value> DocumentSourceSort::extractKeyFast(const Document& doc) const
BSONObj DocumentSourceSort::extractKeyWithArray(const Document& doc) const {
SortKeyGenerator::Metadata metadata;
- if (doc.hasTextScore()) {
- metadata.textScore = doc.getTextScore();
+ if (doc.metadata().hasTextScore()) {
+ metadata.textScore = doc.metadata().getTextScore();
}
- if (doc.hasRandMetaField()) {
- metadata.randVal = doc.getRandMetaField();
+ if (doc.metadata().hasRandVal()) {
+ metadata.randVal = doc.metadata().getRandVal();
}
// Convert the Document to a BSONObj, but only do the conversion for the paths we actually need.
@@ -380,7 +380,7 @@ std::pair<Value, Document> DocumentSourceSort::extractSortKey(Document&& doc) co
// We need to be merged, so will have to be serialized. Save the sort key here to avoid
// re-computing it during the merge.
invariant(serializedSortKey);
- toBeSorted.setSortKeyMetaField(*serializedSortKey);
+ toBeSorted.metadata().setSortKey(*serializedSortKey);
}
return {inMemorySortKey, toBeSorted.freeze()};
}
diff --git a/src/mongo/db/pipeline/document_source_sort_test.cpp b/src/mongo/db/pipeline/document_source_sort_test.cpp
index 00a4dad3c70..fe4275d310f 100644
--- a/src/mongo/db/pipeline/document_source_sort_test.cpp
+++ b/src/mongo/db/pipeline/document_source_sort_test.cpp
@@ -309,9 +309,9 @@ TEST_F(DocumentSourceSortExecutionTest, NullValue) {
*/
TEST_F(DocumentSourceSortExecutionTest, TextScore) {
MutableDocument first(Document{{"_id", 0}});
- first.setTextScore(10);
+ first.metadata().setTextScore(10);
MutableDocument second(Document{{"_id", 1}});
- second.setTextScore(20);
+ second.metadata().setTextScore(20);
checkResults({first.freeze(), second.freeze()},
BSON("$computed0" << metaTextScore),
@@ -323,9 +323,9 @@ TEST_F(DocumentSourceSortExecutionTest, TextScore) {
*/
TEST_F(DocumentSourceSortExecutionTest, RandMeta) {
MutableDocument first(Document{{"_id", 0}});
- first.setRandMetaField(0.01);
+ first.metadata().setRandVal(0.01);
MutableDocument second(Document{{"_id", 1}});
- second.setRandMetaField(0.02);
+ second.metadata().setRandVal(0.02);
checkResults({first.freeze(), second.freeze()},
BSON("$computed0" << BSON("$meta"
diff --git a/src/mongo/db/pipeline/document_value_test.cpp b/src/mongo/db/pipeline/document_value_test.cpp
index e651c840fe9..67d669403da 100644
--- a/src/mongo/db/pipeline/document_value_test.cpp
+++ b/src/mongo/db/pipeline/document_value_test.cpp
@@ -500,79 +500,139 @@ namespace MetaFields {
using mongo::Document;
TEST(MetaFields, TextScoreBasics) {
// Documents should not have a text score until it is set.
- ASSERT_FALSE(Document().hasTextScore());
+ ASSERT_FALSE(Document().metadata().hasTextScore());
// Setting the text score should work as expected.
MutableDocument docBuilder;
- docBuilder.setTextScore(1.0);
+ docBuilder.metadata().setTextScore(1.0);
Document doc = docBuilder.freeze();
- ASSERT_TRUE(doc.hasTextScore());
- ASSERT_EQ(1.0, doc.getTextScore());
+ ASSERT_TRUE(doc.metadata().hasTextScore());
+ ASSERT_EQ(1.0, doc.metadata().getTextScore());
}
TEST(MetaFields, RandValBasics) {
// Documents should not have a random value until it is set.
- ASSERT_FALSE(Document().hasRandMetaField());
+ ASSERT_FALSE(Document().metadata().hasRandVal());
// Setting the random value field should work as expected.
MutableDocument docBuilder;
- docBuilder.setRandMetaField(1.0);
+ docBuilder.metadata().setRandVal(1.0);
Document doc = docBuilder.freeze();
- ASSERT_TRUE(doc.hasRandMetaField());
- ASSERT_EQ(1, doc.getRandMetaField());
+ ASSERT_TRUE(doc.metadata().hasRandVal());
+ ASSERT_EQ(1, doc.metadata().getRandVal());
// Setting the random value twice should keep the second value.
MutableDocument docBuilder2;
- docBuilder2.setRandMetaField(1.0);
- docBuilder2.setRandMetaField(2.0);
+ docBuilder2.metadata().setRandVal(1.0);
+ docBuilder2.metadata().setRandVal(2.0);
Document doc2 = docBuilder2.freeze();
- ASSERT_TRUE(doc2.hasRandMetaField());
- ASSERT_EQ(2.0, doc2.getRandMetaField());
+ ASSERT_TRUE(doc2.metadata().hasRandVal());
+ ASSERT_EQ(2.0, doc2.metadata().getRandVal());
}
TEST(MetaFields, SearchScoreBasic) {
// Documents should not have a search score until it is set.
- ASSERT_FALSE(Document().hasSearchScore());
+ ASSERT_FALSE(Document().metadata().hasSearchScore());
// Setting the search score field should work as expected.
MutableDocument docBuilder;
- docBuilder.setSearchScore(1.23);
+ docBuilder.metadata().setSearchScore(1.23);
Document doc = docBuilder.freeze();
- ASSERT_TRUE(doc.hasSearchScore());
- ASSERT_EQ(1.23, doc.getSearchScore());
+ ASSERT_TRUE(doc.metadata().hasSearchScore());
+ ASSERT_EQ(1.23, doc.metadata().getSearchScore());
// Setting the searchScore twice should keep the second value.
MutableDocument docBuilder2;
- docBuilder2.setSearchScore(1.0);
- docBuilder2.setSearchScore(2.0);
+ docBuilder2.metadata().setSearchScore(1.0);
+ docBuilder2.metadata().setSearchScore(2.0);
Document doc2 = docBuilder2.freeze();
- ASSERT_TRUE(doc2.hasSearchScore());
- ASSERT_EQ(2.0, doc2.getSearchScore());
+ ASSERT_TRUE(doc2.metadata().hasSearchScore());
+ ASSERT_EQ(2.0, doc2.metadata().getSearchScore());
}
TEST(MetaFields, SearchHighlightsBasic) {
// Documents should not have a search highlights until it is set.
- ASSERT_FALSE(Document().hasSearchHighlights());
+ ASSERT_FALSE(Document().metadata().hasSearchHighlights());
// Setting the search highlights field should work as expected.
MutableDocument docBuilder;
Value highlights = DOC_ARRAY("a"_sd
<< "b"_sd);
- docBuilder.setSearchHighlights(highlights);
+ docBuilder.metadata().setSearchHighlights(highlights);
Document doc = docBuilder.freeze();
- ASSERT_TRUE(doc.hasSearchHighlights());
- ASSERT_VALUE_EQ(doc.getSearchHighlights(), highlights);
+ ASSERT_TRUE(doc.metadata().hasSearchHighlights());
+ ASSERT_VALUE_EQ(doc.metadata().getSearchHighlights(), highlights);
// Setting the searchHighlights twice should keep the second value.
MutableDocument docBuilder2;
Value otherHighlights = DOC_ARRAY("snippet1"_sd
<< "snippet2"_sd
<< "snippet3"_sd);
- docBuilder2.setSearchHighlights(highlights);
- docBuilder2.setSearchHighlights(otherHighlights);
+ docBuilder2.metadata().setSearchHighlights(highlights);
+ docBuilder2.metadata().setSearchHighlights(otherHighlights);
Document doc2 = docBuilder2.freeze();
- ASSERT_TRUE(doc2.hasSearchHighlights());
- ASSERT_VALUE_EQ(doc2.getSearchHighlights(), otherHighlights);
+ ASSERT_TRUE(doc2.metadata().hasSearchHighlights());
+ ASSERT_VALUE_EQ(doc2.metadata().getSearchHighlights(), otherHighlights);
+}
+
+TEST(MetaFields, IndexKeyMetadataSerializesCorrectly) {
+ Document doc{BSON("a" << 1)};
+ MutableDocument mutableDoc{doc};
+ mutableDoc.metadata().setIndexKey(BSON("b" << 1));
+ doc = mutableDoc.freeze();
+
+ ASSERT_TRUE(doc.metadata().hasIndexKey());
+ ASSERT_BSONOBJ_EQ(doc.metadata().getIndexKey(), BSON("b" << 1));
+
+ auto serialized = doc.toBsonWithMetaData();
+ ASSERT_BSONOBJ_EQ(serialized, BSON("a" << 1 << "$indexKey" << BSON("b" << 1)));
+}
+
+TEST(MetaFields, FromBsonWithMetadataAcceptsIndexKeyMetadata) {
+ auto doc = Document::fromBsonWithMetaData(BSON("a" << 1 << "$indexKey" << BSON("b" << 1)));
+ ASSERT_TRUE(doc.metadata().hasIndexKey());
+ ASSERT_BSONOBJ_EQ(doc.metadata().getIndexKey(), BSON("b" << 1));
+ auto bsonWithoutMetadata = doc.toBson();
+ ASSERT_BSONOBJ_EQ(bsonWithoutMetadata, BSON("a" << 1));
+}
+
+TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
+ Document source = Document::fromBsonWithMetaData(BSON(
+ "a" << 1 << "$textScore" << 9.9 << "b" << 1 << "$randVal" << 42.0 << "c" << 1 << "$sortKey"
+ << BSON("x" << 1)
+ << "d"
+ << 1
+ << "$dis"
+ << 3.2
+ << "e"
+ << 1
+ << "$pt"
+ << BSON_ARRAY(1 << 2)
+ << "f"
+ << 1
+ << "$searchScore"
+ << 5.4
+ << "g"
+ << 1
+ << "$searchHighlights"
+ << "foo"
+ << "h"
+ << 1
+ << "$indexKey"
+ << BSON("y" << 1)));
+
+ MutableDocument destination{};
+ destination.copyMetaDataFrom(source);
+ auto result = destination.freeze();
+
+ ASSERT_EQ(result.metadata().getTextScore(), 9.9);
+ ASSERT_EQ(result.metadata().getRandVal(), 42.0);
+ ASSERT_BSONOBJ_EQ(result.metadata().getSortKey(), BSON("x" << 1));
+ ASSERT_EQ(result.metadata().getGeoNearDistance(), 3.2);
+ ASSERT_VALUE_EQ(result.metadata().getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)});
+ ASSERT_EQ(result.metadata().getSearchScore(), 5.4);
+ ASSERT_VALUE_EQ(result.metadata().getSearchHighlights(), Value{"foo"_sd});
+ ASSERT_BSONOBJ_EQ(result.metadata().getIndexKey(), BSON("y" << 1));
}
class SerializationTest : public unittest::Test {
@@ -588,18 +648,22 @@ protected:
// Round trip to/from a buffer.
auto output = roundTrip(input);
ASSERT_DOCUMENT_EQ(output, input);
- ASSERT_EQ(output.hasTextScore(), input.hasTextScore());
- ASSERT_EQ(output.hasRandMetaField(), input.hasRandMetaField());
- ASSERT_EQ(output.hasSearchScore(), input.hasSearchScore());
- ASSERT_EQ(output.hasSearchHighlights(), input.hasSearchHighlights());
- if (input.hasTextScore())
- ASSERT_EQ(output.getTextScore(), input.getTextScore());
- if (input.hasRandMetaField())
- ASSERT_EQ(output.getRandMetaField(), input.getRandMetaField());
- if (input.hasSearchScore())
- ASSERT_EQ(output.getSearchScore(), input.getSearchScore());
- if (input.hasSearchHighlights())
- ASSERT_VALUE_EQ(output.getSearchHighlights(), input.getSearchHighlights());
+ ASSERT_EQ(output.metadata().hasTextScore(), input.metadata().hasTextScore());
+ ASSERT_EQ(output.metadata().hasRandVal(), input.metadata().hasRandVal());
+ ASSERT_EQ(output.metadata().hasSearchScore(), input.metadata().hasSearchScore());
+ ASSERT_EQ(output.metadata().hasSearchHighlights(), input.metadata().hasSearchHighlights());
+ ASSERT_EQ(output.metadata().hasIndexKey(), input.metadata().hasIndexKey());
+ if (input.metadata().hasTextScore())
+ ASSERT_EQ(output.metadata().getTextScore(), input.metadata().getTextScore());
+ if (input.metadata().hasRandVal())
+ ASSERT_EQ(output.metadata().getRandVal(), input.metadata().getRandVal());
+ if (input.metadata().hasSearchScore())
+ ASSERT_EQ(output.metadata().getSearchScore(), input.metadata().getSearchScore());
+ if (input.metadata().hasSearchHighlights())
+ ASSERT_VALUE_EQ(output.metadata().getSearchHighlights(),
+ input.metadata().getSearchHighlights());
+ if (input.metadata().hasIndexKey())
+ ASSERT_BSONOBJ_EQ(output.metadata().getIndexKey(), input.metadata().getIndexKey());
ASSERT(output.toBson().binaryEqual(input.toBson()));
}
@@ -607,42 +671,43 @@ protected:
TEST_F(SerializationTest, MetaSerializationNoVals) {
MutableDocument docBuilder;
- docBuilder.setTextScore(10.0);
- docBuilder.setRandMetaField(20.0);
- docBuilder.setSearchScore(30.0);
- docBuilder.setSearchHighlights(DOC_ARRAY("abc"_sd
- << "def"_sd));
+ docBuilder.metadata().setTextScore(10.0);
+ docBuilder.metadata().setRandVal(20.0);
+ docBuilder.metadata().setSearchScore(30.0);
+ docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
+ << "def"_sd));
assertRoundTrips(docBuilder.freeze());
}
TEST_F(SerializationTest, MetaSerializationWithVals) {
// Same as above test, but add a non-meta field as well.
MutableDocument docBuilder(DOC("foo" << 10));
- docBuilder.setTextScore(10.0);
- docBuilder.setRandMetaField(20.0);
- docBuilder.setSearchScore(30.0);
- docBuilder.setSearchHighlights(DOC_ARRAY("abc"_sd
- << "def"_sd));
+ docBuilder.metadata().setTextScore(10.0);
+ docBuilder.metadata().setRandVal(20.0);
+ docBuilder.metadata().setSearchScore(30.0);
+ docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
+ << "def"_sd));
+ docBuilder.metadata().setIndexKey(BSON("key" << 42));
assertRoundTrips(docBuilder.freeze());
}
TEST_F(SerializationTest, MetaSerializationSearchHighlightsNonArray) {
MutableDocument docBuilder;
- docBuilder.setTextScore(10.0);
- docBuilder.setRandMetaField(20.0);
- docBuilder.setSearchScore(30.0);
+ docBuilder.metadata().setTextScore(10.0);
+ docBuilder.metadata().setRandVal(20.0);
+ docBuilder.metadata().setSearchScore(30.0);
// Everything should still round trip even if the searchHighlights metadata isn't an array.
- docBuilder.setSearchHighlights(Value(1.23));
+ docBuilder.metadata().setSearchHighlights(Value(1.23));
assertRoundTrips(docBuilder.freeze());
}
TEST(MetaFields, ToAndFromBson) {
MutableDocument docBuilder;
- docBuilder.setTextScore(10.0);
- docBuilder.setRandMetaField(20.0);
- docBuilder.setSearchScore(30.0);
- docBuilder.setSearchHighlights(DOC_ARRAY("abc"_sd
- << "def"_sd));
+ docBuilder.metadata().setTextScore(10.0);
+ docBuilder.metadata().setRandVal(20.0);
+ docBuilder.metadata().setSearchScore(30.0);
+ docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
+ << "def"_sd));
Document doc = docBuilder.freeze();
BSONObj obj = doc.toBsonWithMetaData();
ASSERT_EQ(10.0, obj[Document::metaFieldTextScore].Double());
@@ -652,29 +717,29 @@ TEST(MetaFields, ToAndFromBson) {
BSON_ARRAY("abc"_sd
<< "def"_sd));
Document fromBson = Document::fromBsonWithMetaData(obj);
- ASSERT_TRUE(fromBson.hasTextScore());
- ASSERT_TRUE(fromBson.hasRandMetaField());
- ASSERT_EQ(10.0, fromBson.getTextScore());
- ASSERT_EQ(20, fromBson.getRandMetaField());
+ ASSERT_TRUE(fromBson.metadata().hasTextScore());
+ ASSERT_TRUE(fromBson.metadata().hasRandVal());
+ ASSERT_EQ(10.0, fromBson.metadata().getTextScore());
+ ASSERT_EQ(20, fromBson.metadata().getRandVal());
}
TEST(MetaFields, MetaFieldsIncludedInDocumentApproximateSize) {
MutableDocument docBuilder;
- docBuilder.setSearchHighlights(DOC_ARRAY("abc"_sd
- << "def"_sd));
+ docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
+ << "def"_sd));
const size_t smallMetadataDocSize = docBuilder.freeze().getApproximateSize();
// The second document has a larger "search highlights" object.
MutableDocument docBuilder2;
- docBuilder2.setSearchHighlights(DOC_ARRAY("abc"_sd
- << "def"_sd
- << "ghijklmnop"_sd));
+ docBuilder2.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
+ << "def"_sd
+ << "ghijklmnop"_sd));
Document doc2 = docBuilder2.freeze();
const size_t bigMetadataDocSize = doc2.getApproximateSize();
ASSERT_GT(bigMetadataDocSize, smallMetadataDocSize);
// Do a sanity check on the amount of space taken by metadata in document 2.
- ASSERT_LT(doc2.getMetadataApproximateSize(), 200U);
+ ASSERT_LT(doc2.getMetadataApproximateSize(), 250U);
Document emptyDoc;
ASSERT_LT(emptyDoc.getMetadataApproximateSize(), 100U);
@@ -686,7 +751,7 @@ TEST(MetaFields, BadSerialization) {
// Signal there are 0 fields.
bb.appendNum(0);
// This would specify a meta field with an invalid type.
- bb.appendNum(char(MetaType::NUM_FIELDS) + 1);
+ bb.appendNum(char(DocumentMetadataFields::MetaType::NUM_FIELDS) + 1);
// Signals end of input.
bb.appendNum(char(0));
BufReader reader(bb.buf(), bb.len());
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index d28e2505030..0c0ef86f963 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -2585,15 +2585,16 @@ Value ExpressionMeta::serialize(bool explain) const {
}
Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const {
+ const auto& metadata = root.metadata();
switch (_metaType) {
case MetaType::TEXT_SCORE:
- return root.hasTextScore() ? Value(root.getTextScore()) : Value();
+ return metadata.hasTextScore() ? Value(metadata.getTextScore()) : Value();
case MetaType::RAND_VAL:
- return root.hasRandMetaField() ? Value(root.getRandMetaField()) : Value();
+ return metadata.hasRandVal() ? Value(metadata.getRandVal()) : Value();
case MetaType::SEARCH_SCORE:
- return root.hasSearchScore() ? Value(root.getSearchScore()) : Value();
+ return metadata.hasSearchScore() ? Value(metadata.getSearchScore()) : Value();
case MetaType::SEARCH_HIGHLIGHTS:
- return root.hasSearchHighlights() ? Value(root.getSearchHighlights()) : Value();
+ return metadata.hasSearchHighlights() ? Value(metadata.getSearchHighlights()) : Value();
}
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 403a4cf5882..34cd1a61335 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -6091,7 +6091,7 @@ TEST(ExpressionMetaTest, ExpressionMetaSearchScore) {
auto expressionMeta = ExpressionMeta::parse(expCtx, expr.firstElement(), vps);
MutableDocument doc;
- doc.setSearchScore(1.234);
+ doc.metadata().setSearchScore(1.234);
Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables);
ASSERT_EQ(val.getDouble(), 1.234);
}
@@ -6104,7 +6104,7 @@ TEST(ExpressionMetaTest, ExpressionMetaSearchHighlights) {
MutableDocument doc;
Document highlights = DOC("this part" << 1 << "is opaque to the server" << 1);
- doc.setSearchHighlights(Value(highlights));
+ doc.metadata().setSearchHighlights(Value(highlights));
Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables);
ASSERT_DOCUMENT_EQ(val.getDocument(), highlights);
diff --git a/src/mongo/db/pipeline/parsed_add_fields_test.cpp b/src/mongo/db/pipeline/parsed_add_fields_test.cpp
index 9d52327b985..e720908b080 100644
--- a/src/mongo/db/pipeline/parsed_add_fields_test.cpp
+++ b/src/mongo/db/pipeline/parsed_add_fields_test.cpp
@@ -545,8 +545,8 @@ TEST(ParsedAddFieldsExecutionTest, AlwaysKeepsMetadataFromOriginalDoc) {
addition.parse(BSON("a" << true));
MutableDocument inputDocBuilder(Document{{"a", 1}});
- inputDocBuilder.setRandMetaField(1.0);
- inputDocBuilder.setTextScore(10.0);
+ inputDocBuilder.metadata().setRandVal(1.0);
+ inputDocBuilder.metadata().setTextScore(10.0);
Document inputDoc = inputDocBuilder.freeze();
auto result = addition.applyProjection(inputDoc);
diff --git a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
index e0283ed101b..62a2875f18c 100644
--- a/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
+++ b/src/mongo/db/pipeline/parsed_exclusion_projection_test.cpp
@@ -339,8 +339,8 @@ TEST(ExclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc)
exclusion.parse(BSON("a" << false));
MutableDocument inputDocBuilder(Document{{"_id", "ID"_sd}, {"a", 1}});
- inputDocBuilder.setRandMetaField(1.0);
- inputDocBuilder.setTextScore(10.0);
+ inputDocBuilder.metadata().setRandVal(1.0);
+ inputDocBuilder.metadata().setTextScore(10.0);
Document inputDoc = inputDocBuilder.freeze();
auto result = exclusion.applyProjection(inputDoc);
diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
index ca85af5fae1..09d4b0cb4d6 100644
--- a/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
+++ b/src/mongo/db/pipeline/parsed_inclusion_projection_test.cpp
@@ -640,8 +640,8 @@ TEST(InclusionProjectionExecutionTest, ShouldAlwaysKeepMetadataFromOriginalDoc)
inclusion.parse(BSON("a" << true));
MutableDocument inputDocBuilder(Document{{"a", 1}});
- inputDocBuilder.setRandMetaField(1.0);
- inputDocBuilder.setTextScore(10.0);
+ inputDocBuilder.metadata().setRandVal(1.0);
+ inputDocBuilder.metadata().setTextScore(10.0);
Document inputDoc = inputDocBuilder.freeze();
auto result = inclusion.applyProjection(inputDoc);
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp
index d64f43b4611..7720824f7f7 100644
--- a/src/mongo/db/query/planner_analysis.cpp
+++ b/src/mongo/db/query/planner_analysis.cpp
@@ -341,7 +341,7 @@ std::unique_ptr<ProjectionNode> analyzeProjection(const CanonicalQuery& query,
const QueryRequest& qr = query.getQueryRequest();
// If there's no sort stage but we have a sortKey meta-projection, we need to add a stage to
- // generate the sort key computed data.
+ // generate the sort key metadata.
auto addSortKeyGeneratorStageIfNeeded = [&]() {
if (!hasSortStage && query.getProj()->wantSortKey()) {
auto keyGenNode = std::make_unique<SortKeyGeneratorNode>();
diff --git a/src/mongo/db/storage/index_entry_comparison.h b/src/mongo/db/storage/index_entry_comparison.h
index 649333a36ab..41ff05f3dd3 100644
--- a/src/mongo/db/storage/index_entry_comparison.h
+++ b/src/mongo/db/storage/index_entry_comparison.h
@@ -45,6 +45,25 @@ namespace mongo {
* and a disk location.
*/
struct IndexKeyEntry {
+ /**
+ * Given an index key 'dehyratedKey' with no field names, returns a new BSONObj representing the
+ * index key after adding field names according to 'keyPattern'.
+ */
+ static BSONObj rehydrateKey(const BSONObj& keyPattern, const BSONObj& dehydratedKey) {
+ BSONObjBuilder bob;
+ BSONObjIterator keyIter(keyPattern);
+ BSONObjIterator valueIter(dehydratedKey);
+
+ while (keyIter.more() && valueIter.more()) {
+ bob.appendAs(valueIter.next(), keyIter.next().fieldNameStringData());
+ }
+
+ invariant(!keyIter.more());
+ invariant(!valueIter.more());
+
+ return bob.obj();
+ }
+
IndexKeyEntry(BSONObj key, RecordId loc) : key(std::move(key)), loc(std::move(loc)) {}
BSONObj key;
diff --git a/src/mongo/dbtests/query_stage_sort_key_generator.cpp b/src/mongo/dbtests/query_stage_sort_key_generator.cpp
index adc2bb2db50..daa667313e4 100644
--- a/src/mongo/dbtests/query_stage_sort_key_generator.cpp
+++ b/src/mongo/dbtests/query_stage_sort_key_generator.cpp
@@ -33,7 +33,6 @@
#include "mongo/db/exec/queued_data_stage.h"
#include "mongo/db/exec/sort_key_generator.h"
-#include "mongo/db/exec/working_set_computed_data.h"
#include "mongo/db/json.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/query_test_service_context.h"
@@ -52,9 +51,7 @@ BSONObj extractKeyFromKeyGenStage(SortKeyGeneratorStage* sortKeyGen, WorkingSet*
ASSERT_EQ(state, PlanStage::ADVANCED);
auto wsm = workingSet->get(wsid);
- auto sortKeyComputedData =
- static_cast<const SortKeyComputedData*>(wsm->getComputed(WSM_SORT_KEY));
- return sortKeyComputedData->getSortKey();
+ return wsm->metadata().getSortKey();
}
/**
diff --git a/src/mongo/s/query/router_stage_pipeline.cpp b/src/mongo/s/query/router_stage_pipeline.cpp
index ce1c56c103b..b617af5ba01 100644
--- a/src/mongo/s/query/router_stage_pipeline.cpp
+++ b/src/mongo/s/query/router_stage_pipeline.cpp
@@ -97,7 +97,7 @@ BSONObj RouterStagePipeline::_validateAndConvertToBSON(const Document& event) {
}
// Confirm that the document _id field matches the original resume token in the sort key field.
auto eventBSON = event.toBson();
- auto resumeToken = event.getSortKeyMetaField();
+ auto resumeToken = event.metadata().getSortKey();
auto idField = eventBSON.getObjectField("_id");
invariant(!resumeToken.isEmpty());
uassert(ErrorCodes::ChangeStreamFatalError,