summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberto Massari <alberto.massari@mongodb.com>2022-12-21 19:56:54 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-12-21 21:14:53 +0000
commit5977e706431fd5705b59115ec0e0d2d7a2203246 (patch)
tree2161b9d853c2e7453a19d12b88bd39bc9bf87dd5
parent9ac3279cc3459b31a597ed4659e07ad93f073d8f (diff)
downloadmongo-5977e706431fd5705b59115ec0e0d2d7a2203246.tar.gz
SERVER-65364 Allow fine-grained selection of indexes to update
-rw-r--r--src/mongo/bson/bsonobj.cpp2
-rw-r--r--src/mongo/bson/bsonobj.h1
-rw-r--r--src/mongo/db/catalog/collection_test.cpp12
-rw-r--r--src/mongo/db/catalog/collection_write_path.cpp21
-rw-r--r--src/mongo/db/catalog/collection_write_path.h13
-rw-r--r--src/mongo/db/catalog/index_catalog.h4
-rw-r--r--src/mongo/db/catalog/index_catalog_entry.h3
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp1
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.h8
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp50
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.h1
-rw-r--r--src/mongo/db/catalog/views_for_database.cpp2
-rw-r--r--src/mongo/db/dbhelpers.cpp10
-rw-r--r--src/mongo/db/exec/update_stage.cpp41
-rw-r--r--src/mongo/db/index/index_access_method.cpp1
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp14
-rw-r--r--src/mongo/db/query/collection_query_info.cpp136
-rw-r--r--src/mongo/db/query/collection_query_info.h8
-rw-r--r--src/mongo/db/repl/storage_timestamp_test.cpp14
-rw-r--r--src/mongo/db/repl/tenant_migration_donor_service.cpp2
-rw-r--r--src/mongo/db/serverless/shard_split_donor_service.cpp17
-rw-r--r--src/mongo/db/transaction/transaction_participant.cpp4
-rw-r--r--src/mongo/db/update/addtoset_node_test.cpp17
-rw-r--r--src/mongo/db/update/arithmetic_node_test.cpp46
-rw-r--r--src/mongo/db/update/compare_node_test.cpp19
-rw-r--r--src/mongo/db/update/current_date_node_test.cpp7
-rw-r--r--src/mongo/db/update/document_diff_calculator.cpp122
-rw-r--r--src/mongo/db/update/document_diff_calculator.h12
-rw-r--r--src/mongo/db/update/update_node_test_fixture.h17
-rw-r--r--src/mongo/db/update/update_oplog_entry_serialization.cpp12
-rw-r--r--src/mongo/db/update/update_oplog_entry_serialization.h5
-rw-r--r--src/mongo/db/update_index_data.cpp4
-rw-r--r--src/mongo/db/update_index_data.h5
-rw-r--r--src/mongo/dbtests/multikey_paths_test.cpp24
-rw-r--r--src/mongo/dbtests/query_stage_count.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_sort.cpp20
36 files changed, 521 insertions, 156 deletions
diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp
index 88715dd68de..6f5ca8aec3f 100644
--- a/src/mongo/bson/bsonobj.cpp
+++ b/src/mongo/bson/bsonobj.cpp
@@ -89,6 +89,8 @@ int compareObjects(const BSONObj& firstObj,
/* BSONObj ------------------------------------------------------------*/
+const BSONObj BSONObj::kEmptyObject;
+
void BSONObj::_assertInvalid(int maxSize) const {
StringBuilder ss;
int os = objsize();
diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h
index 83453bcc9e1..0c24e4cd676 100644
--- a/src/mongo/bson/bsonobj.h
+++ b/src/mongo/bson/bsonobj.h
@@ -123,6 +123,7 @@ public:
using ComparisonRulesSet = BSONComparatorInterfaceBase<BSONObj>::ComparisonRulesSet;
static constexpr char kMinBSONLength = 5;
+ static const BSONObj kEmptyObject;
/**
* Construct an empty BSONObj -- that is, {}.
diff --git a/src/mongo/db/catalog/collection_test.cpp b/src/mongo/db/catalog/collection_test.cpp
index 48c653f975f..4337263b70a 100644
--- a/src/mongo/db/catalog/collection_test.cpp
+++ b/src/mongo/db/catalog/collection_test.cpp
@@ -278,8 +278,14 @@ TEST_F(CollectionTest, VerifyIndexIsUpdated) {
WriteUnitOfWork wuow(opCtx);
Snapshotted<BSONObj> oldSnap(opCtx->recoveryUnit()->getSnapshotId(), oldDoc);
CollectionUpdateArgs args{oldDoc};
- collection_internal::updateDocument(
- opCtx, coll, oldRecordId, oldSnap, newDoc, true, nullptr, &args);
+ collection_internal::updateDocument(opCtx,
+ coll,
+ oldRecordId,
+ oldSnap,
+ newDoc,
+ collection_internal::kUpdateAllIndexes,
+ nullptr,
+ &args);
wuow.commit();
}
auto indexRecordId = userIdx->getEntry()->accessMethod()->asSortedData()->findSingle(
@@ -329,7 +335,7 @@ TEST_F(CollectionTest, VerifyIndexIsUpdatedWithDamages) {
oldSnap,
damagesOutput.damageSource.get(),
damagesOutput.damages,
- true,
+ collection_internal::kUpdateAllIndexes,
nullptr,
&args);
ASSERT_OK(newDocStatus);
diff --git a/src/mongo/db/catalog/collection_write_path.cpp b/src/mongo/db/catalog/collection_write_path.cpp
index 266f31f42eb..a3fab3026e3 100644
--- a/src/mongo/db/catalog/collection_write_path.cpp
+++ b/src/mongo/db/catalog/collection_write_path.cpp
@@ -400,7 +400,7 @@ RecordId updateDocument(OperationContext* opCtx,
const RecordId& oldLocation,
const Snapshotted<BSONObj>& oldDoc,
const BSONObj& newDoc,
- bool indexesAffected,
+ const BSONObj* opDiff,
OpDebug* opDebug,
CollectionUpdateArgs* args) {
{
@@ -473,7 +473,8 @@ RecordId updateDocument(OperationContext* opCtx,
uassertStatusOK(collection->getRecordStore()->updateRecord(
opCtx, oldLocation, newDoc.objdata(), newDoc.objsize()));
- if (indexesAffected) {
+ // don't update the indexes if kUpdateNoIndexes has been specified.
+ if (opDiff != kUpdateNoIndexes) {
int64_t keysInserted = 0;
int64_t keysDeleted = 0;
@@ -481,6 +482,7 @@ RecordId updateDocument(OperationContext* opCtx,
collection,
args->preImageDoc,
newDoc,
+ opDiff,
oldLocation,
&keysInserted,
&keysDeleted));
@@ -512,7 +514,7 @@ StatusWith<BSONObj> updateDocumentWithDamages(OperationContext* opCtx,
const Snapshotted<BSONObj>& oldDoc,
const char* damageSource,
const mutablebson::DamageVector& damages,
- bool indexesAffected,
+ const BSONObj* opDiff,
OpDebug* opDebug,
CollectionUpdateArgs* args) {
dassert(opCtx->lockState()->isCollectionLockedForMode(collection->ns(), MODE_IX));
@@ -551,12 +553,19 @@ StatusWith<BSONObj> updateDocumentWithDamages(OperationContext* opCtx,
args->changeStreamPreAndPostImagesEnabledForCollection =
collection->isChangeStreamPreAndPostImagesEnabled();
- if (indexesAffected) {
+ // don't update the indexes if kUpdateNoIndexes has been specified.
+ if (opDiff != kUpdateNoIndexes) {
int64_t keysInserted = 0;
int64_t keysDeleted = 0;
- uassertStatusOK(collection->getIndexCatalog()->updateRecord(
- opCtx, collection, oldDoc.value(), args->updatedDoc, loc, &keysInserted, &keysDeleted));
+ uassertStatusOK(collection->getIndexCatalog()->updateRecord(opCtx,
+ collection,
+ oldDoc.value(),
+ args->updatedDoc,
+ opDiff,
+ loc,
+ &keysInserted,
+ &keysDeleted));
if (opDebug) {
opDebug->additiveMetrics.incrementKeysInserted(keysInserted);
diff --git a/src/mongo/db/catalog/collection_write_path.h b/src/mongo/db/catalog/collection_write_path.h
index e492aef05ca..482c5911634 100644
--- a/src/mongo/db/catalog/collection_write_path.h
+++ b/src/mongo/db/catalog/collection_write_path.h
@@ -45,6 +45,12 @@ enum class StoreDeletedDoc { Off, On };
enum class RetryableWrite { kYes, kNo };
/**
+ * Constants used for the opDiff argument in updateDocument and updateDocumentWithDamages.
+ */
+constexpr const BSONObj* kUpdateAllIndexes = nullptr;
+constexpr const BSONObj* kUpdateNoIndexes = &BSONObj::kEmptyObject;
+
+/**
* Inserts a document into the record store for a bulk loader that manages the index building. The
* bulk loader is notified with the RecordId of the document inserted into the RecordStore through
* the 'OnRecordInsertedFn' callback.
@@ -94,6 +100,9 @@ Status checkFailCollectionInsertsFailPoint(const NamespaceString& ns, const BSON
*
* If the document fits in the old space, it is put there; if not, it is moved.
* Sets 'args.updatedDoc' to the updated version of the document with damages applied, on success.
+ * 'opDiff' Optional argument. If set to kUpdateAllIndexes, all the indexes are updated. If it is
+ * set to kUpdateNoIndexes, no indexes are updated. Otherwise, it is the precomputed difference
+ * between 'oldDoc' and 'newDoc', used to determine which indexes need to be updated.
* 'opDebug' Optional argument. When not null, will be used to record operation statistics.
* @return the post update location of the doc (may or may not be the same as oldLocation)
*/
@@ -102,7 +111,7 @@ RecordId updateDocument(OperationContext* opCtx,
const RecordId& oldLocation,
const Snapshotted<BSONObj>& oldDoc,
const BSONObj& newDoc,
- bool indexesAffected,
+ const BSONObj* opDiff,
OpDebug* opDebug,
CollectionUpdateArgs* args);
@@ -117,7 +126,7 @@ StatusWith<BSONObj> updateDocumentWithDamages(OperationContext* opCtx,
const Snapshotted<BSONObj>& oldDoc,
const char* damageSource,
const mutablebson::DamageVector& damages,
- bool indexesAffected,
+ const BSONObj* opDiff,
OpDebug* opDebug,
CollectionUpdateArgs* args);
diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h
index 7a038076125..e4006aa773e 100644
--- a/src/mongo/db/catalog/index_catalog.h
+++ b/src/mongo/db/catalog/index_catalog.h
@@ -490,6 +490,9 @@ public:
/**
* Both 'keysInsertedOut' and 'keysDeletedOut' are required and will be set to the number of
* index keys inserted and deleted by this operation, respectively.
+ * The 'opDiff' argument specifies an optional document containing the differences between
+ * 'oldDoc' and 'newDoc' that can be used to decide which indexes have to be modified. If
+ * set to null, all indexes should be updated.
*
* This method may throw.
*/
@@ -497,6 +500,7 @@ public:
const CollectionPtr& coll,
const BSONObj& oldDoc,
const BSONObj& newDoc,
+ const BSONObj* opDiff,
const RecordId& recordId,
int64_t* keysInsertedOut,
int64_t* keysDeletedOut) const = 0;
diff --git a/src/mongo/db/catalog/index_catalog_entry.h b/src/mongo/db/catalog/index_catalog_entry.h
index 0a21729e758..b4f6d6d582e 100644
--- a/src/mongo/db/catalog/index_catalog_entry.h
+++ b/src/mongo/db/catalog/index_catalog_entry.h
@@ -55,6 +55,7 @@ class IndexBuildInterceptor;
class IndexDescriptor;
class MatchExpression;
class OperationContext;
+class UpdateIndexData;
class IndexCatalogEntry : public std::enable_shared_from_this<IndexCatalogEntry> {
public:
@@ -190,6 +191,8 @@ public:
virtual boost::optional<Timestamp> getMinimumVisibleSnapshot() const = 0;
virtual void setMinimumVisibleSnapshot(Timestamp name) = 0;
+
+ virtual const UpdateIndexData& getIndexedPaths() const = 0;
};
class IndexCatalogEntryContainer {
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
index 31619ec9136..65de03edfed 100644
--- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
@@ -126,6 +126,7 @@ IndexCatalogEntryImpl::IndexCatalogEntryImpl(OperationContext* const opCtx,
void IndexCatalogEntryImpl::setAccessMethod(std::unique_ptr<IndexAccessMethod> accessMethod) {
invariant(!_accessMethod);
_accessMethod = std::move(accessMethod);
+ CollectionQueryInfo::computeUpdateIndexData(this, _accessMethod.get(), &_indexedPaths);
}
bool IndexCatalogEntryImpl::isReady(OperationContext* opCtx) const {
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.h b/src/mongo/db/catalog/index_catalog_entry_impl.h
index 9d6c40f95b1..8955ba64947 100644
--- a/src/mongo/db/catalog/index_catalog_entry_impl.h
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.h
@@ -38,6 +38,7 @@
#include "mongo/db/index/multikey_paths.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/record_id.h"
+#include "mongo/db/update_index_data.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/mutex.h"
@@ -190,6 +191,10 @@ public:
*/
void setMinimumVisibleSnapshot(Timestamp newMinimumVisibleSnapshot) final;
+ const UpdateIndexData& getIndexedPaths() const final {
+ return _indexedPaths;
+ }
+
private:
/**
* Sets this index to be multikey when we are running inside a multi-document transaction.
@@ -247,5 +252,8 @@ private:
// Used to improve lookups without having to search for the index name
// accessing the collection metadata.
int _indexOffset;
+
+ // Describes the paths indexed by this index.
+ UpdateIndexData _indexedPaths;
};
} // namespace mongo
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp
index f3cab48c45a..ed48ba36774 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -76,6 +76,7 @@
#include "mongo/db/storage/storage_parameters_gen.h"
#include "mongo/db/storage/storage_util.h"
#include "mongo/db/ttl_collection_cache.h"
+#include "mongo/db/update/document_diff_calculator.h"
#include "mongo/db/vector_clock.h"
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
@@ -1852,32 +1853,43 @@ Status IndexCatalogImpl::updateRecord(OperationContext* const opCtx,
const CollectionPtr& coll,
const BSONObj& oldDoc,
const BSONObj& newDoc,
+ const BSONObj* opDiff,
const RecordId& recordId,
int64_t* const keysInsertedOut,
int64_t* const keysDeletedOut) const {
*keysInsertedOut = 0;
*keysDeletedOut = 0;
- // Ready indexes go directly through the IndexAccessMethod.
- for (IndexCatalogEntryContainer::const_iterator it = _readyIndexes.begin();
- it != _readyIndexes.end();
- ++it) {
- IndexCatalogEntry* entry = it->get();
- auto status = _updateRecord(
- opCtx, coll, entry, oldDoc, newDoc, recordId, keysInsertedOut, keysDeletedOut);
- if (!status.isOK())
- return status;
- }
+ const size_t numIndexesToUpdate = _readyIndexes.size() + _buildingIndexes.size();
+ if (numIndexesToUpdate > 0) {
+ mongo::doc_diff::BitVector toUpdate;
+ if (opDiff) {
+ std::vector<const UpdateIndexData*> allIndexPaths;
+ allIndexPaths.reserve(numIndexesToUpdate);
+ for (auto& indexes : {std::ref(_readyIndexes), std::ref(_buildingIndexes)}) {
+ for (const auto& indexEntry : indexes.get()) {
+ dassert(!indexEntry->getIndexedPaths().isEmpty());
+ allIndexPaths.push_back(&indexEntry->getIndexedPaths());
+ }
+ }
+ toUpdate = mongo::doc_diff::anyIndexesMightBeAffected(*opDiff, allIndexPaths);
+ } else {
+ toUpdate = mongo::doc_diff::BitVector(numIndexesToUpdate);
+ toUpdate.set();
+ }
- // Building indexes go through the interceptor.
- for (IndexCatalogEntryContainer::const_iterator it = _buildingIndexes.begin();
- it != _buildingIndexes.end();
- ++it) {
- IndexCatalogEntry* entry = it->get();
- auto status = _updateRecord(
- opCtx, coll, entry, oldDoc, newDoc, recordId, keysInsertedOut, keysDeletedOut);
- if (!status.isOK())
- return status;
+ for (size_t pos = toUpdate.find_first(); pos != mongo::doc_diff::BitVector::npos;
+ pos = toUpdate.find_next(pos)) {
+ const IndexCatalogEntry* entry = (pos < _readyIndexes.size())
+ ? (_readyIndexes.begin() + pos)->get()
+ : (_buildingIndexes.begin() + (pos - _readyIndexes.size()))->get();
+
+ auto status = _updateRecord(
+ opCtx, coll, entry, oldDoc, newDoc, recordId, keysInsertedOut, keysDeletedOut);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
}
return Status::OK();
}
diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h
index 3c09fec3fb4..463408421d4 100644
--- a/src/mongo/db/catalog/index_catalog_impl.h
+++ b/src/mongo/db/catalog/index_catalog_impl.h
@@ -257,6 +257,7 @@ public:
const CollectionPtr& coll,
const BSONObj& oldDoc,
const BSONObj& newDoc,
+ const BSONObj* opDiff,
const RecordId& recordId,
int64_t* keysInsertedOut,
int64_t* keysDeletedOut) const override;
diff --git a/src/mongo/db/catalog/views_for_database.cpp b/src/mongo/db/catalog/views_for_database.cpp
index e41ed08f9db..07706e1f0d8 100644
--- a/src/mongo/db/catalog/views_for_database.cpp
+++ b/src/mongo/db/catalog/views_for_database.cpp
@@ -332,7 +332,7 @@ Status ViewsForDatabase::_upsertIntoCatalog(OperationContext* opCtx,
id,
oldView,
viewObj,
- true /* indexesAffected */,
+ collection_internal::kUpdateAllIndexes,
&CurOp::get(opCtx)->debug(),
&args);
}
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 0aad03060cd..a05560fd078 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -387,8 +387,14 @@ bool Helpers::findByIdAndNoopUpdate(OperationContext* opCtx,
CollectionUpdateArgs args(snapshottedDoc.value());
args.criteria = idQuery;
args.update = BSONObj();
- collection_internal::updateDocument(
- opCtx, collection, recordId, snapshottedDoc, result, false, nullptr, &args);
+ collection_internal::updateDocument(opCtx,
+ collection,
+ recordId,
+ snapshottedDoc,
+ result,
+ collection_internal::kUpdateNoIndexes,
+ nullptr,
+ &args);
return true;
}
diff --git a/src/mongo/db/exec/update_stage.cpp b/src/mongo/db/exec/update_stage.cpp
index 7e6423854c6..5e1b5eff939 100644
--- a/src/mongo/db/exec/update_stage.cpp
+++ b/src/mongo/db/exec/update_stage.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/s/operation_sharding_state.h"
#include "mongo/db/update/path_support.h"
+#include "mongo/db/update/update_oplog_entry_serialization.h"
#include "mongo/logv2/log.h"
#include "mongo/s/grid.h"
#include "mongo/s/would_change_owning_shard_exception.h"
@@ -273,17 +274,18 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj,
checkUpdateChangesShardKeyFields(boost::none /* newObj */, oldObj);
}
+ auto diff = update_oplog_entry::extractDiffFromOplogEntry(logObj);
WriteUnitOfWork wunit(opCtx());
- newObj = uassertStatusOK(
- collection_internal::updateDocumentWithDamages(opCtx(),
- collection(),
- recordId,
- oldObj,
- source,
- _damages,
- driver->modsAffectIndices(),
- _params.opDebug,
- &args));
+ newObj = uassertStatusOK(collection_internal::updateDocumentWithDamages(
+ opCtx(),
+ collection(),
+ recordId,
+ oldObj,
+ source,
+ _damages,
+ diff.has_value() ? &*diff : collection_internal::kUpdateAllIndexes,
+ _params.opDebug,
+ &args));
invariant(oldObj.snapshotId() == opCtx()->recoveryUnit()->getSnapshotId());
wunit.commit();
}
@@ -304,15 +306,18 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj,
if (_isUserInitiatedWrite) {
checkUpdateChangesShardKeyFields(newObj, oldObj);
}
+
+ auto diff = update_oplog_entry::extractDiffFromOplogEntry(logObj);
WriteUnitOfWork wunit(opCtx());
- newRecordId = collection_internal::updateDocument(opCtx(),
- collection(),
- recordId,
- oldObj,
- newObj,
- driver->modsAffectIndices(),
- _params.opDebug,
- &args);
+ newRecordId = collection_internal::updateDocument(
+ opCtx(),
+ collection(),
+ recordId,
+ oldObj,
+ newObj,
+ diff.has_value() ? &*diff : collection_internal::kUpdateAllIndexes,
+ _params.opDebug,
+ &args);
invariant(oldObj.snapshotId() == opCtx()->recoveryUnit()->getSnapshotId());
wunit.commit();
}
diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp
index 6e62bd1eb10..62b8b5be476 100644
--- a/src/mongo/db/index/index_access_method.cpp
+++ b/src/mongo/db/index/index_access_method.cpp
@@ -258,7 +258,6 @@ Status SortedDataIndexAccessMethod::update(OperationContext* opCtx,
const InsertDeleteOptions& options,
int64_t* numInserted,
int64_t* numDeleted) {
-
UpdateTicket updateTicket;
prepareUpdate(opCtx, coll, oldDoc, newDoc, loc, options, &updateTicket);
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index 49fb1dd32fb..49286f8019d 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -1444,15 +1444,19 @@ Status performAtomicTimeseriesWrites(
args.source = OperationSource::kTimeseriesInsert;
BSONObj updated;
- bool indexesAffected = true;
+ BSONObj diffFromUpdate;
+ const BSONObj* diffOnIndexes =
+ collection_internal::kUpdateAllIndexes; // Assume all indexes are affected.
if (update.getU().type() == write_ops::UpdateModification::Type::kDelta) {
+ diffFromUpdate = update.getU().getDiff();
auto result = doc_diff::applyDiff(original.value(),
- update.getU().getDiff(),
+ diffFromUpdate,
&CollectionQueryInfo::get(*coll).getIndexKeys(opCtx),
static_cast<bool>(repl::tenantMigrationInfo(opCtx)));
updated = result.postImage;
- indexesAffected = result.indexesAffected;
- args.update = update_oplog_entry::makeDeltaOplogEntry(update.getU().getDiff());
+ diffOnIndexes =
+ result.indexesAffected ? &diffFromUpdate : collection_internal::kUpdateNoIndexes;
+ args.update = update_oplog_entry::makeDeltaOplogEntry(diffFromUpdate);
} else if (update.getU().type() == write_ops::UpdateModification::Type::kTransform) {
const auto& transform = update.getU().getTransform();
auto transformed = transform(original.value());
@@ -1472,7 +1476,7 @@ Status performAtomicTimeseriesWrites(
}
collection_internal::updateDocument(
- opCtx, *coll, recordId, original, updated, indexesAffected, &curOp->debug(), &args);
+ opCtx, *coll, recordId, original, updated, diffOnIndexes, &curOp->debug(), &args);
if (slot) {
if (participant) {
// Manually sets the timestamp so that the "prevOpTime" field in the oplog entry is
diff --git a/src/mongo/db/query/collection_query_info.cpp b/src/mongo/db/query/collection_query_info.cpp
index 55261978b21..1f689e766db 100644
--- a/src/mongo/db/query/collection_query_info.cpp
+++ b/src/mongo/db/query/collection_query_info.cpp
@@ -118,78 +118,84 @@ const UpdateIndexData& CollectionQueryInfo::getIndexKeys(OperationContext* opCtx
return _indexedPaths;
}
-void CollectionQueryInfo::computeIndexKeys(OperationContext* opCtx, const CollectionPtr& coll) {
- _indexedPaths.clear();
+void CollectionQueryInfo::computeUpdateIndexData(const IndexCatalogEntry* entry,
+ const IndexAccessMethod* accessMethod,
+ UpdateIndexData* outData) {
+ const IndexDescriptor* descriptor = entry->descriptor();
+ if (bool isWildcard = (descriptor->getAccessMethodName() == IndexNames::WILDCARD);
+ isWildcard || descriptor->getAccessMethodName() == IndexNames::COLUMN) {
+ // Obtain the projection used by the $** index's key generator.
+ const auto* pathProj = isWildcard
+ ? static_cast<const IndexPathProjection*>(
+ static_cast<const WildcardAccessMethod*>(accessMethod)->getWildcardProjection())
+ : static_cast<const IndexPathProjection*>(
+ static_cast<const ColumnStoreAccessMethod*>(accessMethod)
+ ->getColumnstoreProjection());
+ // If the projection is an exclusion, then we must check the new document's keys on all
+ // updates, since we do not exhaustively know the set of paths to be indexed.
+ if (pathProj->exec()->getType() ==
+ TransformerInterface::TransformerType::kExclusionProjection) {
+ outData->allPathsIndexed();
+ } else {
+ // If a subtree was specified in the keyPattern, or if an inclusion projection is
+ // present, then we need only index the path(s) preserved by the projection.
+ const auto& exhaustivePaths = pathProj->exhaustivePaths();
+ invariant(exhaustivePaths);
+ for (const auto& path : *exhaustivePaths) {
+ outData->addPath(path);
+ }
+ }
+ } else if (descriptor->getAccessMethodName() == IndexNames::TEXT) {
+ fts::FTSSpec ftsSpec(descriptor->infoObj());
- auto it = coll->getIndexCatalog()->getIndexIterator(
- opCtx, IndexCatalog::InclusionPolicy::kReady | IndexCatalog::InclusionPolicy::kUnfinished);
- while (it->more()) {
- const IndexCatalogEntry* entry = it->next();
- const IndexDescriptor* descriptor = entry->descriptor();
- const IndexAccessMethod* iam = entry->accessMethod();
-
- if (bool isWildcard = (descriptor->getAccessMethodName() == IndexNames::WILDCARD);
- isWildcard || descriptor->getAccessMethodName() == IndexNames::COLUMN) {
- // Obtain the projection used by the $** index's key generator.
- const auto* pathProj = isWildcard
- ? static_cast<const IndexPathProjection*>(
- static_cast<const WildcardAccessMethod*>(iam)->getWildcardProjection())
- : static_cast<const IndexPathProjection*>(
- static_cast<const ColumnStoreAccessMethod*>(iam)->getColumnstoreProjection());
- // If the projection is an exclusion, then we must check the new document's keys on all
- // updates, since we do not exhaustively know the set of paths to be indexed.
- if (pathProj->exec()->getType() ==
- TransformerInterface::TransformerType::kExclusionProjection) {
- _indexedPaths.allPathsIndexed();
- } else {
- // If a subtree was specified in the keyPattern, or if an inclusion projection is
- // present, then we need only index the path(s) preserved by the projection.
- const auto& exhaustivePaths = pathProj->exhaustivePaths();
- invariant(exhaustivePaths);
- for (const auto& path : *exhaustivePaths) {
- _indexedPaths.addPath(path);
- }
+ if (ftsSpec.wildcard()) {
+ outData->allPathsIndexed();
+ } else {
+ for (size_t i = 0; i < ftsSpec.numExtraBefore(); ++i) {
+ outData->addPath(FieldRef(ftsSpec.extraBefore(i)));
}
- } else if (descriptor->getAccessMethodName() == IndexNames::TEXT) {
- fts::FTSSpec ftsSpec(descriptor->infoObj());
-
- if (ftsSpec.wildcard()) {
- _indexedPaths.allPathsIndexed();
- } else {
- for (size_t i = 0; i < ftsSpec.numExtraBefore(); ++i) {
- _indexedPaths.addPath(FieldRef(ftsSpec.extraBefore(i)));
- }
- for (fts::Weights::const_iterator it = ftsSpec.weights().begin();
- it != ftsSpec.weights().end();
- ++it) {
- _indexedPaths.addPath(FieldRef(it->first));
- }
- for (size_t i = 0; i < ftsSpec.numExtraAfter(); ++i) {
- _indexedPaths.addPath(FieldRef(ftsSpec.extraAfter(i)));
- }
- // Any update to a path containing "language" as a component could change the
- // language of a subdocument. Add the override field as a path component.
- _indexedPaths.addPathComponent(ftsSpec.languageOverrideField());
+ for (fts::Weights::const_iterator it = ftsSpec.weights().begin();
+ it != ftsSpec.weights().end();
+ ++it) {
+ outData->addPath(FieldRef(it->first));
}
- } else {
- BSONObj key = descriptor->keyPattern();
- BSONObjIterator j(key);
- while (j.more()) {
- BSONElement e = j.next();
- _indexedPaths.addPath(FieldRef(e.fieldName()));
+ for (size_t i = 0; i < ftsSpec.numExtraAfter(); ++i) {
+ outData->addPath(FieldRef(ftsSpec.extraAfter(i)));
}
+ // Any update to a path containing "language" as a component could change the
+ // language of a subdocument. Add the override field as a path component.
+ outData->addPathComponent(ftsSpec.languageOverrideField());
+ }
+ } else {
+ BSONObj key = descriptor->keyPattern();
+ BSONObjIterator j(key);
+ while (j.more()) {
+ BSONElement e = j.next();
+ outData->addPath(FieldRef(e.fieldName()));
}
+ }
- // handle partial indexes
- const MatchExpression* filter = entry->getFilterExpression();
- if (filter) {
- stdx::unordered_set<std::string> paths;
- QueryPlannerIXSelect::getFields(filter, &paths);
- for (auto it = paths.begin(); it != paths.end(); ++it) {
- _indexedPaths.addPath(FieldRef(*it));
- }
+ // handle partial indexes
+ const MatchExpression* filter = entry->getFilterExpression();
+ if (filter) {
+ stdx::unordered_set<std::string> paths;
+ QueryPlannerIXSelect::getFields(filter, &paths);
+ for (auto it = paths.begin(); it != paths.end(); ++it) {
+ outData->addPath(FieldRef(*it));
}
}
+}
+
+void CollectionQueryInfo::computeUpdateIndexData(OperationContext* opCtx,
+ const CollectionPtr& coll) {
+ _indexedPaths.clear();
+
+ auto it = coll->getIndexCatalog()->getIndexIterator(
+ opCtx, IndexCatalog::InclusionPolicy::kReady | IndexCatalog::InclusionPolicy::kUnfinished);
+ while (it->more()) {
+ const IndexCatalogEntry* entry = it->next();
+ computeUpdateIndexData(entry, entry->accessMethod(), &_indexedPaths);
+ }
_keysComputed = true;
}
@@ -261,7 +267,7 @@ void CollectionQueryInfo::init(OperationContext* opCtx, const CollectionPtr& col
void CollectionQueryInfo::rebuildIndexData(OperationContext* opCtx, const CollectionPtr& coll) {
_keysComputed = false;
- computeIndexKeys(opCtx, coll);
+ computeUpdateIndexData(opCtx, coll);
updatePlanCacheIndexEntries(opCtx, coll);
}
diff --git a/src/mongo/db/query/collection_query_info.h b/src/mongo/db/query/collection_query_info.h
index e5fbffe720f..2b621d69a2a 100644
--- a/src/mongo/db/query/collection_query_info.h
+++ b/src/mongo/db/query/collection_query_info.h
@@ -58,6 +58,12 @@ public:
static CollectionQueryInfo& get(Collection* collection) {
return CollectionQueryInfo::getCollectionQueryInfo(collection);
}
+ /**
+ * Populate the outData structure using the index keys found in the index definition.
+ */
+ static void computeUpdateIndexData(const IndexCatalogEntry* entry,
+ const IndexAccessMethod* accessMethod,
+ UpdateIndexData* outData);
/**
* Gets the PlanCache for this collection.
@@ -145,7 +151,7 @@ private:
PlanCacheIndexabilityState planCacheIndexabilityState;
};
- void computeIndexKeys(OperationContext* opCtx, const CollectionPtr& coll);
+ void computeUpdateIndexData(OperationContext* opCtx, const CollectionPtr& coll);
void updatePlanCacheIndexEntries(OperationContext* opCtx, const CollectionPtr& coll);
// --- index keys cache
diff --git a/src/mongo/db/repl/storage_timestamp_test.cpp b/src/mongo/db/repl/storage_timestamp_test.cpp
index 9accf4a37b8..70a54a98b63 100644
--- a/src/mongo/db/repl/storage_timestamp_test.cpp
+++ b/src/mongo/db/repl/storage_timestamp_test.cpp
@@ -3205,7 +3205,7 @@ TEST_F(RetryableFindAndModifyTest, RetryableFindAndModifyUpdate) {
record->id,
Snapshotted<BSONObj>(_opCtx->recoveryUnit()->getSnapshotId(), oldObj),
newObj,
- false,
+ collection_internal::kUpdateNoIndexes,
nullptr,
&args);
wuow.commit();
@@ -3258,8 +3258,16 @@ TEST_F(RetryableFindAndModifyTest, RetryableFindAndModifyUpdateWithDamages) {
auto record = cursor->next();
invariant(record);
WriteUnitOfWork wuow(_opCtx);
- const auto statusWith = collection_internal::updateDocumentWithDamages(
- _opCtx, *autoColl, record->id, objSnapshot, source, damages, false, nullptr, &args);
+ const auto statusWith =
+ collection_internal::updateDocumentWithDamages(_opCtx,
+ *autoColl,
+ record->id,
+ objSnapshot,
+ source,
+ damages,
+ collection_internal::kUpdateNoIndexes,
+ nullptr,
+ &args);
wuow.commit();
ASSERT_OK(statusWith.getStatus());
}
diff --git a/src/mongo/db/repl/tenant_migration_donor_service.cpp b/src/mongo/db/repl/tenant_migration_donor_service.cpp
index e68cb891b50..3906900198f 100644
--- a/src/mongo/db/repl/tenant_migration_donor_service.cpp
+++ b/src/mongo/db/repl/tenant_migration_donor_service.cpp
@@ -666,7 +666,7 @@ ExecutorFuture<repl::OpTime> TenantMigrationDonorService::Instance::_updateState
originalRecordId,
originalSnapshot,
updatedStateDocBson,
- false,
+ collection_internal::kUpdateNoIndexes,
nullptr /* OpDebug* */,
&args);
diff --git a/src/mongo/db/serverless/shard_split_donor_service.cpp b/src/mongo/db/serverless/shard_split_donor_service.cpp
index f0d930c171b..ed52e344a70 100644
--- a/src/mongo/db/serverless/shard_split_donor_service.cpp
+++ b/src/mongo/db/serverless/shard_split_donor_service.cpp
@@ -975,14 +975,15 @@ ExecutorFuture<repl::OpTime> ShardSplitDonorService::DonorStateMachine::_updateS
args.oplogSlots = {oplogSlot};
args.update = updatedStateDocBson;
- collection_internal::updateDocument(opCtx,
- *collection,
- originalRecordId,
- originalSnapshot,
- updatedStateDocBson,
- false,
- nullptr /* OpDebug* */,
- &args);
+ collection_internal::updateDocument(
+ opCtx,
+ *collection,
+ originalRecordId,
+ originalSnapshot,
+ updatedStateDocBson,
+ collection_internal::kUpdateNoIndexes,
+ nullptr /* OpDebug* */,
+ &args);
return oplogSlot;
}();
diff --git a/src/mongo/db/transaction/transaction_participant.cpp b/src/mongo/db/transaction/transaction_participant.cpp
index 2d5b49665d1..e615798495f 100644
--- a/src/mongo/db/transaction/transaction_participant.cpp
+++ b/src/mongo/db/transaction/transaction_participant.cpp
@@ -451,14 +451,14 @@ void updateSessionEntry(OperationContext* opCtx,
args.criteria = toUpdateIdDoc;
args.update = updateMod;
- // Specify indexesAffected = false because the sessions collection has two indexes: {_id: 1} and
+ // Specify kUpdateNoIndexes because the sessions collection has two indexes: {_id: 1} and
// {parentLsid: 1, _id.txnNumber: 1, _id: 1}, and none of the fields are mutable.
collection_internal::updateDocument(opCtx,
*collection,
recordId,
Snapshotted<BSONObj>(startingSnapshotId, originalDoc),
updateMod,
- false, /* indexesAffected */
+ collection_internal::kUpdateNoIndexes,
nullptr,
&args);
diff --git a/src/mongo/db/update/addtoset_node_test.cpp b/src/mongo/db/update/addtoset_node_test.cpp
index c09d1eb71e5..6c031e43875 100644
--- a/src/mongo/db/update/addtoset_node_test.cpp
+++ b/src/mongo/db/update/addtoset_node_test.cpp
@@ -133,6 +133,7 @@ TEST_F(AddToSetNodeTest, ApplyNonEach) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -153,6 +154,7 @@ TEST_F(AddToSetNodeTest, ApplyNonEachArray) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, [1]]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -172,6 +174,7 @@ TEST_F(AddToSetNodeTest, ApplyEach) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1, 2]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -191,6 +194,7 @@ TEST_F(AddToSetNodeTest, ApplyToEmptyArray) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -210,6 +214,7 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicateElementsToAdd) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -230,6 +235,7 @@ TEST_F(AddToSetNodeTest, ApplyDoNotAddExistingElements) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -250,6 +256,7 @@ TEST_F(AddToSetNodeTest, ApplyDoNotDeduplicateExistingElements) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -269,6 +276,7 @@ TEST_F(AddToSetNodeTest, ApplyNoElementsToAdd) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0]}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -288,6 +296,7 @@ TEST_F(AddToSetNodeTest, ApplyNoNonDuplicateElementsToAdd) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0]}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -307,6 +316,7 @@ TEST_F(AddToSetNodeTest, ApplyCreateArray) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -326,6 +336,7 @@ TEST_F(AddToSetNodeTest, ApplyCreateEmptyArrayIsNotNoop) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: []}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -348,6 +359,7 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicationOfElementsToAddRespectsCollation) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -370,6 +382,7 @@ TEST_F(AddToSetNodeTest, ApplyComparisonToExistingElementsRespectsCollation) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: ['ABC', 'def']}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -393,6 +406,7 @@ TEST_F(AddToSetNodeTest, ApplyRespectsCollationFromSetCollator) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -438,6 +452,7 @@ TEST_F(AddToSetNodeTest, ApplyNestedArray) {
auto result = node.apply(getApplyParams(doc.root()["a"][1]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{ _id : 1, a : [1, [1]] }"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -458,6 +473,7 @@ TEST_F(AddToSetNodeTest, ApplyIndexesNotAffected) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_FALSE(doc.isInPlaceModeEnabled());
assertOplogEntry(fromjson("{$v: 2, diff: {u: {a: [0, 1]}}}"));
@@ -476,6 +492,7 @@ TEST_F(AddToSetNodeTest, ApplyNoIndexDataOrLogBuilder) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
diff --git a/src/mongo/db/update/arithmetic_node_test.cpp b/src/mongo/db/update/arithmetic_node_test.cpp
index 9aeb06c5b88..328ecbb8d62 100644
--- a/src/mongo/db/update/arithmetic_node_test.cpp
+++ b/src/mongo/db/update/arithmetic_node_test.cpp
@@ -123,6 +123,7 @@ TEST_F(ArithmeticNodeTest, ApplyIncNoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 5}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -142,6 +143,7 @@ TEST_F(ArithmeticNodeTest, ApplyMulNoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 5}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -161,6 +163,7 @@ TEST_F(ArithmeticNodeTest, ApplyRoundingNoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 6.022e23}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -180,6 +183,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyPathToCreate) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 11}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -200,6 +204,7 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePath) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -220,6 +225,7 @@ TEST_F(ArithmeticNodeTest, ApplyExtendPath) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {c: 1, b: 2}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -237,6 +243,7 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePathFromRoot) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -257,6 +264,7 @@ TEST_F(ArithmeticNodeTest, ApplyPositional) {
auto result = node.apply(getApplyParams(doc.root()["a"][1]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [0, 7, 2]}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -295,6 +303,7 @@ TEST_F(ArithmeticNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 5}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -314,6 +323,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoIndexDataNoLogBuilder) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 11}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -331,6 +341,7 @@ TEST_F(ArithmeticNodeTest, ApplyDoesNotAffectIndexes) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 11}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -348,6 +359,7 @@ TEST_F(ArithmeticNodeTest, IncTypePromotionIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -365,6 +377,7 @@ TEST_F(ArithmeticNodeTest, MulTypePromotionIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -382,6 +395,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromIntToDecimalIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -401,6 +415,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromLongToDecimalIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -420,6 +435,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromDoubleToDecimalIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.25\")}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -443,6 +459,7 @@ TEST_F(ArithmeticNodeTest, ApplyPromoteToFloatingPoint) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1.2}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -460,6 +477,7 @@ TEST_F(ArithmeticNodeTest, IncrementedDecimalStaysDecimal) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"11.5\")}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -485,6 +503,7 @@ TEST_F(ArithmeticNodeTest, OverflowIntToLong) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
ASSERT_EQUALS(BSON("a" << static_cast<long long>(initialValue) + 1), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -505,6 +524,7 @@ TEST_F(ArithmeticNodeTest, UnderflowIntToLong) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
ASSERT_EQUALS(BSON("a" << static_cast<long long>(initialValue) - 1), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -524,6 +544,7 @@ TEST_F(ArithmeticNodeTest, IncModeCanBeReused) {
auto result = node.apply(getApplyParams(doc1.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 2}"), doc1);
ASSERT_TRUE(doc1.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -534,6 +555,7 @@ TEST_F(ArithmeticNodeTest, IncModeCanBeReused) {
result = node.apply(getApplyParams(doc2.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 3}"), doc2);
ASSERT_TRUE(doc1.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -551,6 +573,7 @@ TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsInc) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{b: 6, a: NumberLong(5)}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -568,6 +591,7 @@ TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsMul) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{b: 6, a: NumberLong(0)}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -585,6 +609,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyDocument) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 2}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -671,6 +696,7 @@ TEST_F(ArithmeticNodeTest, ApplyNewPath) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{b: 1, a: 2}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -704,6 +730,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoOpDottedPath) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b : 2}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -721,6 +748,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionOnDottedPathIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b : NumberLong(2)}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -754,6 +782,7 @@ TEST_F(ArithmeticNodeTest, ApplyInPlaceDottedPath) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -771,6 +800,7 @@ TEST_F(ArithmeticNodeTest, ApplyPromotionDottedPath) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: NumberLong(5)}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -788,6 +818,7 @@ TEST_F(ArithmeticNodeTest, ApplyDottedPathEmptyDoc) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -805,6 +836,7 @@ TEST_F(ArithmeticNodeTest, ApplyFieldWithDot) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{'a.b':4, a: {b: 2}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
@@ -822,6 +854,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoOpArrayIndex) {
auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -840,6 +873,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionInArrayIsNotANoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: NumberLong(2)}]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -873,6 +907,7 @@ TEST_F(ArithmeticNodeTest, ApplyInPlaceArrayIndex) {
auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 3}]}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -891,6 +926,7 @@ TEST_F(ArithmeticNodeTest, ApplyAppendArray) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -909,6 +945,7 @@ TEST_F(ArithmeticNodeTest, ApplyPaddingArray) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [{b: 0},null,{b: 2}]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -927,6 +964,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericObject) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 0, '2': {b: 2}}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -947,6 +985,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericField) {
auto result = node.apply(getApplyParams(doc.root()["a"]["2"]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {'2': {b: 3}}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -967,6 +1006,7 @@ TEST_F(ArithmeticNodeTest, ApplyExtendNumericField) {
auto result = node.apply(getApplyParams(doc.root()["a"]["2"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {'2': {c: 1, b: 2}}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -985,6 +1025,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericFieldToEmptyObject) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}");
@@ -1003,6 +1044,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyArray) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: [null, null, {b: 2}]}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
ASSERT_EQUALS(getModifiedPaths(), "{a}");
@@ -1073,6 +1115,7 @@ TEST_F(ArithmeticNodeTest, ApplyDeserializedDocNotNoOp) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1, b: NumberInt(0)}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -1094,6 +1137,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNoOp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: NumberInt(2)}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -1115,6 +1159,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNoop) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: NumberInt(1)}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -1136,6 +1181,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNotNoop) {
auto result = node.apply(getApplyParams(doc.root()["a"]["b"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
diff --git a/src/mongo/db/update/compare_node_test.cpp b/src/mongo/db/update/compare_node_test.cpp
index 367525b1356..c72d0a41fe5 100644
--- a/src/mongo/db/update/compare_node_test.cpp
+++ b/src/mongo/db/update/compare_node_test.cpp
@@ -68,6 +68,7 @@ TEST_F(CompareNodeTest, ApplyMaxSameNumber) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -86,6 +87,7 @@ TEST_F(CompareNodeTest, ApplyMinSameNumber) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -104,6 +106,7 @@ TEST_F(CompareNodeTest, ApplyMaxNumberIsLess) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -122,6 +125,7 @@ TEST_F(CompareNodeTest, ApplyMinNumberIsMore) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -140,6 +144,7 @@ TEST_F(CompareNodeTest, ApplyMaxSameValInt) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1.0}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -158,6 +163,7 @@ TEST_F(CompareNodeTest, ApplyMaxSameValIntZero) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 0.0}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -176,6 +182,7 @@ TEST_F(CompareNodeTest, ApplyMinSameValIntZero) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 0.0}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -194,6 +201,7 @@ TEST_F(CompareNodeTest, ApplyMissingFieldMinNumber) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 0}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -212,6 +220,7 @@ TEST_F(CompareNodeTest, ApplyExistingNumberMinNumber) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 0}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -230,6 +239,7 @@ TEST_F(CompareNodeTest, ApplyMissingFieldMaxNumber) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 0}"), doc);
ASSERT_FALSE(doc.isInPlaceModeEnabled());
@@ -248,6 +258,7 @@ TEST_F(CompareNodeTest, ApplyExistingNumberMaxNumber) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 2}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -266,6 +277,7 @@ TEST_F(CompareNodeTest, ApplyExistingDateMaxDate) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {$date: 123123123}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -284,6 +296,7 @@ TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxDoc) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -302,6 +315,7 @@ TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxNumber) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_TRUE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -323,6 +337,7 @@ TEST_F(CompareNodeTest, ApplyMinRespectsCollation) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 'dba'}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -345,6 +360,7 @@ TEST_F(CompareNodeTest, ApplyMinRespectsCollationFromSetCollator) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 'dba'}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -367,6 +383,7 @@ TEST_F(CompareNodeTest, ApplyMaxRespectsCollationFromSetCollator) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 'abd'}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -411,6 +428,7 @@ TEST_F(CompareNodeTest, ApplyIndexesNotAffected) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
@@ -429,6 +447,7 @@ TEST_F(CompareNodeTest, ApplyNoIndexDataOrLogBuilder) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(fromjson("{a: 1}"), doc);
ASSERT_TRUE(doc.isInPlaceModeEnabled());
}
diff --git a/src/mongo/db/update/current_date_node_test.cpp b/src/mongo/db/update/current_date_node_test.cpp
index 540a4b26f3b..2643d2bd28a 100644
--- a/src/mongo/db/update/current_date_node_test.cpp
+++ b/src/mongo/db/update/current_date_node_test.cpp
@@ -143,6 +143,7 @@ TEST_F(CurrentDateNodeTest, ApplyTrue) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
@@ -163,6 +164,7 @@ TEST_F(CurrentDateNodeTest, ApplyFalse) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
@@ -183,6 +185,7 @@ TEST_F(CurrentDateNodeTest, ApplyDate) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
@@ -203,6 +206,7 @@ TEST_F(CurrentDateNodeTest, ApplyTimestamp) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
@@ -223,6 +227,7 @@ TEST_F(CurrentDateNodeTest, ApplyFieldDoesNotExist) {
auto result = node.apply(getApplyParams(doc.root()), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_TRUE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
@@ -245,6 +250,7 @@ TEST_F(CurrentDateNodeTest, ApplyIndexesNotAffected) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
assertOplogEntryIsUpdateOfExpectedType(getOplogEntry(), "a");
}
@@ -261,6 +267,7 @@ TEST_F(CurrentDateNodeTest, ApplyNoIndexDataOrLogBuilder) {
auto result = node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams());
ASSERT_FALSE(result.noop);
ASSERT_FALSE(result.indexesAffected);
+ ASSERT_EQUALS(result.indexesAffected, getIndexAffectedFromLogEntry());
ASSERT_EQUALS(doc.root().countChildren(), 1U);
ASSERT_TRUE(doc.root()["a"].ok());
diff --git a/src/mongo/db/update/document_diff_calculator.cpp b/src/mongo/db/update/document_diff_calculator.cpp
index 19969f7644f..5252a8ebd0a 100644
--- a/src/mongo/db/update/document_diff_calculator.cpp
+++ b/src/mongo/db/update/document_diff_calculator.cpp
@@ -334,6 +334,114 @@ void serializeInlineDiff(diff_tree::DocumentSubDiffNode const* node, BSONObjBuil
}
}
}
+
+void anyIndexesMightBeAffected(ArrayDiffReader* reader,
+ const std::vector<const UpdateIndexData*>& indexData,
+ FieldRef* fieldRef,
+ BitVector* result);
+
+void anyIndexesMightBeAffected(DocumentDiffReader* reader,
+ const std::vector<const UpdateIndexData*>& indexData,
+ FieldRef* fieldRef,
+ BitVector* result) {
+ boost::optional<StringData> delItem;
+ while ((delItem = reader->nextDelete())) {
+ FieldRef::FieldRefTempAppend tempAppend(*fieldRef, *delItem);
+ for (size_t i = 0; i < indexData.size(); i++) {
+ if (!(*result)[i]) {
+ (*result)[i] = indexData[i]->mightBeIndexed(*fieldRef);
+ }
+ }
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+
+ boost::optional<BSONElement> updItem;
+ while ((updItem = reader->nextUpdate())) {
+ FieldRef::FieldRefTempAppend tempAppend(*fieldRef, updItem->fieldNameStringData());
+ for (size_t i = 0; i < indexData.size(); i++) {
+ if (!(*result)[i]) {
+ (*result)[i] = indexData[i]->mightBeIndexed(*fieldRef);
+ }
+ }
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+
+ boost::optional<BSONElement> insItem;
+ while ((insItem = reader->nextInsert())) {
+ FieldRef::FieldRefTempAppend tempAppend(*fieldRef, insItem->fieldNameStringData());
+ for (size_t i = 0; i < indexData.size(); i++) {
+ if (!(*result)[i]) {
+ (*result)[i] = indexData[i]->mightBeIndexed(*fieldRef);
+ }
+ }
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+
+ for (auto subItem = reader->nextSubDiff(); subItem; subItem = reader->nextSubDiff()) {
+ FieldRef::FieldRefTempAppend tempAppend(*fieldRef, subItem->first);
+ stdx::visit(
+ OverloadedVisitor{[&indexData, &fieldRef, &result](DocumentDiffReader& item) {
+ anyIndexesMightBeAffected(&item, indexData, fieldRef, result);
+ },
+ [&indexData, &fieldRef, &result](ArrayDiffReader& item) {
+ anyIndexesMightBeAffected(&item, indexData, fieldRef, result);
+ }},
+ subItem->second);
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+}
+
+void anyIndexesMightBeAffected(ArrayDiffReader* reader,
+ const std::vector<const UpdateIndexData*>& indexData,
+ FieldRef* fieldRef,
+ BitVector* result) {
+ if (reader->newSize()) {
+ for (size_t i = 0; i < indexData.size(); i++) {
+ if (!(*result)[i]) {
+ (*result)[i] = indexData[i]->mightBeIndexed(*fieldRef);
+ }
+ }
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+ for (auto item = reader->next(); item; item = reader->next()) {
+ auto idxAsStr = std::to_string(item->first);
+ FieldRef::FieldRefTempAppend tempAppend(*fieldRef, idxAsStr);
+ stdx::visit(
+ OverloadedVisitor{[&indexData, &fieldRef, &result](BSONElement& update) {
+ for (size_t i = 0; i < indexData.size(); i++) {
+ if (!(*result)[i]) {
+ (*result)[i] = indexData[i]->mightBeIndexed(*fieldRef);
+ }
+ }
+ },
+ [&indexData, &fieldRef, &result](DocumentDiffReader& item) {
+ anyIndexesMightBeAffected(&item, indexData, fieldRef, result);
+ },
+ [&indexData, &fieldRef, &result](ArrayDiffReader& item) {
+ anyIndexesMightBeAffected(&item, indexData, fieldRef, result);
+ }},
+ item->second);
+ // early exit
+ if (result->all()) {
+ return;
+ }
+ }
+}
} // namespace
boost::optional<DiffResult> computeOplogDiff(const BSONObj& pre,
@@ -362,4 +470,18 @@ boost::optional<BSONObj> computeInlineDiff(const BSONObj& pre, const BSONObj& po
return bob.obj();
}
+
+BitVector anyIndexesMightBeAffected(const Diff& diff,
+ const std::vector<const UpdateIndexData*>& indexData) {
+ invariant(!indexData.empty());
+ BitVector result(indexData.size());
+ if (diff.isEmpty()) {
+ return result;
+ }
+
+ DocumentDiffReader reader(diff);
+ FieldRef path;
+ anyIndexesMightBeAffected(&reader, indexData, &path, &result);
+ return result;
+}
} // namespace mongo::doc_diff
diff --git a/src/mongo/db/update/document_diff_calculator.h b/src/mongo/db/update/document_diff_calculator.h
index a8ce159edbf..f1850a34e03 100644
--- a/src/mongo/db/update/document_diff_calculator.h
+++ b/src/mongo/db/update/document_diff_calculator.h
@@ -29,11 +29,12 @@
#pragma once
+#include "boost/dynamic_bitset.hpp"
+
#include "mongo/bson/bsonobj.h"
#include "mongo/db/update/document_diff_serialization.h"
#include "mongo/db/update_index_data.h"
-
namespace mongo::doc_diff {
struct DiffResult {
@@ -81,4 +82,13 @@ boost::optional<DiffResult> computeOplogDiff(const BSONObj& pre,
*/
boost::optional<BSONObj> computeInlineDiff(const BSONObj& pre, const BSONObj& post);
+using BitVector = boost::dynamic_bitset<size_t>;
+/**
+ * Returns a bitset of the same size of the indexData argument, where each bit indicates
+ * whether one of the modifications described in the diff document affects one of the
+ * indexed paths described in the matching object.
+ */
+BitVector anyIndexesMightBeAffected(const Diff& diff,
+ const std::vector<const UpdateIndexData*>& indexData);
+
}; // namespace mongo::doc_diff
diff --git a/src/mongo/db/update/update_node_test_fixture.h b/src/mongo/db/update/update_node_test_fixture.h
index 5125c77cfce..609eb1f2833 100644
--- a/src/mongo/db/update/update_node_test_fixture.h
+++ b/src/mongo/db/update/update_node_test_fixture.h
@@ -31,7 +31,9 @@
#include "mongo/db/concurrency/locker_noop_service_context_test_fixture.h"
#include "mongo/db/service_context.h"
+#include "mongo/db/update/document_diff_calculator.h"
#include "mongo/db/update/update_node.h"
+#include "mongo/db/update/update_oplog_entry_serialization.h"
#include "mongo/db/update/v2_log_builder.h"
#include "mongo/unittest/unittest.h"
@@ -89,6 +91,21 @@ protected:
return applyParams;
}
+ bool getIndexAffectedFromLogEntry() {
+ if (!_logBuilder || !_indexData) {
+ return false;
+ }
+ // Keep the object alive, extractDiffFromOplogEntry returns a subdocument of this document.
+ BSONObj logEntry = getOplogEntry();
+ auto diff = update_oplog_entry::extractDiffFromOplogEntry(logEntry);
+ if (!diff) {
+ return false;
+ }
+ return mongo::doc_diff::anyIndexesMightBeAffected(
+ *diff, std::vector<const UpdateIndexData*>{_indexData.get()})
+ .any();
+ }
+
UpdateNode::UpdateNodeApplyParams getUpdateNodeApplyParams() {
UpdateNode::UpdateNodeApplyParams applyParams;
applyParams.pathToCreate = _pathToCreate;
diff --git a/src/mongo/db/update/update_oplog_entry_serialization.cpp b/src/mongo/db/update/update_oplog_entry_serialization.cpp
index 274fe568baa..daee76db1c2 100644
--- a/src/mongo/db/update/update_oplog_entry_serialization.cpp
+++ b/src/mongo/db/update/update_oplog_entry_serialization.cpp
@@ -40,6 +40,18 @@ BSONObj makeDeltaOplogEntry(const doc_diff::Diff& diff) {
return builder.obj();
}
+boost::optional<BSONObj> extractDiffFromOplogEntry(const BSONObj& opLog) {
+ auto version = opLog["$v"];
+ if (version.ok() &&
+ version.numberInt() == static_cast<int>(UpdateOplogEntryVersion::kDeltaV2)) {
+ auto diff = opLog[kDiffObjectFieldName];
+ if (diff.type() == BSONType::Object) {
+ return diff.embeddedObject();
+ }
+ }
+ return {};
+}
+
namespace {
BSONElement extractNewValueForFieldFromV1Entry(const BSONObj& oField, StringData fieldName) {
// Check the '$set' section.
diff --git a/src/mongo/db/update/update_oplog_entry_serialization.h b/src/mongo/db/update/update_oplog_entry_serialization.h
index 24ace4c3315..a438c9ee5e2 100644
--- a/src/mongo/db/update/update_oplog_entry_serialization.h
+++ b/src/mongo/db/update/update_oplog_entry_serialization.h
@@ -67,6 +67,11 @@ enum class FieldRemovedStatus { kFieldRemoved, kFieldNotRemoved, kUnknown };
BSONObj makeDeltaOplogEntry(const doc_diff::Diff& diff);
/**
+ * Given a $v: 2 delta-style oplog entry, return the embedded diff object.
+ */
+boost::optional<BSONObj> extractDiffFromOplogEntry(const BSONObj& opLog);
+
+/**
* Produce the contents of the 'o' field of a replacement style oplog entry.
*/
inline BSONObj makeReplacementOplogEntry(const BSONObj& replacement) {
diff --git a/src/mongo/db/update_index_data.cpp b/src/mongo/db/update_index_data.cpp
index 5d1ce33fc34..0c5a6a4f5b8 100644
--- a/src/mongo/db/update_index_data.cpp
+++ b/src/mongo/db/update_index_data.cpp
@@ -126,6 +126,10 @@ void UpdateIndexData::clear() {
_allPathsIndexed = false;
}
+bool UpdateIndexData::isEmpty() const {
+ return !_allPathsIndexed && _canonicalPaths.empty() && _pathComponents.empty();
+}
+
bool UpdateIndexData::mightBeIndexed(const FieldRef& path) const {
if (_allPathsIndexed) {
return true;
diff --git a/src/mongo/db/update_index_data.h b/src/mongo/db/update_index_data.h
index 7fb7f5f553b..d23f0fbea9c 100644
--- a/src/mongo/db/update_index_data.h
+++ b/src/mongo/db/update_index_data.h
@@ -78,6 +78,11 @@ public:
bool mightBeIndexed(const FieldRef& path) const;
+ /**
+ * Return whether this structure has been cleared or has not been initialized yet.
+ */
+ bool isEmpty() const;
+
private:
/**
* Returns true if 'b' is a prefix of 'a', or if the two paths are equal.
diff --git a/src/mongo/dbtests/multikey_paths_test.cpp b/src/mongo/dbtests/multikey_paths_test.cpp
index f77b68b7c44..6498311e026 100644
--- a/src/mongo/dbtests/multikey_paths_test.cpp
+++ b/src/mongo/dbtests/multikey_paths_test.cpp
@@ -251,7 +251,6 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnDocumentUpdate) {
auto oldDoc = collection->docFor(_opCtx.get(), record->id);
{
WriteUnitOfWork wuow(_opCtx.get());
- const bool indexesAffected = true;
OpDebug* opDebug = nullptr;
CollectionUpdateArgs args{oldDoc.value()};
collection_internal::updateDocument(
@@ -260,7 +259,7 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnDocumentUpdate) {
record->id,
oldDoc,
BSON("_id" << 0 << "a" << 5 << "b" << BSON_ARRAY(1 << 2 << 3)),
- indexesAffected,
+ collection_internal::kUpdateAllIndexes,
opDebug,
&args);
wuow.commit();
@@ -302,19 +301,18 @@ TEST_F(MultikeyPathsTest, PathsUpdatedOnDocumentUpdateWithDamages) {
auto damagesOutput = doc_diff::computeDamages(oldDoc.value(), diffResult->diff, false);
{
WriteUnitOfWork wuow(_opCtx.get());
- const bool indexesAffected = true;
OpDebug* opDebug = nullptr;
CollectionUpdateArgs args{oldDoc.value()};
- auto newDocResult =
- collection_internal::updateDocumentWithDamages(_opCtx.get(),
- *collection,
- record->id,
- oldDoc,
- damagesOutput.damageSource.get(),
- damagesOutput.damages,
- indexesAffected,
- opDebug,
- &args);
+ auto newDocResult = collection_internal::updateDocumentWithDamages(
+ _opCtx.get(),
+ *collection,
+ record->id,
+ oldDoc,
+ damagesOutput.damageSource.get(),
+ damagesOutput.damages,
+ collection_internal::kUpdateAllIndexes,
+ opDebug,
+ &args);
ASSERT_TRUE(newDocResult.getValue().woCompare(newDoc) == 0);
ASSERT_TRUE(newDocResult.isOK());
wuow.commit();
diff --git a/src/mongo/dbtests/query_stage_count.cpp b/src/mongo/dbtests/query_stage_count.cpp
index d5db5948ddb..c4b7df24d5d 100644
--- a/src/mongo/dbtests/query_stage_count.cpp
+++ b/src/mongo/dbtests/query_stage_count.cpp
@@ -131,7 +131,7 @@ public:
oldrecordId,
Snapshotted<BSONObj>(_opCtx.recoveryUnit()->getSnapshotId(), oldDoc),
newDoc,
- true,
+ collection_internal::kUpdateAllIndexes,
nullptr,
&args);
wunit.commit();
diff --git a/src/mongo/dbtests/query_stage_sort.cpp b/src/mongo/dbtests/query_stage_sort.cpp
index fed69ed967a..d7b67d4cb85 100644
--- a/src/mongo/dbtests/query_stage_sort.cpp
+++ b/src/mongo/dbtests/query_stage_sort.cpp
@@ -400,8 +400,14 @@ public:
CollectionUpdateArgs args{oldDoc.value()};
{
WriteUnitOfWork wuow(&_opCtx);
- collection_internal::updateDocument(
- &_opCtx, coll, *it, oldDoc, newDoc(oldDoc), false, nullptr, &args);
+ collection_internal::updateDocument(&_opCtx,
+ coll,
+ *it,
+ oldDoc,
+ newDoc(oldDoc),
+ collection_internal::kUpdateNoIndexes,
+ nullptr,
+ &args);
wuow.commit();
}
exec->restoreState(&coll);
@@ -419,8 +425,14 @@ public:
oldDoc = coll->docFor(&_opCtx, *it);
{
WriteUnitOfWork wuow(&_opCtx);
- collection_internal::updateDocument(
- &_opCtx, coll, *it++, oldDoc, newDoc(oldDoc), false, nullptr, &args);
+ collection_internal::updateDocument(&_opCtx,
+ coll,
+ *it++,
+ oldDoc,
+ newDoc(oldDoc),
+ collection_internal::kUpdateNoIndexes,
+ nullptr,
+ &args);
wuow.commit();
}
}