summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/document_value/document_value_test.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
commit294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch)
tree279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/db/exec/document_value/document_value_test.cpp
parent70be7c27a251621187a1de533462ae2bb1e3bd39 (diff)
parent1e917fd798aa25b7066d4b414b51184f13d5a092 (diff)
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10' with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/db/exec/document_value/document_value_test.cpp')
-rw-r--r--src/mongo/db/exec/document_value/document_value_test.cpp193
1 files changed, 190 insertions, 3 deletions
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 421da707001..ecc550f6ee0 100644
--- a/src/mongo/db/exec/document_value/document_value_test.cpp
+++ b/src/mongo/db/exec/document_value/document_value_test.cpp
@@ -46,6 +46,16 @@
#include "mongo/dbtests/dbtests.h"
#include "mongo/logv2/log.h"
+#define ASSERT_DOES_NOT_THROW(EXPRESSION) \
+ try { \
+ EXPRESSION; \
+ } catch (const AssertionException& e) { \
+ ::mongo::str::stream err; \
+ err << "Threw an exception incorrectly: " << e.toString() \
+ << " Exception occured in: " << #EXPRESSION; \
+ ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, err).stream(); \
+ }
+
namespace DocumentTests {
using std::numeric_limits;
@@ -355,6 +365,46 @@ 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.fillCache();
+ 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);
+}
+
/** Add Document fields. */
class AddField {
public:
@@ -705,6 +755,22 @@ 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 {
@@ -840,7 +906,8 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
<< "foo"
<< "h" << 1 << "$indexKey" << BSON("y" << 1) << "$searchScoreDetails"
<< BSON("scoreDetails"
- << "foo")));
+ << "foo")
+ << "$searchSortValues" << BSON("a" << 1) << "$vectorSearchScore" << 6.7));
MutableDocument destination{};
destination.copyMetaDataFrom(source);
@@ -857,6 +924,8 @@ 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 {
@@ -877,6 +946,8 @@ 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());
}
@@ -897,6 +968,10 @@ 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()));
}
@@ -911,6 +986,7 @@ TEST_F(SerializationTest, MetaSerializationNoVals) {
<< "def"_sd));
docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ docBuilder.metadata().setVectorSearchScore(40.0);
assertRoundTrips(docBuilder.freeze());
}
@@ -925,6 +1001,7 @@ TEST_F(SerializationTest, MetaSerializationWithVals) {
docBuilder.metadata().setIndexKey(BSON("key" << 42));
docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ docBuilder.metadata().setVectorSearchScore(40.0);
assertRoundTrips(docBuilder.freeze());
}
@@ -947,6 +1024,8 @@ 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());
@@ -958,6 +1037,8 @@ 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());
@@ -966,6 +1047,110 @@ 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) {
@@ -983,8 +1168,10 @@ 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.
- ASSERT_LT(doc2.getMetadataApproximateSize(), 300U);
+ // 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);
Document emptyDoc;
ASSERT_LT(emptyDoc.getMetadataApproximateSize(), 100U);