list<intrusive_ptr<DocumentSource>> DocumentSourceChangeStream::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { // TODO: Add sharding support here (SERVER-29141). uassert( 40470, "The $changeStream stage is not supported on sharded systems.", !expCtx->inRouter); uassert(40471, "Only default collation is allowed when using a $changeStream stage.", !expCtx->getCollator()); auto replCoord = repl::ReplicationCoordinator::get(expCtx->opCtx); uassert(40573, "The $changeStream stage is only supported on replica sets", replCoord); Timestamp startFrom = replCoord->getLastCommittedOpTime().getTimestamp(); intrusive_ptr<DocumentSourceCheckResumeToken> resumeStage = nullptr; auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), elem.embeddedObject()); if (auto resumeAfter = spec.getResumeAfter()) { ResumeToken token = resumeAfter.get(); startFrom = token.getTimestamp(); DocumentSourceCheckResumeTokenSpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceCheckResumeToken::create(expCtx, std::move(spec)); } const bool changeStreamIsResuming = resumeStage != nullptr; auto fullDocOption = spec.getFullDocument(); uassert(40575, str::stream() << "unrecognized value for the 'fullDocument' option to the " "$changeStream stage. Expected \"default\" or " "\"updateLookup\", got \"" << fullDocOption << "\"", fullDocOption == "updateLookup"_sd || fullDocOption == "default"_sd); const bool shouldLookupPostImage = (fullDocOption == "updateLookup"_sd); auto oplogMatch = DocumentSourceOplogMatch::create( buildMatchFilter(expCtx->ns, startFrom, changeStreamIsResuming), expCtx); auto transformation = createTransformationStage(elem.embeddedObject(), expCtx); list<intrusive_ptr<DocumentSource>> stages = {oplogMatch, transformation}; if (resumeStage) { stages.push_back(resumeStage); } auto closeCursorSource = DocumentSourceCloseCursor::create(expCtx); stages.push_back(closeCursorSource); if (shouldLookupPostImage) { stages.push_back(DocumentSourceLookupChangePostImage::create(expCtx)); } return stages; }
list<intrusive_ptr<DocumentSource>> DocumentSourceChangeStream::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { // TODO: Add sharding support here (SERVER-29141). uassert( 40470, "The $changeStream stage is not supported on sharded systems.", !expCtx->inMongos); uassert(40471, "Only default collation is allowed when using a $changeStream stage.", !expCtx->getCollator()); auto replCoord = repl::ReplicationCoordinator::get(expCtx->opCtx); uassert(40573, "The $changeStream stage is only supported on replica sets", replCoord && replCoord->getReplicationMode() == repl::ReplicationCoordinator::Mode::modeReplSet); Timestamp startFrom = replCoord->getMyLastAppliedOpTime().getTimestamp(); intrusive_ptr<DocumentSource> resumeStage = nullptr; auto spec = DocumentSourceChangeStreamSpec::parse(IDLParserErrorContext("$changeStream"), elem.embeddedObject()); if (auto resumeAfter = spec.getResumeAfter()) { ResumeToken token = resumeAfter.get(); auto resumeNamespace = UUIDCatalog::get(expCtx->opCtx).lookupNSSByUUID(token.getUuid()); uassert(40615, "The resume token UUID does not exist. Has the collection been dropped?", !resumeNamespace.isEmpty()); startFrom = token.getTimestamp(); if (expCtx->needsMerge) { DocumentSourceShardCheckResumabilitySpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceShardCheckResumability::create(expCtx, std::move(spec)); } else { DocumentSourceEnsureResumeTokenPresentSpec spec; spec.setResumeToken(std::move(token)); resumeStage = DocumentSourceEnsureResumeTokenPresent::create(expCtx, std::move(spec)); } } const bool changeStreamIsResuming = resumeStage != nullptr; auto fullDocOption = spec.getFullDocument(); uassert(40575, str::stream() << "unrecognized value for the 'fullDocument' option to the " "$changeStream stage. Expected \"default\" or " "\"updateLookup\", got \"" << fullDocOption << "\"", fullDocOption == "updateLookup"_sd || fullDocOption == "default"_sd); const bool shouldLookupPostImage = (fullDocOption == "updateLookup"_sd); auto oplogMatch = DocumentSourceOplogMatch::create( buildMatchFilter(expCtx->ns, startFrom, changeStreamIsResuming), expCtx); auto transformation = createTransformationStage(elem.embeddedObject(), expCtx); list<intrusive_ptr<DocumentSource>> stages = {oplogMatch, transformation}; if (resumeStage) { stages.push_back(resumeStage); } auto closeCursorSource = DocumentSourceCloseCursor::create(expCtx); stages.push_back(closeCursorSource); if (shouldLookupPostImage) { stages.push_back(DocumentSourceLookupChangePostImage::create(expCtx)); } return stages; }