diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/exec/document_value | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/exec/document_value')
| -rw-r--r-- | src/mongo/db/exec/document_value/document.cpp | 128 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document.h | 100 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_internal.h | 62 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_metadata_fields.cpp | 55 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_metadata_fields.h | 152 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_metadata_fields_test.cpp | 192 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_value_test.cpp | 235 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/document_value_test_util.h | 7 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/value.cpp | 18 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/value.h | 13 | ||||
| -rw-r--r-- | src/mongo/db/exec/document_value/value_comparator_test.cpp | 28 |
11 files changed, 178 insertions, 812 deletions
diff --git a/src/mongo/db/exec/document_value/document.cpp b/src/mongo/db/exec/document_value/document.cpp index 7172370e4c3..dd833b3c28f 100644 --- a/src/mongo/db/exec/document_value/document.cpp +++ b/src/mongo/db/exec/document_value/document.cpp @@ -88,11 +88,8 @@ const StringDataSet Document::allMetadataFieldNames{Document::metaFieldTextScore Document::metaFieldGeoNearPoint, Document::metaFieldSearchScore, Document::metaFieldSearchHighlights, - Document::metaFieldSearchSortValues, Document::metaFieldIndexKey, - Document::metaFieldSearchScoreDetails, - Document::metaFieldVectorSearchScore, - Document::metaFieldSearchSequenceToken}; + Document::metaFieldSearchScoreDetails}; DocumentStorageIterator::DocumentStorageIterator(DocumentStorage* storage, BSONObjIterator bsonIt) : _bsonIt(std::move(bsonIt)), @@ -133,7 +130,7 @@ bool DocumentStorageIterator::shouldSkipDeleted() { // If we strip the metadata see if a field name matches the known list. All metadata fields // start with '$' so optimize for a quick bailout. - if (_storage->bsonHasMetadata() && !fieldName.empty() && fieldName[0] == '$' && + if (_storage->stripMetadata() && fieldName[0] == '$' && Document::allMetadataFieldNames.contains(fieldName)) { return true; } @@ -346,8 +343,8 @@ void DocumentStorage::reserveFields(size_t expectedFields) { } intrusive_ptr<DocumentStorage> DocumentStorage::clone() const { - auto out = make_intrusive<DocumentStorage>( - _bson, _bsonHasMetadata, _modified, _numBytesFromBSONInCache); + auto out = + make_intrusive<DocumentStorage>(_bson, _stripMetadata, _modified, _numBytesFromBSONInCache); if (_cache) { // Make a copy of the buffer with the fields. @@ -376,7 +373,6 @@ intrusive_ptr<DocumentStorage> DocumentStorage::clone() const { out->_haveLazyLoadedMetadata = _haveLazyLoadedMetadata; out->_metadataFields = _metadataFields; - out->_snapshottedSize = _snapshottedSize; return out; } @@ -393,12 +389,11 @@ DocumentStorage::~DocumentStorage() { } } -void DocumentStorage::reset(const BSONObj& bson, bool bsonHasMetadata) { +void DocumentStorage::reset(const BSONObj& bson, bool stripMetadata) { _bson = bson; _numBytesFromBSONInCache = 0; - _bsonHasMetadata = bsonHasMetadata; + _stripMetadata = stripMetadata; _modified = false; - _snapshottedSize = 0; // Clean cache. for (auto it = iteratorCacheOnly(); !it.atEnd(); it.advance()) { @@ -414,22 +409,10 @@ void DocumentStorage::reset(const BSONObj& bson, bool bsonHasMetadata) { _metadataFields = DocumentMetadataFields{}; } -Document DocumentStorage::shred() const { - MutableDocument md; - // Iterate raw bson if possible. This avoids caching all of the values in a doc that might get - // thrown away. - if (!isModified() && !bsonHasMetadata()) { - for (const auto& elem : _bson) { - md[elem.fieldNameStringData()] = Value(elem).shred(); - } - } else { - for (DocumentStorageIterator it = iterator(); !it.atEnd(); it.advance()) { - const auto& valueElem = it.get(); - md[it.fieldName()] = valueElem.val.shred(); - } +void DocumentStorage::fillCache() const { + for (DocumentStorageIterator it = iterator(); !it.atEnd(); it.advance()) { + it->val.fillCache(); } - md.setMetadata(DocumentMetadataFields(metadata())); - return md.freeze(); } void DocumentStorage::loadLazyMetadata() const { @@ -437,13 +420,11 @@ void DocumentStorage::loadLazyMetadata() const { return; } - bool oldModified = _metadataFields.isModified(); - BSONObjIterator it(_bson); while (it.more()) { BSONElement elem(it.next()); auto fieldName = elem.fieldNameStringData(); - if (!fieldName.empty() && fieldName[0] == '$') { + if (fieldName[0] == '$') { if (fieldName == Document::metaFieldTextScore) { _metadataFields.setTextScore(elem.Double()); } else if (fieldName == Document::metaFieldSearchScore) { @@ -479,17 +460,10 @@ void DocumentStorage::loadLazyMetadata() const { _metadataFields.setIndexKey(elem.Obj()); } else if (fieldName == Document::metaFieldSearchScoreDetails) { _metadataFields.setSearchScoreDetails(elem.Obj()); - } else if (fieldName == Document::metaFieldSearchSortValues) { - _metadataFields.setSearchSortValues(elem.Obj()); - } else if (fieldName == Document::metaFieldVectorSearchScore) { - _metadataFields.setVectorSearchScore(elem.Double()); - } else if (fieldName == Document::metaFieldSearchSequenceToken) { - _metadataFields.setSearchSequenceToken(Value(elem)); } } } - _metadataFields.setModified(oldModified); _haveLazyLoadedMetadata = true; } @@ -539,11 +513,22 @@ void Document::toBson(BSONObjBuilder* builder, size_t recursionLevel) const { } } +BSONObj Document::toBson() const { + if (!storage().isModified() && !storage().stripMetadata()) { + return storage().bsonObj(); + } + + BSONObjBuilder bb; + toBson(&bb); + return bb.obj(); +} + boost::optional<BSONObj> Document::toBsonIfTriviallyConvertible() const { - if (isTriviallyConvertible()) { + if (!storage().isModified() && !storage().stripMetadata()) { return storage().bsonObj(); + } else { + return boost::none; } - return boost::none; } constexpr StringData Document::metaFieldTextScore; @@ -554,44 +539,35 @@ constexpr StringData Document::metaFieldGeoNearPoint; constexpr StringData Document::metaFieldSearchScore; constexpr StringData Document::metaFieldSearchHighlights; constexpr StringData Document::metaFieldSearchScoreDetails; -constexpr StringData Document::metaFieldSearchSortValues; -constexpr StringData Document::metaFieldVectorSearchScore; -void Document::toBsonWithMetaData(BSONObjBuilder* builder) const { - toBson(builder); +BSONObj Document::toBsonWithMetaData() const { + BSONObjBuilder bb; + toBson(&bb); if (!metadata()) { - return; + return bb.obj(); } if (metadata().hasTextScore()) - builder->append(metaFieldTextScore, metadata().getTextScore()); + bb.append(metaFieldTextScore, metadata().getTextScore()); if (metadata().hasRandVal()) - builder->append(metaFieldRandVal, metadata().getRandVal()); + bb.append(metaFieldRandVal, metadata().getRandVal()); if (metadata().hasSortKey()) - builder->append(metaFieldSortKey, - DocumentMetadataFields::serializeSortKey(metadata().isSingleElementKey(), - metadata().getSortKey())); + bb.append(metaFieldSortKey, + DocumentMetadataFields::serializeSortKey(metadata().isSingleElementKey(), + metadata().getSortKey())); if (metadata().hasGeoNearDistance()) - builder->append(metaFieldGeoNearDistance, metadata().getGeoNearDistance()); + bb.append(metaFieldGeoNearDistance, metadata().getGeoNearDistance()); if (metadata().hasGeoNearPoint()) - metadata().getGeoNearPoint().addToBsonObj(builder, metaFieldGeoNearPoint); + metadata().getGeoNearPoint().addToBsonObj(&bb, metaFieldGeoNearPoint); if (metadata().hasSearchScore()) - builder->append(metaFieldSearchScore, metadata().getSearchScore()); + bb.append(metaFieldSearchScore, metadata().getSearchScore()); if (metadata().hasSearchHighlights()) - metadata().getSearchHighlights().addToBsonObj(builder, metaFieldSearchHighlights); + metadata().getSearchHighlights().addToBsonObj(&bb, metaFieldSearchHighlights); if (metadata().hasIndexKey()) - builder->append(metaFieldIndexKey, metadata().getIndexKey()); + bb.append(metaFieldIndexKey, metadata().getIndexKey()); if (metadata().hasSearchScoreDetails()) - builder->append(metaFieldSearchScoreDetails, metadata().getSearchScoreDetails()); - if (metadata().hasSearchSortValues()) { - builder->append(metaFieldSearchSortValues, metadata().getSearchSortValues()); - } - if (metadata().hasSearchSequenceToken()) { - metadata().getSearchSequenceToken().addToBsonObj(builder, metaFieldSearchSequenceToken); - } - if (metadata().hasVectorSearchScore()) { - builder->append(metaFieldVectorSearchScore, metadata().getVectorSearchScore()); - } + bb.append(metaFieldSearchScoreDetails, metadata().getSearchScoreDetails()); + return bb.obj(); } Document Document::fromBsonWithMetaData(const BSONObj& bson) { @@ -728,17 +704,31 @@ const Value Document::getNestedField(const FieldPath& path, vector<Position>* po return getNestedFieldHelper(*this, path, positions, 0); } -size_t Document::getApproximateSize() const { - return sizeof(Document) + storage().snapshottedApproximateSize(); +size_t Document::getApproximateSizeWithoutBackingBSON() const { + size_t size = sizeof(Document); + if (!_storage) + return size; + + size += sizeof(DocumentStorage); + size += storage().allocatedBytes(); + + for (auto it = storage().iteratorCacheOnly(); !it.atEnd(); it.advance()) { + size += it->val.getApproximateSize(); + size -= sizeof(Value); // already accounted for above + } + + // The metadata also occupies space in the document storage that's pre-allocated. + size += storage().getMetadataApproximateSize(); + + return size; } -size_t Document::getCurrentApproximateSize() const { - return sizeof(Document) + storage().currentApproximateSize(); +size_t Document::getApproximateSize() const { + return getApproximateSizeWithoutBackingBSON() + storage().bsonObjSize(); } size_t Document::memUsageForSorter() const { - return storage().currentApproximateSize() - storage().bsonObjSize() + - storage().nonCachedBsonObjSize(); + return getApproximateSizeWithoutBackingBSON() + storage().nonCachedBsonObjSize(); } void Document::hash_combine(size_t& seed, diff --git a/src/mongo/db/exec/document_value/document.h b/src/mongo/db/exec/document_value/document.h index 6114aee792d..8fcfb28dd8b 100644 --- a/src/mongo/db/exec/document_value/document.h +++ b/src/mongo/db/exec/document_value/document.h @@ -100,10 +100,7 @@ public: static constexpr StringData metaFieldSearchScore = "$searchScore"_sd; static constexpr StringData metaFieldSearchHighlights = "$searchHighlights"_sd; static constexpr StringData metaFieldSearchScoreDetails = "$searchScoreDetails"_sd; - static constexpr StringData metaFieldSearchSortValues = "$searchSortValues"_sd; static constexpr StringData metaFieldIndexKey = "$indexKey"_sd; - static constexpr StringData metaFieldVectorSearchScore = "$vectorSearchScore"_sd; - static constexpr StringData metaFieldSearchSequenceToken = "$searchSequenceToken"_sd; static const StringDataSet allMetadataFieldNames; @@ -194,8 +191,7 @@ public: /** * Get the approximate size of the Document, plus its underlying storage and sub-values. Returns - * size in bytes. The return value of this function is snapshotted. All subsequent calls of this - * method will return the same value. + * size in bytes. * * Note: Some memory may be shared with other Documents or between fields within a single * Document so this can overestimate usage. @@ -206,11 +202,6 @@ public: size_t getApproximateSize() const; /** - * Same as 'getApproximateSize()', but this method re-computes the size on every call. - */ - size_t getCurrentApproximateSize() const; - - /** * Return the approximate amount of space used by metadata. */ size_t getMetadataApproximateSize() const { @@ -248,10 +239,10 @@ public: } /** - * Returns a cache-only copy of the document with no backing bson. + * Populates the internal cache by recursively walking the underlying BSON. */ - Document shred() const { - return storage().shred(); + void fillCache() const { + storage().fillCache(); } /** Calculate a hash value. @@ -262,38 +253,12 @@ public: void hash_combine(size_t& seed, const StringData::ComparatorInterface* stringComparator) const; /** - * Returns true, if this document is trivially convertible to BSON, meaning the underlying - * storage is already in BSON format and there are no damages. - */ - bool isTriviallyConvertible() const { - return !storage().isModified() && !storage().bsonHasMetadata(); - } - - /** - * Returns true, if this document is trivially convertible to BSON with metadata, meaning the - * underlying storage is already in BSON format and there are no damages. - */ - bool isTriviallyConvertibleWithMetadata() const { - return !storage().isModified() && !storage().isMetadataModified(); - } - - /** * Serializes this document to the BSONObj under construction in 'builder'. Metadata is not * included. Throws a AssertionException if 'recursionLevel' exceeds the maximum allowable * depth. */ void toBson(BSONObjBuilder* builder, size_t recursionLevel = 1) const; - - template <typename BSONTraits = BSONObj::DefaultSizeTrait> - BSONObj toBson() const { - if (isTriviallyConvertible()) { - return storage().bsonObj(); - } - - BSONObjBuilder bb; - toBson(&bb); - return bb.obj<BSONTraits>(); - } + BSONObj toBson() const; /** * Serializes this document iff the conversion is "trivial," meaning that the underlying storage @@ -307,18 +272,7 @@ public: /** * Like the 'toBson()' method, but includes metadata as top-level fields. */ - void toBsonWithMetaData(BSONObjBuilder* builder) const; - - template <typename BSONTraits = BSONObj::DefaultSizeTrait> - BSONObj toBsonWithMetaData() const { - if (isTriviallyConvertibleWithMetadata()) { - return storage().bsonObj(); - } - - BSONObjBuilder bb; - toBsonWithMetaData(&bb); - return bb.obj<BSONTraits>(); - } + BSONObj toBsonWithMetaData() const; /** * Like Document(BSONObj) but treats top-level fields with special names as metadata. @@ -416,6 +370,12 @@ private: getNestedFieldNonCachingHelper(const FieldPath& dottedField, size_t level) const; boost::intrusive_ptr<const DocumentStorage> _storage; + + /** + * Returns the approximate size of this `Document` instance without considering the size of its + * backing BSON object. + */ + size_t getApproximateSizeWithoutBackingBSON() const; }; // @@ -551,11 +511,13 @@ public: } /** - * Replace the current base Document with the BSON object. Setting 'bsonHasMetadata' to true - * signals that the BSON object contains metadata fields. + * Replace the current base Document with bson. + * + * The paramater 'stripMetadata' controls whether we strip the metadata fields from the + * underlying bson when converting the document object back to bson. */ - void reset(const BSONObj& bson, bool bsonHasMetadata) { - storage().reset(bson, bsonHasMetadata); + void reset(const BSONObj& bson, bool stripMetadata) { + storage().reset(bson, stripMetadata); } /** Add the given field to the Document. @@ -692,7 +654,6 @@ public: * TODO: there are some optimizations that may make sense at freeze time. */ Document freeze() { - resetSnapshottedApproximateSize(); // This essentially moves _storage into a new Document by way of temp. Document ret; boost::intrusive_ptr<const DocumentStorage> temp(storagePtr(), /*inc_ref_count=*/false); @@ -711,12 +672,8 @@ public: * Note that unlike freeze(), this indicates intention to continue * modifying this document. The returned Document will not observe * future changes to this MutableDocument. - * - * Note that the computed snapshotted approximate size of the Document - * is not preserved across calls. */ Document peek() { - resetSnapshottedApproximateSize(); return Document(storagePtr()); } @@ -738,13 +695,13 @@ public: storage().makeOwned(); } - /** - * Creates a new document storage with the BSON object. Setting 'bsonHasMetadata' to true - * signals that the BSON object contains metadata fields (the complete list is in - * Document::allMetadataFieldNames). + /** Create a new document storage with the BSON object. + * + * The optional paramater 'stripMetadata' controls whether we strip the metadata fields (the + * complete list is in Document::allMetadataFieldNames). */ - DocumentStorage& newStorageWithBson(const BSONObj& bson, bool bsonHasMetadata) { - reset(make_intrusive<DocumentStorage>(bson, bsonHasMetadata, false, 0)); + DocumentStorage& newStorageWithBson(const BSONObj& bson, bool stripMetadata) { + reset(make_intrusive<DocumentStorage>(bson, stripMetadata, false, 0)); return const_cast<DocumentStorage&>(*storagePtr()); } @@ -782,19 +739,12 @@ private: MutableValue getNestedFieldHelper(const FieldPath& dottedField, size_t level); MutableValue getNestedFieldHelper(const std::vector<Position>& positions, size_t level); - // this should only be called by storage methods and peek/freeze/resetsnapshottedApproximateSize + // this should only be called by storage methods and peek/freeze const DocumentStorage* storagePtr() const { dassert(!_storage || typeid(*_storage) == typeid(const DocumentStorage)); return static_cast<const DocumentStorage*>(_storage); } - void resetSnapshottedApproximateSize() { - auto mutableStorage = const_cast<DocumentStorage*>(storagePtr()); - if (mutableStorage) { - mutableStorage->resetSnapshottedApproximateSize(); - } - } - // These are both const to prevent modifications bypassing storage() method. // They always point to NULL or an object with dynamic type DocumentStorage. const RefCountable* _storageHolder; // Only used in constructors and destructor diff --git a/src/mongo/db/exec/document_value/document_internal.h b/src/mongo/db/exec/document_value/document_internal.h index d7fb3b337d6..1387ca908b4 100644 --- a/src/mongo/db/exec/document_value/document_internal.h +++ b/src/mongo/db/exec/document_value/document_internal.h @@ -359,11 +359,11 @@ public: /** * Construct a storage from the BSON. The BSON is lazily processed as fields are requested from - * the document. If we know that the BSON contains metadata fields we can set the - * 'bsonHasMetadata' flag to true. + * the document. If we know that the BSON does not contain any metadata fields we can set the + * 'stripMetadata' flag to false that will speed up the field iteration. */ DocumentStorage(const BSONObj& bson, - bool bsonHasMetadata, + bool stripMetadata, bool modified, uint32_t numBytesFromBSONInCache) : _cache(nullptr), @@ -373,17 +373,17 @@ public: _hashTabMask(0), _bson(bson), _numBytesFromBSONInCache(numBytesFromBSONInCache), - _bsonHasMetadata(bsonHasMetadata), + _stripMetadata(stripMetadata), _modified(modified) {} ~DocumentStorage(); - void reset(const BSONObj& bson, bool bsonHasMetadata); + void reset(const BSONObj& bson, bool stripMetadata); /** - * Returns a cache-only copy of the document with no backing bson. + * Populates the cache by recursively walking the underlying BSON. */ - Document shred() const; + void fillCache() const; static const DocumentStorage& emptyDoc() { return kEmptyDoc; @@ -551,7 +551,7 @@ public: * WorkingSetMember. */ const DocumentMetadataFields& metadata() const { - if (_bsonHasMetadata) { + if (_stripMetadata) { loadLazyMetadata(); } return _metadataFields; @@ -589,8 +589,8 @@ public: return _firstElement ? _firstElement->plusBytes(_usedBytes) : nullptr; } - auto bsonHasMetadata() const { - return _bsonHasMetadata; + auto stripMetadata() const { + return _stripMetadata; } Position constructInCache(const BSONElement& elem); @@ -599,37 +599,10 @@ public: return _modified; } - auto isMetadataModified() const { - return _metadataFields.isModified(); - } - auto bsonObj() const { return _bson; } - size_t currentApproximateSize() const { - size_t size = sizeof(DocumentStorage) + allocatedBytes() + getMetadataApproximateSize() + - bsonObjSize(); - - for (auto it = iteratorCacheOnly(); !it.atEnd(); it.advance()) { - size += it->val.getApproximateSize() - sizeof(Value); - } - - return size; - } - - size_t snapshottedApproximateSize() const { - if (_snapshottedSize == 0) { - const_cast<DocumentStorage*>(this)->_snapshottedSize = currentApproximateSize(); - } - - return _snapshottedSize; - } - - void resetSnapshottedApproximateSize() { - _snapshottedSize = 0; - } - private: /// Returns the position of the named field in the cache or Position() template <typename T> @@ -712,23 +685,22 @@ private: // whole backing BSON, but only the portion of backing BSON that's not already in the cache. uint32_t _numBytesFromBSONInCache = 0; - // Tracks whether or not the metadata has been lazy-loaded from the backing '_bson' object. If - // so, then no attempt will be made to load the metadata again, even if the metadata has been - // released by a call to 'releaseMetadata()'. + // If '_stripMetadata' is true, tracks whether or not the metadata has been lazy-loaded from the + // backing '_bson' object. If so, then no attempt will be made to load the metadata again, even + // if the metadata has been released by a call to 'releaseMetadata()'. mutable bool _haveLazyLoadedMetadata = false; mutable DocumentMetadataFields _metadataFields; - // True if this storage was constructed from BSON with metadata. Serializing this object using - // the 'toBson()' method will omit (strip) the metadata fields. - bool _bsonHasMetadata{false}; + // 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 + // have any metadata we can set _stripMetadata to false that will speed up the iteration. + bool _stripMetadata{false}; // This flag is set to true anytime the storage returns a mutable field. It is used to optimize // a conversion to BSON; i.e. if there are not any modifications we can directly return _bson. bool _modified{false}; - size_t _snapshottedSize{0}; - // Defined in document.cpp static const DocumentStorage kEmptyDoc; diff --git a/src/mongo/db/exec/document_value/document_metadata_fields.cpp b/src/mongo/db/exec/document_value/document_metadata_fields.cpp index 27f64ef7286..90e15ef5c2a 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields.cpp +++ b/src/mongo/db/exec/document_value/document_metadata_fields.cpp @@ -46,7 +46,6 @@ DocumentMetadataFields::DocumentMetadataFields(const DocumentMetadataFields& oth DocumentMetadataFields& DocumentMetadataFields::operator=(const DocumentMetadataFields& other) { _holder = other._holder ? std::make_unique<MetadataHolder>(*other._holder) : nullptr; - _modified = true; return *this; } @@ -55,7 +54,6 @@ DocumentMetadataFields::DocumentMetadataFields(DocumentMetadataFields&& other) DocumentMetadataFields& DocumentMetadataFields::operator=(DocumentMetadataFields&& other) { _holder = std::move(other._holder); - _modified = true; return *this; } @@ -87,21 +85,12 @@ void DocumentMetadataFields::mergeWith(const DocumentMetadataFields& other) { if (!hasSearchScoreDetails() && other.hasSearchScoreDetails()) { setSearchScoreDetails(other.getSearchScoreDetails()); } - if (!hasSearchSequenceToken() && other.hasSearchSequenceToken()) { - setSearchSequenceToken(other.getSearchSequenceToken()); - } if (!hasTimeseriesBucketMinTime() && other.hasTimeseriesBucketMinTime()) { setTimeseriesBucketMinTime(other.getTimeseriesBucketMinTime()); } if (!hasTimeseriesBucketMaxTime() && other.hasTimeseriesBucketMaxTime()) { setTimeseriesBucketMaxTime(other.getTimeseriesBucketMaxTime()); } - if (!hasSearchSortValues() && other.hasSearchSortValues()) { - setSearchSortValues(other.getSearchSortValues()); - } - if (!hasVectorSearchScore() && other.hasVectorSearchScore()) { - setVectorSearchScore(other.getVectorSearchScore()); - } } void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) { @@ -132,21 +121,12 @@ void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) { if (other.hasSearchScoreDetails()) { setSearchScoreDetails(other.getSearchScoreDetails()); } - if (other.hasSearchSequenceToken()) { - setSearchSequenceToken(other.getSearchSequenceToken()); - } if (other.hasTimeseriesBucketMinTime()) { setTimeseriesBucketMinTime(other.getTimeseriesBucketMinTime()); } if (other.hasTimeseriesBucketMaxTime()) { setTimeseriesBucketMaxTime(other.getTimeseriesBucketMaxTime()); } - if (other.hasSearchSortValues()) { - setSearchSortValues(other.getSearchSortValues()); - } - if (other.hasVectorSearchScore()) { - setVectorSearchScore(other.getVectorSearchScore()); - } } size_t DocumentMetadataFields::getApproximateSize() const { @@ -168,8 +148,7 @@ size_t DocumentMetadataFields::getApproximateSize() const { size -= sizeof(_holder->searchHighlights); size += _holder->indexKey.objsize(); size += _holder->searchScoreDetails.objsize(); - size += _holder->searchSortValues.objsize(); - size -= sizeof(_holder->searchSequenceToken); + return size; } @@ -217,10 +196,6 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const { buf.appendNum(static_cast<char>(MetaType::kSearchScoreDetails + 1)); getSearchScoreDetails().appendSelfToBufBuilder(buf); } - if (hasSearchSequenceToken()) { - buf.appendNum(static_cast<char>(MetaType::kSearchSequenceToken + 1)); - getSearchSequenceToken().serializeForSorter(buf); - } if (hasTimeseriesBucketMinTime()) { buf.appendNum(static_cast<char>(MetaType::kTimeseriesBucketMinTime + 1)); buf.appendNum(getTimeseriesBucketMinTime().toMillisSinceEpoch()); @@ -229,14 +204,6 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const { buf.appendNum(static_cast<char>(MetaType::kTimeseriesBucketMaxTime + 1)); buf.appendNum(getTimeseriesBucketMaxTime().toMillisSinceEpoch()); } - if (hasSearchSortValues()) { - buf.appendNum(static_cast<char>(MetaType::kSearchSortValues + 1)); - getSearchSortValues().appendSelfToBufBuilder(buf); - } - if (hasVectorSearchScore()) { - buf.appendNum(static_cast<char>(MetaType::kVectorSearchScore + 1)); - buf.appendNum(getVectorSearchScore()); - } buf.appendNum(static_cast<char>(0)); } @@ -269,19 +236,9 @@ void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetada out->setSearchScoreDetails( BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings())); } else if (marker == static_cast<char>(MetaType::kTimeseriesBucketMinTime) + 1) { - out->setTimeseriesBucketMinTime( - Date_t::fromMillisSinceEpoch(buf.read<LittleEndian<long long>>())); + out->setTimeseriesBucketMinTime(Date_t::fromMillisSinceEpoch(buf.read<long long>())); } else if (marker == static_cast<char>(MetaType::kTimeseriesBucketMaxTime) + 1) { - out->setTimeseriesBucketMaxTime( - Date_t::fromMillisSinceEpoch(buf.read<LittleEndian<long long>>())); - } else if (marker == static_cast<char>(MetaType::kSearchSortValues) + 1) { - out->setSearchSortValues( - BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings())); - } else if (marker == static_cast<char>(MetaType::kVectorSearchScore) + 1) { - out->setVectorSearchScore(buf.read<LittleEndian<double>>()); - } else if (marker == static_cast<char>(MetaType::kSearchSequenceToken) + 1) { - out->setSearchSequenceToken( - Value::deserializeForSorter(buf, Value::SorterDeserializeSettings())); + out->setTimeseriesBucketMaxTime(Date_t::fromMillisSinceEpoch(buf.read<long long>())); } else { uasserted(28744, "Unrecognized marker, unable to deserialize buffer"); } @@ -341,12 +298,6 @@ const char* DocumentMetadataFields::typeNameToDebugString(DocumentMetadataFields return "timeseries bucket min time"; case DocumentMetadataFields::kTimeseriesBucketMaxTime: return "timeseries bucket max time"; - case DocumentMetadataFields::kSearchSortValues: - return "$search sort values"; - case DocumentMetadataFields::kSearchSequenceToken: - return "$search sequence token"; - case DocumentMetadataFields::kVectorSearchScore: - return "$vectorSearch distance"; default: MONGO_UNREACHABLE; } diff --git a/src/mongo/db/exec/document_value/document_metadata_fields.h b/src/mongo/db/exec/document_value/document_metadata_fields.h index 6759675c7ea..12932c29686 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields.h +++ b/src/mongo/db/exec/document_value/document_metadata_fields.h @@ -67,9 +67,6 @@ public: kSearchScoreDetails, kTimeseriesBucketMinTime, kTimeseriesBucketMaxTime, - kSearchSortValues, - kVectorSearchScore, - kSearchSequenceToken, // New fields must be added before the kNumFields sentinel. kNumFields @@ -151,7 +148,11 @@ public: } void setTextScore(double score) { - _setCommon(MetaType::kTextScore); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kTextScore); _holder->textScore = score; } @@ -165,7 +166,11 @@ public: } void setRandVal(double val) { - _setCommon(MetaType::kRandVal); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kRandVal); _holder->randVal = val; } @@ -179,7 +184,11 @@ public: } void setSortKey(Value sortKey, bool isSingleElementKey) { - _setCommon(MetaType::kSortKey); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kSortKey); _holder->isSingleElementKey = isSingleElementKey; _holder->sortKey = std::move(sortKey); } @@ -198,7 +207,11 @@ public: } void setGeoNearDistance(double dist) { - _setCommon(MetaType::kGeoNearDist); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kGeoNearDist); _holder->geoNearDistance = dist; } @@ -212,7 +225,11 @@ public: } void setGeoNearPoint(Value point) { - _setCommon(MetaType::kGeoNearPoint); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kGeoNearPoint); _holder->geoNearPoint = std::move(point); } @@ -226,7 +243,11 @@ public: } void setSearchScore(double score) { - _setCommon(MetaType::kSearchScore); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kSearchScore); _holder->searchScore = score; } @@ -240,7 +261,11 @@ public: } void setSearchHighlights(Value highlights) { - _setCommon(MetaType::kSearchHighlights); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kSearchHighlights); _holder->searchHighlights = highlights; } @@ -254,7 +279,11 @@ public: } void setIndexKey(BSONObj indexKey) { - _setCommon(MetaType::kIndexKey); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kIndexKey); _holder->indexKey = indexKey.getOwned(); } @@ -268,7 +297,11 @@ public: } void setRecordId(RecordId rid) { - _setCommon(MetaType::kRecordId); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + + _holder->metaFields.set(MetaType::kRecordId); _holder->recordId = rid; } @@ -282,7 +315,10 @@ public: } void setSearchScoreDetails(BSONObj details) { - _setCommon(MetaType::kSearchScoreDetails); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + _holder->metaFields.set(MetaType::kSearchScoreDetails); _holder->searchScoreDetails = details.getOwned(); } @@ -291,14 +327,15 @@ public: } Date_t getTimeseriesBucketMinTime() const { - tassert(6850100, - "Document must have timeseries bucket min time metadata field set", - hasTimeseriesBucketMinTime()); + invariant(hasTimeseriesBucketMinTime()); return _holder->timeseriesBucketMinTime; } void setTimeseriesBucketMinTime(Date_t time) { - _setCommon(MetaType::kTimeseriesBucketMinTime); + if (!_holder) { + _holder = std::make_unique<MetadataHolder>(); + } + _holder->metaFields.set(MetaType::kTimeseriesBucketMinTime); _holder->timeseriesBucketMinTime = time; } @@ -307,83 +344,20 @@ public: } Date_t getTimeseriesBucketMaxTime() const { - tassert(6850101, - "Document must have timeseries bucket max time metadata field set", - hasTimeseriesBucketMaxTime()); + invariant(hasTimeseriesBucketMaxTime()); return _holder->timeseriesBucketMaxTime; } void setTimeseriesBucketMaxTime(Date_t time) { - _setCommon(MetaType::kTimeseriesBucketMaxTime); - _holder->timeseriesBucketMaxTime = time; - } - - bool hasSearchSortValues() const { - return _holder && _holder->metaFields.test(MetaType::kSearchSortValues); - } - - BSONObj getSearchSortValues() const { - tassert(7320401, "Document must have $searchSortValues set", hasSearchSortValues()); - return _holder->searchSortValues; - } - - void setSearchSortValues(BSONObj vals) { - _setCommon(MetaType::kSearchSortValues); - _holder->searchSortValues = vals.getOwned(); - } - - bool hasVectorSearchScore() const { - return _holder && _holder->metaFields.test(MetaType::kVectorSearchScore); - } - - double getVectorSearchScore() const { - tassert(7828400, "vectorSearchScore must be present in metadata", hasVectorSearchScore()); - return _holder->vectorSearchScore; - } - - void setVectorSearchScore(double vectorSearchScore) { - _setCommon(MetaType::kVectorSearchScore); - _holder->vectorSearchScore = vectorSearchScore; - } - - bool hasSearchSequenceToken() const { - return _holder && _holder->metaFields.test(MetaType::kSearchSequenceToken); - } - - Value getSearchSequenceToken() const { - invariant(hasSearchSequenceToken()); - return _holder->searchSequenceToken; - } - - void setSearchSequenceToken(Value details) { - _setCommon(MetaType::kSearchSequenceToken); - _holder->searchSequenceToken = details; - } - - void serializeForSorter(BufBuilder& buf) const; - - bool isModified() const { - return _modified; - } - - /** - * Sets the 'modified' flag to the given value. Necessary for implementing a lazy load - * optimization for the contained mutable 'DocumentMetadataFields' instance inside the - * 'Document' class. - */ - void setModified(bool newValue) { - _modified = newValue; - } - -private: - inline void _setCommon(MetaType mt) { if (!_holder) { _holder = std::make_unique<MetadataHolder>(); } - _holder->metaFields.set(mt); - _modified = true; + _holder->metaFields.set(MetaType::kTimeseriesBucketMaxTime); + _holder->timeseriesBucketMaxTime = time; } + void serializeForSorter(BufBuilder& buf) const; +private: // A simple data struct housing all possible metadata fields. struct MetadataHolder { std::bitset<MetaType::kNumFields> metaFields; @@ -405,18 +379,10 @@ private: BSONObj searchScoreDetails; Date_t timeseriesBucketMinTime; Date_t timeseriesBucketMaxTime; - BSONObj searchSortValues; - double vectorSearchScore{0.0}; - Value searchSequenceToken; }; // Null until the first setter is called, at which point a MetadataHolder struct is allocated. std::unique_ptr<MetadataHolder> _holder; - - // This flag is set to true anytime a 'DocumentMetadataFields' instance is modified. It is used - // to optimize document conversion to BSON with metadata; i.e. if there are no modifications we - // can directly return the underlying BSON. - bool _modified{false}; }; using QueryMetadataBitSet = std::bitset<DocumentMetadataFields::MetaType::kNumFields>; diff --git a/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp b/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp index 23ab2277c96..5515009b2bd 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp +++ b/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp @@ -34,7 +34,6 @@ #include "mongo/db/exec/document_value/document_metadata_fields.h" #include "mongo/db/exec/document_value/document_value_test_util.h" #include "mongo/unittest/bson_test_util.h" -#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -51,8 +50,6 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { metadata.setIndexKey(BSON("b" << 1)); metadata.setSearchScoreDetails(BSON("scoreDetails" << "foo")); - metadata.setSearchSortValues(BSON("a" << 1)); - metadata.setVectorSearchScore(7.6); BufBuilder builder; metadata.serializeForSorter(builder); @@ -72,8 +69,6 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { ASSERT_BSONOBJ_EQ(deserialized.getSearchScoreDetails(), BSON("scoreDetails" << "foo")); - ASSERT_BSONOBJ_EQ(deserialized.getSearchSortValues(), BSON("a" << 1)); - ASSERT_EQ(deserialized.getVectorSearchScore(), 7.6); } TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) { @@ -88,8 +83,6 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) { ASSERT_FALSE(metadata.hasSearchHighlights()); ASSERT_FALSE(metadata.hasIndexKey()); ASSERT_FALSE(metadata.hasSearchScoreDetails()); - ASSERT_FALSE(metadata.hasSearchSortValues()); - ASSERT_FALSE(metadata.hasVectorSearchScore()); } TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) { @@ -132,14 +125,6 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) { metadata.setSearchScoreDetails(BSON("scoreDetails" << "foo")); ASSERT_TRUE(metadata.hasSearchScoreDetails()); - - ASSERT_FALSE(metadata.hasSearchSortValues()); - metadata.setSearchSortValues(BSON("a" << 1)); - ASSERT_TRUE(metadata.hasSearchSortValues()); - - ASSERT_FALSE(metadata.hasVectorSearchScore()); - metadata.setVectorSearchScore(7.6); - ASSERT_TRUE(metadata.hasVectorSearchScore()); } TEST(DocumentMetadataFieldsTest, MoveConstructor) { @@ -154,8 +139,6 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { metadata.setIndexKey(BSON("b" << 1)); metadata.setSearchScoreDetails(BSON("scoreDetails" << "foo")); - metadata.setSearchSortValues(BSON("a" << 1)); - metadata.setVectorSearchScore(7.6); DocumentMetadataFields moveConstructed(std::move(metadata)); ASSERT_TRUE(moveConstructed); @@ -171,8 +154,6 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { ASSERT_BSONOBJ_EQ(moveConstructed.getSearchScoreDetails(), BSON("scoreDetails" << "foo")); - ASSERT_BSONOBJ_EQ(moveConstructed.getSearchSortValues(), BSON("a" << 1)); - ASSERT_EQ(moveConstructed.getVectorSearchScore(), 7.6); ASSERT_FALSE(metadata); // NOLINT(bugprone-use-after-move) } @@ -189,8 +170,6 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { metadata.setIndexKey(BSON("b" << 1)); metadata.setSearchScoreDetails(BSON("scoreDetails" << "foo")); - metadata.setSearchSortValues(BSON("a" << 1)); - metadata.setVectorSearchScore(7.6); DocumentMetadataFields moveAssigned; moveAssigned.setTextScore(12.3); @@ -209,8 +188,6 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { ASSERT_BSONOBJ_EQ(moveAssigned.getSearchScoreDetails(), BSON("scoreDetails" << "foo")); - ASSERT_BSONOBJ_EQ(moveAssigned.getSearchSortValues(), BSON("a" << 1)); - ASSERT_EQ(moveAssigned.getVectorSearchScore(), 7.6); ASSERT_FALSE(metadata); // NOLINT(bugprone-use-after-move) } @@ -264,8 +241,6 @@ TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesN ASSERT_FALSE(destination.hasSearchHighlights()); ASSERT_FALSE(destination.hasIndexKey()); ASSERT_FALSE(destination.hasSearchScoreDetails()); - ASSERT_FALSE(destination.hasSearchSortValues()); - ASSERT_FALSE(destination.hasVectorSearchScore()); } TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) { @@ -291,173 +266,6 @@ TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) { ASSERT_FALSE(destination.hasSearchHighlights()); ASSERT_FALSE(destination.hasIndexKey()); ASSERT_FALSE(destination.hasSearchScoreDetails()); - ASSERT_FALSE(destination.hasSearchSortValues()); - ASSERT_FALSE(destination.hasVectorSearchScore()); -} - -TEST(DocumentMetadataFieldsTest, GetTimeseriesBucketMinTimeExists) { - DocumentMetadataFields source; - Date_t time; - source.setTimeseriesBucketMinTime(time); - ASSERT_EQ(source.getTimeseriesBucketMinTime(), time); -} - -TEST(DocumentMetadataFieldsTest, GetTimeseriesBucketMaxTimeExists) { - DocumentMetadataFields source; - Date_t time; - source.setTimeseriesBucketMaxTime(time); - ASSERT_EQ(source.getTimeseriesBucketMaxTime(), time); -} - -TEST(DocumentMetadataFieldsTest, MetadataIsMarkedModifiedOnSetMetadataField) { - // Test setting metadata fields directly. - - auto testFieldSetter = [](std::function<void(DocumentMetadataFields&)> invokeSetter) { - DocumentMetadataFields metadata; - ASSERT_FALSE(metadata.isModified()); - invokeSetter(metadata); - ASSERT_TRUE(metadata.isModified()); - }; - - testFieldSetter([](DocumentMetadataFields& md) { md.setTextScore(10.0); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setRandVal(20.0); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setSortKey(Value(30), true); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setGeoNearDistance(40.0); }); - testFieldSetter( - [](DocumentMetadataFields& md) { md.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)}); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setSearchScore(50.0); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setSearchHighlights(Value{"foo"_sd}); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setIndexKey(BSON("b" << 1)); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setRecordId(RecordId{6}); }); - testFieldSetter([](DocumentMetadataFields& md) { - md.setSearchScoreDetails(BSON("scoreDetails" - << "foo")); - }); - testFieldSetter([](DocumentMetadataFields& md) { md.setTimeseriesBucketMinTime(Date_t()); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setTimeseriesBucketMaxTime(Date_t()); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setSearchSortValues(BSON("a" << 1)); }); - testFieldSetter([](DocumentMetadataFields& md) { md.setVectorSearchScore(60.0); }); -} - -TEST(DocumentMetadataFieldsTest, MetadataIsConstructedUnmodified) { - // We consider new instances as unmodified for all constructors (default, copy, move) even when - // the original metadata has the 'modified' flag set. - - // Testing the default constructor. - DocumentMetadataFields metadata1; - ASSERT_FALSE(metadata1.isModified()); - metadata1.setTextScore(10.0); - ASSERT_TRUE(metadata1.isModified()); - - // Testing the copy-constructor. - DocumentMetadataFields metadata2(metadata1); - ASSERT_FALSE(metadata2.isModified()); - - // Testing the move-constructor. - DocumentMetadataFields metadata3(std::move(metadata1)); - ASSERT_FALSE(metadata3.isModified()); -} - -TEST(DocumentMetadataFieldsTest, CopyAssignmentIsModification) { - // We consider copy-assignment as modification even when the instances are equal and the copied - // metadata does not have the 'modified' flag set. - - DocumentMetadataFields metadata1; - ASSERT_FALSE(metadata1.isModified()); - - DocumentMetadataFields metadata2; - ASSERT_FALSE(metadata2.isModified()); - - // Testing copy-assignment to empty object (metadata2 == metadata1). - metadata2 = metadata1; - ASSERT_TRUE(metadata2.isModified()); - - // Testing copy-assignment in a more common case (metadata1 is empty, metadata2 is not empty). - metadata2.setTextScore(10.0); - metadata2.setModified(false); - ASSERT_FALSE(metadata2.isModified()); - metadata1 = metadata2; - ASSERT_TRUE(metadata1.isModified()); - ASSERT_EQ(metadata2.getTextScore(), metadata1.getTextScore()); -} - -TEST(DocumentMetadataFieldsTest, MoveAssignmentIsModification) { - // We consider move-assignment as modification even when the instances are equal and the moved - // metadata does not have the 'modified' flag set. - - DocumentMetadataFields metadata1; - ASSERT_FALSE(metadata1.isModified()); - - DocumentMetadataFields metadata2; - ASSERT_FALSE(metadata2.isModified()); - - // Testing move-assignment to empty object (metadata2 == metadata1). - metadata2 = std::move(metadata1); - ASSERT_TRUE(metadata2.isModified()); - - // Testing move-assignment in a more common case (metadata3 is empty, metadata2 is not empty). - metadata2.setTextScore(10.0); - metadata2.setModified(false); - ASSERT_FALSE(metadata2.isModified()); - - DocumentMetadataFields metadata3; - ASSERT_FALSE(metadata3.isModified()); - - metadata3 = std::move(metadata2); - ASSERT_TRUE(metadata3.isModified()); - ASSERT_EQ(10.0, metadata3.getTextScore()); -} - -TEST(DocumentMetadataFieldsTest, MetadataIsMarkedModifiedOnCopyFrom) { - DocumentMetadataFields metadata1; - metadata1.setRandVal(20.0); - ASSERT_TRUE(metadata1.isModified()); - - // Testing 'setModified(false)'. - metadata1.setModified(false); - ASSERT_FALSE(metadata1.isModified()); - - // Calling 'copyFrom(metadata1)' modifies metadata2 even when metadata2 == metadata1 and - // metadata1 is not marked as modified. - DocumentMetadataFields metadata2(metadata1); - ASSERT_FALSE(metadata2.isModified()); - metadata2.copyFrom(metadata1); - ASSERT_TRUE(metadata2.isModified()); -} - -TEST(DocumentMetadataFieldsTest, MetadataIsMarkedModifiedOnMergeWith) { - DocumentMetadataFields metadata1; - metadata1.setRandVal(20.0); - ASSERT_TRUE(metadata1.isModified()); - - metadata1.setModified(false); - ASSERT_FALSE(metadata1.isModified()); - - // Calling 'mergeWith(metadata1)' modifies metadata2 only when metadata1 has fields not set in - // metadata1. - DocumentMetadataFields metadata2(metadata1); - ASSERT_FALSE(metadata2.isModified()); - metadata2.mergeWith(metadata1); - ASSERT_FALSE(metadata2.isModified()); - - DocumentMetadataFields metadata3; - ASSERT_FALSE(metadata3.isModified()); - metadata3.mergeWith(metadata2); - ASSERT_TRUE(metadata3.isModified()); -} - -DEATH_TEST_REGEX(DocumentMetadataFieldsTest, - GetTimeseriesBucketMinTimeDoesntExist, - "Tripwire assertion.*6850100") { - DocumentMetadataFields source; - source.getTimeseriesBucketMinTime(); -} - -DEATH_TEST_REGEX(DocumentMetadataFieldsTest, - GetTimeseriesBucketMaxTimeDoesntExist, - "Tripwire assertion.*6850101") { - DocumentMetadataFields source; - source.getTimeseriesBucketMaxTime(); } } // namespace mongo diff --git a/src/mongo/db/exec/document_value/document_value_test.cpp b/src/mongo/db/exec/document_value/document_value_test.cpp index ca1123a7555..421da707001 100644 --- a/src/mongo/db/exec/document_value/document_value_test.cpp +++ b/src/mongo/db/exec/document_value/document_value_test.cpp @@ -355,89 +355,6 @@ TEST(DocumentGetFieldNonCaching, TraverseArray) { checkArrayTagIsReturned(); } -TEST(DocumentSize, ApproximateSizeIsSnapshotted) { - const auto rawBson = BSON("field" - << "value"); - const Document document{rawBson}; - const auto noCacheSize = document.getApproximateSize(); - - // Force the cache construction, making the total size of the 'Document' bigger. - // 'getApproximateSize()' must still return the same value. - document["field"]; - const auto fullCacheSizeSnapshot = document.getApproximateSize(); - const auto fullCacheSizeCurrent = document.getCurrentApproximateSize(); - ASSERT_EQ(noCacheSize, fullCacheSizeSnapshot); - ASSERT_LT(noCacheSize, fullCacheSizeCurrent); -} - -TEST(DocumentSize, ApproximateSizeDuringBuildIsUpdated) { - MutableDocument builder; - builder.addField("a1", Value(1)); - builder.addField("a2", mongo::Value(2)); - builder.addField("a3", mongo::Value(3)); - auto middleBuildSize = builder.getApproximateSize(); - - builder.addField("a4", Value(4)); - builder.addField("a5", mongo::Value(5)); - builder.addField("a6", mongo::Value(6)); - auto peekSize = builder.peek().getApproximateSize(); - - builder.addField("a7", Value(7)); - builder.addField("a8", mongo::Value(8)); - builder.addField("a9", mongo::Value(9)); - auto beforeFreezeSize = builder.getApproximateSize(); - - Document result = builder.freeze(); - auto frozenSize = result.getApproximateSize(); - - ASSERT_LT(middleBuildSize, peekSize); - ASSERT_LT(peekSize, beforeFreezeSize); - ASSERT_EQ(beforeFreezeSize, frozenSize); -} - -TEST(ShredDocument, OutputHasNoBackingBSON) { - BSONObj bson = - BSON("a" << 1 << "subObj" << BSON("a" << 1) << "subArray" << BSON_ARRAY(BSON("a" << 1))); - auto original = fromBson(bson); - auto originalSize = original.getApproximateSize(); - - auto shredded = original.shred(); - auto originalSizeAfterShredding = original.getApproximateSize(); - // Fields in the original doc shouldn't be cached since it was raw bson - ASSERT_EQ(originalSize, originalSizeAfterShredding); - - // BSON is more compact than ValueElement - auto shreddedSize = shredded.getApproximateSize(); - ASSERT_LT(originalSize, shreddedSize); - - // Accessing a field shouldn't change the size since all fields are already cached. - shredded["a"]; - ASSERT_EQ(shredded.getCurrentApproximateSize(), shreddedSize); -} - -TEST(ShredDocument, HandlesModifiedDocuments) { - BSONObj bson = BSON("a" << 1 << "subObj" << BSON("a" << 1)); - Document original = fromBson(bson); - MutableDocument md(original); - md["b"] = Value(2); - md["subObj"]["b"] = Value(2); - Document shredded = md.freeze().shred(); - - ASSERT(!shredded["b"].missing()); - ASSERT(!shredded["subObj"]["b"].missing()); -} - -TEST(ShredDocument, HandlesMetadata) { - BSONObj bson = BSON("a" << 1 << "subObj" << BSON("a" << 1)); - Document original = fromBson(bson); - MutableDocument md(original); - DocumentMetadataFields meta; - meta.setSearchScore(6); - md.setMetadata(std::move(meta)); - Document shredded = md.freeze().shred(); - ASSERT_EQ(6, shredded.metadata().getSearchScore()); -} - /** Add Document fields. */ class AddField { public: @@ -788,22 +705,6 @@ public: BSONObjBuilder objBuilder; BSONArrayBuilder arrBuilder; }; - -TEST(DocumentTest, ToBsonSizeTraits) { - constexpr size_t longStringLength = 9 * 1024 * 1024; - static_assert(longStringLength <= BSONObjMaxInternalSize && - 2 * longStringLength > BSONObjMaxInternalSize && - 2 * longStringLength <= BufferMaxSize); - std::string longString(longStringLength, 'A'); - MutableDocument md; - md.addField("a", Value(longString)); - ASSERT_DOES_NOT_THROW(md.peek().toBson()); - md.addField("b", Value(longString)); - ASSERT_THROWS_CODE(md.peek().toBson(), DBException, ErrorCodes::BSONObjectTooLarge); - ASSERT_THROWS_CODE( - md.peek().toBson<BSONObj::DefaultSizeTrait>(), DBException, ErrorCodes::BSONObjectTooLarge); - ASSERT_DOES_NOT_THROW(md.peek().toBson<BSONObj::LargeSizeTrait>()); -} } // namespace Document namespace MetaFields { @@ -930,15 +831,6 @@ TEST(MetaFields, FromBsonWithMetadataAcceptsIndexKeyMetadata) { ASSERT_BSONOBJ_EQ(bsonWithoutMetadata, BSON("a" << 1)); } -TEST(MetaFields, FromBsonWithMetadataHandlesEmptyFieldName) { - auto bson = BSON("" << 1 << "$indexKey" << BSON("b" << 1)); - auto doc = Document::fromBsonWithMetaData(bson); - ASSERT_TRUE(doc.metadata().hasIndexKey()); - ASSERT_BSONOBJ_EQ(doc.metadata().getIndexKey(), BSON("b" << 1)); - auto bsonWithoutMetadata = doc.toBson(); - ASSERT_BSONOBJ_EQ(bsonWithoutMetadata, BSON("" << 1)); -} - TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) { Document source = Document::fromBsonWithMetaData( BSON("a" << 1 << "$textScore" << 9.9 << "b" << 1 << "$randVal" << 42.0 << "c" << 1 @@ -948,8 +840,7 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) { << "foo" << "h" << 1 << "$indexKey" << BSON("y" << 1) << "$searchScoreDetails" << BSON("scoreDetails" - << "foo") - << "$searchSortValues" << BSON("a" << 1) << "$vectorSearchScore" << 6.7)); + << "foo"))); MutableDocument destination{}; destination.copyMetaDataFrom(source); @@ -966,8 +857,6 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) { ASSERT_BSONOBJ_EQ(result.metadata().getSearchScoreDetails(), BSON("scoreDetails" << "foo")); - ASSERT_BSONOBJ_EQ(result.metadata().getSearchSortValues(), BSON("a" << 1)); - ASSERT_EQ(result.metadata().getVectorSearchScore(), 6.7); } class SerializationTest : public unittest::Test { @@ -988,8 +877,6 @@ protected: ASSERT_EQ(output.metadata().hasSearchScore(), input.metadata().hasSearchScore()); ASSERT_EQ(output.metadata().hasSearchHighlights(), input.metadata().hasSearchHighlights()); ASSERT_EQ(output.metadata().hasIndexKey(), input.metadata().hasIndexKey()); - ASSERT_EQ(output.metadata().hasVectorSearchScore(), - input.metadata().hasVectorSearchScore()); if (input.metadata().hasTextScore()) { ASSERT_EQ(output.metadata().getTextScore(), input.metadata().getTextScore()); } @@ -1010,10 +897,6 @@ protected: ASSERT_BSONOBJ_EQ(output.metadata().getSearchScoreDetails(), input.metadata().getSearchScoreDetails()); } - if (input.metadata().hasVectorSearchScore()) { - ASSERT_EQ(output.metadata().getVectorSearchScore(), - input.metadata().getVectorSearchScore()); - } ASSERT(output.toBson().binaryEqual(input.toBson())); } @@ -1028,7 +911,6 @@ TEST_F(SerializationTest, MetaSerializationNoVals) { << "def"_sd)); docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails" << "foo")); - docBuilder.metadata().setVectorSearchScore(40.0); assertRoundTrips(docBuilder.freeze()); } @@ -1043,7 +925,6 @@ TEST_F(SerializationTest, MetaSerializationWithVals) { docBuilder.metadata().setIndexKey(BSON("key" << 42)); docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails" << "foo")); - docBuilder.metadata().setVectorSearchScore(40.0); assertRoundTrips(docBuilder.freeze()); } @@ -1066,8 +947,6 @@ TEST(MetaFields, ToAndFromBson) { << "def"_sd)); docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails" << "foo")); - docBuilder.metadata().setSearchSortValues(BSON("a" << 42)); - docBuilder.metadata().setVectorSearchScore(40.0); Document doc = docBuilder.freeze(); BSONObj obj = doc.toBsonWithMetaData(); ASSERT_EQ(10.0, obj[Document::metaFieldTextScore].Double()); @@ -1079,8 +958,6 @@ TEST(MetaFields, ToAndFromBson) { ASSERT_BSONOBJ_EQ(obj[Document::metaFieldSearchScoreDetails].Obj(), BSON("scoreDetails" << "foo")); - ASSERT_BSONOBJ_EQ(BSON("a" << 42), obj[Document::metaFieldSearchSortValues].Obj()); - ASSERT_EQ(40.0, obj[Document::metaFieldVectorSearchScore].Double()); Document fromBson = Document::fromBsonWithMetaData(obj); ASSERT_TRUE(fromBson.metadata().hasTextScore()); ASSERT_TRUE(fromBson.metadata().hasRandVal()); @@ -1089,110 +966,6 @@ TEST(MetaFields, ToAndFromBson) { ASSERT_BSONOBJ_EQ(BSON("scoreDetails" << "foo"), fromBson.metadata().getSearchScoreDetails()); - ASSERT_BSONOBJ_EQ(BSON("a" << 42), fromBson.metadata().getSearchSortValues()); - ASSERT_EQ(40.0, fromBson.metadata().getVectorSearchScore()); -} - -TEST(MetaFields, ToAndFromBsonTrivialConvertibility) { - Value sortKey{Document{{"token"_sd, "SOMENCODEDATA"_sd}}}; - // Create a document with a backing BSONObj and separate metadata. - auto origObjNoMetadata = BSON("a" << 42); - ASSERT_FALSE(origObjNoMetadata.hasField(Document::metaFieldSortKey)); - - MutableDocument docBuilder; - docBuilder.reset(origObjNoMetadata, false); - docBuilder.metadata().setSortKey(sortKey, true); - Document docWithSeparateBsonAndMetadata = docBuilder.freeze(); - - BSONObj origObjWithMetadata = docWithSeparateBsonAndMetadata.toBsonWithMetaData(); - ASSERT_TRUE(origObjWithMetadata.hasField(Document::metaFieldSortKey)); - Document restoredDocWithMetadata = Document::fromBsonWithMetaData(origObjWithMetadata); - ASSERT_DOCUMENT_EQ(docWithSeparateBsonAndMetadata, restoredDocWithMetadata); - - // Test the 'isTriviallyConvertible()' function. - // The original document is trivially convertible without metadata because the metadata was - // added to the document separately from the backing BSON object. - ASSERT_TRUE(docWithSeparateBsonAndMetadata.isTriviallyConvertible()); - // The original document is NOT trivially convertible with metadata because the metadata was - // added to the document and does not exist in the BSONObj. - ASSERT_FALSE(docWithSeparateBsonAndMetadata.isTriviallyConvertibleWithMetadata()); - // The restored document is trivially convertible with metadata because the underlying BSONObj - // contains the metadata serialized from the original document. - ASSERT_TRUE(restoredDocWithMetadata.isTriviallyConvertibleWithMetadata()); - // The restored document is NOT trivially convertible without metadata because the metadata - // fields need to be stripped from the underlying BSONObj. - ASSERT_FALSE(restoredDocWithMetadata.isTriviallyConvertible()); - - // Test that the conversion with metadata 'origObjWithMetadata' -> 'restoredDocWithMetadata' -> - // 'restoredObjWithMetadata' is trivial because the backing BSON already contains metadata and - // neither the metadata nor the non-metadata fields have been modified. - BSONObj restoredObjWithMetadata = restoredDocWithMetadata.toBsonWithMetaData(); - ASSERT_TRUE(restoredObjWithMetadata.hasField(Document::metaFieldSortKey)); - // Test that 'restoredObjWithMetadata' is referring to the exact same memory location as - // 'origObjWithMetadata', i.e. both objdata() and objsize() match. - ASSERT_EQ(origObjWithMetadata.objdata(), restoredObjWithMetadata.objdata()); - ASSERT_EQ(origObjWithMetadata.objsize(), restoredObjWithMetadata.objsize()); - - // Test that the conversion without metadata 'origObjWithMetadata' -> 'restoredDocWithMetadata' - // -> 'strippedRestoredObj' is NOT trivial because the backing BSON has metadata that must be - // omitted during serialization. - BSONObj strippedRestoredObj = restoredDocWithMetadata.toBson(); - ASSERT_FALSE(strippedRestoredObj.hasField(Document::metaFieldSortKey)); - // 'restoredDocWithMetadata' is trivially convertible with metadata and converting it to BSON - // without metadata will return a new BSON object. - ASSERT_TRUE(origObjNoMetadata.binaryEqual(strippedRestoredObj)); - ASSERT_NE(origObjNoMetadata.objdata(), strippedRestoredObj.objdata()); - - // Test that the conversion without metadata 'origObjNoMetadata' -> - // 'docWithSeparateBsonAndMetadata' -> 'restoredObjNoMetadata' is trivial because - // 'origObjNoMetadata' does not contain any metadata. - BSONObj restoredObjNoMetadata = docWithSeparateBsonAndMetadata.toBson(); - ASSERT_FALSE(restoredObjNoMetadata.hasField(Document::metaFieldSortKey)); - // Test that 'restoredObjNoMetadata' is referring to the exact same memory location as - // 'origObjNoMetadata', i.e. both objdata() and objsize() match. - ASSERT_EQ(origObjNoMetadata.objdata(), restoredObjNoMetadata.objdata()); - ASSERT_EQ(origObjNoMetadata.objsize(), restoredObjNoMetadata.objsize()); -} - -TEST(MetaFields, TrivialConvertibilityBsonWithoutMetadata) { - // Test that an unmodified document without metadata is trivially convertible to BSON with and - // without metadata. - auto bsonWithoutMetadata = BSON("a" << 42); - Document doc(bsonWithoutMetadata); - ASSERT_TRUE(doc.isTriviallyConvertible()); - ASSERT_TRUE(doc.isTriviallyConvertibleWithMetadata()); -} - -TEST(MetaFields, TrivialConvertibilityNoBson) { - // A Document created with no backing BSON is not trivially convertible. - auto docNoBson = Document{{"a", 42}}; - ASSERT_FALSE(docNoBson.isTriviallyConvertible()); - ASSERT_FALSE(docNoBson.isTriviallyConvertibleWithMetadata()); - - // An empty Document is trivially convertible, since the default BSONObj is also empty. - auto emptyDoc = Document{}; - ASSERT_TRUE(emptyDoc.isTriviallyConvertible()); - ASSERT_TRUE(emptyDoc.isTriviallyConvertibleWithMetadata()); -} - -TEST(MetaFields, TrivialConvertibilityModified) { - // Modifying a document with a backing BSON renders it not trivially convertible. - MutableDocument mutDocModified(Document(BSON("a" << 42))); - mutDocModified.addField("b", Value(43)); - auto modifiedDoc = mutDocModified.freeze(); - ASSERT_FALSE(modifiedDoc.isTriviallyConvertible()); - ASSERT_FALSE(modifiedDoc.isTriviallyConvertibleWithMetadata()); -} - -TEST(MetaFields, TrivialConvertibilityMetadataModified) { - // Modifying the metadata of a document with a backing BSON renders it not trivially convertible - // with metadata. - MutableDocument mutDocModifiedMd( - Document::fromBsonWithMetaData(BSON(Document::metaFieldTextScore << 10.0))); - mutDocModifiedMd.metadata().setRandVal(20.0); - auto modifiedMdDoc = mutDocModifiedMd.freeze(); - ASSERT_FALSE(modifiedMdDoc.isTriviallyConvertible()); - ASSERT_FALSE(modifiedMdDoc.isTriviallyConvertibleWithMetadata()); } TEST(MetaFields, MetaFieldsIncludedInDocumentApproximateSize) { @@ -1210,10 +983,8 @@ TEST(MetaFields, MetaFieldsIncludedInDocumentApproximateSize) { 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. Note that the size - // of certain data types may vary on different build variants, so we cannot assert on the exact - // size. - ASSERT_LT(doc2.getMetadataApproximateSize(), 400U); + // Do a sanity check on the amount of space taken by metadata in document 2. + ASSERT_LT(doc2.getMetadataApproximateSize(), 300U); Document emptyDoc; ASSERT_LT(emptyDoc.getMetadataApproximateSize(), 100U); diff --git a/src/mongo/db/exec/document_value/document_value_test_util.h b/src/mongo/db/exec/document_value/document_value_test_util.h index 7b88c9688fa..b6959a7d17f 100644 --- a/src/mongo/db/exec/document_value/document_value_test_util.h +++ b/src/mongo/db/exec/document_value/document_value_test_util.h @@ -59,13 +59,6 @@ #define _ASSERT_DOCVAL_COMPARISON(NAME, a, b) \ ::mongo::unittest::assertComparison_##NAME(__FILE__, __LINE__, #a, #b, a, b) -// TODO SERVER-87736 make these not say "AUTO". -// These are backport-special macros, adapted from the "AUTO" version on more recent branches. The -// automatic functionality doesn't exist on this branch. But the assertions should still pass. -#define ASSERT_VALUE_EQ_AUTO(expected, val) ASSERT_EQ(expected, val.toString()) -#define ASSERT_DOCUMENT_EQ_AUTO(expected, actual) \ - ASSERT_BSONOBJ_EQ(fromjson(expected), actual.toBson()) - namespace mongo { namespace unittest { diff --git a/src/mongo/db/exec/document_value/value.cpp b/src/mongo/db/exec/document_value/value.cpp index 616efb2128a..248514180f0 100644 --- a/src/mongo/db/exec/document_value/value.cpp +++ b/src/mongo/db/exec/document_value/value.cpp @@ -45,7 +45,6 @@ #include "mongo/db/query/datetime/date_time_support.h" #include "mongo/platform/decimal128.h" #include "mongo/util/hex.h" -#include "mongo/util/murmur3.h" #include "mongo/util/represent_as.h" #include "mongo/util/str.h" @@ -936,7 +935,7 @@ void Value::hash_combine(size_t& seed, case Code: case Symbol: { StringData sd = getRawData(); - seed = murmur3<sizeof(size_t)>(sd, seed); + MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); break; } @@ -945,7 +944,7 @@ void Value::hash_combine(size_t& seed, if (stringComparator) { stringComparator->hash_combine(seed, sd); } else { - seed = murmur3<sizeof(size_t)>(sd, seed); + MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); } break; } @@ -969,14 +968,14 @@ void Value::hash_combine(size_t& seed, case BinData: { StringData sd = getRawData(); - seed = murmur3<sizeof(size_t)>(sd, seed); + MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); boost::hash_combine(seed, _storage.binDataType()); break; } case RegEx: { StringData sd = getRawData(); - seed = murmur3<sizeof(size_t)>(sd, seed); + MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); break; } @@ -1254,17 +1253,14 @@ ostream& operator<<(ostream& out, const Value& val) { verify(false); } -Value Value::shred() const { +void Value::fillCache() const { if (isObject()) { - return Value(getDocument().shred()); + getDocument().fillCache(); } else if (isArray()) { - std::vector<Value> values; for (auto&& val : getArray()) { - values.push_back(val.shred()); + val.fillCache(); } - return Value(values); } - return Value(*this); } void Value::serializeForSorter(BufBuilder& buf) const { diff --git a/src/mongo/db/exec/document_value/value.h b/src/mongo/db/exec/document_value/value.h index 62a31f25727..bf54221326c 100644 --- a/src/mongo/db/exec/document_value/value.h +++ b/src/mongo/db/exec/document_value/value.h @@ -344,9 +344,9 @@ public: friend std::ostream& operator<<(std::ostream& out, const Value& v); /** - * Returns a cache-only copy of the value with no backing bson. + * Populates the internal cache by recursively walking the underlying BSON. */ - Value shred() const; + void fillCache() const; void swap(Value& rhs) { _storage.swap(rhs._storage); @@ -430,16 +430,13 @@ public: ImplicitValue(T&& arg) : Value(std::forward<T>(arg)) {} ImplicitValue(std::initializer_list<ImplicitValue> values) : Value(convertToValues(values)) {} - ImplicitValue(std::vector<ImplicitValue> values) : Value(convertToValues(values)) {} - template <typename T> - ImplicitValue(std::vector<T> values) : Value(convertToValues(values)) {} + ImplicitValue(std::vector<int> values) : Value(convertToValues(values)) {} - template <typename T> - static std::vector<Value> convertToValues(const std::vector<T>& vec) { + static std::vector<Value> convertToValues(const std::vector<int>& vec) { std::vector<Value> values; values.reserve(vec.size()); - for_each(vec.begin(), vec.end(), ([&](const T& val) { values.emplace_back(val); })); + for_each(vec.begin(), vec.end(), ([&](const int& val) { values.emplace_back(val); })); return values; } diff --git a/src/mongo/db/exec/document_value/value_comparator_test.cpp b/src/mongo/db/exec/document_value/value_comparator_test.cpp index ac854fdadbe..963a29bc11a 100644 --- a/src/mongo/db/exec/document_value/value_comparator_test.cpp +++ b/src/mongo/db/exec/document_value/value_comparator_test.cpp @@ -312,33 +312,5 @@ TEST(ValueComparatorTest, HashingCodeShouldNotRespectCollation) { ASSERT_NE(comparator.hash(val1), comparator.hash(val2)); } -// This test was originally designed to reproduce SERVER-78126. -TEST(ValueComparatorTest, ArraysDifferingByOneStringShouldHaveDifferentHashes) { - const ValueComparator comparator{}; - const Value val1{std::vector<Value>{Value{std::string{"a"}}, Value{std::string{"x"}}}}; - const Value val2{std::vector<Value>{Value{std::string{"b"}}, Value{std::string{"x"}}}}; - ASSERT_NE(comparator.compare(val1, val2), 0); - ASSERT_NE(comparator.hash(val1), comparator.hash(val2)); -} - -TEST(ValueComparatorTest, ArraysDifferingByOneStringShouldHaveDifferentHashesWithCollation) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - const ValueComparator comparator{&collator}; - const Value val1{std::vector<Value>{Value{std::string{"abc"}}, Value{std::string{"xyz"}}}}; - const Value val2{std::vector<Value>{Value{std::string{"bcd"}}, Value{std::string{"xyz"}}}}; - ASSERT_NE(comparator.compare(val1, val2), 0); - ASSERT_NE(comparator.hash(val1), comparator.hash(val2)); -} - -TEST(ValueComparatorTest, ObjectsDifferingByOneStringShouldHaveDifferentHashes) { - const ValueComparator comparator{}; - const Value val1( - Document({{"foo"_sd, Value{std::string{"abc"}}}, {"bar"_sd, Value{std::string{"xyz"}}}})); - const Value val2( - Document({{"foo"_sd, Value{std::string{"def"}}}, {"bar"_sd, Value{std::string{"xyz"}}}})); - ASSERT_NE(comparator.compare(val1, val2), 0); - ASSERT_NE(comparator.hash(val1), comparator.hash(val2)); -} - } // namespace } // namespace mongo |
