Status IndexAccessMethod::BulkBuilder::insert(OperationContext* txn, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, int64_t* numInserted) { BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); MultikeyPaths multikeyPaths; _real->getKeys(obj, &keys, &multikeyPaths); _everGeneratedMultipleKeys = _everGeneratedMultipleKeys || (keys.size() > 1); if (!multikeyPaths.empty()) { if (_indexMultikeyPaths.empty()) { _indexMultikeyPaths = multikeyPaths; } else { invariant(_indexMultikeyPaths.size() == multikeyPaths.size()); for (size_t i = 0; i < multikeyPaths.size(); ++i) { _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); } } } for (BSONObjSet::iterator it = keys.begin(); it != keys.end(); ++it) { _sorter->add(*it, loc); _keysInserted++; } if (NULL != numInserted) { *numInserted += keys.size(); } return Status::OK(); }
Status AbstractIndexAccessMethod::BulkBuilderImpl::insert(OperationContext* opCtx, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options) { BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); MultikeyPaths multikeyPaths; _real->getKeys(obj, options.getKeysMode, &keys, &_multikeyMetadataKeys, &multikeyPaths); if (!multikeyPaths.empty()) { if (_indexMultikeyPaths.empty()) { _indexMultikeyPaths = multikeyPaths; } else { invariant(_indexMultikeyPaths.size() == multikeyPaths.size()); for (size_t i = 0; i < multikeyPaths.size(); ++i) { _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); } } } for (const auto& key : keys) { _sorter->add(key, loc); ++_keysInserted; } _isMultiKey = _isMultiKey || _real->shouldMarkIndexAsMultikey(keys, _multikeyMetadataKeys, multikeyPaths); return Status::OK(); }
bool KVCollectionCatalogEntry::setIndexIsMultikey(OperationContext* txn, StringData indexName, const MultikeyPaths& multikeyPaths) { MetaData md = _getMetaData(txn); int offset = md.findIndexOffset(indexName); invariant(offset >= 0); const bool tracksPathLevelMultikeyInfo = !md.indexes[offset].multikeyPaths.empty(); if (tracksPathLevelMultikeyInfo) { invariant(!multikeyPaths.empty()); invariant(multikeyPaths.size() == md.indexes[offset].multikeyPaths.size()); } else { invariant(multikeyPaths.empty()); if (md.indexes[offset].multikey) { // The index is already set as multikey and we aren't tracking path-level multikey // information for it. We return false to indicate that the index metadata is unchanged. return false; } } md.indexes[offset].multikey = true; if (tracksPathLevelMultikeyInfo) { bool newPathIsMultikey = false; bool somePathIsMultikey = false; // Store new path components that cause this index to be multikey in catalog's index // metadata. for (size_t i = 0; i < multikeyPaths.size(); ++i) { std::set<size_t>& indexMultikeyComponents = md.indexes[offset].multikeyPaths[i]; for (const auto multikeyComponent : multikeyPaths[i]) { auto result = indexMultikeyComponents.insert(multikeyComponent); newPathIsMultikey = newPathIsMultikey || result.second; somePathIsMultikey = true; } } // If all of the sets in the multikey paths vector were empty, then no component of any // indexed field caused the index to be multikey. setIndexIsMultikey() therefore shouldn't // have been called. invariant(somePathIsMultikey); if (!newPathIsMultikey) { // We return false to indicate that the index metadata is unchanged. return false; } } _catalog->putMetaData(txn, ns().toString(), md); return true; }
void QueryPlannerTest::addIndex(BSONObj keyPattern, const MultikeyPaths& multikeyPaths) { invariant(multikeyPaths.size() == static_cast<size_t>(keyPattern.nFields())); const bool multikey = std::any_of(multikeyPaths.cbegin(), multikeyPaths.cend(), [](const std::set<size_t>& components) { return !components.empty(); }); const bool sparse = false; const bool unique = false; const char name[] = "my_index_with_path_level_multikey_info"; const MatchExpression* filterExpr = nullptr; const BSONObj infoObj; IndexEntry entry(keyPattern, multikey, sparse, unique, name, filterExpr, infoObj); entry.multikeyPaths = multikeyPaths; params.indices.push_back(entry); }
void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, const MultikeyPaths& multikeyPaths) { if (!_indexTracksPathLevelMultikeyInfo && isMultikey()) { // If the index is already set as multikey and we don't have any path-level information to // update, then there's nothing more for us to do. return; } if (_indexTracksPathLevelMultikeyInfo) { stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex); invariant(multikeyPaths.size() == _indexMultikeyPaths.size()); bool newPathIsMultikey = false; for (size_t i = 0; i < multikeyPaths.size(); ++i) { if (!std::includes(_indexMultikeyPaths[i].begin(), _indexMultikeyPaths[i].end(), multikeyPaths[i].begin(), multikeyPaths[i].end())) { // If 'multikeyPaths' contains a new path component that causes this index to be // multikey, then we must update the index metadata in the CollectionCatalogEntry. newPathIsMultikey = true; break; } } if (!newPathIsMultikey) { // Otherwise, if all the path components in 'multikeyPaths' are already tracked in // '_indexMultikeyPaths', then there's nothing more for us to do. return; } } { // Only one thread should set the multi-key value per collection, because the metadata for a // collection is one large document. Lock::ResourceLock collMDLock( opCtx->lockState(), ResourceId(RESOURCE_METADATA, _ns), MODE_X); if (!_indexTracksPathLevelMultikeyInfo && isMultikey()) { // It's possible that we raced with another thread when acquiring the MD lock. If the // index is already set as multikey and we don't have any path-level information to // update, then there's nothing more for us to do. return; } // This effectively emulates a side-transaction off the main transaction, which invoked // setMultikey. The reason we need is to avoid artificial WriteConflicts, which happen with // snapshot isolation. { StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); RecoveryUnitSwap ruSwap(opCtx, storageEngine->newRecoveryUnit()); WriteUnitOfWork wuow(opCtx); // It's possible that the index type (e.g. ascending/descending index) supports tracking // path-level multikey information, but this particular index doesn't. // CollectionCatalogEntry::setIndexIsMultikey() requires that we discard the path-level // multikey information in order to avoid unintentionally setting path-level multikey // information on an index created before 3.4. if (_collection->setIndexIsMultikey( opCtx, _descriptor->indexName(), _indexTracksPathLevelMultikeyInfo ? multikeyPaths : MultikeyPaths{})) { if (_infoCache) { LOG(1) << _ns << ": clearing plan cache - index " << _descriptor->keyPattern() << " set to multi key."; _infoCache->clearQueryCache(); } } wuow.commit(); } } _isMultikey.store(true); if (_indexTracksPathLevelMultikeyInfo) { stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex); for (size_t i = 0; i < multikeyPaths.size(); ++i) { _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end()); } } }