DocumentSourceChangeStreamTransform::DocumentSourceChangeStreamTransform( const boost::intrusive_ptr<ExpressionContext>& expCtx, const ServerGlobalParams::FeatureCompatibility::Version& fcv, BSONObj changeStreamSpec) : DocumentSource(expCtx), _changeStreamSpec(changeStreamSpec.getOwned()), _isIndependentOfAnyCollection(expCtx->ns.isCollectionlessAggregateNS()), _fcv(fcv) { _nsRegex.emplace(DocumentSourceChangeStream::getNsRegexForChangeStream(expCtx->ns)); auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), _changeStreamSpec); // If the change stream spec includes a resumeToken with a shard key, populate the document key // cache with the field paths. auto resumeAfter = spec.getResumeAfter(); auto startAfter = spec.getStartAfter(); if (resumeAfter || startAfter) { ResumeToken token = resumeAfter ? resumeAfter.get() : startAfter.get(); ResumeTokenData tokenData = token.getData(); if (!tokenData.documentKey.missing() && tokenData.uuid) { std::vector<FieldPath> docKeyFields; auto docKey = tokenData.documentKey.getDocument(); auto iter = docKey.fieldIterator(); while (iter.more()) { auto fieldPair = iter.next(); docKeyFields.push_back(fieldPair.first); } // If the document key from the resume token has more than one field, that means it // includes the shard key and thus should never change. auto isFinal = docKey.size() > 1; _documentKeyCache[tokenData.uuid.get()] = DocumentKeyCacheEntry({docKeyFields, isFinal}); } } }