diff options
Diffstat (limited to 'src/mongo')
57 files changed, 2120 insertions, 1272 deletions
diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp index 5985ec18976..0e44b7d398d 100644 --- a/src/mongo/bson/bsonobj.cpp +++ b/src/mongo/bson/bsonobj.cpp @@ -378,6 +378,25 @@ BSONObj BSONObj::replaceFieldNames(const BSONObj& names) const { return b.obj(); } +BSONObj BSONObj::stripFieldNames(const BSONObj& obj) { + if (!obj.hasFieldNames()) + return obj; + + BSONObjBuilder bb; + for (auto e : obj) { + bb.appendAs(e, StringData()); + } + return bb.obj(); +} + +bool BSONObj::hasFieldNames() const { + for (auto e : *this) { + if (e.fieldName()[0]) + return true; + } + return false; +} + Status BSONObj::storageValidEmbedded() const { BSONObjIterator i(*this); diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index 82efac98902..bf8559f7dbd 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -532,6 +532,10 @@ public: passed object. */ BSONObj replaceFieldNames(const BSONObj& obj) const; + static BSONObj stripFieldNames(const BSONObj& obj); + + bool hasFieldNames() const; + /** * Returns true if this object is valid according to the specified BSON version, and returns * false otherwise. diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index f5181ffe234..786bafdb68e 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -1228,8 +1228,8 @@ const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, Status IndexCatalogImpl::_indexKeys(OperationContext* opCtx, IndexCatalogEntry* index, - const std::vector<BSONObj>& keys, - const BSONObjSet& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const BSONObj& obj, RecordId loc, @@ -1292,12 +1292,16 @@ Status IndexCatalogImpl::_indexFilteredRecords(OperationContext* opCtx, return status; } - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - BSONObjSet multikeyMetadataKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; + KeyStringSet multikeyMetadataKeys; MultikeyPaths multikeyPaths; - index->accessMethod()->getKeys( - *bsonRecord.docPtr, options.getKeysMode, &keys, &multikeyMetadataKeys, &multikeyPaths); + index->accessMethod()->getKeys(*bsonRecord.docPtr, + options.getKeysMode, + &keys, + &multikeyMetadataKeys, + &multikeyPaths, + bsonRecord.id); Status status = _indexKeys(opCtx, index, @@ -1381,7 +1385,7 @@ Status IndexCatalogImpl::_updateRecord(OperationContext* const opCtx, void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, IndexCatalogEntry* index, - const std::vector<BSONObj>& keys, + const std::vector<KeyString::Value>& keys, const BSONObj& obj, RecordId loc, bool logIfError, @@ -1404,13 +1408,7 @@ void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, int64_t removed; fassert(31155, index->indexBuildInterceptor()->sideWrite( - opCtx, - keys, - SimpleBSONObjComparator::kInstance.makeBSONObjSet(), - {}, - loc, - IndexBuildInterceptor::Op::kDelete, - &removed)); + opCtx, keys, {}, {}, loc, IndexBuildInterceptor::Op::kDelete, &removed)); if (keysDeletedOut) { *keysDeletedOut += removed; } @@ -1449,10 +1447,14 @@ void IndexCatalogImpl::_unindexRecord(OperationContext* opCtx, // There's no need to compute the prefixes of the indexed fields that cause the index to be // multikey when removing a document since the index metadata isn't updated when keys are // deleted. - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - - entry->accessMethod()->getKeys( - obj, IndexAccessMethod::GetKeysMode::kRelaxConstraintsUnfiltered, &keys, nullptr, nullptr); + KeyStringSet keys; + + entry->accessMethod()->getKeys(obj, + IndexAccessMethod::GetKeysMode::kRelaxConstraintsUnfiltered, + &keys, + nullptr, + nullptr, + loc); _unindexKeys(opCtx, entry, {keys.begin(), keys.end()}, obj, loc, logIfError, keysDeletedOut); } diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h index 9eecee90e52..6746fab533d 100644 --- a/src/mongo/db/catalog/index_catalog_impl.h +++ b/src/mongo/db/catalog/index_catalog_impl.h @@ -313,8 +313,8 @@ private: Status _indexKeys(OperationContext* opCtx, IndexCatalogEntry* index, - const std::vector<BSONObj>& keys, - const BSONObjSet& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const BSONObj& obj, RecordId loc, @@ -341,7 +341,7 @@ private: void _unindexKeys(OperationContext* opCtx, IndexCatalogEntry* index, - const std::vector<BSONObj>& keys, + const std::vector<KeyString::Value>& keys, const BSONObj& obj, RecordId loc, bool logIfError, diff --git a/src/mongo/db/catalog/record_store_validate_adaptor.cpp b/src/mongo/db/catalog/record_store_validate_adaptor.cpp index f86fa1e0bd5..674576f9057 100644 --- a/src/mongo/db/catalog/record_store_validate_adaptor.cpp +++ b/src/mongo/db/catalog/record_store_validate_adaptor.cpp @@ -93,14 +93,15 @@ Status RecordStoreValidateAdaptor::validate(const RecordId& recordId, } } - BSONObjSet documentKeySet = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - BSONObjSet multikeyMetadataKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet documentKeySet; + KeyStringSet multikeyMetadataKeys; MultikeyPaths multikeyPaths; iam->getKeys(recordBson, IndexAccessMethod::GetKeysMode::kEnforceConstraints, &documentKeySet, &multikeyMetadataKeys, - &multikeyPaths); + &multikeyPaths, + recordId); if (!descriptor->isMultikey(_opCtx) && iam->shouldMarkIndexAsMultikey( @@ -115,14 +116,30 @@ Status RecordStoreValidateAdaptor::validate(const RecordId& recordId, curRecordResults.valid = false; } - for (const auto& key : multikeyMetadataKeys) { - _indexConsistency->addMultikeyMetadataPath(makeWildCardMultikeyMetadataKeyString(key), - &indexInfo); + for (const auto& keyString : multikeyMetadataKeys) { + try { + auto key = KeyString::toBsonSafe(keyString.getBuffer(), + keyString.getSize(), + indexInfo.ord, + keyString.getTypeBits()); + _indexConsistency->addMultikeyMetadataPath( + makeWildCardMultikeyMetadataKeyString(key), &indexInfo); + } catch (...) { + return exceptionToStatus(); + } } - for (const auto& key : documentKeySet) { - indexInfo.ks->resetToKey(key, indexInfo.ord, recordId); - _indexConsistency->addDocKey(*indexInfo.ks, &indexInfo, recordId, key); + for (const auto& keyString : documentKeySet) { + try { + auto key = KeyString::toBsonSafe(keyString.getBuffer(), + keyString.getSize(), + indexInfo.ord, + keyString.getTypeBits()); + indexInfo.ks->resetToKey(key, indexInfo.ord, recordId); + _indexConsistency->addDocKey(*indexInfo.ks, &indexInfo, recordId, key); + } catch (...) { + return exceptionToStatus(); + } } } return status; diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp index 73701afe43b..ecca17e1287 100644 --- a/src/mongo/db/exec/stagedebug_cmd.cpp +++ b/src/mongo/db/exec/stagedebug_cmd.cpp @@ -70,19 +70,6 @@ using std::string; using std::unique_ptr; using std::vector; -namespace { - -BSONObj stripFieldNames(const BSONObj& obj) { - BSONObjIterator it(obj); - BSONObjBuilder bob; - while (it.more()) { - bob.appendAs(it.next(), ""); - } - return bob.obj(); -} - -} // namespace - /** * A command for manually constructing a query tree and running it. * @@ -300,8 +287,8 @@ public: IndexScanParams params(opCtx, desc); params.bounds.isSimpleRange = true; - params.bounds.startKey = stripFieldNames(nodeArgs["startKey"].Obj()); - params.bounds.endKey = stripFieldNames(nodeArgs["endKey"].Obj()); + params.bounds.startKey = BSONObj::stripFieldNames(nodeArgs["startKey"].Obj()); + params.bounds.endKey = BSONObj::stripFieldNames(nodeArgs["endKey"].Obj()); params.bounds.boundInclusion = IndexBounds::makeBoundInclusionFromBoundBools( nodeArgs["startKeyInclusive"].Bool(), nodeArgs["endKeyInclusive"].Bool()); params.direction = nodeArgs["direction"].numberInt(); diff --git a/src/mongo/db/exec/working_set_common.cpp b/src/mongo/db/exec/working_set_common.cpp index c3d65fa448b..39f08c153bb 100644 --- a/src/mongo/db/exec/working_set_common.cpp +++ b/src/mongo/db/exec/working_set_common.cpp @@ -80,17 +80,23 @@ bool WorkingSetCommon::fetch(OperationContext* opCtx, // unneeded due to the structure of the plan. invariant(!member->keyData.empty()); for (size_t i = 0; i < member->keyData.size(); i++) { - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; // There's no need to compute the prefixes of the indexed fields that cause the index to // be multikey when ensuring the keyData is still valid. - BSONObjSet* multikeyMetadataKeys = nullptr; + KeyStringSet* multikeyMetadataKeys = nullptr; MultikeyPaths* multikeyPaths = nullptr; - member->keyData[i].index->getKeys(member->obj.value(), - IndexAccessMethod::GetKeysMode::kEnforceConstraints, - &keys, - multikeyMetadataKeys, - multikeyPaths); - if (!keys.count(member->keyData[i].keyData)) { + auto* iam = member->keyData[i].index; + iam->getKeys(member->obj.value(), + IndexAccessMethod::GetKeysMode::kEnforceConstraints, + &keys, + multikeyMetadataKeys, + multikeyPaths, + member->recordId); + KeyString::HeapBuilder keyString(iam->getSortedDataInterface()->getKeyStringVersion(), + member->keyData[i].keyData, + iam->getSortedDataInterface()->getOrdering(), + member->recordId); + if (!keys.count(keyString.release())) { // document would no longer be at this position in the index. return false; } diff --git a/src/mongo/db/fts/fts_index_format.cpp b/src/mongo/db/fts/fts_index_format.cpp index ab9950635fb..b514a67a6dd 100644 --- a/src/mongo/db/fts/fts_index_format.cpp +++ b/src/mongo/db/fts/fts_index_format.cpp @@ -135,7 +135,12 @@ MONGO_INITIALIZER(FTSIndexFormat)(InitializerContext* context) { return Status::OK(); } -void FTSIndexFormat::getKeys(const FTSSpec& spec, const BSONObj& obj, BSONObjSet* keys) { +void FTSIndexFormat::getKeys(const FTSSpec& spec, + const BSONObj& obj, + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { int extraSize = 0; vector<BSONElement> extrasBefore; vector<BSONElement> extrasAfter; @@ -182,7 +187,11 @@ void FTSIndexFormat::getKeys(const FTSSpec& spec, const BSONObj& obj, BSONObjSet verify(guess >= res.objsize()); - keys->insert(res); + KeyString::HeapBuilder keyString(keyStringVersion, res, ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); keyBSONSize += res.objsize(); } } diff --git a/src/mongo/db/fts/fts_index_format.h b/src/mongo/db/fts/fts_index_format.h index dd83e8603a8..5ac0823c0c0 100644 --- a/src/mongo/db/fts/fts_index_format.h +++ b/src/mongo/db/fts/fts_index_format.h @@ -34,6 +34,8 @@ #include "mongo/base/string_data.h" #include "mongo/bson/bsonobj_comparator_interface.h" #include "mongo/db/fts/fts_util.h" +#include "mongo/db/storage/key_string.h" +#include "mongo/db/storage/sorted_data_interface.h" namespace mongo { @@ -43,7 +45,12 @@ class FTSSpec; class FTSIndexFormat { public: - static void getKeys(const FTSSpec& spec, const BSONObj& document, BSONObjSet* keys); + static void getKeys(const FTSSpec& spec, + const BSONObj& document, + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); /** * Helper method to get return entry from the FTSIndex as a BSONObj diff --git a/src/mongo/db/fts/fts_index_format_test.cpp b/src/mongo/db/fts/fts_index_format_test.cpp index c9d6779e639..46b10c81228 100644 --- a/src/mongo/db/fts/fts_index_format_test.cpp +++ b/src/mongo/db/fts/fts_index_format_test.cpp @@ -51,15 +51,17 @@ using unittest::assertGet; TEST(FTSIndexFormat, Simple1) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text"))))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; FTSIndexFormat::getKeys(spec, BSON("data" << "cat sat"), - &keys); + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(2U, keys.size()); - for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) { - BSONObj key = *i; + for (auto& keyString : keys) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); ASSERT_EQUALS(2, key.nFields()); ASSERT_EQUALS(String, key.firstElement().type()); } @@ -69,15 +71,17 @@ TEST(FTSIndexFormat, ExtraBack1) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text" << "x" << 1))))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; FTSIndexFormat::getKeys(spec, BSON("data" << "cat" << "x" << 5), - &keys); + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(1U, keys.size()); - BSONObj key = *(keys.begin()); + auto key = KeyString::toBson(*keys.begin(), Ordering::make(BSONObj())); ASSERT_EQUALS(3, key.nFields()); BSONObjIterator i(key); ASSERT_EQUALS(StringData("cat"), i.next().valuestr()); @@ -88,15 +92,17 @@ TEST(FTSIndexFormat, ExtraBack1) { TEST(FTSIndexFormat, ExtraFront1) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" << 1 << "data" << "text"))))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; FTSIndexFormat::getKeys(spec, BSON("data" << "cat" << "x" << 5), - &keys); + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(1U, keys.size()); - BSONObj key = *(keys.begin()); + auto key = KeyString::toBson(*keys.begin(), Ordering::make(BSONObj())); ASSERT_EQUALS(3, key.nFields()); BSONObjIterator i(key); ASSERT_EQUALS(5, i.next().numberInt()); @@ -108,18 +114,22 @@ TEST(FTSIndexFormat, StopWords1) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text"))))); - BSONObjSet keys1 = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys1; FTSIndexFormat::getKeys(spec, BSON("data" << "computer"), - &keys1); + &keys1, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(1U, keys1.size()); - BSONObjSet keys2 = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys2; FTSIndexFormat::getKeys(spec, BSON("data" << "any computer"), - &keys2); + &keys2, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(1U, keys2.size()); } @@ -127,10 +137,10 @@ TEST(FTSIndexFormat, StopWords1) { * Helper function to compare keys returned in getKeys() result * with expected values. */ -void assertEqualsIndexKeys(std::set<std::string>& expectedKeys, const BSONObjSet& keys) { +void assertEqualsIndexKeys(std::set<std::string>& expectedKeys, const KeyStringSet& keys) { ASSERT_EQUALS(expectedKeys.size(), keys.size()); - for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) { - BSONObj key = *i; + for (auto& keyString : keys) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); ASSERT_EQUALS(2, key.nFields()); ASSERT_EQUALS(String, key.firstElement().type()); string s = key.firstElement().String(); @@ -156,14 +166,18 @@ TEST(FTSIndexFormat, LongWordsTextIndexVersion1) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text") << "textIndexVersion" << 1)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" string longWordCat = longPrefix + "cat"; // "aaa...aaasat" string longWordSat = longPrefix + "sat"; string text = str::stream() << longWordCat << " " << longWordSat; - FTSIndexFormat::getKeys(spec, BSON("data" << text), &keys); + FTSIndexFormat::getKeys(spec, + BSON("data" << text), + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); // Hard-coded expected computed keys for future-proofing. std::set<string> expectedKeys; @@ -185,7 +199,7 @@ TEST(FTSIndexFormat, LongWordTextIndexVersion2) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text") << "textIndexVersion" << 2)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" string longWordCat = longPrefix + "cat"; @@ -194,7 +208,11 @@ TEST(FTSIndexFormat, LongWordTextIndexVersion2) { // "aaa...aaamongodbfts" string longWordMongoDBFts = longPrefix + "mongodbfts"; string text = str::stream() << longWordCat << " " << longWordSat << " " << longWordMongoDBFts; - FTSIndexFormat::getKeys(spec, BSON("data" << text), &keys); + FTSIndexFormat::getKeys(spec, + BSON("data" << text), + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); // Hard-coded expected computed keys for future-proofing. std::set<string> expectedKeys; @@ -218,14 +236,18 @@ TEST(FTSIndexFormat, LongWordTextIndexVersion3) { FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" << "text") << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" string longWordCat = longPrefix + "cat"; // "aaa...aaasat" string longWordSat = longPrefix + "sat"; string text = str::stream() << longWordCat << " " << longWordSat; - FTSIndexFormat::getKeys(spec, BSON("data" << text), &keys); + FTSIndexFormat::getKeys(spec, + BSON("data" << text), + &keys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); // Hard-coded expected computed keys for future-proofing. std::set<string> expectedKeys; @@ -246,63 +268,74 @@ TEST(FTSIndexFormat, LongWordTextIndexVersion3) { TEST(FTSIndexFormat, GetKeysWithLeadingEmptyArrayThrows) { BSONObj keyPattern = fromjson("{'a.b': 1, data: 'text'}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: {b: []}, data: 'foo'}"); - ASSERT_THROWS_CODE(FTSIndexFormat::getKeys(spec, objToIndex, &keys), - AssertionException, - ErrorCodes::CannotBuildIndexKeys); + ASSERT_THROWS_CODE( + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())), + AssertionException, + ErrorCodes::CannotBuildIndexKeys); } TEST(FTSIndexFormat, GetKeysWithTrailingEmptyArrayThrows) { BSONObj keyPattern = fromjson("{data: 'text', 'a.b': 1}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: {b: []}, data: 'foo'}"); - ASSERT_THROWS_CODE(FTSIndexFormat::getKeys(spec, objToIndex, &keys), - AssertionException, - ErrorCodes::CannotBuildIndexKeys); + ASSERT_THROWS_CODE( + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())), + AssertionException, + ErrorCodes::CannotBuildIndexKeys); } TEST(FTSIndexFormat, GetKeysWithLeadingSingleElementArrayThrows) { BSONObj keyPattern = fromjson("{'a.b': 1, data: 'text'}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: [{b: 9}], data: 'foo'}"); - ASSERT_THROWS_CODE(FTSIndexFormat::getKeys(spec, objToIndex, &keys), - AssertionException, - ErrorCodes::CannotBuildIndexKeys); + ASSERT_THROWS_CODE( + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())), + AssertionException, + ErrorCodes::CannotBuildIndexKeys); } TEST(FTSIndexFormat, GetKeysWithTrailingSingleElementArrayThrows) { BSONObj keyPattern = fromjson("{data: 'text', 'a.b': 1}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: [{b: 9}], data: 'foo'}"); - ASSERT_THROWS_CODE(FTSIndexFormat::getKeys(spec, objToIndex, &keys), - AssertionException, - ErrorCodes::CannotBuildIndexKeys); + ASSERT_THROWS_CODE( + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())), + AssertionException, + ErrorCodes::CannotBuildIndexKeys); } TEST(FTSIndexFormat, GetKeysWithMultiElementArrayThrows) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 'text'}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: [{b: 9, c: 'foo'}, {b: 10, c: 'bar'}]}"); - ASSERT_THROWS_CODE(FTSIndexFormat::getKeys(spec, objToIndex, &keys), - AssertionException, - ErrorCodes::CannotBuildIndexKeys); + ASSERT_THROWS_CODE( + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())), + AssertionException, + ErrorCodes::CannotBuildIndexKeys); } TEST(FTSIndexFormat, GetKeysWithPositionalPathAllowed) { BSONObj keyPattern = fromjson("{'a.0': 1, 'a.b': 'text'}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << keyPattern << "textIndexVersion" << 3)))); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; BSONObj objToIndex = fromjson("{a: [{b: 'foo'}, {b: 'bar'}]}"); - FTSIndexFormat::getKeys(spec, objToIndex, &keys); + FTSIndexFormat::getKeys( + spec, objToIndex, &keys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())); ASSERT_EQ(2U, keys.size()); { - BSONObj key = *(keys.begin()); + auto key = KeyString::toBson(*keys.begin(), Ordering::make(BSONObj())); ASSERT_EQ(3, key.nFields()); BSONObjIterator it{key}; ASSERT_BSONELT_EQ(it.next(), fromjson("{'': {b: 'foo'}}").firstElement()); @@ -310,7 +343,8 @@ TEST(FTSIndexFormat, GetKeysWithPositionalPathAllowed) { } { - BSONObj key = *(++keys.begin()); + auto next = ++keys.begin(); + auto key = KeyString::toBson(*next, Ordering::make(BSONObj())); ASSERT_EQ(3, key.nFields()); BSONObjIterator it{key}; ASSERT_BSONELT_EQ(it.next(), fromjson("{'': {b: 'foo'}}").firstElement()); diff --git a/src/mongo/db/index/2d_access_method.cpp b/src/mongo/db/index/2d_access_method.cpp index efd9c56b4f6..3aca45956ee 100644 --- a/src/mongo/db/index/2d_access_method.cpp +++ b/src/mongo/db/index/2d_access_method.cpp @@ -51,10 +51,16 @@ TwoDAccessMethod::TwoDAccessMethod(IndexCatalogEntry* btreeState, /** Finds the key objects to put in an index */ void TwoDAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - ExpressionKeysPrivate::get2DKeys(obj, _params, keys); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + ExpressionKeysPrivate::get2DKeys(obj, + _params, + keys, + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering(), + id); } } // namespace mongo diff --git a/src/mongo/db/index/2d_access_method.h b/src/mongo/db/index/2d_access_method.h index 9e40c7aa2fc..5a8b426fb12 100644 --- a/src/mongo/db/index/2d_access_method.h +++ b/src/mongo/db/index/2d_access_method.h @@ -59,9 +59,10 @@ private: * indexes don't support tracking path-level multikey information. */ void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; TwoDIndexingParams _params; }; diff --git a/src/mongo/db/index/2d_key_generator_test.cpp b/src/mongo/db/index/2d_key_generator_test.cpp index b7f5d77c56c..2745eb06b8a 100644 --- a/src/mongo/db/index/2d_key_generator_test.cpp +++ b/src/mongo/db/index/2d_key_generator_test.cpp @@ -47,28 +47,26 @@ using namespace mongo; namespace { -std::string dumpKeyset(const BSONObjSet& objs) { +std::string dumpKeyset(const KeyStringSet& keyStrings) { std::stringstream ss; ss << "[ "; - for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { - ss << i->toString() << " "; + for (auto& keyString : keyStrings) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); + ss << key.toString() << " "; } ss << "]"; return ss.str(); } -bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) { +bool assertKeysetsEqual(const KeyStringSet& expectedKeys, const KeyStringSet& actualKeys) { if (expectedKeys.size() != actualKeys.size()) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; } - if (!std::equal(expectedKeys.begin(), - expectedKeys.end(), - actualKeys.begin(), - SimpleBSONObjComparator::kInstance.makeEqualTo())) { + if (!std::equal(expectedKeys.begin(), expectedKeys.end(), actualKeys.begin())) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; @@ -77,12 +75,17 @@ bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actual return true; } -BSONObj make2DKey(const TwoDIndexingParams& params, int x, int y, BSONElement trailingFields) { +KeyString::Value make2DKey(const TwoDIndexingParams& params, + int x, + int y, + BSONElement trailingFields) { BSONObjBuilder bob; BSONObj locObj = BSON_ARRAY(x << y); params.geoHashConverter->hash(locObj, nullptr).appendHashMin(&bob, ""); bob.append(trailingFields); - return bob.obj(); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, bob.obj(), Ordering::make(BSONObj())); + return keyString.release(); } TEST(2dKeyGeneratorTest, TrailingField) { @@ -90,10 +93,11 @@ TEST(2dKeyGeneratorTest, TrailingField) { BSONObj infoObj = fromjson("{key: {a: '2d', b: 1}}"); TwoDIndexingParams params; ExpressionParams::parseTwoDParams(infoObj, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - ExpressionKeysPrivate::get2DKeys(obj, params, &actualKeys); + KeyStringSet actualKeys; + ExpressionKeysPrivate::get2DKeys( + obj, params, &actualKeys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; BSONObj trailingFields = BSON("" << 5); expectedKeys.insert(make2DKey(params, 0, 0, trailingFields.firstElement())); @@ -105,10 +109,11 @@ TEST(2dKeyGeneratorTest, ArrayTrailingField) { BSONObj infoObj = fromjson("{key: {a: '2d', b: 1}}"); TwoDIndexingParams params; ExpressionParams::parseTwoDParams(infoObj, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - ExpressionKeysPrivate::get2DKeys(obj, params, &actualKeys); + KeyStringSet actualKeys; + ExpressionKeysPrivate::get2DKeys( + obj, params, &actualKeys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; BSONObj trailingFields = BSON("" << BSON_ARRAY(5 << 6)); expectedKeys.insert(make2DKey(params, 0, 0, trailingFields.firstElement())); @@ -120,10 +125,11 @@ TEST(2dKeyGeneratorTest, ArrayOfObjectsTrailingField) { BSONObj infoObj = fromjson("{key: {a: '2d', 'b.c': 1}}"); TwoDIndexingParams params; ExpressionParams::parseTwoDParams(infoObj, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - ExpressionKeysPrivate::get2DKeys(obj, params, &actualKeys); + KeyStringSet actualKeys; + ExpressionKeysPrivate::get2DKeys( + obj, params, &actualKeys, KeyString::Version::kLatestVersion, Ordering::make(BSONObj())); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; BSONObj trailingFields = BSON("" << BSON_ARRAY(5 << 6)); expectedKeys.insert(make2DKey(params, 0, 0, trailingFields.firstElement())); diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript index 3d7f912bd6f..3d70ec06eb6 100644 --- a/src/mongo/db/index/SConscript +++ b/src/mongo/db/index/SConscript @@ -102,6 +102,7 @@ serveronlyEnv.Library( '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', '$BUILD_DIR/mongo/db/storage/encryption_hooks', '$BUILD_DIR/mongo/db/storage/index_entry_comparison', + '$BUILD_DIR/mongo/db/storage/key_string', '$BUILD_DIR/mongo/db/storage/storage_options', '$BUILD_DIR/third_party/shim_snappy', 'index_descriptor', diff --git a/src/mongo/db/index/btree_access_method.cpp b/src/mongo/db/index/btree_access_method.cpp index 51d4ba3acbe..da13aa00f50 100644 --- a/src/mongo/db/index/btree_access_method.cpp +++ b/src/mongo/db/index/btree_access_method.cpp @@ -56,15 +56,21 @@ BtreeAccessMethod::BtreeAccessMethod(IndexCatalogEntry* btreeState, fixed.push_back(BSONElement()); } - _keyGenerator = std::make_unique<BtreeKeyGenerator>( - fieldNames, fixed, _descriptor->isSparse(), btreeState->getCollator()); + _keyGenerator = + std::make_unique<BtreeKeyGenerator>(fieldNames, + fixed, + _descriptor->isSparse(), + btreeState->getCollator(), + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering()); } void BtreeAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - _keyGenerator->getKeys(obj, keys, multikeyPaths); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + _keyGenerator->getKeys(obj, keys, multikeyPaths, id); } } // namespace mongo diff --git a/src/mongo/db/index/btree_access_method.h b/src/mongo/db/index/btree_access_method.h index 3af203d1f78..ca9db80f4c3 100644 --- a/src/mongo/db/index/btree_access_method.h +++ b/src/mongo/db/index/btree_access_method.h @@ -49,9 +49,10 @@ public: private: void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; // Our keys differ for V0 and V1. std::unique_ptr<BtreeKeyGenerator> _keyGenerator; diff --git a/src/mongo/db/index/btree_key_generator.cpp b/src/mongo/db/index/btree_key_generator.cpp index 7f337879d9d..71b557cb4c9 100644 --- a/src/mongo/db/index/btree_key_generator.cpp +++ b/src/mongo/db/index/btree_key_generator.cpp @@ -58,19 +58,19 @@ const BSONElement undefinedElt = undefinedObj.firstElement(); BtreeKeyGenerator::BtreeKeyGenerator(std::vector<const char*> fieldNames, std::vector<BSONElement> fixed, bool isSparse, - const CollatorInterface* collator) - : _fieldNames(fieldNames), + const CollatorInterface* collator, + KeyString::Version keyStringVersion, + Ordering ordering) + : _keyStringVersion(keyStringVersion), + _ordering(ordering), + _fieldNames(fieldNames), + _isIdIndex(fieldNames.size() == 1 && std::string("_id") == fieldNames[0]), _isSparse(isSparse), + _nullKeyString(_buildNullKeyString()), _fixed(fixed), _emptyPositionalInfo(fieldNames.size()), _collator(collator) { - BSONObjBuilder nullKeyBuilder; - for (size_t i = 0; i < fieldNames.size(); ++i) { - nullKeyBuilder.appendNull(""); - } - _nullKey = nullKeyBuilder.obj(); - _isIdIndex = fieldNames.size() == 1 && std::string("_id") == fieldNames[0]; for (const char* fieldName : fieldNames) { size_t pathLength = FieldRef{fieldName}.numParts(); invariant(pathLength > 0); @@ -117,13 +117,14 @@ BSONElement BtreeKeyGenerator::_extractNextElement(const BSONObj& obj, void BtreeKeyGenerator::_getKeysArrEltFixed(std::vector<const char*>* fieldNames, std::vector<BSONElement>* fixed, const BSONElement& arrEntry, - BSONObjSet* keys, + KeyStringSet* keys, unsigned numNotFound, const BSONElement& arrObjElt, const std::set<size_t>& arrIdxs, bool mayExpandArrayUnembedded, const std::vector<PositionalPathInfo>& positionalInfo, - MultikeyPaths* multikeyPaths) const { + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { // Set up any terminal array values. for (const auto idx : arrIdxs) { if (*(*fieldNames)[idx] == '\0') { @@ -138,29 +139,43 @@ void BtreeKeyGenerator::_getKeysArrEltFixed(std::vector<const char*>* fieldNames keys, numNotFound, positionalInfo, - multikeyPaths); + multikeyPaths, + id); } void BtreeKeyGenerator::getKeys(const BSONObj& obj, - BSONObjSet* keys, - MultikeyPaths* multikeyPaths) const { + KeyStringSet* keys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { if (_isIdIndex) { // we special case for speed BSONElement e = obj["_id"]; if (e.eoo()) { - keys->insert(_nullKey); + keys->insert(_nullKeyString); } else if (_collator) { BSONObjBuilder b; CollationIndexKey::collationAwareIndexKeyAppend(e, _collator, &b); - // Insert a copy so its buffer size fits the object size. - keys->insert(b.obj().copy()); + KeyString::Builder keyString(_keyStringVersion, b.obj(), _ordering); + if (id) { + keyString.appendRecordId(*id); + } + /* + * Insert a copy so its buffer size fits the key size. + */ + keys->insert(keyString.getValueCopy()); } else { int size = e.size() + 5 /* bson over head*/ - 3 /* remove _id string */; BSONObjBuilder b(size); b.appendAs(e, ""); - keys->insert(b.obj()); - invariant(keys->begin()->objsize() == size); + KeyString::Builder keyString(_keyStringVersion, b.obj(), _ordering); + if (id) { + keyString.appendRecordId(*id); + } + /* + * Insert a copy so its buffer size fits the key size. + */ + keys->insert(keyString.getValueCopy()); } // The {_id: 1} index can never be multikey because the _id field isn't allowed to be an @@ -175,20 +190,22 @@ void BtreeKeyGenerator::getKeys(const BSONObj& obj, } // '_fieldNames' and '_fixed' are passed by value so that their copies can be mutated as // part of the _getKeysWithArray method. - _getKeysWithArray(_fieldNames, _fixed, obj, keys, 0, _emptyPositionalInfo, multikeyPaths); + _getKeysWithArray( + _fieldNames, _fixed, obj, keys, 0, _emptyPositionalInfo, multikeyPaths, id); } if (keys->empty() && !_isSparse) { - keys->insert(_nullKey); + keys->insert(_nullKeyString); } } void BtreeKeyGenerator::_getKeysWithArray(std::vector<const char*> fieldNames, std::vector<BSONElement> fixed, const BSONObj& obj, - BSONObjSet* keys, + KeyStringSet* keys, unsigned numNotFound, const std::vector<PositionalPathInfo>& positionalInfo, - MultikeyPaths* multikeyPaths) const { + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { BSONElement arrElt; // A set containing the position of any indexed fields in the key pattern that traverse through @@ -261,7 +278,11 @@ void BtreeKeyGenerator::_getKeysWithArray(std::vector<const char*> fieldNames, for (std::vector<BSONElement>::iterator i = fixed.begin(); i != fixed.end(); ++i) { CollationIndexKey::collationAwareIndexKeyAppend(*i, _collator, &b); } - keys->insert(b.obj()); + KeyString::HeapBuilder keyString(_keyStringVersion, b.obj(), _ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); } else if (arrElt.embeddedObject().firstElement().eoo()) { // We've encountered an empty array. if (multikeyPaths && mayExpandArrayUnembedded) { @@ -289,7 +310,8 @@ void BtreeKeyGenerator::_getKeysWithArray(std::vector<const char*> fieldNames, arrIdxs, true, _emptyPositionalInfo, - multikeyPaths); + multikeyPaths, + id); } else { BSONObj arrObj = arrElt.embeddedObject(); @@ -371,7 +393,8 @@ void BtreeKeyGenerator::_getKeysWithArray(std::vector<const char*> fieldNames, arrIdxs, mayExpandArrayUnembedded, subPositionalInfo, - multikeyPaths); + multikeyPaths, + id); } } @@ -385,4 +408,13 @@ void BtreeKeyGenerator::_getKeysWithArray(std::vector<const char*> fieldNames, } } +KeyString::Value BtreeKeyGenerator::_buildNullKeyString() const { + BSONObjBuilder nullKeyBuilder; + for (size_t i = 0; i < _fieldNames.size(); ++i) { + nullKeyBuilder.appendNull(""); + } + KeyString::HeapBuilder nullKeyString(_keyStringVersion, nullKeyBuilder.obj(), _ordering); + return nullKeyString.release(); +} + } // namespace mongo diff --git a/src/mongo/db/index/btree_key_generator.h b/src/mongo/db/index/btree_key_generator.h index a8b529ea4e0..08b960acfc9 100644 --- a/src/mongo/db/index/btree_key_generator.h +++ b/src/mongo/db/index/btree_key_generator.h @@ -37,6 +37,7 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/multikey_paths.h" #include "mongo/db/jsobj.h" +#include "mongo/db/storage/key_string.h" namespace mongo { @@ -55,7 +56,9 @@ public: BtreeKeyGenerator(std::vector<const char*> fieldNames, std::vector<BSONElement> fixed, bool isSparse, - const CollatorInterface* collator); + const CollatorInterface* collator, + KeyString::Version keyStringVersion, + Ordering ordering); /** * Generates the index keys for the document 'obj', and stores them in the set 'keys'. @@ -66,15 +69,20 @@ public: * element with the prefixes of the indexed field that would cause this index to be multikey as * a result of inserting 'keys'. */ - void getKeys(const BSONObj& obj, BSONObjSet* keys, MultikeyPaths* multikeyPaths) const; + void getKeys(const BSONObj& obj, + KeyStringSet* keys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id = boost::none) const; private: + const KeyString::Version _keyStringVersion; + const Ordering _ordering; // These are used by getKeys below. - std::vector<const char*> _fieldNames; - bool _isIdIndex; - bool _isSparse; - BSONObj _nullKey; // A full key with all fields null. - BSONSizeTracker _sizeTracker; + const std::vector<const char*> _fieldNames; + const bool _isIdIndex; + const bool _isSparse; + const KeyString::Value _nullKeyString; // A full key with all fields null. + const BSONSizeTracker _sizeTracker; std::vector<BSONElement> _fixed; /** @@ -136,10 +144,11 @@ private: void _getKeysWithArray(std::vector<const char*> fieldNames, std::vector<BSONElement> fixed, const BSONObj& obj, - BSONObjSet* keys, + KeyStringSet* keys, unsigned numNotFound, const std::vector<PositionalPathInfo>& positionalInfo, - MultikeyPaths* multikeyPaths) const; + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const; /** * A call to _getKeysWithArray() begins by calling this for each field in the key pattern. It @@ -187,13 +196,16 @@ private: void _getKeysArrEltFixed(std::vector<const char*>* fieldNames, std::vector<BSONElement>* fixed, const BSONElement& arrEntry, - BSONObjSet* keys, + KeyStringSet* keys, unsigned numNotFound, const BSONElement& arrObjElt, const std::set<size_t>& arrIdxs, bool mayExpandArrayUnembedded, const std::vector<PositionalPathInfo>& positionalInfo, - MultikeyPaths* multikeyPaths) const; + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const; + + KeyString::Value _buildNullKeyString() const; const std::vector<PositionalPathInfo> _emptyPositionalInfo; diff --git a/src/mongo/db/index/btree_key_generator_test.cpp b/src/mongo/db/index/btree_key_generator_test.cpp index da569fdb203..811c5d41bab 100644 --- a/src/mongo/db/index/btree_key_generator_test.cpp +++ b/src/mongo/db/index/btree_key_generator_test.cpp @@ -54,11 +54,12 @@ namespace { // Helper functions // -std::string dumpKeyset(const BSONObjSet& objs) { +std::string dumpKeyset(const KeyStringSet& keyStrings) { std::stringstream ss; ss << "[ "; - for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { - ss << i->toString() << " "; + for (auto& keyString : keyStrings) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); + ss << key.toString() << " "; } ss << "]"; @@ -81,15 +82,12 @@ std::string dumpMultikeyPaths(const MultikeyPaths& multikeyPaths) { return ss.str(); } -bool keysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) { +bool keysetsEqual(const KeyStringSet& expectedKeys, const KeyStringSet& actualKeys) { if (expectedKeys.size() != actualKeys.size()) { return false; } - if (!std::equal(expectedKeys.begin(), - expectedKeys.end(), - actualKeys.begin(), - SimpleBSONObjComparator::kInstance.makeEqualTo())) { + if (!std::equal(expectedKeys.begin(), expectedKeys.end(), actualKeys.begin())) { return false; } @@ -98,7 +96,7 @@ bool keysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) bool testKeygen(const BSONObj& kp, const BSONObj& obj, - const BSONObjSet& expectedKeys, + const KeyStringSet& expectedKeys, const MultikeyPaths& expectedMultikeyPaths, bool sparse = false, const CollatorInterface* collator = nullptr) { @@ -118,14 +116,19 @@ bool testKeygen(const BSONObj& kp, fixed.push_back(BSONElement()); } - auto keyGen = std::make_unique<BtreeKeyGenerator>(fieldNames, fixed, sparse, collator); + auto keyGen = std::make_unique<BtreeKeyGenerator>(fieldNames, + fixed, + sparse, + collator, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); // // Step 2: ask 'keyGen' to generate index keys for the object 'obj' and report any prefixes of // the indexed fields that would cause the index to be multikey as a result of inserting // 'actualKeys'. // - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; keyGen->getKeys(obj, &actualKeys, &actualMultikeyPaths); @@ -156,8 +159,9 @@ bool testKeygen(const BSONObj& kp, TEST(BtreeKeyGeneratorTest, GetIdKeyFromObject) { BSONObj keyPattern = fromjson("{_id: 1}"); BSONObj genKeysFrom = fromjson("{_id: 'foo', b: 4}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'foo'}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 'foo'}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -165,8 +169,9 @@ TEST(BtreeKeyGeneratorTest, GetIdKeyFromObject) { TEST(BtreeKeyGeneratorTest, GetKeysFromObjectSimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: 5}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -174,8 +179,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromObjectSimple) { TEST(BtreeKeyGeneratorTest, GetKeysFromObjectDotted) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: 4}, c: 'foo'}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 4}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 4}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -183,10 +189,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromObjectDotted) { TEST(BtreeKeyGeneratorTest, GetKeysFromArraySimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -194,8 +203,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArraySimple) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithIdenticalValues) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [0, 0, 0]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 0}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 0}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -203,8 +213,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithIdenticalValues) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithEquivalentValues) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [0, NumberInt(0), NumberLong(0)]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 0}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 0}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -212,10 +223,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithEquivalentValues) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayFirstElement) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3], b: 2}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1, '': 2}")); - expectedKeys.insert(fromjson("{'': 2, '': 2}")); - expectedKeys.insert(fromjson("{'': 3, '': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1, '': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2, '': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3, '': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -223,10 +237,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayFirstElement) { TEST(BtreeKeyGeneratorTest, GetKeysFromArraySecondElement) { BSONObj keyPattern = fromjson("{first: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{first: 5, a: [1, 2, 3]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5, '': 1}")); - expectedKeys.insert(fromjson("{'': 5, '': 2}")); - expectedKeys.insert(fromjson("{'': 5, '': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 5, '': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 5, '': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 5, '': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -234,10 +251,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArraySecondElement) { TEST(BtreeKeyGeneratorTest, GetKeysFromSecondLevelArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: [1, 2, 3]}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -245,7 +265,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromSecondLevelArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysBasic) { BSONObj keyPattern = fromjson("{'a': 1, 'b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3], b: [1, 2, 3]}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -254,10 +274,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysBasic) { TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectBasic) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b:1,c:4}, {b:2,c:4}, {b:3,c:4}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -265,9 +288,11 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectBasic) { TEST(BtreeKeyGeneratorTest, GetKeysFromSubobjectWithArrayOfSubobjects) { BSONObj keyPattern = fromjson("{'a.b.c': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: [{c: 1}, {c: 2}]}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -275,10 +300,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromSubobjectWithArrayOfSubobjects) { TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectCompoundIndex) { BSONObj keyPattern = fromjson("{'a.b': 1, d: 99}"); BSONObj genKeysFrom = fromjson("{a: [{b:1,c:4}, {b:2,c:4}, {b:3,c:4}], d: 99}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1, '': 99}")); - expectedKeys.insert(fromjson("{'': 2, '': 99}")); - expectedKeys.insert(fromjson("{'': 3, '': 99}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1, '': 99}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2, '': 99}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3, '': 99}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -286,11 +314,16 @@ TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectCompoundIndex) { TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectSingleMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{foo: 41}, {b:1,c:4}, {b:2,c:4}, {b:3,c:4}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -298,8 +331,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectSingleMissing) { TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{foo: 41}, {foo: 41}, {foo: 41}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -307,8 +341,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectMissing) { TEST(BtreeKeyGeneratorTest, GetKeysMissingField) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 1}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -316,8 +351,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysMissingField) { TEST(BtreeKeyGeneratorTest, GetKeysSubobjectMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -325,8 +361,10 @@ TEST(BtreeKeyGeneratorTest, GetKeysSubobjectMissing) { TEST(BtreeKeyGeneratorTest, GetKeysFromCompound) { BSONObj keyPattern = fromjson("{x: 1, y: 1}"); BSONObj genKeysFrom = fromjson("{x: 'a', y: 'b'}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'a', '': 'b'}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': 'a', '': 'b'}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -334,8 +372,10 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromCompound) { TEST(BtreeKeyGeneratorTest, GetKeysFromCompoundMissing) { BSONObj keyPattern = fromjson("{x: 1, y: 1}"); BSONObj genKeysFrom = fromjson("{x: 'a'}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'a', '': null}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': 'a', '': null}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -343,8 +383,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromCompoundMissing) { TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubelementComplex) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[2]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; // Both the 'a' and 'a.b' arrays contain a single element, so they are considered multikey. MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); @@ -353,7 +394,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubelementComplex) { TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysComplex) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[1],c:[2]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -362,9 +403,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysComplex) { TEST(BtreeKeyGeneratorTest, GetKeysAlternateMissing) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1},{c:2}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null, '': 2}")); - expectedKeys.insert(fromjson("{'': 1, '': null}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'': null, '': 2}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + fromjson("{'': 1, '': null}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -372,10 +417,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysAlternateMissing) { TEST(BtreeKeyGeneratorTest, GetKeysFromMultiComplex) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1},{b:[1,2,3]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -383,10 +431,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromMultiComplex) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayOfSubobjectsWithArrayValues) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1, 2]}, {b: [2, 3]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -394,10 +445,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayOfSubobjectsWithArrayValues) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayOfSubobjectsWithNonDistinctArrayValues) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1, 2, 3]}, {b: [2]}, {b: [3, 1]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -405,25 +459,31 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayOfSubobjectsWithNonDistinctArrayValu TEST(BtreeKeyGeneratorTest, GetKeysArrayEmpty) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a:[1,2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.getValueCopy(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: [1]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); + expectedKeys.insert(keyString1.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: []}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': undefined}")); + expectedKeys.insert(keyString3.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: null}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': null}")); + expectedKeys.insert(keyString4.release()); expectedMultikeyPaths[0].clear(); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -431,9 +491,11 @@ TEST(BtreeKeyGeneratorTest, GetKeysArrayEmpty) { TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleArray) { BSONObj keyPattern = fromjson("{a: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{a:[1,2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1, '': 1}")); - expectedKeys.insert(fromjson("{'': 2, '': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1, '': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2, '': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -441,8 +503,10 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleEmptyArray) { BSONObj keyPattern = fromjson("{a: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': undefined, '': undefined}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': undefined, '': undefined}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -450,28 +514,34 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleEmptyArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromMultiEmptyArray) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: 1, b: [1, 2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1, '': 1}")); - expectedKeys.insert(fromjson("{'': 1, '': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1, '': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 1, '': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'': 1, '': undefined}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.getValueCopy(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: 1, b: [1]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1, '': 1}")); + expectedKeys.insert(keyString1.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: 1, b: []}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1, '': undefined}")); + expectedKeys.insert(keyString3.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } TEST(BtreeKeyGeneratorTest, GetKeysFromNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -479,8 +549,10 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromNestedEmptyArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromMultiNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null, '': null}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': null, '': null}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -488,19 +560,27 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromMultiNestedEmptyArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromUnevenNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': undefined, '': null}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'': undefined, '': null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + fromjson("{'': {b:1}, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[]}, '': undefined}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[{b: 1}]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': {b:1}, '': 1}")); + expectedKeys.insert(keyString2.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[{b: []}]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': {b:[]}, '': undefined}")); + expectedKeys.insert(keyString3.release()); expectedMultikeyPaths[1].insert(1U); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -508,8 +588,10 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromUnevenNestedEmptyArray) { TEST(BtreeKeyGeneratorTest, GetKeysFromReverseUnevenNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null, '': undefined}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': null, '': undefined}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -518,8 +600,10 @@ TEST(BtreeKeyGeneratorTest, SparseReverseUnevenNestedEmptyArray) { const bool sparse = true; BSONObj keyPattern = fromjson("{'a.b': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null, '': undefined}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': null, '': undefined}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths, sparse)); } @@ -528,7 +612,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromSparseEmptyArray) { const bool sparse = true; BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:1}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths, sparse)); @@ -544,7 +628,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromSparseEmptyArraySecond) { const bool sparse = true; BSONObj keyPattern = fromjson("{z: 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:1}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths, sparse)); @@ -560,7 +644,9 @@ TEST(BtreeKeyGeneratorTest, SparseNonObjectMissingNestedField) { const bool sparse = true; BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths, sparse)); @@ -569,36 +655,41 @@ TEST(BtreeKeyGeneratorTest, SparseNonObjectMissingNestedField) { genKeysFrom = fromjson("{a:[1,{b:1}]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); + expectedKeys.insert(keyString.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths, sparse)); } TEST(BtreeKeyGeneratorTest, GetKeysFromIndexedArrayIndex) { BSONObj keyPattern = fromjson("{'a.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[1]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': [1]}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.getValueCopy()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[1]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': [1]}")); + expectedKeys.insert(keyString2.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': undefined}")); + expectedKeys.insert(keyString3.getValueCopy()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': undefined}")); + expectedKeys.insert(keyString3.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:{'0':1}}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); + expectedKeys.insert(keyString1.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[{'0':1}]}"); @@ -614,19 +705,24 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromIndexedArrayIndex) { TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleIndexedArrayIndex) { BSONObj keyPattern = fromjson("{'a.0.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[[1]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': null}")); + expectedKeys.insert(keyString2.getValueCopy()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': null}")); + expectedKeys.insert(keyString2.release()); // Here the first "0" path component acts like a regular field name rather than a positional // path element, since the 0th array index does not exist. Therefore, path component "a" is // considered multikey. @@ -635,7 +731,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleIndexedArrayIndex) { genKeysFrom = fromjson("{a:[[[]]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': undefined}")); + expectedKeys.insert(keyString3.release()); expectedMultikeyPaths = {std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -643,46 +739,47 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleIndexedArrayIndex) { TEST(BtreeKeyGeneratorTest, GetKeysFromObjectWithinArray) { BSONObj keyPattern = fromjson("{'a.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': [1]}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.getValueCopy()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[{b:[1]}]}"); - expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); expectedMultikeyPaths = {{2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[{b:[[1]]}]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': [1]}")); + expectedKeys.insert(keyString2.getValueCopy()); expectedMultikeyPaths = {{2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[{b:1}]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); + expectedKeys.insert(keyString1.release()); // Path component "0" refers to an array which is subsequently expanded, so it is considered // multikey. expectedMultikeyPaths = {{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[{b:[1]}]]}"); - expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': 1}")); expectedMultikeyPaths = {{1U, 2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[{b:[[1]]}]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': [1]}")); + expectedKeys.insert(keyString2.release()); expectedMultikeyPaths = {{1U, 2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a:[[{b:[]}]]}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': undefined}")); + expectedKeys.insert(keyString3.release()); expectedMultikeyPaths = {{1U, 2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -690,9 +787,11 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromObjectWithinArray) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalElementIsExpandedArray) { BSONObj keyPattern = fromjson("{'a.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[[{b:1}, {b:2}]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -700,8 +799,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalElementIsExpandedArray) { TEST(BtreeKeyGeneratorTest, GetKeysTrailingPositionalElementIsSingletonArray) { BSONObj keyPattern = fromjson("{'a.b.c.3': 1}"); BSONObj genKeysFrom = fromjson("{a:{b:{c:[0,1,2,[3]]}}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': [3]}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': [3]}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -709,8 +809,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysTrailingPositionalElementIsSingletonArray) { TEST(BtreeKeyGeneratorTest, GetKeysTrailingPositionalElementIsEmptyArray) { BSONObj keyPattern = fromjson("{'a.b.c.3': 1}"); BSONObj genKeysFrom = fromjson("{a:{b:{c:[0,1,2,[]]}}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': undefined}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -718,8 +819,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysTrailingPositionalElementIsEmptyArray) { TEST(BtreeKeyGeneratorTest, GetKeysManyPositionalElementsComplex) { BSONObj keyPattern = fromjson("{'a.0.1.2.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[[1, [1, 2, [{b: [[], 2]}]]], 1]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': undefined}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': undefined}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{3U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -727,8 +829,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysManyPositionalElementsComplex) { TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithinObjectWithinArray) { BSONObj keyPattern = fromjson("{'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[1]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -736,7 +839,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithinObjectWithinArray) { TEST(BtreeKeyGeneratorTest, ParallelArraysInNestedObjects) { BSONObj keyPattern = fromjson("{'a.a': 1, 'b.a': 1}"); BSONObj genKeysFrom = fromjson("{a:{a:[1]}, b:{a:[1]}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -745,7 +848,7 @@ TEST(BtreeKeyGeneratorTest, ParallelArraysInNestedObjects) { TEST(BtreeKeyGeneratorTest, ParallelArraysUneven) { BSONObj keyPattern = fromjson("{'b.a': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{b:{a:[1]}, a:[1,2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -754,10 +857,13 @@ TEST(BtreeKeyGeneratorTest, ParallelArraysUneven) { TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallel) { BSONObj keyPattern = fromjson("{'a.b.c': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, {b: {c: [3, 4]}}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); - expectedKeys.insert(fromjson("{'': 3}")); - expectedKeys.insert(fromjson("{'': 4}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 4}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -765,10 +871,14 @@ TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallel) { TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallelCompound) { BSONObj keyPattern = fromjson("{'a.b.c': 1, 'a.b.d': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, {b: {c: [3, 4], d: 5}}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null, '': null}")); - expectedKeys.insert(fromjson("{'': 3, '': 5}")); - expectedKeys.insert(fromjson("{'': 4, '': 5}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'': null, '': null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 3, '': 5}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 4, '': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 2U}, {0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -776,12 +886,26 @@ TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallelCompound) { TEST(BtreeKeyGeneratorTest, GetKeysComplexNestedArrays) { BSONObj keyPattern = fromjson("{'a.b.c.d': 1, 'a.g': 1, 'a.b.f': 1, 'a.b.c': 1, 'a.b.e': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, {b: [2, {c: [3, {d: 1}], e: 4}, 5, {f: 6}], g: 7}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'':null, '':null, '':null, '':null, '':null}")); - expectedKeys.insert(fromjson("{'':null, '':7, '':null, '':null, '':null}")); - expectedKeys.insert(fromjson("{'':null, '':7, '':null, '':3, '':4}")); - expectedKeys.insert(fromjson("{'':null, '':7, '':6, '':null, '':null}")); - expectedKeys.insert(fromjson("{'':1, '':7, '':null, '':{d: 1}, '':4}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'':null, '':null, '':null, '':null, '':null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + fromjson("{'':null, '':7, '':null, '':null, '':null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'':null, '':7, '':null, '':3, '':4}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + fromjson("{'':null, '':7, '':6, '':null, '':null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString5(KeyString::Version::kLatestVersion, + fromjson("{'':1, '':7, '':null, '':{d: 1}, '':4}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), + keyString2.release(), + keyString3.release(), + keyString4.release(), + keyString5.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U, 2U}, {0U}, {0U, 1U}, {0U, 1U, 2U}, {0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -790,8 +914,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysComplexNestedArrays) { TEST(BtreeKeyGeneratorTest, GetKeys2DArray) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [[2]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': [2]}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': [2]}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -801,7 +926,7 @@ TEST(BtreeKeyGeneratorTest, GetKeys2DArray) { TEST(BtreeKeyGeneratorTest, GetKeysParallelEmptyArrays) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [], b: []}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -810,7 +935,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysParallelEmptyArrays) { TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmpty) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [], b: [1, 2, 3]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -819,7 +944,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmpty) { TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmptyNested) { BSONObj keyPattern = fromjson("{'a.b.c': 1, 'a.b.d': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [{c: [1, 2, 3], d: []}]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -829,8 +954,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmptyNested) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternMissingElement) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{'2': 5}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -839,8 +965,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternMissingElement) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -849,8 +976,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray2) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5], [3, 4, 6], [0, 1, 2]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': [0, 1, 2]}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': [0, 1, 2]}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -859,8 +987,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray2) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray3) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{'0': 1, '1': 2, '2': 5}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -869,8 +998,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray3) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray4) { BSONObj keyPattern = fromjson("{'a.b.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [[1, 2, 5]]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -879,9 +1009,11 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray4) { TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray5) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5], {'2': 6}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); - expectedKeys.insert(fromjson("{'': 6}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 6}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -889,8 +1021,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray5) { TEST(BtreeKeyGeneratorTest, GetNullKeyNestedArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -898,11 +1031,20 @@ TEST(BtreeKeyGeneratorTest, GetNullKeyNestedArray) { TEST(BtreeKeyGeneratorTest, GetKeysUnevenNestedArrays) { BSONObj keyPattern = fromjson("{a: 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, {b: [2, 3, 4]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1, '': null}")); - expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 2}")); - expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 3}")); - expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 4}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'': 1, '': null}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[2,3,4]}, '': 2}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[2,3,4]}, '': 3}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[2,3,4]}, '': 4}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -912,8 +1054,9 @@ TEST(BtreeKeyGeneratorTest, GetKeysUnevenNestedArrays) { TEST(BtreeKeyGeneratorTest, GetKeysRepeatedFieldName) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: 2, a: 3}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -923,9 +1066,11 @@ TEST(BtreeKeyGeneratorTest, GetKeysRepeatedFieldName) { TEST(BtreeKeyGeneratorTest, GetKeysEmptyPathPiece) { BSONObj keyPattern = fromjson("{'a..c': 1}"); BSONObj genKeysFrom = fromjson("{a: {'': [{c: 1}, {c: 2}]}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; MultikeyPaths expectedMultikeyPaths{{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -934,24 +1079,27 @@ TEST(BtreeKeyGeneratorTest, GetKeysEmptyPathPiece) { // handling of empty path components. TEST(BtreeKeyGeneratorTest, GetKeysLastPathPieceEmpty) { BSONObj keyPattern = fromjson("{'a.': 1}"); - BSONObj genKeysFrom = fromjson("{a: 2}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': {'': 2}}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); genKeysFrom = fromjson("{a: {'': 2}}"); expectedKeys.clear(); - expectedKeys.insert(fromjson("{'': {'': 2}}")); + expectedKeys.insert(keyString2.release()); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty) { BSONObj keyPattern = fromjson("{'.a': 1}"); BSONObj genKeysFrom = fromjson("{a: 2}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -959,10 +1107,13 @@ TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty) { TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty2) { BSONObj keyPattern = fromjson("{'.a': 1}"); BSONObj genKeysFrom = fromjson("{'': [{a: [1, 2, 3]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{0U, 1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -970,7 +1121,7 @@ TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty2) { TEST(BtreeKeyGeneratorTest, PositionalKeyPatternParallelArrays) { BSONObj keyPattern = fromjson("{a: 1, 'b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [1], b: [2]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; MultikeyPaths expectedMultikeyPaths(keyPattern.nFields()); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths), AssertionException); @@ -979,8 +1130,9 @@ TEST(BtreeKeyGeneratorTest, PositionalKeyPatternParallelArrays) { TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_b_Extracts_b_ElementInsideSingleton2DArray) { BSONObj keyPattern = fromjson("{'a.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[{b: 1}]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{1U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -988,8 +1140,9 @@ TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_b_Extracts_b_ElementInsideSingleton2D TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_Extracts_b_ElementInsideSingleton2DArray) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[{b: 1}]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -998,10 +1151,13 @@ TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_ExtractsEachValueFrom_b_ArrayInsideSingleton2DArray) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[{b: [1, 2, 3]}]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{3U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1009,8 +1165,9 @@ TEST(BtreeKeyGeneratorTest, TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_Extracts_b_ElementInsideSingleton3DArray) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[[ {b: 1} ]]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1018,10 +1175,13 @@ TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_Extracts_b_ElementInsideSingleton TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_ExtractsEach_b_ElementInside3DArray) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[[{b: 1}, {b: 2}, {b: 3}]]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 1}")); - expectedKeys.insert(fromjson("{'': 2}")); - expectedKeys.insert(fromjson("{'': 3}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 1}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 3}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; MultikeyPaths expectedMultikeyPaths{{2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1029,8 +1189,9 @@ TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_ExtractsEach_b_ElementInside3DArr TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_ExtractsNullFrom4DArray) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[[[ {b: 1} ]]]]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': null}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': null}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{2U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1038,8 +1199,9 @@ TEST(BtreeKeyGeneratorTest, KeyPattern_a_0_0_b_ExtractsNullFrom4DArray) { TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays5) { BSONObj keyPattern = fromjson("{'a.b.1': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1, 2]}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 2}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 2}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1048,11 +1210,20 @@ TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays5) { TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays6) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1, 'a.0.b':1, 'a.b.0': 1, 'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1,2]}, {b: 3}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': {b:3}, '': 3, '': 1, '': null, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:3}, '': 3, '': 2, '': null, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}")); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + fromjson("{'': {b:3}, '': 3, '': 1, '': null, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + fromjson("{'': {b:3}, '': 3, '': 2, '': null, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U, 1U}, {2U}, {0U}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1061,11 +1232,22 @@ TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays6) { TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays7) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1, 'a.0.b':1, 'a.b.0': 1, 'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1,2]}, {b: {'0': 3}}]}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 1, '': 3, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 2, '': 3, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}")); - expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, + fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 1, '': 3, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, + fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 2, '': 3, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; MultikeyPaths expectedMultikeyPaths{{0U}, {0U, 1U}, {2U}, {0U}, std::set<size_t>{}}; ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, expectedMultikeyPaths)); } @@ -1073,8 +1255,9 @@ TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays7) { TEST(BtreeKeyGeneratorTest, GetCollationAwareIdKeyFromObject) { BSONObj keyPattern = fromjson("{_id: 1}"); BSONObj genKeysFrom = fromjson("{_id: 'foo', b: 4}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'oof'}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 'oof'}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1084,8 +1267,9 @@ TEST(BtreeKeyGeneratorTest, GetCollationAwareIdKeyFromObject) { TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromObjectSimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: 'foo'}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'oof'}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 'oof'}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1095,8 +1279,9 @@ TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromObjectSimple) { TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromObjectDotted) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: 'foo'}, c: 4}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'oof'}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 'oof'}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1106,10 +1291,13 @@ TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromObjectDotted) { TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromArraySimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: ['foo', 'bar', 'baz']}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 'oof'}")); - expectedKeys.insert(fromjson("{'': 'rab'}")); - expectedKeys.insert(fromjson("{'': 'zab'}")); + KeyString::HeapBuilder keyString1( + KeyString::Version::kLatestVersion, fromjson("{'': 'oof'}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2( + KeyString::Version::kLatestVersion, fromjson("{'': 'rab'}"), Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3( + KeyString::Version::kLatestVersion, fromjson("{'': 'zab'}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{{0U}}; ASSERT( @@ -1119,8 +1307,9 @@ TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromArraySimple) { TEST(BtreeKeyGeneratorTest, CollatorDoesNotAffectNonStringIdKey) { BSONObj keyPattern = fromjson("{_id: 1}"); BSONObj genKeysFrom = fromjson("{_id: 5, b: 4}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1130,8 +1319,9 @@ TEST(BtreeKeyGeneratorTest, CollatorDoesNotAffectNonStringIdKey) { TEST(BtreeKeyGeneratorTest, CollatorDoesNotAffectNonStringKeys) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: 5}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': 5}")); + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, fromjson("{'': 5}"), Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1141,8 +1331,10 @@ TEST(BtreeKeyGeneratorTest, CollatorDoesNotAffectNonStringKeys) { TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromNestedObject) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: {c: 'foo'}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': {c: 'oof'}}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': {c: 'oof'}}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( @@ -1152,8 +1344,10 @@ TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromNestedObject) { TEST(BtreeKeyGeneratorTest, GetCollationAwareKeysFromNestedArray) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: {c: ['foo', 'bar', 'baz']}}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(fromjson("{'': {c: ['oof', 'rab', 'zab']}}")); + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + fromjson("{'': {c: ['oof', 'rab', 'zab']}}"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); MultikeyPaths expectedMultikeyPaths{std::set<size_t>{}}; ASSERT( diff --git a/src/mongo/db/index/expression_keys_private.cpp b/src/mongo/db/index/expression_keys_private.cpp index 11ed573f27f..c7b67beef8a 100644 --- a/src/mongo/db/index/expression_keys_private.cpp +++ b/src/mongo/db/index/expression_keys_private.cpp @@ -67,7 +67,12 @@ namespace dps = ::mongo::dotted_path_support; * Insert the BSONObj into keys. * Used by getHaystackKeys. */ -void addKey(const string& root, const BSONElement& e, BSONObjSet* keys) { +void addKey(const string& root, + const BSONElement& e, + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { BSONObjBuilder buf; buf.append("", root); @@ -76,7 +81,11 @@ void addKey(const string& root, const BSONElement& e, BSONObjSet* keys) { else buf.appendAs(e, ""); - keys->insert(buf.obj()); + KeyString::HeapBuilder keyString(keyStringVersion, buf.obj(), ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); } // @@ -236,7 +245,10 @@ using std::vector; // static void ExpressionKeysPrivate::get2DKeys(const BSONObj& obj, const TwoDIndexingParams& params, - BSONObjSet* keys) { + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { BSONElementMultiSet bSet; // Get all the nested location fields, but don't return individual elements from @@ -323,7 +335,11 @@ void ExpressionKeysPrivate::get2DKeys(const BSONObj& obj, b.append("", aBuilder.arr()); } } - keys->insert(b.obj()); + KeyString::Builder keyString(keyStringVersion, b.obj(), ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.getValueCopy()); if (singleElement) break; } @@ -333,8 +349,11 @@ void ExpressionKeysPrivate::get2DKeys(const BSONObj& obj, // static void ExpressionKeysPrivate::getFTSKeys(const BSONObj& obj, const fts::FTSSpec& ftsSpec, - BSONObjSet* keys) { - fts::FTSIndexFormat::getKeys(ftsSpec, obj, keys); + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { + fts::FTSIndexFormat::getKeys(ftsSpec, obj, keys, keyStringVersion, ordering, id); } // static @@ -344,7 +363,10 @@ void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj, int hashVersion, bool isSparse, const CollatorInterface* collator, - BSONObjSet* keys) { + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { const char* cstr = hashedField.c_str(); BSONElement fieldVal = dps::extractElementAtPath(obj, cstr); @@ -363,10 +385,19 @@ void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj, if (!fieldVal.eoo()) { BSONObj key = BSON("" << makeSingleHashKey(fieldVal, seed, hashVersion)); - keys->insert(key); + KeyString::HeapBuilder keyString(keyStringVersion, key, ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); } else if (!isSparse) { BSONObj nullObj = BSON("" << BSONNULL); - keys->insert(BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion))); + BSONObj key = BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion)); + KeyString::HeapBuilder keyString(keyStringVersion, key, ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); } } @@ -381,7 +412,10 @@ void ExpressionKeysPrivate::getHaystackKeys(const BSONObj& obj, const std::string& geoField, const std::vector<std::string>& otherFields, double bucketSize, - BSONObjSet* keys) { + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { BSONElement loc = dps::extractElementAtPath(obj, geoField); if (loc.eoo()) { @@ -414,14 +448,14 @@ void ExpressionKeysPrivate::getHaystackKeys(const BSONObj& obj, // We're indexing a document that doesn't have the secondary non-geo field present. // XXX: do we want to add this even if all.size() > 0? result:empty search terms // match everything instead of only things w/empty search terms) - addKey(root, BSONElement(), keys); + addKey(root, BSONElement(), keys, keyStringVersion, ordering, id); } else { // Ex:If our secondary field is type: "foo" or type: {a:"foo", b:"bar"}, // all.size()==1. We can query on the complete field. // Ex: If our secondary field is type: ["A", "B"] all.size()==2 and all has values // "A" and "B". The query looks for any of the fields in the array. for (BSONElementSet::iterator i = all.begin(); i != all.end(); ++i) { - addKey(root, *i, keys); + addKey(root, *i, keys, keyStringVersion, ordering, id); } } } @@ -445,8 +479,11 @@ std::string ExpressionKeysPrivate::makeHaystackString(int hashedX, int hashedY) void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, const BSONObj& keyPattern, const S2IndexingParams& params, - BSONObjSet* keys, - MultikeyPaths* multikeyPaths) { + KeyStringSet* keys, + MultikeyPaths* multikeyPaths, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id) { BSONObjSet keysToAdd = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); // Does one of our documents have a geo field? @@ -560,7 +597,14 @@ void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, << " num keys: " << keysToAdd.size() << " obj inserted: " << redact(obj); } - *keys = std::move(keysToAdd); + invariant(keys->empty()); + for (const auto& key : keysToAdd) { + KeyString::HeapBuilder keyString(keyStringVersion, key, ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); + } } } // namespace mongo diff --git a/src/mongo/db/index/expression_keys_private.h b/src/mongo/db/index/expression_keys_private.h index b5fbed56e70..17e24f98bf7 100644 --- a/src/mongo/db/index/expression_keys_private.h +++ b/src/mongo/db/index/expression_keys_private.h @@ -35,6 +35,7 @@ #include "mongo/bson/bsonobj_comparator_interface.h" #include "mongo/db/hasher.h" #include "mongo/db/index/multikey_paths.h" +#include "mongo/db/storage/key_string.h" namespace mongo { @@ -59,13 +60,23 @@ public: // 2d // - static void get2DKeys(const BSONObj& obj, const TwoDIndexingParams& params, BSONObjSet* keys); + static void get2DKeys(const BSONObj& obj, + const TwoDIndexingParams& params, + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); // // FTS // - static void getFTSKeys(const BSONObj& obj, const fts::FTSSpec& ftsSpec, BSONObjSet* keys); + static void getFTSKeys(const BSONObj& obj, + const fts::FTSSpec& ftsSpec, + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); // // Hash @@ -80,7 +91,10 @@ public: int hashVersion, bool isSparse, const CollatorInterface* collator, - BSONObjSet* keys); + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); /** * Hashing function used by both getHashKeys and the cursors we create. @@ -100,7 +114,10 @@ public: const std::string& geoField, const std::vector<std::string>& otherFields, double bucketSize, - BSONObjSet* keys); + KeyStringSet* keys, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); /** * Returns a hash of a BSON element. @@ -124,8 +141,11 @@ public: static void getS2Keys(const BSONObj& obj, const BSONObj& keyPattern, const S2IndexingParams& params, - BSONObjSet* keys, - MultikeyPaths* multikeyPaths); + KeyStringSet* keys, + MultikeyPaths* multikeyPaths, + KeyString::Version keyStringVersion, + Ordering ordering, + boost::optional<RecordId> id = boost::none); }; } // namespace mongo diff --git a/src/mongo/db/index/fts_access_method.cpp b/src/mongo/db/index/fts_access_method.cpp index 46f20b77b78..0734d5fa5fd 100644 --- a/src/mongo/db/index/fts_access_method.cpp +++ b/src/mongo/db/index/fts_access_method.cpp @@ -40,10 +40,16 @@ FTSAccessMethod::FTSAccessMethod(IndexCatalogEntry* btreeState, _ftsSpec(btreeState->descriptor()->infoObj()) {} void FTSAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - ExpressionKeysPrivate::getFTSKeys(obj, _ftsSpec, keys); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + ExpressionKeysPrivate::getFTSKeys(obj, + _ftsSpec, + keys, + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering(), + id); } } // namespace mongo diff --git a/src/mongo/db/index/fts_access_method.h b/src/mongo/db/index/fts_access_method.h index f4ec0c14468..016246b0208 100644 --- a/src/mongo/db/index/fts_access_method.h +++ b/src/mongo/db/index/fts_access_method.h @@ -53,9 +53,10 @@ private: * indexes don't support tracking path-level multikey information. */ void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; fts::FTSSpec _ftsSpec; }; diff --git a/src/mongo/db/index/hash_access_method.cpp b/src/mongo/db/index/hash_access_method.cpp index 3aae1930d40..f8a58929e87 100644 --- a/src/mongo/db/index/hash_access_method.cpp +++ b/src/mongo/db/index/hash_access_method.cpp @@ -56,11 +56,20 @@ HashAccessMethod::HashAccessMethod(IndexCatalogEntry* btreeState, } void HashAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - ExpressionKeysPrivate::getHashKeys( - obj, _hashedField, _seed, _hashVersion, _descriptor->isSparse(), _collator, keys); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + ExpressionKeysPrivate::getHashKeys(obj, + _hashedField, + _seed, + _hashVersion, + _descriptor->isSparse(), + _collator, + keys, + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering(), + id); } } // namespace mongo diff --git a/src/mongo/db/index/hash_access_method.h b/src/mongo/db/index/hash_access_method.h index e96ce878625..7dd6536de75 100644 --- a/src/mongo/db/index/hash_access_method.h +++ b/src/mongo/db/index/hash_access_method.h @@ -56,9 +56,10 @@ private: * indexes don't support tracking path-level multikey information. */ void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; // Only one of our fields is hashed. This is the field name for it. std::string _hashedField; diff --git a/src/mongo/db/index/hash_key_generator_test.cpp b/src/mongo/db/index/hash_key_generator_test.cpp index e66e2bef5f6..fb84de9f5d1 100644 --- a/src/mongo/db/index/hash_key_generator_test.cpp +++ b/src/mongo/db/index/hash_key_generator_test.cpp @@ -50,28 +50,26 @@ namespace { const HashSeed kHashSeed = 0; const int kHashVersion = 0; -std::string dumpKeyset(const BSONObjSet& objs) { +std::string dumpKeyset(const KeyStringSet& keyStrings) { std::stringstream ss; ss << "[ "; - for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { - ss << i->toString() << " "; + for (auto& keyString : keyStrings) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); + ss << key.toString() << " "; } ss << "]"; return ss.str(); } -bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) { +bool assertKeysetsEqual(const KeyStringSet& expectedKeys, const KeyStringSet& actualKeys) { if (expectedKeys.size() != actualKeys.size()) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; } - if (!std::equal(expectedKeys.begin(), - expectedKeys.end(), - actualKeys.begin(), - SimpleBSONObjComparator::kInstance.makeEqualTo())) { + if (!std::equal(expectedKeys.begin(), expectedKeys.end(), actualKeys.begin())) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; @@ -80,19 +78,29 @@ bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actual return true; } -BSONObj makeHashKey(BSONElement elt) { - return BSON("" << BSONElementHasher::hash64(elt, kHashSeed)); +KeyString::Value makeHashKey(BSONElement elt) { + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << BSONElementHasher::hash64(elt, kHashSeed)), + Ordering::make(BSONObj())); + return keyString.release(); } TEST(HashKeyGeneratorTest, CollationAppliedBeforeHashing) { BSONObj obj = fromjson("{a: 'string'}"); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - ExpressionKeysPrivate::getHashKeys( - obj, "a", kHashSeed, kHashVersion, false, &collator, &actualKeys); + ExpressionKeysPrivate::getHashKeys(obj, + "a", + kHashSeed, + kHashVersion, + false, + &collator, + &actualKeys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); BSONObj backwardsObj = fromjson("{a: 'gnirts'}"); - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet expectedKeys; expectedKeys.insert(makeHashKey(backwardsObj["a"])); ASSERT(assertKeysetsEqual(expectedKeys, actualKeys)); @@ -100,12 +108,19 @@ TEST(HashKeyGeneratorTest, CollationAppliedBeforeHashing) { TEST(HashKeyGeneratorTest, CollationDoesNotAffectNonStringFields) { BSONObj obj = fromjson("{a: 5}"); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - ExpressionKeysPrivate::getHashKeys( - obj, "a", kHashSeed, kHashVersion, false, &collator, &actualKeys); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + ExpressionKeysPrivate::getHashKeys(obj, + "a", + kHashSeed, + kHashVersion, + false, + &collator, + &actualKeys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyStringSet expectedKeys; expectedKeys.insert(makeHashKey(obj["a"])); ASSERT(assertKeysetsEqual(expectedKeys, actualKeys)); @@ -114,12 +129,19 @@ TEST(HashKeyGeneratorTest, CollationDoesNotAffectNonStringFields) { TEST(HashKeyGeneratorTest, CollatorAppliedBeforeHashingNestedObject) { BSONObj obj = fromjson("{a: {b: 'string'}}"); BSONObj backwardsObj = fromjson("{a: {b: 'gnirts'}}"); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - ExpressionKeysPrivate::getHashKeys( - obj, "a", kHashSeed, kHashVersion, false, &collator, &actualKeys); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + ExpressionKeysPrivate::getHashKeys(obj, + "a", + kHashSeed, + kHashVersion, + false, + &collator, + &actualKeys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyStringSet expectedKeys; expectedKeys.insert(makeHashKey(backwardsObj["a"])); ASSERT(assertKeysetsEqual(expectedKeys, actualKeys)); @@ -127,11 +149,18 @@ TEST(HashKeyGeneratorTest, CollatorAppliedBeforeHashingNestedObject) { TEST(HashKeyGeneratorTest, NoCollation) { BSONObj obj = fromjson("{a: 'string'}"); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - ExpressionKeysPrivate::getHashKeys( - obj, "a", kHashSeed, kHashVersion, false, nullptr, &actualKeys); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; + ExpressionKeysPrivate::getHashKeys(obj, + "a", + kHashSeed, + kHashVersion, + false, + nullptr, + &actualKeys, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyStringSet expectedKeys; expectedKeys.insert(makeHashKey(obj["a"])); ASSERT(assertKeysetsEqual(expectedKeys, actualKeys)); diff --git a/src/mongo/db/index/haystack_access_method.cpp b/src/mongo/db/index/haystack_access_method.cpp index 93be4804030..bd4993ff3e5 100644 --- a/src/mongo/db/index/haystack_access_method.cpp +++ b/src/mongo/db/index/haystack_access_method.cpp @@ -65,10 +65,18 @@ HaystackAccessMethod::HaystackAccessMethod(IndexCatalogEntry* btreeState, } void HaystackAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - ExpressionKeysPrivate::getHaystackKeys(obj, _geoField, _otherFields, _bucketSize, keys); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + ExpressionKeysPrivate::getHaystackKeys(obj, + _geoField, + _otherFields, + _bucketSize, + keys, + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering(), + id); } void HaystackAccessMethod::searchCommand(OperationContext* opCtx, diff --git a/src/mongo/db/index/haystack_access_method.h b/src/mongo/db/index/haystack_access_method.h index 6c609e04cde..d434fd9ca38 100644 --- a/src/mongo/db/index/haystack_access_method.h +++ b/src/mongo/db/index/haystack_access_method.h @@ -77,9 +77,10 @@ private: * geoHaystack indexes don't support tracking path-level multikey information. */ void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; std::string _geoField; std::vector<std::string> _otherFields; diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp index 46971923f03..852460066dd 100644 --- a/src/mongo/db/index/index_access_method.cpp +++ b/src/mongo/db/index/index_access_method.cpp @@ -81,8 +81,8 @@ bool isMultikeyFromPaths(const MultikeyPaths& multikeyPaths) { [](const std::set<std::size_t>& components) { return !components.empty(); }); } -std::vector<BSONObj> asVector(const BSONObjSet& objSet) { - return {objSet.begin(), objSet.end()}; +std::vector<KeyString::Value> asVector(const KeyStringSet& keySet) { + return {keySet.begin(), keySet.end()}; } } // namespace @@ -114,7 +114,9 @@ AbstractIndexAccessMethod::AbstractIndexAccessMethod(IndexCatalogEntry* btreeSta verify(IndexDescriptor::isIndexVersionSupported(_descriptor->version())); } -bool AbstractIndexAccessMethod::isFatalError(OperationContext* opCtx, Status status, BSONObj key) { +bool AbstractIndexAccessMethod::isFatalError(OperationContext* opCtx, + Status status, + KeyString::Value key) { // If the status is Status::OK() return false immediately. if (status.isOK()) { return false; @@ -123,7 +125,7 @@ bool AbstractIndexAccessMethod::isFatalError(OperationContext* opCtx, Status sta // A document might be indexed multiple times during a background index build if it moves ahead // of the cursor (e.g. via an update). We test this scenario and swallow the error accordingly. if (status == ErrorCodes::DuplicateKeyValue && !_btreeState->isReady(opCtx)) { - LOG(3) << "key " << key << " already in index during background indexing (ok)"; + LOG(3) << "KeyString " << key << " already in index during background indexing (ok)"; return false; } return true; @@ -137,12 +139,12 @@ Status AbstractIndexAccessMethod::insert(OperationContext* opCtx, InsertResult* result) { invariant(options.fromIndexBuilder || !_btreeState->isHybridBuilding()); - BSONObjSet multikeyMetadataKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet multikeyMetadataKeys; + KeyStringSet keys; MultikeyPaths multikeyPaths; // Delegate to the subclass. - getKeys(obj, options.getKeysMode, &keys, &multikeyMetadataKeys, &multikeyPaths); + getKeys(obj, options.getKeysMode, &keys, &multikeyMetadataKeys, &multikeyPaths, loc); return insertKeys(opCtx, {keys.begin(), keys.end()}, @@ -154,8 +156,8 @@ Status AbstractIndexAccessMethod::insert(OperationContext* opCtx, } Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, - const vector<BSONObj>& keys, - const vector<BSONObj>& multikeyMetadataKeys, + const vector<KeyString::Value>& keys, + const vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, @@ -165,25 +167,24 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, // the multikey metadata keys, they should point to the reserved 'kMultikeyMetadataKeyId'. for (const auto keyVec : {&keys, &multikeyMetadataKeys}) { const auto& recordId = (keyVec == &keys ? loc : kMultikeyMetadataKeyId); - for (const auto& key : *keyVec) { + for (const auto& keyString : *keyVec) { bool unique = _descriptor->unique(); - Status status = _newInterface->insert(opCtx, key, recordId, !unique /* dupsAllowed */); + Status status = + _newInterface->insert(opCtx, keyString, recordId, !unique /* dupsAllowed */); // When duplicates are encountered and allowed, retry with dupsAllowed. Add the // key to the output vector so callers know which duplicate keys were inserted. if (ErrorCodes::DuplicateKey == status.code() && options.dupsAllowed) { invariant(unique); - status = _newInterface->insert(opCtx, key, recordId, true /* dupsAllowed */); + status = _newInterface->insert(opCtx, keyString, recordId, true /* dupsAllowed */); - // This is speculative in that the 'dupsInserted' vector is not used by any code - // today. It is currently in place to test detecting duplicate key errors during - // hybrid index builds. Duplicate detection in the future will likely not take - // place in this insert() method. if (status.isOK() && result) { + auto key = + KeyString::toBson(keyString, getSortedDataInterface()->getOrdering()); result->dupsInserted.push_back(key); } } - if (isFatalError(opCtx, status, key)) { + if (isFatalError(opCtx, status, keyString)) { return status; } } @@ -200,16 +201,16 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, } void AbstractIndexAccessMethod::removeOneKey(OperationContext* opCtx, - const BSONObj& key, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { try { - _newInterface->unindex(opCtx, key, loc, dupsAllowed); + _newInterface->unindex(opCtx, keyString, loc, dupsAllowed); } catch (AssertionException& e) { log() << "Assertion failure: _unindex failed on: " << _descriptor->parentNS() << " for index: " << _descriptor->indexName(); - log() << "Assertion failure: _unindex failed: " << redact(e) << " key:" << key.toString() + log() << "Assertion failure: _unindex failed: " << redact(e) << " KeyString:" << keyString << " dl:" << loc; logContext(); } @@ -226,7 +227,7 @@ std::unique_ptr<SortedDataInterface::Cursor> AbstractIndexAccessMethod::newCurso } Status AbstractIndexAccessMethod::removeKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys, + const std::vector<KeyString::Value>& keys, const RecordId& loc, const InsertDeleteOptions& options, int64_t* numDeleted) { @@ -244,15 +245,19 @@ Status AbstractIndexAccessMethod::initializeAsEmpty(OperationContext* opCtx) { } Status AbstractIndexAccessMethod::touch(OperationContext* opCtx, const BSONObj& obj) { - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; // There's no need to compute the prefixes of the indexed fields that cause the index to be // multikey when paging a document's index entries into memory. - BSONObjSet* multikeyMetadataKeys = nullptr; + KeyStringSet* multikeyMetadataKeys = nullptr; MultikeyPaths* multikeyPaths = nullptr; getKeys(obj, GetKeysMode::kEnforceConstraints, &keys, multikeyMetadataKeys, multikeyPaths); std::unique_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(opCtx)); - for (const auto& key : keys) { + for (const auto& keyString : keys) { + auto key = KeyString::toBson(keyString.getBuffer(), + keyString.getSize(), + getSortedDataInterface()->getOrdering(), + keyString.getTypeBits()); cursor->seekExact(key); } @@ -267,11 +272,11 @@ Status AbstractIndexAccessMethod::touch(OperationContext* opCtx) const { RecordId AbstractIndexAccessMethod::findSingle(OperationContext* opCtx, const BSONObj& requestedKey) const { // Generate the key for this index. - BSONObj actualKey; + boost::optional<KeyString::Value> actualKey; if (_btreeState->getCollator()) { // For performance, call get keys only if there is a non-simple collation. - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - BSONObjSet* multikeyMetadataKeys = nullptr; + KeyStringSet keys; + KeyStringSet* multikeyMetadataKeys = nullptr; MultikeyPaths* multikeyPaths = nullptr; getKeys(requestedKey, GetKeysMode::kEnforceConstraints, @@ -279,19 +284,25 @@ RecordId AbstractIndexAccessMethod::findSingle(OperationContext* opCtx, multikeyMetadataKeys, multikeyPaths); invariant(keys.size() == 1); - actualKey = *keys.begin(); + actualKey.emplace(std::move(*keys.begin())); } else { - actualKey = requestedKey; + KeyString::HeapBuilder requestedKeyString(getSortedDataInterface()->getKeyStringVersion(), + BSONObj::stripFieldNames(requestedKey), + getSortedDataInterface()->getOrdering()); + actualKey.emplace(requestedKeyString.release()); } std::unique_ptr<SortedDataInterface::Cursor> cursor(_newInterface->newCursor(opCtx)); const auto requestedInfo = kDebugBuild ? SortedDataInterface::Cursor::kKeyAndLoc : SortedDataInterface::Cursor::kWantLoc; - if (auto kv = cursor->seekExact(actualKey, requestedInfo)) { + auto key = KeyString::toBson(actualKey->getBuffer(), + actualKey->getSize(), + getSortedDataInterface()->getOrdering(), + actualKey->getTypeBits()); + if (auto kv = cursor->seekExact(key, requestedInfo)) { // StorageEngine should guarantee these. dassert(!kv->loc.isNull()); - dassert(kv->key.woCompare(actualKey, /*order*/ BSONObj(), /*considerFieldNames*/ false) == - 0); + dassert(kv->key.woCompare(key, /*order*/ BSONObj(), /*considerFieldNames*/ false) == 0); return kv->loc; } @@ -317,20 +328,26 @@ long long AbstractIndexAccessMethod::getSpaceUsedBytes(OperationContext* opCtx) return _newInterface->getSpaceUsedBytes(opCtx); } -pair<vector<BSONObj>, vector<BSONObj>> AbstractIndexAccessMethod::setDifference( - const BSONObjSet& left, const BSONObjSet& right) { +pair<vector<KeyString::Value>, vector<KeyString::Value>> AbstractIndexAccessMethod::setDifference( + const KeyStringSet& left, const KeyStringSet& right, Ordering ordering) { // Two iterators to traverse the two sets in sorted order. auto leftIt = left.begin(); auto rightIt = right.begin(); - vector<BSONObj> onlyLeft; - vector<BSONObj> onlyRight; + vector<KeyString::Value> onlyLeft; + vector<KeyString::Value> onlyRight; while (leftIt != left.end() && rightIt != right.end()) { - const int cmp = leftIt->woCompare(*rightIt); + const int cmp = leftIt->compare(*rightIt); if (cmp == 0) { - // 'leftIt' and 'rightIt' compare equal using woCompare(), but may not be identical, - // which should result in an index change. - if (!leftIt->binaryEqual(*rightIt)) { + /* + * 'leftIt' and 'rightIt' compare equal using compare(), but may not be identical, which + * should result in an index change. + */ + auto leftKey = KeyString::toBson( + leftIt->getBuffer(), leftIt->getSize(), ordering, leftIt->getTypeBits()); + auto rightKey = KeyString::toBson( + rightIt->getBuffer(), rightIt->getSize(), ordering, rightIt->getTypeBits()); + if (!leftKey.binaryEqual(rightKey)) { onlyLeft.push_back(*leftIt); onlyRight.push_back(*rightIt); } @@ -371,7 +388,7 @@ void AbstractIndexAccessMethod::prepareUpdate(OperationContext* opCtx, // There's no need to compute the prefixes of the indexed fields that possibly caused the // index to be multikey when the old version of the document was written since the index // metadata isn't updated when keys are deleted. - getKeys(from, getKeysMode, &ticket->oldKeys, nullptr, nullptr); + getKeys(from, getKeysMode, &ticket->oldKeys, nullptr, nullptr, record); } if (!indexFilter || indexFilter->matchesBSON(to)) { @@ -379,13 +396,15 @@ void AbstractIndexAccessMethod::prepareUpdate(OperationContext* opCtx, options.getKeysMode, &ticket->newKeys, &ticket->newMultikeyMetadataKeys, - &ticket->newMultikeyPaths); + &ticket->newMultikeyPaths, + record); } ticket->loc = record; ticket->dupsAllowed = options.dupsAllowed; - std::tie(ticket->removed, ticket->added) = setDifference(ticket->oldKeys, ticket->newKeys); + std::tie(ticket->removed, ticket->added) = + setDifference(ticket->oldKeys, ticket->newKeys, getSortedDataInterface()->getOrdering()); ticket->_isValid = true; } @@ -417,9 +436,9 @@ Status AbstractIndexAccessMethod::update(OperationContext* opCtx, const auto newMultikeyMetadataKeys = asVector(ticket.newMultikeyMetadataKeys); for (const auto keySet : {&ticket.added, &newMultikeyMetadataKeys}) { const auto& recordId = (keySet == &ticket.added ? ticket.loc : kMultikeyMetadataKeyId); - for (const auto& key : *keySet) { - Status status = _newInterface->insert(opCtx, key, recordId, ticket.dupsAllowed); - if (isFatalError(opCtx, status, key)) { + for (const auto& keyString : *keySet) { + Status status = _newInterface->insert(opCtx, keyString, recordId, ticket.dupsAllowed); + if (isFatalError(opCtx, status, keyString)) { return status; } } @@ -480,7 +499,7 @@ private: // Caches the set of all multikey metadata keys generated during the bulk build process. // These are inserted into the sorter after all normal data keys have been added, just // before the bulk build is committed. - BSONObjSet _multikeyMetadataKeys{SimpleBSONObjComparator::kInstance.makeBSONObjSet()}; + KeyStringSet _multikeyMetadataKeys; }; std::unique_ptr<IndexAccessMethod::BulkBuilder> AbstractIndexAccessMethod::initiateBulk( @@ -503,11 +522,12 @@ Status AbstractIndexAccessMethod::BulkBuilderImpl::insert(OperationContext* opCt const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options) { - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; MultikeyPaths multikeyPaths; try { - _real->getKeys(obj, options.getKeysMode, &keys, &_multikeyMetadataKeys, &multikeyPaths); + _real->getKeys( + obj, options.getKeysMode, &keys, &_multikeyMetadataKeys, &multikeyPaths, loc); } catch (...) { return exceptionToStatus(); } @@ -523,7 +543,11 @@ Status AbstractIndexAccessMethod::BulkBuilderImpl::insert(OperationContext* opCt } } - for (const auto& key : keys) { + for (const auto& keyString : keys) { + auto key = KeyString::toBson(keyString.getBuffer(), + keyString.getSize(), + _real->getSortedDataInterface()->getOrdering(), + keyString.getTypeBits()); _sorter->add(key, loc); ++_keysInserted; } @@ -547,7 +571,11 @@ bool AbstractIndexAccessMethod::BulkBuilderImpl::isMultikey() const { IndexAccessMethod::BulkBuilder::Sorter::Iterator* AbstractIndexAccessMethod::BulkBuilderImpl::done() { - for (const auto& key : _multikeyMetadataKeys) { + for (const auto& keyString : _multikeyMetadataKeys) { + auto key = KeyString::toBson(keyString.getBuffer(), + keyString.getSize(), + _real->getSortedDataInterface()->getOrdering(), + keyString.getTypeBits()); _sorter->add(key, kMultikeyMetadataKeyId); ++_keysInserted; } @@ -655,9 +683,10 @@ void AbstractIndexAccessMethod::setIndexIsMultikey(OperationContext* opCtx, Mult void AbstractIndexAccessMethod::getKeys(const BSONObj& obj, GetKeysMode mode, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { static stdx::unordered_set<int> whiteList{ErrorCodes::CannotBuildIndexKeys, // Btree ErrorCodes::CannotIndexParallelArrays, @@ -682,7 +711,7 @@ void AbstractIndexAccessMethod::getKeys(const BSONObj& obj, 13026, 13027}; try { - doGetKeys(obj, keys, multikeyMetadataKeys, multikeyPaths); + doGetKeys(obj, keys, multikeyMetadataKeys, multikeyPaths, id); } catch (const AssertionException& ex) { // Suppress all indexing errors when mode is kRelaxConstraints. if (mode == GetKeysMode::kEnforceConstraints) { @@ -712,8 +741,8 @@ void AbstractIndexAccessMethod::getKeys(const BSONObj& obj, } bool AbstractIndexAccessMethod::shouldMarkIndexAsMultikey( - const vector<BSONObj>& keys, - const vector<BSONObj>& multikeyMetadataKeys, + const vector<KeyString::Value>& keys, + const vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths) const { return (keys.size() > 1 || isMultikeyFromPaths(multikeyPaths)); } diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h index 0014b11afe4..fa6cb4e0dcf 100644 --- a/src/mongo/db/index/index_access_method.h +++ b/src/mongo/db/index/index_access_method.h @@ -91,8 +91,8 @@ public: InsertResult* result) = 0; virtual Status insertKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, @@ -103,7 +103,7 @@ public: * 'numDeleted' will be set to the number of keys removed from the index for the provided keys. */ virtual Status removeKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys, + const std::vector<KeyString::Value>& keys, const RecordId& loc, const InsertDeleteOptions& options, int64_t* numDeleted) = 0; @@ -307,17 +307,19 @@ public: */ virtual void getKeys(const BSONObj& obj, GetKeysMode mode, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const = 0; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const = 0; /** * Given the set of keys, multikeyMetadataKeys and multikeyPaths generated by a particular * document, return 'true' if the index should be marked as multikey and 'false' otherwise. */ - virtual bool shouldMarkIndexAsMultikey(const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, - const MultikeyPaths& multikeyPaths) const = 0; + virtual bool shouldMarkIndexAsMultikey( + const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, + const MultikeyPaths& multikeyPaths) const = 0; /** * Returns the intersection of 'fields' and the set of multikey metadata paths stored in the @@ -380,20 +382,15 @@ public: * prepareUpdate fills out the UpdateStatus and update actually applies it. */ struct UpdateTicket { - UpdateTicket() - : oldKeys(SimpleBSONObjComparator::kInstance.makeBSONObjSet()), - newKeys(oldKeys), - newMultikeyMetadataKeys(newKeys) {} - bool _isValid{false}; - BSONObjSet oldKeys; - BSONObjSet newKeys; + KeyStringSet oldKeys; + KeyStringSet newKeys; - BSONObjSet newMultikeyMetadataKeys; + KeyStringSet newMultikeyMetadataKeys; - std::vector<BSONObj> removed; - std::vector<BSONObj> added; + std::vector<KeyString::Value> removed; + std::vector<KeyString::Value> added; RecordId loc; bool dupsAllowed; @@ -444,8 +441,8 @@ public: * setDifference({BSON("a" << 0.0)}, {BSON("a" << 0LL)}) would result in the pair * ( {BSON("a" << 0.0)}, {BSON("a" << 0LL)} ). */ - static std::pair<std::vector<BSONObj>, std::vector<BSONObj>> setDifference( - const BSONObjSet& left, const BSONObjSet& right); + static std::pair<std::vector<KeyString::Value>, std::vector<KeyString::Value>> setDifference( + const KeyStringSet& left, const KeyStringSet& right, Ordering ordering); AbstractIndexAccessMethod(IndexCatalogEntry* btreeState, std::unique_ptr<SortedDataInterface> btree); @@ -457,15 +454,15 @@ public: InsertResult* result) final; Status insertKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, InsertResult* result) final; Status removeKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys, + const std::vector<KeyString::Value>& keys, const RecordId& loc, const InsertDeleteOptions& options, int64_t* numDeleted) final; @@ -519,12 +516,13 @@ public: void getKeys(const BSONObj& obj, GetKeysMode mode, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id = boost::none) const final; - bool shouldMarkIndexAsMultikey(const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, + bool shouldMarkIndexAsMultikey(const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths) const override; SortedDataInterface* getSortedDataInterface() const override final; @@ -545,9 +543,10 @@ protected: * information that must be stored in a reserved keyspace within the index. */ virtual void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const = 0; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const = 0; IndexCatalogEntry* const _btreeState; // owned by IndexCatalogEntry const IndexDescriptor* const _descriptor; @@ -561,7 +560,7 @@ private: * in the event that a non-fatal 'ErrorCodes::DuplicateKeyValue' is encountered during a * background index build. */ - bool isFatalError(OperationContext* opCtx, Status status, BSONObj key); + bool isFatalError(OperationContext* opCtx, Status status, KeyString::Value key); /** * Removes a single key from the index. @@ -569,7 +568,7 @@ private: * Used by remove() only. */ void removeOneKey(OperationContext* opCtx, - const BSONObj& key, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed); diff --git a/src/mongo/db/index/index_build_interceptor.cpp b/src/mongo/db/index/index_build_interceptor.cpp index fc6f6067484..04b80266722 100644 --- a/src/mongo/db/index/index_build_interceptor.cpp +++ b/src/mongo/db/index/index_build_interceptor.cpp @@ -250,7 +250,13 @@ Status IndexBuildInterceptor::_applyWrite(OperationContext* opCtx, const RecordId opRecordId = RecordId(operation["recordId"].Long()); const Op opType = (strcmp(operation.getStringField("op"), "i") == 0) ? Op::kInsert : Op::kDelete; - const BSONObjSet keySet = SimpleBSONObjComparator::kInstance.makeBSONObjSet({key}); + + KeyString::HeapBuilder keyString( + _indexCatalogEntry->accessMethod()->getSortedDataInterface()->getKeyStringVersion(), + key, + _indexCatalogEntry->ordering(), + opRecordId); + const KeyStringSet keySet{std::move(keyString.release())}; auto accessMethod = _indexCatalogEntry->accessMethod(); if (opType == Op::kInsert) { @@ -360,8 +366,8 @@ boost::optional<MultikeyPaths> IndexBuildInterceptor::getMultikeyPaths() const { } Status IndexBuildInterceptor::sideWrite(OperationContext* opCtx, - const std::vector<BSONObj>& keys, - const BSONObjSet& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, RecordId loc, Op op, @@ -391,12 +397,13 @@ Status IndexBuildInterceptor::sideWrite(OperationContext* opCtx, } std::vector<BSONObj> toInsert; - for (const auto& key : keys) { + for (const auto& keyString : keys) { // Documents inserted into this table must be consumed in insert-order. // Additionally, these writes should be timestamped with the same timestamps that the // other writes making up this operation are given. When index builds can cope with // replication rollbacks, side table writes associated with a CUD operation should // remain/rollback along with the corresponding oplog entry. + auto key = KeyString::toBson(keyString, _indexCatalogEntry->ordering()); toInsert.emplace_back(BSON("op" << (op == Op::kInsert ? "i" : "d") << "key" << key << "recordId" << loc.repr())); } @@ -405,7 +412,8 @@ Status IndexBuildInterceptor::sideWrite(OperationContext* opCtx, // Wildcard indexes write multikey path information, typically part of the catalog // document, to the index itself. Multikey information is never deleted, so we only need // to add this data on the insert path. - for (const auto& key : multikeyMetadataKeys) { + for (const auto& keyString : multikeyMetadataKeys) { + auto key = KeyString::toBson(keyString, _indexCatalogEntry->ordering()); toInsert.emplace_back(BSON("op" << "i" << "key" << key << "recordId" diff --git a/src/mongo/db/index/index_build_interceptor.h b/src/mongo/db/index/index_build_interceptor.h index f8afcd4f56a..a9b36131ed5 100644 --- a/src/mongo/db/index/index_build_interceptor.h +++ b/src/mongo/db/index/index_build_interceptor.h @@ -69,8 +69,8 @@ public: * On success, `numKeysOut` if non-null will contain the number of keys added or removed. */ Status sideWrite(OperationContext* opCtx, - const std::vector<BSONObj>& keys, - const BSONObjSet& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, RecordId loc, Op op, diff --git a/src/mongo/db/index/s2_access_method.cpp b/src/mongo/db/index/s2_access_method.cpp index 6881641b23b..777415dc1a5 100644 --- a/src/mongo/db/index/s2_access_method.cpp +++ b/src/mongo/db/index/s2_access_method.cpp @@ -125,10 +125,18 @@ StatusWith<BSONObj> S2AccessMethod::fixSpec(const BSONObj& specObj) { } void S2AccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - ExpressionKeysPrivate::getS2Keys(obj, _descriptor->keyPattern(), _params, keys, multikeyPaths); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + ExpressionKeysPrivate::getS2Keys(obj, + _descriptor->keyPattern(), + _params, + keys, + multikeyPaths, + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering(), + id); } } // namespace mongo diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h index 4851f6fbc7d..e8ff0bd2cb1 100644 --- a/src/mongo/db/index/s2_access_method.h +++ b/src/mongo/db/index/s2_access_method.h @@ -65,9 +65,10 @@ private: * be multikey as a result of inserting 'keys'. */ void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; S2IndexingParams _params; diff --git a/src/mongo/db/index/s2_key_generator_test.cpp b/src/mongo/db/index/s2_key_generator_test.cpp index 93fc8ac545d..9e158f4cb87 100644 --- a/src/mongo/db/index/s2_key_generator_test.cpp +++ b/src/mongo/db/index/s2_key_generator_test.cpp @@ -49,11 +49,12 @@ using namespace mongo; namespace { -std::string dumpKeyset(const BSONObjSet& objs) { +std::string dumpKeyset(const KeyStringSet& keyStrings) { std::stringstream ss; ss << "[ "; - for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { - ss << i->toString() << " "; + for (auto& keyString : keyStrings) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); + ss << key.toString() << " "; } ss << "]"; @@ -76,17 +77,14 @@ std::string dumpMultikeyPaths(const MultikeyPaths& multikeyPaths) { return ss.str(); } -bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) { +bool assertKeysetsEqual(const KeyStringSet& expectedKeys, const KeyStringSet& actualKeys) { if (expectedKeys.size() != actualKeys.size()) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; } - if (!std::equal(expectedKeys.begin(), - expectedKeys.end(), - actualKeys.begin(), - SimpleBSONObjComparator::kInstance.makeEqualTo())) { + if (!std::equal(expectedKeys.begin(), expectedKeys.end(), actualKeys.begin())) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; @@ -120,14 +118,21 @@ long long getCellID(int x, int y, bool multiPoint = false) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; // There's no need to compute the prefixes of the indexed fields that cause the index to be // multikey when computing the cell id of the geo field. MultikeyPaths* multikeyPaths = nullptr; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &keys, multikeyPaths); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &keys, + multikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); ASSERT_EQUALS(1U, keys.size()); - return (*keys.begin()).firstElement().Long(); + auto key = KeyString::toBson(*keys.begin(), Ordering::make(BSONObj())); + return key.firstElement().Long(); } TEST(S2KeyGeneratorTest, GetS2KeysFromSubobjectWithArrayOfGeoAndNonGeoSubobjects) { @@ -141,16 +146,30 @@ TEST(S2KeyGeneratorTest, GetS2KeysFromSubobjectWithArrayOfGeoAndNonGeoSubobjects CollatorInterfaceMock* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys( - genKeysFrom, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(0, 0))); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(3, 3))); - expectedKeys.insert(BSON("" << 2 << "" << getCellID(0, 0))); - expectedKeys.insert(BSON("" << 2 << "" << getCellID(3, 3))); + ExpressionKeysPrivate::getS2Keys(genKeysFrom, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(3, 3)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + BSON("" << 2 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + BSON("" << 2 << "" << getCellID(3, 3)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{{1U}, {1U}}, actualMultikeyPaths); @@ -167,15 +186,26 @@ TEST(S2KeyGeneratorTest, GetS2KeysFromArrayOfNonGeoSubobjectsWithArrayValues) { CollatorInterfaceMock* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys( - genKeysFrom, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(0, 0))); - expectedKeys.insert(BSON("" << 2 << "" << getCellID(0, 0))); - expectedKeys.insert(BSON("" << 3 << "" << getCellID(0, 0))); + ExpressionKeysPrivate::getS2Keys(genKeysFrom, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + BSON("" << 2 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + BSON("" << 3 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{{0U, 1U}, std::set<size_t>{}}, actualMultikeyPaths); @@ -190,16 +220,27 @@ TEST(S2KeyGeneratorTest, GetS2KeysFromMultiPointInGeoField) { CollatorInterfaceMock* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys( - genKeysFrom, keyPattern, params, &actualKeys, &actualMultikeyPaths); + ExpressionKeysPrivate::getS2Keys(genKeysFrom, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); const bool multiPoint = true; - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(0, 0, multiPoint))); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(1, 0, multiPoint))); - expectedKeys.insert(BSON("" << 1 << "" << getCellID(1, 1, multiPoint))); + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(0, 0, multiPoint)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(1, 0, multiPoint)), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + BSON("" << 1 << "" << getCellID(1, 1, multiPoint)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release(), keyString3.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, {0U}}, actualMultikeyPaths); @@ -213,13 +254,21 @@ TEST(S2KeyGeneratorTest, CollationAppliedToNonGeoStringFieldAfterGeoField) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "gnirts")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "gnirts"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -234,14 +283,22 @@ TEST(S2KeyGeneratorTest, CollationAppliedToNonGeoStringFieldBeforeGeoField) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" - << "gnirts" - << "" << getCellID(0, 0))); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" + << "gnirts" + << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -256,15 +313,23 @@ TEST(S2KeyGeneratorTest, CollationAppliedToAllNonGeoStringFields) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" - << "gnirts" - << "" << getCellID(0, 0) << "" - << "2gnirts")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" + << "gnirts" + << "" << getCellID(0, 0) << "" + << "2gnirts"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual( @@ -280,13 +345,21 @@ TEST(S2KeyGeneratorTest, CollationAppliedToNonGeoStringFieldWithMultiplePathComp CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "gnirts")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "gnirts"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -301,15 +374,25 @@ TEST(S2KeyGeneratorTest, CollationAppliedToStringsInArray) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "gnirts")); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "2gnirts")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "gnirts"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "2gnirts"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString1.release(), keyString2.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, {0U}}, actualMultikeyPaths); @@ -324,27 +407,42 @@ TEST(S2KeyGeneratorTest, CollationAppliedToStringsInAllArrays) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "gnirts" - << "" - << "cba")); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "gnirts" - << "" - << "fed")); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "2gnirts" - << "" - << "cba")); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "2gnirts" - << "" - << "fed")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString1(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "gnirts" + << "" + << "cba"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString2(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "gnirts" + << "" + << "fed"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString3(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "2gnirts" + << "" + << "cba"), + Ordering::make(BSONObj())); + KeyString::HeapBuilder keyString4(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "2gnirts" + << "" + << "fed"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{ + keyString1.release(), keyString2.release(), keyString3.release(), keyString4.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, {0U}, {0U}}, actualMultikeyPaths); @@ -358,12 +456,20 @@ TEST(S2KeyGeneratorTest, CollationDoesNotAffectNonStringFields) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" << 5)); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" << 5), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -378,14 +484,22 @@ TEST(S2KeyGeneratorTest, CollationAppliedToStringsInNestedObjects) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); ExpressionParams::initialize2dsphereParams(infoObj, &collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << BSON("c" - << "gnirts"))); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << BSON("c" + << "gnirts")), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -400,13 +514,21 @@ TEST(S2KeyGeneratorTest, NoCollation) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" - << "string")); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" + << "string"), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, std::set<size_t>{}}, @@ -421,12 +543,20 @@ TEST(S2KeyGeneratorTest, EmptyArrayForLeadingFieldIsConsideredMultikey) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << BSONUndefined << "" << getCellID(0, 0))); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << BSONUndefined << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{{0U}, std::set<size_t>{}}, actualMultikeyPaths); @@ -440,12 +570,20 @@ TEST(S2KeyGeneratorTest, EmptyArrayForTrailingFieldIsConsideredMultikey) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << getCellID(0, 0) << "" << BSONUndefined)); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << getCellID(0, 0) << "" << BSONUndefined), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{std::set<size_t>{}, {0U}}, actualMultikeyPaths); @@ -459,12 +597,20 @@ TEST(S2KeyGeneratorTest, SingleElementTrailingArrayIsConsideredMultikey) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << 99 << "" << getCellID(0, 0))); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << 99 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{{1U}, std::set<size_t>{}}, actualMultikeyPaths); @@ -478,12 +624,20 @@ TEST(S2KeyGeneratorTest, MidPathSingleElementArrayIsConsideredMultikey) { const CollatorInterface* collator = nullptr; ExpressionParams::initialize2dsphereParams(infoObj, collator, ¶ms); - BSONObjSet actualKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet actualKeys; MultikeyPaths actualMultikeyPaths; - ExpressionKeysPrivate::getS2Keys(obj, keyPattern, params, &actualKeys, &actualMultikeyPaths); - - BSONObjSet expectedKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - expectedKeys.insert(BSON("" << 99 << "" << getCellID(0, 0))); + ExpressionKeysPrivate::getS2Keys(obj, + keyPattern, + params, + &actualKeys, + &actualMultikeyPaths, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())); + + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, + BSON("" << 99 << "" << getCellID(0, 0)), + Ordering::make(BSONObj())); + KeyStringSet expectedKeys{keyString.release()}; assertKeysetsEqual(expectedKeys, actualKeys); assertMultikeyPathsEqual(MultikeyPaths{{0U}, std::set<size_t>{}}, actualMultikeyPaths); diff --git a/src/mongo/db/index/sort_key_generator.cpp b/src/mongo/db/index/sort_key_generator.cpp index 82122a65448..d47308c5e62 100644 --- a/src/mongo/db/index/sort_key_generator.cpp +++ b/src/mongo/db/index/sort_key_generator.cpp @@ -68,7 +68,12 @@ SortKeyGenerator::SortKeyGenerator(SortPattern sortPattern, const CollatorInterf } constexpr bool isSparse = false; - _indexKeyGen = std::make_unique<BtreeKeyGenerator>(fieldNames, fixed, isSparse, _collator); + _indexKeyGen = std::make_unique<BtreeKeyGenerator>(fieldNames, + fixed, + isSparse, + _collator, + KeyString::Version::kLatestVersion, + Ordering::make(_sortSpecWithoutMeta)); } StatusWith<BSONObj> SortKeyGenerator::getSortKey(const WorkingSetMember& wsm) const { @@ -162,10 +167,7 @@ StatusWith<BSONObj> SortKeyGenerator::getSortKeyFromDocumentWithoutMetadata( // The keys themselves will incorporate the collation, with strings translated to their // corresponding collation keys. Therefore, we use the simple string comparator when comparing // the keys themselves. - const StringData::ComparatorInterface* stringComparator = nullptr; - BSONObjComparator patternCmp( - _sortSpecWithoutMeta, BSONObjComparator::FieldNamesMode::kConsider, stringComparator); - BSONObjSet keys = patternCmp.makeBSONObjSet(); + KeyStringSet keys; try { // There's no need to compute the prefixes of the indexed fields that cause the index to be @@ -187,7 +189,7 @@ StatusWith<BSONObj> SortKeyGenerator::getSortKeyFromDocumentWithoutMetadata( invariant(!keys.empty()); // The sort key is the first index key, ordered according to the pattern '_sortSpecWithoutMeta'. - return *keys.begin(); + return KeyString::toBson(*keys.begin(), Ordering::make(_sortSpecWithoutMeta)); } } // namespace mongo diff --git a/src/mongo/db/index/wildcard_access_method.cpp b/src/mongo/db/index/wildcard_access_method.cpp index 4269d3dcab2..8a6a5564e0a 100644 --- a/src/mongo/db/index/wildcard_access_method.cpp +++ b/src/mongo/db/index/wildcard_access_method.cpp @@ -40,21 +40,25 @@ namespace mongo { WildcardAccessMethod::WildcardAccessMethod(IndexCatalogEntry* wildcardState, std::unique_ptr<SortedDataInterface> btree) : AbstractIndexAccessMethod(wildcardState, std::move(btree)), - _keyGen( - _descriptor->keyPattern(), _descriptor->pathProjection(), _btreeState->getCollator()) {} + _keyGen(_descriptor->keyPattern(), + _descriptor->pathProjection(), + _btreeState->getCollator(), + getSortedDataInterface()->getKeyStringVersion(), + getSortedDataInterface()->getOrdering()) {} bool WildcardAccessMethod::shouldMarkIndexAsMultikey( - const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, + const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths) const { return !multikeyMetadataKeys.empty(); } void WildcardAccessMethod::doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const { - _keyGen.generateKeys(obj, keys, multikeyMetadataKeys); + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const { + _keyGen.generateKeys(obj, keys, multikeyMetadataKeys, id); } FieldRef WildcardAccessMethod::extractMultikeyPathFromIndexKey(const IndexKeyEntry& entry) { diff --git a/src/mongo/db/index/wildcard_access_method.h b/src/mongo/db/index/wildcard_access_method.h index 7fde78eb256..3390261a0f8 100644 --- a/src/mongo/db/index/wildcard_access_method.h +++ b/src/mongo/db/index/wildcard_access_method.h @@ -66,8 +66,8 @@ public: * more multikey metadata keys have been generated; that is, if the 'multikeyMetadataKeys' * vector is non-empty. */ - bool shouldMarkIndexAsMultikey(const std::vector<BSONObj>& keys, - const std::vector<BSONObj>& multikeyMetadataKeys, + bool shouldMarkIndexAsMultikey(const std::vector<KeyString::Value>& keys, + const std::vector<KeyString::Value>& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths) const final; /** @@ -94,9 +94,10 @@ public: private: void doGetKeys(const BSONObj& obj, - BSONObjSet* keys, - BSONObjSet* multikeyMetadataKeys, - MultikeyPaths* multikeyPaths) const final; + KeyStringSet* keys, + KeyStringSet* multikeyMetadataKeys, + MultikeyPaths* multikeyPaths, + boost::optional<RecordId> id) const final; std::set<FieldRef> _getMultikeyPathSet(OperationContext* opCtx, const IndexBounds& indexBounds, diff --git a/src/mongo/db/index/wildcard_key_generator.cpp b/src/mongo/db/index/wildcard_key_generator.cpp index f8a6ff57da4..6056c41d278 100644 --- a/src/mongo/db/index/wildcard_key_generator.cpp +++ b/src/mongo/db/index/wildcard_key_generator.cpp @@ -93,23 +93,31 @@ std::unique_ptr<ProjectionExecAgg> WildcardKeyGenerator::createProjectionExec( WildcardKeyGenerator::WildcardKeyGenerator(BSONObj keyPattern, BSONObj pathProjection, - const CollatorInterface* collator) - : _collator(collator), _keyPattern(keyPattern) { + const CollatorInterface* collator, + KeyString::Version keyStringVersion, + Ordering ordering) + : _collator(collator), + _keyPattern(keyPattern), + _keyStringVersion(keyStringVersion), + _ordering(ordering) { _projExec = createProjectionExec(keyPattern, pathProjection); } void WildcardKeyGenerator::generateKeys(BSONObj inputDoc, - BSONObjSet* keys, - BSONObjSet* multikeyPaths) const { + KeyStringSet* keys, + KeyStringSet* multikeyPaths, + boost::optional<RecordId> id) const { FieldRef rootPath; - _traverseWildcard(_projExec->applyProjection(inputDoc), false, &rootPath, keys, multikeyPaths); + _traverseWildcard( + _projExec->applyProjection(inputDoc), false, &rootPath, keys, multikeyPaths, id); } void WildcardKeyGenerator::_traverseWildcard(BSONObj obj, bool objIsArray, FieldRef* path, - BSONObjSet* keys, - BSONObjSet* multikeyPaths) const { + KeyStringSet* keys, + KeyStringSet* multikeyPaths, + boost::optional<RecordId> id) const { for (const auto elem : obj) { // If the element's fieldName contains a ".", fast-path skip it because it's not queryable. if (elem.fieldNameStringData().find('.', 0) != std::string::npos) @@ -121,22 +129,22 @@ void WildcardKeyGenerator::_traverseWildcard(BSONObj obj, switch (elem.type()) { case BSONType::Array: // If this is a nested array, we don't descend it but instead index it as a value. - if (_addKeyForNestedArray(elem, *path, objIsArray, keys)) + if (_addKeyForNestedArray(elem, *path, objIsArray, keys, id)) break; // Add an entry for the multi-key path, and then fall through to BSONType::Object. _addMultiKey(*path, multikeyPaths); case BSONType::Object: - if (_addKeyForEmptyLeaf(elem, *path, keys)) + if (_addKeyForEmptyLeaf(elem, *path, keys, id)) break; _traverseWildcard( - elem.Obj(), elem.type() == BSONType::Array, path, keys, multikeyPaths); + elem.Obj(), elem.type() == BSONType::Array, path, keys, multikeyPaths, id); break; default: - _addKey(elem, *path, keys); + _addKey(elem, *path, keys, id); } // Remove the element's fieldname from the path, if it was pushed onto it earlier. @@ -147,10 +155,11 @@ void WildcardKeyGenerator::_traverseWildcard(BSONObj obj, bool WildcardKeyGenerator::_addKeyForNestedArray(BSONElement elem, const FieldRef& fullPath, bool enclosingObjIsArray, - BSONObjSet* keys) const { + KeyStringSet* keys, + boost::optional<RecordId> id) const { // If this element is an array whose parent is also an array, index it as a value. if (enclosingObjIsArray && elem.type() == BSONType::Array) { - _addKey(elem, fullPath, keys); + _addKey(elem, fullPath, keys, id); return true; } return false; @@ -158,12 +167,13 @@ bool WildcardKeyGenerator::_addKeyForNestedArray(BSONElement elem, bool WildcardKeyGenerator::_addKeyForEmptyLeaf(BSONElement elem, const FieldRef& fullPath, - BSONObjSet* keys) const { + KeyStringSet* keys, + boost::optional<RecordId> id) const { invariant(elem.isABSONObj()); if (elem.embeddedObject().isEmpty()) { // In keeping with the behaviour of regular indexes, an empty object is indexed as-is while // empty arrays are indexed as 'undefined'. - _addKey(elem.type() == BSONType::Array ? BSONElement{} : elem, fullPath, keys); + _addKey(elem.type() == BSONType::Array ? BSONElement{} : elem, fullPath, keys, id); return true; } return false; @@ -171,7 +181,8 @@ bool WildcardKeyGenerator::_addKeyForEmptyLeaf(BSONElement elem, void WildcardKeyGenerator::_addKey(BSONElement elem, const FieldRef& fullPath, - BSONObjSet* keys) const { + KeyStringSet* keys, + boost::optional<RecordId> id) const { // Wildcard keys are of the form { "": "path.to.field", "": <collation-aware value> }. BSONObjBuilder bob; bob.append("", fullPath.dottedField()); @@ -180,15 +191,26 @@ void WildcardKeyGenerator::_addKey(BSONElement elem, } else { bob.appendUndefined(""); } - keys->insert(bob.obj()); + KeyString::HeapBuilder keyString(_keyStringVersion, bob.obj(), _ordering); + if (id) { + keyString.appendRecordId(*id); + } + keys->insert(keyString.release()); } -void WildcardKeyGenerator::_addMultiKey(const FieldRef& fullPath, BSONObjSet* multikeyPaths) const { +void WildcardKeyGenerator::_addMultiKey(const FieldRef& fullPath, + KeyStringSet* multikeyPaths) const { // Multikey paths are denoted by a key of the form { "": 1, "": "path.to.array" }. The argument // 'multikeyPaths' may be nullptr if the access method is being used in an operation which does // not require multikey path generation. if (multikeyPaths) { - multikeyPaths->insert(BSON("" << 1 << "" << fullPath.dottedField())); + auto key = BSON("" << 1 << "" << fullPath.dottedField()); + KeyString::HeapBuilder keyString( + _keyStringVersion, + key, + _ordering, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); + multikeyPaths->insert(keyString.release()); } } diff --git a/src/mongo/db/index/wildcard_key_generator.h b/src/mongo/db/index/wildcard_key_generator.h index ea76bd1a0af..f5c8524c751 100644 --- a/src/mongo/db/index/wildcard_key_generator.h +++ b/src/mongo/db/index/wildcard_key_generator.h @@ -32,6 +32,8 @@ #include "mongo/db/exec/projection_exec_agg.h" #include "mongo/db/field_ref.h" #include "mongo/db/query/collation/collator_interface.h" +#include "mongo/db/storage/key_string.h" +#include "mongo/db/storage/sorted_data_interface.h" namespace mongo { @@ -54,7 +56,9 @@ public: WildcardKeyGenerator(BSONObj keyPattern, BSONObj pathProjection, - const CollatorInterface* collator); + const CollatorInterface* collator, + KeyString::Version keyStringVersion, + Ordering ordering); /** * Returns a pointer to the key generator's underlying ProjectionExecAgg. @@ -65,35 +69,48 @@ public: /** * Applies the appropriate Wildcard projection to the input doc, and then adds one key-value - * pair to the BSONObjSet 'keys' for each leaf node in the post-projection document: + * pair to the set 'keys' for each leaf node in the post-projection document: * { '': 'path.to.field', '': <collation-aware-field-value> } * Also adds one entry to 'multikeyPaths' for each array encountered in the post-projection * document, in the following format: * { '': 1, '': 'path.to.array' } */ - void generateKeys(BSONObj inputDoc, BSONObjSet* keys, BSONObjSet* multikeyPaths) const; + void generateKeys(BSONObj inputDoc, + KeyStringSet* keys, + KeyStringSet* multikeyPaths, + boost::optional<RecordId> id = boost::none) const; private: // Traverses every path of the post-projection document, adding keys to the set as it goes. void _traverseWildcard(BSONObj obj, bool objIsArray, FieldRef* path, - BSONObjSet* keys, - BSONObjSet* multikeyPaths) const; + KeyStringSet* keys, + KeyStringSet* multikeyPaths, + boost::optional<RecordId> id) const; // Helper functions to format the entry appropriately before adding it to the key/path tracker. - void _addMultiKey(const FieldRef& fullPath, BSONObjSet* multikeyPaths) const; - void _addKey(BSONElement elem, const FieldRef& fullPath, BSONObjSet* keys) const; + void _addMultiKey(const FieldRef& fullPath, KeyStringSet* multikeyPaths) const; + void _addKey(BSONElement elem, + const FieldRef& fullPath, + KeyStringSet* keys, + boost::optional<RecordId> id) const; // Helper to check whether the element is a nested array, and conditionally add it to 'keys'. bool _addKeyForNestedArray(BSONElement elem, const FieldRef& fullPath, bool enclosingObjIsArray, - BSONObjSet* keys) const; - bool _addKeyForEmptyLeaf(BSONElement elem, const FieldRef& fullPath, BSONObjSet* keys) const; + KeyStringSet* keys, + boost::optional<RecordId> id) const; + bool _addKeyForEmptyLeaf(BSONElement elem, + const FieldRef& fullPath, + KeyStringSet* keys, + boost::optional<RecordId> id) const; std::unique_ptr<ProjectionExecAgg> _projExec; const CollatorInterface* _collator; const BSONObj _keyPattern; + const KeyString::Version _keyStringVersion; + const Ordering _ordering; }; } // namespace mongo diff --git a/src/mongo/db/index/wildcard_key_generator_test.cpp b/src/mongo/db/index/wildcard_key_generator_test.cpp index aeb0383c7ee..4d5d7f9f3c1 100644 --- a/src/mongo/db/index/wildcard_key_generator_test.cpp +++ b/src/mongo/db/index/wildcard_key_generator_test.cpp @@ -40,32 +40,39 @@ namespace mongo { namespace { -BSONObjSet makeKeySet(std::initializer_list<BSONObj> init = {}) { - return SimpleBSONObjComparator::kInstance.makeBSONObjSet(std::move(init)); +KeyStringSet makeKeySet(std::initializer_list<BSONObj> init = {}, RecordId id = RecordId()) { + KeyStringSet keys; + Ordering ordering = Ordering::make(BSONObj()); + for (const auto& key : init) { + KeyString::HeapBuilder keyString(KeyString::Version::kLatestVersion, key, ordering); + if (!id.isNull()) { + keyString.appendRecordId(id); + } + keys.insert(keyString.release()); + } + return keys; } -std::string dumpKeyset(const BSONObjSet& objs) { +std::string dumpKeyset(const KeyStringSet& keyStrings) { std::stringstream ss; ss << "[ "; - for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { - ss << i->toString() << " "; + for (auto& keyString : keyStrings) { + auto key = KeyString::toBson(keyString, Ordering::make(BSONObj())); + ss << key.toString() << " "; } ss << "]"; return ss.str(); } -bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actualKeys) { +bool assertKeysetsEqual(const KeyStringSet& expectedKeys, const KeyStringSet& actualKeys) { if (expectedKeys.size() != actualKeys.size()) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; } - if (!std::equal(expectedKeys.begin(), - expectedKeys.end(), - actualKeys.begin(), - SimpleBSONObjComparator::kInstance.makeEqualTo())) { + if (!std::equal(expectedKeys.begin(), expectedKeys.end(), actualKeys.begin())) { log() << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys); return false; @@ -77,7 +84,11 @@ bool assertKeysetsEqual(const BSONObjSet& expectedKeys, const BSONObjSet& actual // Full-document tests with no projection. TEST(WildcardKeyGeneratorFullDocumentTest, ExtractTopLevelKey) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: 1}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': 1}")}); @@ -92,7 +103,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractTopLevelKey) { } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractKeysFromNestedObject) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: {b: 'one', c: 2}}"); auto expectedKeys = @@ -100,8 +115,8 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractKeysFromNestedObject) { auto expectedMultikeyPaths = makeKeySet(); - auto outputKeys = makeKeySet(); - auto multikeyMetadataKeys = makeKeySet(); + KeyStringSet outputKeys; + KeyStringSet multikeyMetadataKeys; keyGen.generateKeys(inputDoc, &outputKeys, &multikeyMetadataKeys); ASSERT(assertKeysetsEqual(expectedKeys, outputKeys)); @@ -109,7 +124,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractKeysFromNestedObject) { } TEST(WildcardKeyGeneratorFullDocumentTest, ShouldIndexEmptyObject) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: 1, b: {}}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': 1}"), fromjson("{'': 'b', '': {}}")}); @@ -124,17 +143,23 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ShouldIndexEmptyObject) { } TEST(WildcardKeyGeneratorFullDocumentTest, ShouldIndexNonNestedEmptyArrayAsUndefined) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{ a: [], b: {c: []}, d: [[], {e: []}]}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': undefined}"), fromjson("{'': 'b.c', '': undefined}"), fromjson("{'': 'd', '': []}"), fromjson("{'': 'd.e', '': undefined}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}"), - fromjson("{'': 1, '': 'b.c'}"), - fromjson("{'': 1, '': 'd'}"), - fromjson("{'': 1, '': 'd.e'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}"), + fromjson("{'': 1, '': 'b.c'}"), + fromjson("{'': 1, '': 'd'}"), + fromjson("{'': 1, '': 'd.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -145,7 +170,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ShouldIndexNonNestedEmptyArrayAsUndef } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPath) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: [1, 2, {b: 'one', c: 2}, {d: 3}]}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': 1}"), @@ -154,7 +183,9 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPath) { fromjson("{'': 'a.c', '': 2}"), fromjson("{'': 'a.d', '': 3}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -165,7 +196,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPath) { } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPathAndDedupKeys) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {d: 3}]}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': 1}"), @@ -174,7 +209,9 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPathAndDedupKeys) { fromjson("{'': 'a.c', '': 2}"), fromjson("{'': 'a.d', '': 3}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -185,7 +222,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMultikeyPathAndDedupKeys) { } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractZeroElementMultikeyPath) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {d: 3}], e: []}"); auto expectedKeys = makeKeySet({fromjson("{'': 'a', '': 1}"), @@ -196,7 +237,8 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractZeroElementMultikeyPath) { fromjson("{'': 'e', '': undefined}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -207,7 +249,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractZeroElementMultikeyPath) { } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractNestedMultikeyPaths) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; // Note: the 'e' array is nested within a subdocument in the enclosing 'a' array; it will // generate a separate multikey entry 'a.e' and index keys for each of its elements. The raw @@ -227,7 +273,8 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractNestedMultikeyPaths) { fromjson("{'': 'a.e', '': 5}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -238,7 +285,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractNestedMultikeyPaths) { } TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMixedPathTypesAndAllSubpaths) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; // Tests a mix of multikey paths, various duplicate-key scenarios, and deeply-nested paths. auto inputDoc = fromjson( @@ -261,10 +312,12 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMixedPathTypesAndAllSubpaths) fromjson("{'': 'g.h.k', '': 12.0}"), fromjson("{'': 'l', '': 'string'}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}"), - fromjson("{'': 1, '': 'a.e'}"), - fromjson("{'': 1, '': 'g.h.j'}"), - fromjson("{'': 1, '': 'g.h.j.k'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}"), + fromjson("{'': 1, '': 'a.e'}"), + fromjson("{'': 1, '': 'g.h.j'}"), + fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -277,7 +330,11 @@ TEST(WildcardKeyGeneratorFullDocumentTest, ExtractMixedPathTypesAndAllSubpaths) // Single-subtree implicit projection. TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithSinglePathComponent) { - WildcardKeyGenerator keyGen{fromjson("{'g.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'g.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -290,7 +347,8 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithSinglePathComponen fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -301,7 +359,11 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithSinglePathComponen } TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithMultiplePathComponents) { - WildcardKeyGenerator keyGen{fromjson("{'g.h.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'g.h.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -314,7 +376,8 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithMultiplePathCompon fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -325,7 +388,11 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractSubtreeWithMultiplePathCompon } TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'g.h.j.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'g.h.j.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -336,7 +403,8 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractMultikeySubtree) { fromjson("{'': 'g.h.j.k', '': 11.5}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -347,7 +415,11 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractMultikeySubtree) { } TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractNestedMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'a.e.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'a.e.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -360,7 +432,8 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractNestedMultikeySubtree) { fromjson("{'': 'a.e', '': 5}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -373,7 +446,11 @@ TEST(WildcardKeyGeneratorSingleSubtreeTest, ExtractNestedMultikeySubtree) { // Explicit inclusion tests. TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionSingleSubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{g: 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{g: 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -386,7 +463,8 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionSingleSubtree) { fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -397,7 +475,11 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionSingleSubtree) { } TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedSubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'g.h': 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'g.h': 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -410,7 +492,8 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedSubtree) { fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -421,7 +504,11 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedSubtree) { } TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'g.h.j': 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'g.h.j': 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -432,7 +519,8 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultikeySubtree) { fromjson("{'': 'g.h.j.k', '': 11.5}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}); + makeKeySet({fromjson("{'': 1, '': 'g.h.j'}"), fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -443,7 +531,11 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultikeySubtree) { } TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'a.e': 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'a.e': 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -454,7 +546,8 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedMultikeySubtree fromjson("{'': 'a.e', '': 5}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -465,8 +558,11 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionNestedMultikeySubtree } TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultipleSubtrees) { - WildcardKeyGenerator keyGen{ - fromjson("{'$**': 1}"), fromjson("{'a.b': 1, 'a.c': 1, 'a.e': 1, 'g.h.i': 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'a.b': 1, 'a.c': 1, 'a.e': 1, 'g.h.i': 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -480,7 +576,8 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultipleSubtrees) { fromjson("{'': 'g.h.i', '': 9}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -493,7 +590,11 @@ TEST(WildcardKeyGeneratorInclusionTest, InclusionProjectionMultipleSubtrees) { // Explicit exclusion tests. TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionSingleSubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{g: 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{g: 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -511,7 +612,8 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionSingleSubtree) { fromjson("{'': 'l', '': 'string'}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -522,7 +624,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionSingleSubtree) { } TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedSubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'g.h': 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'g.h': 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -541,7 +647,8 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedSubtree) { fromjson("{'': 'l', '': 'string'}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -552,7 +659,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedSubtree) { } TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'g.h.j': 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'g.h.j': 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -572,7 +683,8 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultikeySubtree) { fromjson("{'': 'l', '': 'string'}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -583,7 +695,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultikeySubtree) { } TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedMultikeySubtree) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'a.e': 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'a.e': 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -603,9 +719,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedMultikeySubtree fromjson("{'': 'g.h.k', '': 12}"), fromjson("{'': 'l', '': 'string'}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}"), - fromjson("{'': 1, '': 'g.h.j'}"), - fromjson("{'': 1, '': 'g.h.j.k'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}"), + fromjson("{'': 1, '': 'g.h.j'}"), + fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -616,8 +734,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionNestedMultikeySubtree } TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultipleSubtrees) { - WildcardKeyGenerator keyGen{ - fromjson("{'$**': 1}"), fromjson("{'a.b': 0, 'a.c': 0, 'a.e': 0, 'g.h.i': 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'a.b': 0, 'a.c': 0, 'a.e': 0, 'g.h.i': 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{a: [1, 2, {b: 'one', c: 2}, {c: 2, d: 3}, {c: 'two', d: 3, e: [4, 5]}, [6, 7, {f: 8}]], " @@ -634,9 +755,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultipleSubtrees) { fromjson("{'': 'g.h.k', '': 12.0}"), fromjson("{'': 'l', '': 'string'}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}"), - fromjson("{'': 1, '': 'g.h.j'}"), - fromjson("{'': 1, '': 'g.h.j.k'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}"), + fromjson("{'': 1, '': 'g.h.j'}"), + fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -649,7 +772,11 @@ TEST(WildcardKeyGeneratorExclusionTest, ExclusionProjectionMultipleSubtrees) { // Test _id inclusion and exclusion behaviour. TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfProjectionIsEmpty) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -662,7 +789,8 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfProjectionIsEmpty) { fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -673,7 +801,11 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfProjectionIsEmpty) { } TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldForSingleSubtreeKeyPattern) { - WildcardKeyGenerator keyGen{fromjson("{'a.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'a.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -684,7 +816,8 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldForSingleSubtreeKeyPattern) { fromjson("{'': 'a.e', '': 4}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -695,7 +828,11 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldForSingleSubtreeKeyPattern) { } TEST(WildcardKeyGeneratorIdTest, PermitIdFieldAsSingleSubtreeKeyPattern) { - WildcardKeyGenerator keyGen{fromjson("{'_id.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'_id.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -714,7 +851,11 @@ TEST(WildcardKeyGeneratorIdTest, PermitIdFieldAsSingleSubtreeKeyPattern) { } TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldAsSingleSubtreeKeyPattern) { - WildcardKeyGenerator keyGen{fromjson("{'_id.id1.$**': 1}"), {}, nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'_id.id1.$**': 1}"), + {}, + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -732,7 +873,11 @@ TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldAsSingleSubtreeKeyPattern) { } TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldByDefaultForInclusionProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{a: 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{a: 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -743,7 +888,8 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldByDefaultForInclusionProjection) fromjson("{'': 'a.e', '': 4}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -754,7 +900,11 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldByDefaultForInclusionProjection) } TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldInclusionInExplicitProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'_id.id1': 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'_id.id1': 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -772,7 +922,11 @@ TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldInclusionInExplicitProjection) } TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldByDefaultForExclusionProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{a: 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{a: 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -791,7 +945,11 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldByDefaultForExclusionProjection) } TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldExclusionInExplicitProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{'_id.id1': 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{'_id.id1': 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -805,7 +963,8 @@ TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldExclusionInExplicitProjection) fromjson("{'': 'g.h.k', '': 12.0}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -816,7 +975,11 @@ TEST(WildcardKeyGeneratorIdTest, PermitIdSubfieldExclusionInExplicitProjection) } TEST(WildcardKeyGeneratorIdTest, IncludeIdFieldIfExplicitlySpecifiedInProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{_id: 1, a: 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{_id: 1, a: 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -829,7 +992,8 @@ TEST(WildcardKeyGeneratorIdTest, IncludeIdFieldIfExplicitlySpecifiedInProjection fromjson("{'': 'a.e', '': 4}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -840,7 +1004,11 @@ TEST(WildcardKeyGeneratorIdTest, IncludeIdFieldIfExplicitlySpecifiedInProjection } TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfExplicitlySpecifiedInProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{_id: 0, a: 1}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{_id: 0, a: 1}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -851,7 +1019,8 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfExplicitlySpecifiedInProjection fromjson("{'': 'a.e', '': 4}")}); auto expectedMultikeyPaths = - makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}); + makeKeySet({fromjson("{'': 1, '': 'a'}"), fromjson("{'': 1, '': 'a.e'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -862,7 +1031,11 @@ TEST(WildcardKeyGeneratorIdTest, ExcludeIdFieldIfExplicitlySpecifiedInProjection } TEST(WildcardKeyGeneratorIdTest, IncludeIdFieldIfExplicitlySpecifiedInExclusionProjection) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), fromjson("{_id: 1, a: 0}"), nullptr}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + fromjson("{_id: 1, a: 0}"), + nullptr, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{_id: {id1: 1, id2: 2}, a: [1, {b: 1, e: [4]}, [6, 7, {f: 8}]], g: {h: {i: 9, k: 12.0}}}"); @@ -886,7 +1059,11 @@ TEST(WildcardKeyGeneratorIdTest, IncludeIdFieldIfExplicitlySpecifiedInExclusionP TEST(WildcardKeyGeneratorCollationTest, CollationMixedPathAndKeyTypes) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, &collator}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + &collator, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; // Verify that the collation is only applied to String values, but all types are indexed. auto dateVal = "{'$date': 1529453450288}"_sd; @@ -916,10 +1093,12 @@ TEST(WildcardKeyGeneratorCollationTest, CollationMixedPathAndKeyTypes) { fromjson("{'': 'g.h.k', '': 12.0}"), fromjson("{'': 'l', '': 'gnirts'}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'a'}"), - fromjson("{'': 1, '': 'a.e'}"), - fromjson("{'': 1, '': 'g.h.j'}"), - fromjson("{'': 1, '': 'g.h.j.k'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'a'}"), + fromjson("{'': 1, '': 'a.e'}"), + fromjson("{'': 1, '': 'g.h.j'}"), + fromjson("{'': 1, '': 'g.h.j.k'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -930,7 +1109,11 @@ TEST(WildcardKeyGeneratorCollationTest, CollationMixedPathAndKeyTypes) { } TEST(WildcardKeyGeneratorDottedFieldsTest, DoNotIndexDottedFields) { - WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), {}, {}}; + WildcardKeyGenerator keyGen{fromjson("{'$**': 1}"), + {}, + {}, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson( "{'a.b': 0, '.b': 1, 'b.': 2, a: {'.b': 3, 'b.': 4, 'b.c': 5, 'q': 6}, b: [{'d.e': 7}, {r: " @@ -941,7 +1124,9 @@ TEST(WildcardKeyGeneratorDottedFieldsTest, DoNotIndexDottedFields) { fromjson("{'': 'b', '': [{'a.b': 9}]}"), fromjson("{'': 'c', '': 10}")}); - auto expectedMultikeyPaths = makeKeySet({fromjson("{'': 1, '': 'b'}")}); + auto expectedMultikeyPaths = + makeKeySet({fromjson("{'': 1, '': 'b'}")}, + RecordId{RecordId::ReservedId::kWildcardMultikeyMetadataId}); auto outputKeys = makeKeySet(); auto multikeyMetadataKeys = makeKeySet(); @@ -952,7 +1137,11 @@ TEST(WildcardKeyGeneratorDottedFieldsTest, DoNotIndexDottedFields) { } TEST(WildcardKeyGeneratorDottedFieldsTest, DoNotIndexDottedFieldsWithSimilarSubpathInKey) { - WildcardKeyGenerator keyGen{fromjson("{'a.b.$**': 1}"), {}, {}}; + WildcardKeyGenerator keyGen{fromjson("{'a.b.$**': 1}"), + {}, + {}, + KeyString::Version::kLatestVersion, + Ordering::make(BSONObj())}; auto inputDoc = fromjson("{'a.b': 0}"); diff --git a/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp b/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp index 15d604fe069..f6a5003edde 100644 --- a/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp +++ b/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp @@ -65,16 +65,6 @@ bool hasFieldNames(const BSONObj& obj) { return false; } -// This just makes all the fields in a BSON object equal to "". -BSONObj stripFieldNames(const BSONObj& obj) { - BSONObjIterator it(obj); - BSONObjBuilder bob; - while (it.more()) { - bob.appendAs(it.next(), ""); - } - return bob.obj(); -} - // This function converts a key and an ordering to a KeyString::Builder. std::unique_ptr<KeyString::Builder> keyToKeyStringBuilder(const BSONObj& key, Ordering order) { KeyString::Version version = KeyString::Version::V1; @@ -109,7 +99,7 @@ std::string createKeyString(const BSONObj& key, return std::string(ks.getBuffer(), ks.getSize()); } -std::string createKeyString(const KeyString::Builder& keyString, +std::string createKeyString(const KeyString::Value& keyString, const RecordId& loc, std::string prefixToUse, bool isUnique) { @@ -210,13 +200,12 @@ Status SortedDataBuilderInterface::addKey(const BSONObj& key, const RecordId& lo invariant(loc.isNormal() || loc.isReserved()); invariant(!hasFieldNames(key)); - KeyString::Builder keyString(KeyString::Version::V1, key, _order, loc); + KeyString::HeapBuilder keyString(KeyString::Version::V1, key, _order, loc); - return addKey(keyString, loc); + return addKey(std::move(keyString.release()), loc); } -Status SortedDataBuilderInterface::addKey(const KeyString::Builder& keyString, - const RecordId& loc) { +Status SortedDataBuilderInterface::addKey(const KeyString::Value& keyString, const RecordId& loc) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead()); @@ -242,8 +231,7 @@ Status SortedDataBuilderInterface::addKey(const KeyString::Builder& keyString, if (twoKeyCmp == 0 && twoRIDCmp != 0) { if (!_dupsAllowed) { - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _order, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _order); return buildDupKeyErrorStatus(key, _collectionNamespace, _indexName, _keyPattern); } // Duplicate index entries are allowed on this unique index, so we put the RecordId in the @@ -324,13 +312,13 @@ Status SortedDataInterface::insert(OperationContext* opCtx, const RecordId& loc, bool dupsAllowed) { // The KeyString representation of the key. - KeyString::Builder keyString(_keyStringVersion, key, _ordering, loc); + KeyString::HeapBuilder keyString(_keyStringVersion, key, _ordering, loc); - return insert(opCtx, keyString, loc, dupsAllowed); + return insert(opCtx, std::move(keyString.release()), loc, dupsAllowed); } Status SortedDataInterface::insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); @@ -360,10 +348,7 @@ Status SortedDataInterface::insert(OperationContext* opCtx, } else { // There was an attempt to create an index entry with a different RecordId while // dups were not allowed. - auto key = KeyString::toBson(keyString.getBuffer(), - keyString.getSize(), - _ordering, - keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); return buildDupKeyErrorStatus( key, _collectionNamespace, _indexName, _keyPattern); } @@ -398,13 +383,13 @@ void SortedDataInterface::unindex(OperationContext* opCtx, const BSONObj& key, const RecordId& loc, bool dupsAllowed) { - KeyString::Builder keyString(_keyStringVersion, key, _ordering, loc); + KeyString::HeapBuilder keyString(_keyStringVersion, key, _ordering, loc); - unindex(opCtx, keyString, loc, dupsAllowed); + unindex(opCtx, std::move(keyString.release()), loc, dupsAllowed); } void SortedDataInterface::unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); @@ -630,7 +615,7 @@ bool SortedDataInterface::Cursor::checkCursorValid() { // RecordId in the KeyString and use a "<" comparison instead of "<=" since we know // that no RecordId will ever reach RecordId::max() so we don't need to check the // equal side of things. This assumption doesn't hold for unique index KeyStrings. - BSONObj strippedBSON = stripFieldNames(*_endPosKey); + BSONObj strippedBSON = BSONObj::stripFieldNames(*_endPosKey); std::string endPosKeyString = createKeyString(strippedBSON, RecordId::max(), _prefix, _order, _isUnique); @@ -653,7 +638,7 @@ bool SortedDataInterface::Cursor::checkCursorValid() { if (*_endPosReverse == _workingCopy->rend()) return true; - BSONObj strippedBSON = stripFieldNames(*_endPosKey); + BSONObj strippedBSON = BSONObj::stripFieldNames(*_endPosKey); std::string endPosKeyString = createKeyString(strippedBSON, RecordId::min(), _prefix, _order, _isUnique); @@ -670,7 +655,7 @@ bool SortedDataInterface::Cursor::checkCursorValid() { } void SortedDataInterface::Cursor::setEndPosition(const BSONObj& key, bool inclusive) { - auto finalKey = stripFieldNames(key); + auto finalKey = BSONObj::stripFieldNames(key); StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead()); if (finalKey.isEmpty()) { _endPos = boost::none; @@ -808,7 +793,7 @@ boost::optional<IndexKeyEntry> SortedDataInterface::Cursor::seekAfterProcessing( boost::optional<IndexKeyEntry> SortedDataInterface::Cursor::seek(const BSONObj& key, bool inclusive, RequestedInfo parts) { - BSONObj finalKey = stripFieldNames(key); + BSONObj finalKey = BSONObj::stripFieldNames(key); _lastMoveWasRestore = false; _atEOF = false; diff --git a/src/mongo/db/storage/biggie/biggie_sorted_impl.h b/src/mongo/db/storage/biggie/biggie_sorted_impl.h index 6c7e9aacb21..d5183bf530c 100644 --- a/src/mongo/db/storage/biggie/biggie_sorted_impl.h +++ b/src/mongo/db/storage/biggie/biggie_sorted_impl.h @@ -49,7 +49,7 @@ public: const BSONObj& keyPattern); void commit(bool mayInterrupt) override; virtual Status addKey(const BSONObj& key, const RecordId& loc); - virtual Status addKey(const KeyString::Builder& keyString, const RecordId& loc); + virtual Status addKey(const KeyString::Value& keyString, const RecordId& loc); private: OperationContext* _opCtx; @@ -86,7 +86,7 @@ public: const RecordId& loc, bool dupsAllowed) override; virtual Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) override; virtual void unindex(OperationContext* opCtx, @@ -94,7 +94,7 @@ public: const RecordId& loc, bool dupsAllowed) override; virtual void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) override; virtual Status dupKeyCheck(OperationContext* opCtx, const BSONObj& key) override; diff --git a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp index a661666c19d..b1ea7aeee94 100644 --- a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp +++ b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp @@ -173,7 +173,7 @@ public: return Status::OK(); } - virtual Status addKey(const KeyString::Builder& keyString, const RecordId& loc) { + virtual Status addKey(const KeyString::Value& keyString, const RecordId& loc) { return Status::OK(); } }; @@ -197,7 +197,7 @@ public: } virtual Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { return Status::OK(); @@ -209,7 +209,7 @@ public: bool dupsAllowed) {} virtual void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) {} diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp index 3e9476642bd..73de310adf0 100644 --- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp +++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp @@ -48,26 +48,6 @@ using std::vector; namespace { - -bool hasFieldNames(const BSONObj& obj) { - BSONForEach(e, obj) { - if (e.fieldName()[0]) - return true; - } - return false; -} - -BSONObj stripFieldNames(const BSONObj& query) { - if (!hasFieldNames(query)) - return query; - - BSONObjBuilder bb; - BSONForEach(e, query) { - bb.appendAs(e, StringData()); - } - return bb.obj(); -} - typedef std::set<IndexKeyEntry, IndexEntryComparison> IndexSet; bool keyExists(const IndexSet& data, const BSONObj& key) { @@ -111,7 +91,7 @@ public: // inserts should be in ascending (key, RecordId) order. invariant(loc.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); if (!_data->empty()) { // Compare specified key with last inserted key, ignoring its RecordId @@ -131,11 +111,10 @@ public: return Status::OK(); } - Status addKey(const KeyString::Builder& keyString, const RecordId& loc) { + Status addKey(const KeyString::Value& keyString, const RecordId& loc) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _ordering, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); return addKey(key, loc); } @@ -186,7 +165,7 @@ public: const RecordId& loc, bool dupsAllowed) { invariant(loc.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); // TODO optimization: save the iterator from the dup-check to speed up insert @@ -202,13 +181,12 @@ public: } virtual Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _ordering, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); return insert(opCtx, key, loc, dupsAllowed); } @@ -218,7 +196,7 @@ public: const RecordId& loc, bool dupsAllowed) { invariant(loc.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); IndexKeyEntry entry(key.getOwned(), loc); const size_t numDeleted = _data->erase(entry); @@ -230,13 +208,12 @@ public: } virtual void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) { dassert(loc == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _ordering, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); return unindex(opCtx, key, loc, dupsAllowed); } @@ -259,7 +236,7 @@ public: } virtual Status dupKeyCheck(OperationContext* opCtx, const BSONObj& key) { - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); if (isDup(*_data, key)) return buildDupKeyErrorStatus(key, _collectionNamespace, _indexName, _keyPattern); return Status::OK(); @@ -307,7 +284,7 @@ public: // NOTE: this uses the opposite min/max rules as a normal seek because a forward // scan should land after the key if inclusive and before if exclusive. - _endState = EndState(stripFieldNames(key), + _endState = EndState(BSONObj::stripFieldNames(key), _forward == inclusive ? RecordId::max() : RecordId::min()); seekEndCursor(); } @@ -322,7 +299,7 @@ public: return {}; } } else { - const BSONObj query = stripFieldNames(key); + const BSONObj query = BSONObj::stripFieldNames(key); locate(query, _forward == inclusive ? RecordId::min() : RecordId::max()); _lastMoveWasRestore = false; if (_isEOF) diff --git a/src/mongo/db/storage/key_string.cpp b/src/mongo/db/storage/key_string.cpp index a1f23adba73..de20dc242c6 100644 --- a/src/mongo/db/storage/key_string.cpp +++ b/src/mongo/db/storage/key_string.cpp @@ -2099,72 +2099,36 @@ std::string BuilderBase<BufferT>::toString() const { return toHex(getBuffer(), getSize()); } -template <class BufferT> -int BuilderBase<BufferT>::compare(const BuilderBase<BufferT>& other) const { - int a = getSize(); - int b = other.getSize(); - - int min = std::min(a, b); - - int cmp = memcmp(getBuffer(), other.getBuffer(), min); - - if (cmp) { - if (cmp < 0) - return -1; - return 1; - } - - // keys match - - if (a == b) - return 0; - - return a < b ? -1 : 1; +std::string Value::toString() const { + return toHex(getBuffer(), getSize()); } -template <class BufferT> -int BuilderBase<BufferT>::compareWithoutRecordId(const BuilderBase<BufferT>& other) const { - int a = !isEmpty() ? sizeWithoutRecordIdAtEnd(getBuffer(), getSize()) : 0; - int b = !other.isEmpty() ? sizeWithoutRecordIdAtEnd(other.getBuffer(), other.getSize()) : 0; - - int min = std::min(a, b); - - int cmp = memcmp(getBuffer(), other.getBuffer(), min); - - if (cmp) { - if (cmp < 0) - return -1; - return 1; +TypeBits& TypeBits::operator=(const TypeBits& tb) { + if (&tb == this) { + return *this; } - // keys match + version = tb.version; + _curBit = tb._curBit; + _isAllZeros = tb._isAllZeros; - if (a == b) - return 0; + _buf.reset(); + _buf.appendBuf(tb._buf.buf(), tb._buf.len()); - return a < b ? -1 : 1; + return *this; } -int Value::compare(const Value& other) const { - int a = getSize(); - int b = other.getSize(); - - int min = std::min(a, b); - - int cmp = memcmp(getBuffer(), other.getBuffer(), min); - - if (cmp) { - if (cmp < 0) - return -1; - return 1; +Value& Value::operator=(const Value& other) { + if (&other == this) { + return *this; } - // keys match - - if (a == b) - return 0; + _version = other._version; + _typeBits = other._typeBits; + _size = other._size; + _buffer = other._buffer; - return a < b ? -1 : 1; + return *this; } uint32_t TypeBits::readSizeFromBuffer(BufReader* reader) { @@ -2417,6 +2381,25 @@ RecordId decodeRecordId(BufReader* reader) { return RecordId(repr); } +int compare(const char* leftBuf, const char* rightBuf, size_t leftSize, size_t rightSize) { + int min = std::min(leftSize, rightSize); + + int cmp = memcmp(leftBuf, rightBuf, min); + + if (cmp) { + if (cmp < 0) + return -1; + return 1; + } + + // keys match + + if (leftSize == rightSize) + return 0; + + return leftSize < rightSize ? -1 : 1; +} + template class BuilderBase<BufBuilder>; template class BuilderBase<StackBufBuilder>; diff --git a/src/mongo/db/storage/key_string.h b/src/mongo/db/storage/key_string.h index df992e1009d..2727162d15d 100644 --- a/src/mongo/db/storage/key_string.h +++ b/src/mongo/db/storage/key_string.h @@ -76,7 +76,7 @@ public: _buf.appendBuf(tb._buf.buf(), tb._buf.len()); } - TypeBits& operator=(const TypeBits& tb) = delete; + TypeBits& operator=(const TypeBits& tb); TypeBits(TypeBits&&) = default; TypeBits& operator=(TypeBits&&) = default; @@ -293,12 +293,22 @@ public: Value(Version version, TypeBits typeBits, size_t size, ConstSharedBuffer buffer) : _version(version), _typeBits(typeBits), _size(size), _buffer(std::move(buffer)) {} - int compare(const Value& other) const; + Value& operator=(const Value& other); + + template <class T> + int compare(const T& other) const; + + template <class T> + int compareWithoutRecordId(const T& other) const; size_t getSize() const { return _size; } + bool isEmpty() const { + return _size == 0; + } + const char* getBuffer() const { return _buffer.get(); } @@ -307,37 +317,18 @@ public: return _typeBits; } + /** + * Returns a hex encoding of this key. + */ + std::string toString() const; + private: - const Version _version; + Version _version; TypeBits _typeBits; size_t _size; ConstSharedBuffer _buffer; }; -inline bool operator<(const Value& lhs, const Value& rhs) { - return lhs.compare(rhs) < 0; -} - -inline bool operator<=(const Value& lhs, const Value& rhs) { - return lhs.compare(rhs) <= 0; -} - -inline bool operator==(const Value& lhs, const Value& rhs) { - return lhs.compare(rhs) == 0; -} - -inline bool operator>(const Value& lhs, const Value& rhs) { - return lhs.compare(rhs) > 0; -} - -inline bool operator>=(const Value& lhs, const Value& rhs) { - return lhs.compare(rhs) >= 0; -} - -inline bool operator!=(const Value& lhs, const Value& rhs) { - return !(lhs == rhs); -} - enum class Discriminator { kInclusive, // Anything to be stored in an index must use this. kExclusiveBefore, @@ -420,7 +411,7 @@ public: _prepareForRelease(); invariant(_state == BuildState::kEndAdded || _state == BuildState::kAppendedRecordID || _state == BuildState::kAppendedTypeBits); - BufBuilder newBuf; + BufBuilder newBuf(_buffer.len()); newBuf.appendBuf(_buffer.buf(), _buffer.len()); return {version, _typeBits, static_cast<size_t>(newBuf.len()), newBuf.release()}; } @@ -478,8 +469,11 @@ public: return _typeBits; } - int compare(const BuilderBase& other) const; - int compareWithoutRecordId(const BuilderBase& other) const; + template <class T> + int compare(const T& other) const; + + template <class T> + int compareWithoutRecordId(const T& other) const; /** * @return a hex encoding of this key @@ -598,38 +592,58 @@ private: using Builder = BuilderBase<StackBufBuilder>; using HeapBuilder = BuilderBase<BufBuilder>; +/* + * The isKeyString struct allows the operators below to only be enabled if the types being operated + * on are KeyStrings. + */ +template <class T> +struct isKeyString : public std::false_type {}; + template <class BufferT> -inline bool operator<(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +struct isKeyString<BuilderBase<BufferT>> : public std::true_type {}; + +template <> +struct isKeyString<Value> : public std::true_type {}; + +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator<(const T& lhs, + const T& rhs) { return lhs.compare(rhs) < 0; } -template <class BufferT> -inline bool operator<=(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator<=(const T& lhs, + const T& rhs) { return lhs.compare(rhs) <= 0; } -template <class BufferT> -inline bool operator==(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator==(const T& lhs, + const T& rhs) { return lhs.compare(rhs) == 0; } -template <class BufferT> -inline bool operator>(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator>(const T& lhs, + const T& rhs) { return lhs.compare(rhs) > 0; } -template <class BufferT> -inline bool operator>=(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator>=(const T& lhs, + const T& rhs) { return lhs.compare(rhs) >= 0; } -template <class BufferT> -inline bool operator!=(const BuilderBase<BufferT>& lhs, const BuilderBase<BufferT>& rhs) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, bool>::type operator!=(const T& lhs, + const T& rhs) { return !(lhs == rhs); } -template <class BufferT> -inline std::ostream& operator<<(std::ostream& stream, const BuilderBase<BufferT>& value) { +template <class T> +inline typename std::enable_if<isKeyString<T>::value, std::ostream&>::type operator<<( + std::ostream& stream, const T& value) { return stream << value.toString(); } @@ -646,6 +660,11 @@ BSONObj toBson(StringData data, Ordering ord, const TypeBits& types); BSONObj toBson(const char* buffer, size_t len, Ordering ord, const TypeBits& types) noexcept; BSONObj toBsonSafe(const char* buffer, size_t len, Ordering ord, const TypeBits& types); +template <class T> +BSONObj toBson(const T& keyString, Ordering ord) noexcept { + return toBson(keyString.getBuffer(), keyString.getSize(), ord, keyString.getTypeBits()); +} + /** * Decodes a RecordId from the end of a buffer. */ @@ -661,6 +680,40 @@ size_t sizeWithoutRecordIdAtEnd(const void* bufferRaw, size_t bufSize); */ RecordId decodeRecordId(BufReader* reader); +int compare(const char* leftBuf, const char* rightBuf, size_t leftSize, size_t rightSize); + +template <class BufferT> +template <class T> +int BuilderBase<BufferT>::compare(const T& other) const { + return KeyString::compare(getBuffer(), other.getBuffer(), getSize(), other.getSize()); +} + +template <class BufferT> +template <class T> +int BuilderBase<BufferT>::compareWithoutRecordId(const T& other) const { + return KeyString::compare( + getBuffer(), + other.getBuffer(), + !isEmpty() ? sizeWithoutRecordIdAtEnd(getBuffer(), getSize()) : 0, + !other.isEmpty() ? sizeWithoutRecordIdAtEnd(other.getBuffer(), other.getSize()) : 0); +} + +template <class T> +int Value::compare(const T& other) const { + return KeyString::compare(getBuffer(), other.getBuffer(), getSize(), other.getSize()); +} + +template <class T> +int Value::compareWithoutRecordId(const T& other) const { + return KeyString::compare( + getBuffer(), + other.getBuffer(), + !isEmpty() ? sizeWithoutRecordIdAtEnd(getBuffer(), getSize()) : 0, + !other.isEmpty() ? sizeWithoutRecordIdAtEnd(other.getBuffer(), other.getSize()) : 0); +} + } // namespace KeyString +using KeyStringSet = std::set<KeyString::Value>; + } // namespace mongo diff --git a/src/mongo/db/storage/mobile/mobile_index.cpp b/src/mongo/db/storage/mobile/mobile_index.cpp index d5e794c6507..96dd6b690e0 100644 --- a/src/mongo/db/storage/mobile/mobile_index.cpp +++ b/src/mongo/db/storage/mobile/mobile_index.cpp @@ -46,28 +46,6 @@ namespace { using std::shared_ptr; using std::string; using std::vector; - -// BTree stuff - -bool hasFieldNames(const BSONObj& obj) { - BSONForEach(e, obj) { - if (e.fieldName()[0]) - return true; - } - return false; -} - -BSONObj stripFieldNames(const BSONObj& query) { - if (!hasFieldNames(query)) - return query; - - BSONObjBuilder bb; - BSONForEach(e, query) { - bb.appendAs(e, StringData()); - } - return bb.obj(); -} - } // namespace MobileIndex::MobileIndex(OperationContext* opCtx, @@ -86,15 +64,15 @@ Status MobileIndex::insert(OperationContext* opCtx, const RecordId& recId, bool dupsAllowed) { invariant(recId.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); - KeyString::Builder keyString(_keyStringVersion, key, _ordering, recId); + KeyString::HeapBuilder keyString(_keyStringVersion, key, _ordering, recId); - return insert(opCtx, keyString, recId, dupsAllowed); + return insert(opCtx, std::move(keyString.release()), recId, dupsAllowed); } Status MobileIndex::insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) { return _insert(opCtx, keyString, recId, dupsAllowed); @@ -144,18 +122,18 @@ void MobileIndex::unindex(OperationContext* opCtx, const RecordId& recId, bool dupsAllowed) { invariant(recId.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); - KeyString::Builder keyString(_keyStringVersion, key, _ordering, recId); + KeyString::HeapBuilder keyString(_keyStringVersion, key, _ordering, recId); - return unindex(opCtx, keyString, recId, dupsAllowed); + unindex(opCtx, std::move(keyString.release()), recId, dupsAllowed); } void MobileIndex::unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) { - return _unindex(opCtx, keyString, recId, dupsAllowed); + _unindex(opCtx, keyString, recId, dupsAllowed); } void MobileIndex::_doDelete(OperationContext* opCtx, @@ -254,7 +232,7 @@ Status MobileIndex::create(OperationContext* opCtx, const std::string& ident) { } Status MobileIndex::dupKeyCheck(OperationContext* opCtx, const BSONObj& key) { - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); invariant(_isUnique); if (_isDup(opCtx, key)) @@ -295,14 +273,14 @@ public: Status addKey(const BSONObj& key, const RecordId& recId) override { invariant(recId.isValid()); - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); - KeyString::Builder keyString( + KeyString::HeapBuilder keyString( _index->getKeyStringVersion(), key, _index->getOrdering(), recId); - return addKey(keyString, recId); + return addKey(std::move(keyString.release()), recId); } - Status addKey(const KeyString::Builder& keyString, const RecordId& recId) override { + Status addKey(const KeyString::Value& keyString, const RecordId& recId) override { Status status = _checkNextKey(keyString); if (!status.isOK()) { return status; @@ -320,21 +298,18 @@ protected: * Checks whether the new key to be inserted is > or >= the previous one depending * on _dupsAllowed. */ - Status _checkNextKey(const KeyString::Builder& keyString) { - const int cmp = keyString.compare(_lastKeyString); + Status _checkNextKey(const KeyString::Value& keyString) { + const int cmp = _lastKeyString.compare(keyString); if (!_dupsAllowed && cmp == 0) { - auto key = KeyString::toBson(keyString.getBuffer(), - keyString.getSize(), - _index->getOrdering(), - keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _index->getOrdering()); return buildDupKeyErrorStatus(key, _collectionNamespace, _indexName, _keyPattern); - } else if (cmp < 0) { + } else if (cmp > 0) { return Status(ErrorCodes::InternalError, "expected higher RecordId in bulk builder"); } return Status::OK(); } - virtual Status _addKey(const KeyString::Builder& keyString, const RecordId& recId) = 0; + virtual Status _addKey(const KeyString::Value& keyString, const RecordId& recId) = 0; MobileIndex* _index; OperationContext* const _opCtx; @@ -359,7 +334,7 @@ public: : BulkBuilderBase(index, opCtx, dupsAllowed, collectionNamespace, indexName, keyPattern) {} protected: - Status _addKey(const KeyString::Builder& keyString, const RecordId&) override { + Status _addKey(const KeyString::Value& keyString, const RecordId&) override { KeyString::TypeBits typeBits = keyString.getTypeBits(); return _index->doInsert( _opCtx, keyString.getBuffer(), keyString.getSize(), typeBits, typeBits, false); @@ -383,7 +358,7 @@ public: } protected: - Status _addKey(const KeyString::Builder& keyString, const RecordId& recId) override { + Status _addKey(const KeyString::Value& keyString, const RecordId& recId) override { dassert(recId == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); @@ -457,13 +432,14 @@ public: ? KeyString::Discriminator::kExclusiveAfter : KeyString::Discriminator::kExclusiveBefore; _endPosition = std::make_unique<KeyString::Builder>(_index.getKeyStringVersion()); - _endPosition->resetToKey(stripFieldNames(key), _index.getOrdering(), discriminator); + _endPosition->resetToKey( + BSONObj::stripFieldNames(key), _index.getOrdering(), discriminator); } boost::optional<IndexKeyEntry> seek(const BSONObj& key, bool inclusive, RequestedInfo parts) override { - const BSONObj startKey = stripFieldNames(key); + const BSONObj startKey = BSONObj::stripFieldNames(key); // By using a discriminator other than kInclusive, there is no need to distinguish // unique vs non-unique key formats since both start with the key. const auto discriminator = _isForward == inclusive @@ -701,7 +677,7 @@ std::unique_ptr<SortedDataInterface::Cursor> MobileIndexStandard::newCursor(Oper } Status MobileIndexStandard::_insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) { invariant(dupsAllowed); @@ -712,7 +688,7 @@ Status MobileIndexStandard::_insert(OperationContext* opCtx, } void MobileIndexStandard::_unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId&, bool dupsAllowed) { invariant(dupsAllowed); @@ -739,7 +715,7 @@ std::unique_ptr<SortedDataInterface::Cursor> MobileIndexUnique::newCursor(Operat } Status MobileIndexUnique::_insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) { // Replication is not supported so dups are not allowed. @@ -761,7 +737,7 @@ Status MobileIndexUnique::_insert(OperationContext* opCtx, } void MobileIndexUnique::_unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) { // Replication is not supported so dups are not allowed. diff --git a/src/mongo/db/storage/mobile/mobile_index.h b/src/mongo/db/storage/mobile/mobile_index.h index eee3befe307..8855f0c7e1f 100644 --- a/src/mongo/db/storage/mobile/mobile_index.h +++ b/src/mongo/db/storage/mobile/mobile_index.h @@ -58,7 +58,7 @@ public: bool dupsAllowed) override; Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; @@ -68,7 +68,7 @@ public: bool dupsAllowed) override; void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; @@ -128,12 +128,12 @@ protected: KeyString::Builder* value = nullptr); virtual Status _insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) = 0; virtual void _unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) = 0; @@ -162,12 +162,12 @@ public: protected: Status _insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; void _unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; }; @@ -185,12 +185,12 @@ public: protected: Status _insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; void _unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& recId, bool dupsAllowed) override; diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h index d2c3608a073..1f0408d688a 100644 --- a/src/mongo/db/storage/sorted_data_interface.h +++ b/src/mongo/db/storage/sorted_data_interface.h @@ -107,7 +107,7 @@ public: * at a RecordId other than 'loc' and duplicates were not allowed */ virtual Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) = 0; @@ -131,7 +131,7 @@ public: * otherwise */ virtual void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& loc, bool dupsAllowed) = 0; @@ -421,7 +421,7 @@ public: * this is violated an error Status (ErrorCodes::InternalError) will be returned. */ virtual Status addKey(const BSONObj& key, const RecordId& loc) = 0; - virtual Status addKey(const KeyString::Builder& keyString, const RecordId& loc) = 0; + virtual Status addKey(const KeyString::Value& keyString, const RecordId& loc) = 0; /** * Do any necessary work to finish building the tree. diff --git a/src/mongo/db/storage/sorted_data_interface_test_bulkbuilder.cpp b/src/mongo/db/storage/sorted_data_interface_test_bulkbuilder.cpp index e2cf3ff4757..76bba035b5f 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_bulkbuilder.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_bulkbuilder.cpp @@ -83,7 +83,7 @@ TEST(SortedDataInterface, BuilderAddKeyString) { const std::unique_ptr<SortedDataBuilderInterface> builder( sorted->getBulkBuilder(opCtx.get(), true)); - ASSERT_OK(builder->addKey(keyString1, loc1)); + ASSERT_OK(builder->addKey(keyString1.getValueCopy(), loc1)); builder->commit(false); } @@ -200,8 +200,9 @@ TEST(SortedDataInterface, BuilderAddSameKeyString) { const std::unique_ptr<SortedDataBuilderInterface> builder( sorted->getBulkBuilder(opCtx.get(), false)); - ASSERT_OK(builder->addKey(keyStringLoc1, loc1)); - ASSERT_EQUALS(ErrorCodes::DuplicateKey, builder->addKey(keyStringLoc2, loc2)); + ASSERT_OK(builder->addKey(keyStringLoc1.getValueCopy(), loc1)); + ASSERT_EQUALS(ErrorCodes::DuplicateKey, + builder->addKey(keyStringLoc2.getValueCopy(), loc2)); builder->commit(false); } @@ -263,8 +264,8 @@ TEST(SortedDataInterface, BuilderAddSameKeyStringWithDupsAllowed) { const std::unique_ptr<SortedDataBuilderInterface> builder( sorted->getBulkBuilder(opCtx.get(), true /* allow duplicates */)); - ASSERT_OK(builder->addKey(keyStringLoc1, loc1)); - ASSERT_OK(builder->addKey(keyStringLoc2, loc2)); + ASSERT_OK(builder->addKey(keyStringLoc1.getValueCopy(), loc1)); + ASSERT_OK(builder->addKey(keyStringLoc2.getValueCopy(), loc2)); builder->commit(false); } @@ -324,9 +325,9 @@ TEST(SortedDataInterface, BuilderAddMultipleKeyStrings) { const std::unique_ptr<SortedDataBuilderInterface> builder( sorted->getBulkBuilder(opCtx.get(), true)); - ASSERT_OK(builder->addKey(keyString1, loc1)); - ASSERT_OK(builder->addKey(keyString2, loc2)); - ASSERT_OK(builder->addKey(keyString3, loc3)); + ASSERT_OK(builder->addKey(keyString1.getValueCopy(), loc1)); + ASSERT_OK(builder->addKey(keyString2.getValueCopy(), loc2)); + ASSERT_OK(builder->addKey(keyString3.getValueCopy(), loc3)); builder->commit(false); } diff --git a/src/mongo/db/storage/sorted_data_interface_test_insert.cpp b/src/mongo/db/storage/sorted_data_interface_test_insert.cpp index ebb604cb943..5b5ac187b55 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_insert.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_insert.cpp @@ -84,7 +84,7 @@ TEST(SortedDataInterface, InsertKeyString) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyString1, loc1, true)); + ASSERT_OK(sorted->insert(opCtx.get(), keyString1.getValueCopy(), loc1, true)); uow.commit(); } } @@ -281,8 +281,8 @@ TEST(SortedDataInterface, InsertSameKeyString) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyStringLoc1, loc1, false)); - ASSERT_NOT_OK(sorted->insert(opCtx.get(), keyStringLoc2, loc2, false)); + ASSERT_OK(sorted->insert(opCtx.get(), keyStringLoc1.getValueCopy(), loc1, false)); + ASSERT_NOT_OK(sorted->insert(opCtx.get(), keyStringLoc2.getValueCopy(), loc2, false)); uow.commit(); } } @@ -299,7 +299,7 @@ TEST(SortedDataInterface, InsertSameKeyString) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_NOT_OK(sorted->insert(opCtx.get(), keyStringLoc2, loc2, false)); + ASSERT_NOT_OK(sorted->insert(opCtx.get(), keyStringLoc2.getValueCopy(), loc2, false)); uow.commit(); } } @@ -454,8 +454,8 @@ TEST(SortedDataInterface, InsertMultipleKeyStrings) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyString1, loc1, false)); - ASSERT_OK(sorted->insert(opCtx.get(), keyString2, loc2, false)); + ASSERT_OK(sorted->insert(opCtx.get(), keyString1.getValueCopy(), loc1, false)); + ASSERT_OK(sorted->insert(opCtx.get(), keyString2.getValueCopy(), loc2, false)); uow.commit(); } } @@ -473,7 +473,7 @@ TEST(SortedDataInterface, InsertMultipleKeyStrings) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyString3, loc3, false)); + ASSERT_OK(sorted->insert(opCtx.get(), keyString3.getValueCopy(), loc3, false)); uow.commit(); } } diff --git a/src/mongo/db/storage/sorted_data_interface_test_unindex.cpp b/src/mongo/db/storage/sorted_data_interface_test_unindex.cpp index 4281dc9010d..ff1956110ca 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_unindex.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_unindex.cpp @@ -105,7 +105,7 @@ void unindexKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyString1, loc1, true)); + ASSERT_OK(sorted->insert(opCtx.get(), keyString1.getValueCopy(), loc1, true)); uow.commit(); } } @@ -119,7 +119,7 @@ void unindexKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - sorted->unindex(opCtx.get(), keyString1, loc1, true); + sorted->unindex(opCtx.get(), keyString1.getValueCopy(), loc1, true); ASSERT(sorted->isEmpty(opCtx.get())); uow.commit(); } @@ -375,9 +375,9 @@ void unindexMultipleSameKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK(sorted->insert(opCtx.get(), keyStringLoc1, loc1, true)); - ASSERT_OK( - sorted->insert(opCtx.get(), keyStringLoc2, loc2, true /* allow duplicates */)); + ASSERT_OK(sorted->insert(opCtx.get(), keyStringLoc1.getValueCopy(), loc1, true)); + ASSERT_OK(sorted->insert( + opCtx.get(), keyStringLoc2.getValueCopy(), loc2, true /* allow duplicates */)); uow.commit(); } } @@ -391,7 +391,7 @@ void unindexMultipleSameKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - sorted->unindex(opCtx.get(), keyStringLoc2, loc2, true); + sorted->unindex(opCtx.get(), keyStringLoc2.getValueCopy(), loc2, true); ASSERT_EQUALS(1, sorted->numEntries(opCtx.get())); uow.commit(); } @@ -406,8 +406,8 @@ void unindexMultipleSameKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - ASSERT_OK( - sorted->insert(opCtx.get(), keyStringLoc3, loc3, true /* allow duplicates */)); + ASSERT_OK(sorted->insert( + opCtx.get(), keyStringLoc3.getValueCopy(), loc3, true /* allow duplicates */)); uow.commit(); } } @@ -421,9 +421,9 @@ void unindexMultipleSameKeyString(bool partial) { const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext()); { WriteUnitOfWork uow(opCtx.get()); - sorted->unindex(opCtx.get(), keyStringLoc1, loc1, true); + sorted->unindex(opCtx.get(), keyStringLoc1.getValueCopy(), loc1, true); ASSERT_EQUALS(1, sorted->numEntries(opCtx.get())); - sorted->unindex(opCtx.get(), keyStringLoc3, loc3, true); + sorted->unindex(opCtx.get(), keyStringLoc3.getValueCopy(), loc3, true); ASSERT(sorted->isEmpty(opCtx.get())); uow.commit(); } diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp index edba35d6869..94cbf637f95 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp @@ -79,26 +79,6 @@ using std::string; using std::vector; static const WiredTigerItem emptyItem(nullptr, 0); - - -bool hasFieldNames(const BSONObj& obj) { - BSONForEach(e, obj) { - if (e.fieldName()[0]) - return true; - } - return false; -} - -BSONObj stripFieldNames(const BSONObj& query) { - if (!hasFieldNames(query)) - return query; - - BSONObjBuilder bb; - BSONForEach(e, query) { - bb.appendAs(e, StringData()); - } - return bb.obj(); -} } // namespace @@ -270,17 +250,17 @@ Status WiredTigerIndex::insert(OperationContext* opCtx, bool dupsAllowed) { dassert(opCtx->lockState()->isWriteLocked()); invariant(id.isValid()); - dassert(!hasFieldNames(key)); + dassert(!key.hasFieldNames()); TRACE_INDEX << " key: " << key << " id: " << id; - KeyString::Builder keyString(getKeyStringVersion(), key, _ordering, id); + KeyString::HeapBuilder keyString(getKeyStringVersion(), key, _ordering, id); - return insert(opCtx, keyString, id, dupsAllowed); + return insert(opCtx, std::move(keyString.release()), id, dupsAllowed); } Status WiredTigerIndex::insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { dassert(opCtx->lockState()->isWriteLocked()); @@ -300,14 +280,14 @@ void WiredTigerIndex::unindex(OperationContext* opCtx, const RecordId& id, bool dupsAllowed) { invariant(id.isValid()); - dassert(!hasFieldNames(key)); - KeyString::Builder keyString(getKeyStringVersion(), key, _ordering, id); + dassert(!key.hasFieldNames()); + KeyString::HeapBuilder keyString(getKeyStringVersion(), key, _ordering, id); - unindex(opCtx, keyString, id, dupsAllowed); + unindex(opCtx, std::move(keyString.release()), id, dupsAllowed); } void WiredTigerIndex::unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { dassert(opCtx->lockState()->isWriteLocked()); @@ -402,7 +382,7 @@ bool WiredTigerIndex::appendCustomStats(OperationContext* opCtx, } Status WiredTigerIndex::dupKeyCheck(OperationContext* opCtx, const BSONObj& key) { - invariant(!hasFieldNames(key)); + invariant(!key.hasFieldNames()); invariant(unique()); WiredTigerCursor curwrap(_uri, _tableId, false, opCtx); @@ -648,12 +628,12 @@ public: : BulkBuilder(idx, opCtx, prefix), _idx(idx) {} Status addKey(const BSONObj& key, const RecordId& id) override { - KeyString::Builder keyString(_idx->getKeyStringVersion(), key, _idx->_ordering, id); + KeyString::HeapBuilder keyString(_idx->getKeyStringVersion(), key, _idx->_ordering, id); - return addKey(keyString, id); + return addKey(std::move(keyString.release()), id); } - Status addKey(const KeyString::Builder& keyString, const RecordId& id) override { + Status addKey(const KeyString::Value& keyString, const RecordId& id) override { dassert(id == KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize())); // Can't use WiredTigerCursor since we aren't using the cache. @@ -703,12 +683,12 @@ public: _previousKeyString(idx->getKeyStringVersion()) {} Status addKey(const BSONObj& newKey, const RecordId& id) override { - KeyString::Builder newKeyString( + KeyString::HeapBuilder newKeyString( _idx->getKeyStringVersion(), newKey, _idx->getOrdering(), id); - return addKey(newKeyString, id); + return addKey(std::move(newKeyString.release()), id); } - Status addKey(const KeyString::Builder& newKeyString, const RecordId& id) override { + Status addKey(const KeyString::Value& newKeyString, const RecordId& id) override { dassert(id == KeyString::decodeRecordIdAtEnd(newKeyString.getBuffer(), newKeyString.getSize())); @@ -728,16 +708,13 @@ public: } private: - Status addKeyTimestampSafe(const KeyString::Builder& newKeyString) { + Status addKeyTimestampSafe(const KeyString::Value& newKeyString) { // Do a duplicate check, but only if dups aren't allowed. if (!_dupsAllowed) { const int cmp = newKeyString.compareWithoutRecordId(_previousKeyString); if (cmp == 0) { // Duplicate found! - auto newKey = KeyString::toBson(newKeyString.getBuffer(), - newKeyString.getSize(), - _idx->_ordering, - newKeyString.getTypeBits()); + auto newKey = KeyString::toBson(newKeyString, _idx->_ordering); return buildDupKeyErrorStatus( newKey, _idx->collectionNamespace(), _idx->indexName(), _idx->keyPattern()); } else { @@ -769,12 +746,12 @@ private: return Status::OK(); } - Status addKeyTimestampUnsafe(const KeyString::Builder& newKeyString, const RecordId& id) { + Status addKeyTimestampUnsafe(const KeyString::Value& newKeyString, const RecordId& id) { const int cmp = newKeyString.compareWithoutRecordId(_previousKeyString); if (cmp != 0) { if (!_previousKeyString.isEmpty()) { // _previousKeyString.isEmpty() is only true on the first call to addKey(). - invariant(cmp > 0); // newKey must be > the last key + invariant(cmp > 0); // newKey must be > the last key. // We are done with dups of the last key so we can insert it now. doInsert(); } @@ -782,10 +759,7 @@ private: } else { // Dup found! if (!_dupsAllowed) { - auto newKey = KeyString::toBson(newKeyString.getBuffer(), - newKeyString.getSize(), - _idx->_ordering, - newKeyString.getTypeBits()); + auto newKey = KeyString::toBson(newKeyString, _idx->_ordering); return buildDupKeyErrorStatus( newKey, _idx->collectionNamespace(), _idx->indexName(), _idx->keyPattern()); } @@ -879,14 +853,14 @@ public: ? KeyString::Discriminator::kExclusiveAfter : KeyString::Discriminator::kExclusiveBefore; _endPosition = std::make_unique<KeyString::Builder>(_idx.getKeyStringVersion()); - _endPosition->resetToKey(stripFieldNames(key), _idx.getOrdering(), discriminator); + _endPosition->resetToKey(BSONObj::stripFieldNames(key), _idx.getOrdering(), discriminator); } boost::optional<IndexKeyEntry> seek(const BSONObj& key, bool inclusive, RequestedInfo parts) override { dassert(_opCtx->lockState()->isReadLocked()); - const BSONObj finalKey = stripFieldNames(key); + const BSONObj finalKey = BSONObj::stripFieldNames(key); const auto discriminator = _forward == inclusive ? KeyString::Discriminator::kExclusiveBefore : KeyString::Discriminator::kExclusiveAfter; @@ -1394,7 +1368,7 @@ bool WiredTigerIndexUnique::isDup(OperationContext* opCtx, WT_CURSOR* c, const B Status WiredTigerIndexUnique::_insert(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { if (isTimestampSafeUniqueIdx()) { @@ -1405,7 +1379,7 @@ Status WiredTigerIndexUnique::_insert(OperationContext* opCtx, Status WiredTigerIndexUnique::_insertTimestampUnsafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { invariant(id.isValid()); @@ -1462,8 +1436,7 @@ Status WiredTigerIndexUnique::_insertTimestampUnsafe(OperationContext* opCtx, } if (!dupsAllowed) { - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _ordering, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); return buildDupKeyErrorStatus(key, _collectionNamespace, _indexName, _keyPattern); } @@ -1485,7 +1458,7 @@ Status WiredTigerIndexUnique::_insertTimestampUnsafe(OperationContext* opCtx, Status WiredTigerIndexUnique::_insertTimestampSafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, bool dupsAllowed) { TRACE_INDEX << "Timestamp safe unique idx KeyString: " << keyString; @@ -1547,7 +1520,7 @@ Status WiredTigerIndexUnique::_insertTimestampSafe(OperationContext* opCtx, void WiredTigerIndexUnique::_unindex(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { if (isTimestampSafeUniqueIdx()) { @@ -1558,7 +1531,7 @@ void WiredTigerIndexUnique::_unindex(OperationContext* opCtx, void WiredTigerIndexUnique::_unindexTimestampUnsafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { invariant(id.isValid()); @@ -1645,8 +1618,7 @@ void WiredTigerIndexUnique::_unindexTimestampUnsafe(OperationContext* opCtx, } if (!foundId) { - auto key = KeyString::toBson( - keyString.getBuffer(), keyString.getSize(), _ordering, keyString.getTypeBits()); + auto key = KeyString::toBson(keyString, _ordering); warning().stream() << id << " not found in the index for key " << redact(key); return; // nothing to do } @@ -1670,7 +1642,7 @@ void WiredTigerIndexUnique::_unindexTimestampUnsafe(OperationContext* opCtx, void WiredTigerIndexUnique::_unindexTimestampSafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, bool dupsAllowed) { WiredTigerItem item(keyString.getBuffer(), keyString.getSize()); setKey(c, item.Get()); @@ -1726,7 +1698,7 @@ SortedDataBuilderInterface* WiredTigerIndexStandard::getBulkBuilder(OperationCon Status WiredTigerIndexStandard::_insert(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) { invariant(dupsAllowed); @@ -1752,7 +1724,7 @@ Status WiredTigerIndexStandard::_insert(OperationContext* opCtx, void WiredTigerIndexStandard::_unindex(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId&, bool dupsAllowed) { invariant(dupsAllowed); diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h index df17969fc5a..6f614f40f57 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h @@ -97,7 +97,7 @@ public: bool dupsAllowed); virtual Status insert(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed); @@ -107,7 +107,7 @@ public: bool dupsAllowed); virtual void unindex(OperationContext* opCtx, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed); @@ -163,13 +163,13 @@ public: protected: virtual Status _insert(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) = 0; virtual void _unindex(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) = 0; @@ -226,36 +226,36 @@ public: Status _insert(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) override; Status _insertTimestampUnsafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed); Status _insertTimestampSafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, bool dupsAllowed); void _unindex(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) override; void _unindexTimestampUnsafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed); void _unindexTimestampSafe(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, bool dupsAllowed); private: @@ -290,13 +290,13 @@ public: Status _insert(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) override; void _unindex(OperationContext* opCtx, WT_CURSOR* c, - const KeyString::Builder& keyString, + const KeyString::Value& keyString, const RecordId& id, bool dupsAllowed) override; }; diff --git a/src/mongo/dbtests/index_access_method_test.cpp b/src/mongo/dbtests/index_access_method_test.cpp index 3327ea73cbb..79690c28874 100644 --- a/src/mongo/dbtests/index_access_method_test.cpp +++ b/src/mongo/dbtests/index_access_method_test.cpp @@ -40,44 +40,53 @@ namespace mongo { namespace { using std::vector; +KeyStringSet makeKeyStringSet(std::initializer_list<BSONObj> objs) { + KeyStringSet keyStrings; + for (auto& obj : objs) { + KeyString::HeapBuilder keyString( + KeyString::Version::kLatestVersion, obj, Ordering::make(BSONObj())); + keyStrings.insert(keyString.release()); + } + return keyStrings; +} + TEST(IndexAccessMethodSetDifference, EmptyInputsShouldHaveNoDifference) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet(); - BSONObjSet right = bsonCmp.makeBSONObjSet(); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + KeyStringSet left; + KeyStringSet right; + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, EmptyLeftShouldHaveNoDifference) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet(); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 0)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + KeyStringSet left; + auto right = makeKeyStringSet({BSON("" << 0)}); + + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(1UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, EmptyRightShouldReturnAllOfLeft) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << 1)}); - BSONObjSet right = bsonCmp.makeBSONObjSet(); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 0), BSON("" << 1)}); + KeyStringSet right; + + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQ(2UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, IdenticalSetsShouldHaveNoDifference) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), - BSON("" - << "string"), - BSON("" << BSONNULL)}); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 0), - BSON("" - << "string"), - BSON("" << BSONNULL)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 0), + BSON("" + << "string"), + BSON("" << BSONNULL)}); + auto right = makeKeyStringSet({BSON("" << 0), + BSON("" + << "string"), + BSON("" << BSONNULL)}); + + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } @@ -87,10 +96,10 @@ TEST(IndexAccessMethodSetDifference, IdenticalSetsShouldHaveNoDifference) { // void assertDistinct(BSONObj left, BSONObj right) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet leftSet = bsonCmp.makeBSONObjSet({left}); - BSONObjSet rightSet = bsonCmp.makeBSONObjSet({right}); - auto diff = AbstractIndexAccessMethod::setDifference(leftSet, rightSet); + auto leftSet = makeKeyStringSet({left}); + auto rightSet = makeKeyStringSet({right}); + auto diff = + AbstractIndexAccessMethod::setDifference(leftSet, rightSet, Ordering::make(BSONObj())); ASSERT_EQ(1UL, diff.first.size()); ASSERT_EQ(1UL, diff.second.size()); } @@ -127,53 +136,47 @@ TEST(IndexAccessMethodSetDifference, ZerosOfDifferentTypesAreNotEquivalent) { } TEST(IndexAccessMethodSetDifference, ShouldDetectOneDifferenceAmongManySimilarities) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = - bsonCmp.makeBSONObjSet({BSON("" << 0), - BSON("" - << "string"), - BSON("" << BSONNULL), - BSON("" << static_cast<long long>(1)), // This is different. - BSON("" << BSON("sub" - << "document")), - BSON("" << BSON_ARRAY(1 << "hi" << 42))}); - BSONObjSet right = - bsonCmp.makeBSONObjSet({BSON("" << 0), - BSON("" - << "string"), - BSON("" << BSONNULL), - BSON("" << static_cast<double>(1.0)), // This is different. - BSON("" << BSON("sub" - << "document")), - BSON("" << BSON_ARRAY(1 << "hi" << 42))}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 0), + BSON("" + << "string"), + BSON("" << BSONNULL), + BSON("" << static_cast<long long>(1)), // This is different. + BSON("" << BSON("sub" + << "document")), + BSON("" << BSON_ARRAY(1 << "hi" << 42))}); + auto right = makeKeyStringSet({BSON("" << 0), + BSON("" + << "string"), + BSON("" << BSONNULL), + BSON("" << static_cast<double>(1.0)), // This is different. + BSON("" << BSON("sub" + << "document")), + BSON("" << BSON_ARRAY(1 << "hi" << 42))}); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(1UL, diff.first.size()); ASSERT_EQUALS(1UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, SingleObjInLeftShouldFindCorrespondingObjInRight) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 2)}); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 2)}); + auto right = makeKeyStringSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(0UL, diff.first.size()); ASSERT_EQUALS(2UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, SingleObjInRightShouldFindCorrespondingObjInLeft) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 2)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); + auto right = makeKeyStringSet({BSON("" << 2)}); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(2UL, diff.first.size()); ASSERT_EQUALS(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, LeftSetAllSmallerThanRightShouldBeDisjoint) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); + auto right = makeKeyStringSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(3UL, diff.first.size()); ASSERT_EQUALS(3UL, diff.second.size()); for (auto&& obj : diff.first) { @@ -185,10 +188,9 @@ TEST(IndexAccessMethodSetDifference, LeftSetAllSmallerThanRightShouldBeDisjoint) } TEST(IndexAccessMethodSetDifference, LeftSetAllLargerThanRightShouldBeDisjoint) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); - BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto left = makeKeyStringSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); + auto right = makeKeyStringSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(3UL, diff.first.size()); ASSERT_EQUALS(3UL, diff.second.size()); for (auto&& obj : diff.first) { @@ -200,23 +202,23 @@ TEST(IndexAccessMethodSetDifference, LeftSetAllLargerThanRightShouldBeDisjoint) } TEST(IndexAccessMethodSetDifference, ShouldNotReportOverlapsFromNonDisjointSets) { - SimpleBSONObjComparator bsonCmp; - BSONObjSet left = - bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << 1), BSON("" << 4), BSON("" << 6)}); - BSONObjSet right = bsonCmp.makeBSONObjSet( + auto left = makeKeyStringSet({BSON("" << 0), BSON("" << 1), BSON("" << 4), BSON("" << 6)}); + auto right = makeKeyStringSet( {BSON("" << -1), BSON("" << 1), BSON("" << 3), BSON("" << 4), BSON("" << 7)}); - auto diff = AbstractIndexAccessMethod::setDifference(left, right); + auto diff = AbstractIndexAccessMethod::setDifference(left, right, Ordering::make(BSONObj())); ASSERT_EQUALS(2UL, diff.first.size()); // 0, 6. ASSERT_EQUALS(3UL, diff.second.size()); // -1, 3, 7. - for (auto&& obj : diff.first) { - ASSERT(left.find(obj) != left.end()); + for (auto&& keyString : diff.first) { + ASSERT(left.find(keyString) != left.end()); // Make sure it's not in the intersection. + auto obj = KeyString::toBson(keyString, Ordering::make(BSONObj())); ASSERT_BSONOBJ_NE(obj, BSON("" << 1)); ASSERT_BSONOBJ_NE(obj, BSON("" << 4)); } - for (auto&& obj : diff.second) { - ASSERT(right.find(obj) != right.end()); + for (auto&& keyString : diff.second) { + ASSERT(right.find(keyString) != right.end()); // Make sure it's not in the intersection. + auto obj = KeyString::toBson(keyString, Ordering::make(BSONObj())); ASSERT_BSONOBJ_NE(obj, BSON("" << 1)); ASSERT_BSONOBJ_NE(obj, BSON("" << 4)); } diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index ed805c905f7..e813b21cef2 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -825,12 +825,13 @@ public: options.dupsAllowed = true; options.logIfError = true; - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; iam->getKeys(actualKey, IndexAccessMethod::GetKeysMode::kRelaxConstraintsUnfiltered, &keys, nullptr, - nullptr); + nullptr, + id1); auto removeStatus = iam->removeKeys(&_opCtx, {keys.begin(), keys.end()}, id1, options, &numDeleted); auto insertStatus = iam->insert(&_opCtx, badKey, id1, options, &insertResult); @@ -1255,12 +1256,13 @@ public: options.logIfError = true; options.dupsAllowed = true; - BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); + KeyStringSet keys; iam->getKeys(actualKey, IndexAccessMethod::GetKeysMode::kRelaxConstraintsUnfiltered, &keys, nullptr, - nullptr); + nullptr, + rid); auto removeStatus = iam->removeKeys(&_opCtx, {keys.begin(), keys.end()}, rid, options, &numDeleted); |