summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/database_name.h2
-rw-r--r--src/mongo/db/namespace_string.h4
-rw-r--r--src/mongo/db/pipeline/aggregation_context_fixture.h10
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.cpp10
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup_test.cpp84
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp16
-rw-r--r--src/mongo/db/pipeline/document_source_lookup_test.cpp129
-rw-r--r--src/mongo/db/pipeline/document_source_merge.cpp8
-rw-r--r--src/mongo/db/pipeline/document_source_merge_cursors_test.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_merge_spec.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_merge_test.cpp62
-rw-r--r--src/mongo/db/pipeline/document_source_out.cpp17
-rw-r--r--src/mongo/db/pipeline/document_source_out.h2
-rw-r--r--src/mongo/db/pipeline/document_source_out_test.cpp61
-rw-r--r--src/mongo/db/pipeline/document_source_union_with.cpp12
-rw-r--r--src/mongo/db/pipeline/document_source_union_with_test.cpp71
16 files changed, 394 insertions, 99 deletions
diff --git a/src/mongo/db/database_name.h b/src/mongo/db/database_name.h
index c2099fc654e..a4a549eb75a 100644
--- a/src/mongo/db/database_name.h
+++ b/src/mongo/db/database_name.h
@@ -73,7 +73,7 @@ public:
static DatabaseName createSystemTenantDbName(StringData dbString);
- boost::optional<TenantId> tenantId() const {
+ const boost::optional<TenantId>& tenantId() const {
return _tenantId;
}
diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h
index 692d768dcca..687fb431bb1 100644
--- a/src/mongo/db/namespace_string.h
+++ b/src/mongo/db/namespace_string.h
@@ -327,7 +327,7 @@ public:
Allow, // Deprecated
};
- boost::optional<TenantId> tenantId() const {
+ const boost::optional<TenantId>& tenantId() const {
return _dbName.tenantId();
}
@@ -336,7 +336,7 @@ public:
return StringData(_dbName.toString());
}
- DatabaseName dbName() const {
+ const DatabaseName& dbName() const {
return _dbName;
}
diff --git a/src/mongo/db/pipeline/aggregation_context_fixture.h b/src/mongo/db/pipeline/aggregation_context_fixture.h
index a4dfcfeb769..76cc01a40c4 100644
--- a/src/mongo/db/pipeline/aggregation_context_fixture.h
+++ b/src/mongo/db/pipeline/aggregation_context_fixture.h
@@ -46,7 +46,7 @@ namespace mongo {
class AggregationContextFixture : public ServiceContextTest {
public:
AggregationContextFixture()
- : AggregationContextFixture(NamespaceString("unittests.pipeline_test")) {}
+ : AggregationContextFixture(NamespaceString(boost::none, "unittests", "pipeline_test")) {}
AggregationContextFixture(NamespaceString nss) {
auto service = getServiceContext();
@@ -75,4 +75,12 @@ private:
ServiceContext::UniqueOperationContext _opCtx;
boost::intrusive_ptr<ExpressionContextForTest> _expCtx;
};
+
+class ServerlessAggregationContextFixture : public AggregationContextFixture {
+public:
+ ServerlessAggregationContextFixture()
+ : AggregationContextFixture(
+ NamespaceString(TenantId(OID::gen()), "unittests", "pipeline_test")) {}
+};
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
index 006a84c68b3..5957299c224 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
@@ -61,7 +61,7 @@ namespace {
//
// {from: {db: "local", coll: "system.tenantMigration.oplogView"}, ...}.
NamespaceString parseGraphLookupFromAndResolveNamespace(const BSONElement& elem,
- StringData defaultDb) {
+ const DatabaseName& defaultDb) {
// The object syntax only works for 'local.system.tenantMigration.oplogView' which is not a user
// namespace so object type is omitted from the error message below.
uassert(ErrorCodes::FailedToParse,
@@ -79,6 +79,7 @@ NamespaceString parseGraphLookupFromAndResolveNamespace(const BSONElement& elem,
// Valdate the db and coll names.
auto spec = NamespaceSpec::parse({elem.fieldNameStringData()}, elem.embeddedObject());
+ // TODO SERVER-62491 Use system tenantId to construct nss.
auto nss = NamespaceString(spec.getDb().value_or(""), spec.getColl().value_or(""));
uassert(ErrorCodes::FailedToParse,
str::stream()
@@ -109,7 +110,7 @@ std::unique_ptr<DocumentSourceGraphLookUp::LiteParsed> DocumentSourceGraphLookUp
fromElement);
return std::make_unique<LiteParsed>(
- spec.fieldName(), parseGraphLookupFromAndResolveNamespace(fromElement, nss.db()));
+ spec.fieldName(), parseGraphLookupFromAndResolveNamespace(fromElement, nss.dbName()));
}
REGISTER_DOCUMENT_SOURCE(graphLookup,
@@ -548,9 +549,10 @@ void DocumentSourceGraphLookUp::checkMemoryUsage() {
void DocumentSourceGraphLookUp::serializeToArray(
std::vector<Value>& array, boost::optional<ExplainOptions::Verbosity> explain) const {
+ // Do not include tenantId in serialized 'from' namespace.
auto fromValue = (pExpCtx->ns.db() == _from.db())
? Value(_from.coll())
- : Value(Document{{"db", _from.db()}, {"coll", _from.coll()}});
+ : Value(Document{{"db", _from.dbName().db()}, {"coll", _from.coll()}});
// Serialize default options.
MutableDocument spec(DOC("from" << fromValue << "as" << _as.fullPath() << "connectToField"
@@ -750,7 +752,7 @@ intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson(
}
if (argName == "from") {
- from = parseGraphLookupFromAndResolveNamespace(argument, expCtx->ns.db().toString());
+ from = parseGraphLookupFromAndResolveNamespace(argument, expCtx->ns.dbName());
} else if (argName == "as") {
as = argument.String();
} else if (argName == "connectFromField") {
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp b/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
index 26345aa16ab..852ce00d519 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
@@ -82,6 +82,7 @@ private:
// system.tenantMigration.oplogView} can be round tripped.
TEST_F(DocumentSourceGraphLookUpTest, LookupReParseSerializedStageWithFromDBAndColl) {
auto expCtx = getExpCtx();
+ // TODO SERVER-62491 Use system tenantId for nss 'local.system.tenantMigration.oplogView'.
NamespaceString fromNs("local", "system.tenantMigration.oplogView");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -125,7 +126,7 @@ TEST_F(DocumentSourceGraphLookUpTest, LookupReParseSerializedStageWithFromDBAndC
// local.system.tenantMigration.oplogView.
TEST_F(DocumentSourceGraphLookUpTest, RejectsPipelineFromDBAndCollNotLocalDBOrRsOplogView) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -143,7 +144,7 @@ TEST_F(DocumentSourceGraphLookUpTest, RejectsPipelineFromDBAndCollNotLocalDBOrRs
// not "system.tenantMigration.oplogView".
TEST_F(DocumentSourceGraphLookUpTest, RejectsPipelineFromDBAndCollNotRsOplogView) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("local", "coll");
+ NamespaceString fromNs(boost::none, "local", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -161,7 +162,7 @@ TEST_F(DocumentSourceGraphLookUpTest, RejectsPipelineFromDBAndCollNotRsOplogView
// "system.tenantMigration.oplogView" but "db" is not "local".
TEST_F(DocumentSourceGraphLookUpTest, RejectsPipelineFromDBAndCollNotLocalDB) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "system.tenantMigration.oplogView");
+ NamespaceString fromNs(boost::none, "test", "system.tenantMigration.oplogView");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -184,7 +185,7 @@ TEST_F(DocumentSourceGraphLookUpTest,
std::deque<DocumentSource::GetNextResult> fromContents{Document{{"to", 0}}};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -213,7 +214,7 @@ TEST_F(DocumentSourceGraphLookUpTest,
std::deque<DocumentSource::GetNextResult> fromContents{
Document{{"_id", "a"_sd}, {"to", 0}, {"from", 1}}, Document{{"to", 1}}};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -242,7 +243,7 @@ TEST_F(DocumentSourceGraphLookUpTest,
std::deque<DocumentSource::GetNextResult> fromContents{Document{{"to", 0}}};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -286,7 +287,7 @@ TEST_F(DocumentSourceGraphLookUpTest,
std::deque<DocumentSource::GetNextResult> fromContents{
Document(to1), Document(to2), Document(to0from1), Document(to0from2)};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -350,7 +351,7 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldPropagatePauses) {
std::deque<DocumentSource::GetNextResult> fromContents{
Document{{"_id", "a"_sd}, {"to", 0}, {"from", 1}}, Document{{"_id", "b"_sd}, {"to", 1}}};
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -418,7 +419,7 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldPropagatePausesWhileUnwinding) {
std::deque<DocumentSource::GetNextResult> fromContents{
Document{{"_id", "a"_sd}, {"to", 0}, {"from", 1}}, Document{{"_id", "b"_sd}, {"to", 1}}};
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -484,7 +485,7 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldPropagatePausesWhileUnwinding) {
TEST_F(DocumentSourceGraphLookUpTest, GraphLookupShouldReportAsFieldIsModified) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface =
@@ -509,7 +510,7 @@ TEST_F(DocumentSourceGraphLookUpTest, GraphLookupShouldReportAsFieldIsModified)
TEST_F(DocumentSourceGraphLookUpTest, GraphLookupShouldReportFieldsModifiedByAbsorbedUnwind) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface =
@@ -541,7 +542,7 @@ TEST_F(DocumentSourceGraphLookUpTest, GraphLookupWithComparisonExpressionForStar
auto inputMock =
DocumentSourceMock::createForTest(Document({{"_id", 0}, {"a", 1}, {"b", 2}}), expCtx);
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
std::deque<DocumentSource::GetNextResult> fromContents{Document{{"_id", 0}, {"to", true}},
@@ -606,7 +607,7 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldExpandArraysAtEndOfConnectFromField)
Document(middle3),
Document(sinkDoc)};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -679,7 +680,7 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldNotExpandArraysWithinArraysAtEndOfCo
std::deque<DocumentSource::GetNextResult> fromContents{
Document(startDoc), Document(target1), Document(target2), Document(soloDoc)};
- NamespaceString fromNs("test", "graph_lookup");
+ NamespaceString fromNs(boost::none, "test", "graph_lookup");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
@@ -713,5 +714,60 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldNotExpandArraysWithinArraysAtEndOfCo
ASSERT(graphLookupStage->getNext().isEOF());
}
+
+using DocumentSourceUnionWithServerlessTest = ServerlessAggregationContextFixture;
+
+TEST_F(DocumentSourceUnionWithServerlessTest,
+ LiteParsedDocumentSourceLookupContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ auto originalBSON = BSON("$graphLookup" << BSON("from"
+ << "foo"
+ << "startWith"
+ << "$x"
+ << "connectFromField"
+ << "id"
+ << "connectToField"
+ << "id"
+ << "as"
+ << "connections"));
+
+ std::vector<BSONObj> pipeline;
+ NamespaceString nss(expCtx->ns.dbName(), "testColl");
+ auto liteParsedLookup =
+ DocumentSourceGraphLookUp::LiteParsed::parse(nss, originalBSON.firstElement());
+ auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(expCtx->ns.dbName(), "foo")));
+}
+
+TEST_F(DocumentSourceUnionWithServerlessTest,
+ CreateFromBSONContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ auto tenantId = expCtx->ns.tenantId();
+ ASSERT(tenantId);
+
+ NamespaceString graphLookupNs(expCtx->ns.dbName(), "foo");
+ expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
+ {graphLookupNs.coll().toString(), {graphLookupNs, std::vector<BSONObj>()}}});
+
+ auto spec = BSON("$graphLookup" << BSON("from"
+ << "foo"
+ << "startWith"
+ << "$x"
+ << "connectFromField"
+ << "id"
+ << "connectToField"
+ << "id"
+ << "as"
+ << "connections"));
+ auto graphLookupStage = DocumentSourceGraphLookUp::createFromBson(spec.firstElement(), expCtx);
+
+ auto pipeline =
+ Pipeline::create({DocumentSourceMock::createForTest(expCtx), graphLookupStage}, expCtx);
+ auto involvedNssSet = pipeline->getInvolvedCollections();
+ ASSERT_EQ(involvedNssSet.size(), 1UL);
+ ASSERT_EQ(1ul, involvedNssSet.count(graphLookupNs));
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index ee4c9414f24..f29df62a1a0 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -89,7 +89,8 @@ void lookupPipeValidator(const Pipeline& pipeline) {
// {from: {db: "config", coll: "cache.chunks.*"}, ...} or
// {from: {db: "local", coll: "oplog.rs"}, ...} or
// {from: {db: "local", coll: "tenantMigration.oplogView"}, ...} .
-NamespaceString parseLookupFromAndResolveNamespace(const BSONElement& elem, StringData defaultDb) {
+NamespaceString parseLookupFromAndResolveNamespace(const BSONElement& elem,
+ const DatabaseName& defaultDb) {
// The object syntax only works for 'cache.chunks.*', 'local.oplog.rs', and
// 'local.tenantMigration.oplogViewwhich' which are not user namespaces so object type is
// omitted from the error message below.
@@ -104,6 +105,7 @@ NamespaceString parseLookupFromAndResolveNamespace(const BSONElement& elem, Stri
// Valdate the db and coll names.
auto spec = NamespaceSpec::parse({elem.fieldNameStringData()}, elem.embeddedObject());
+ // TODO SERVER-62491 Use system tenantId to construct nss if running in serverless.
auto nss = NamespaceString(spec.getDb().value_or(""), spec.getColl().value_or(""));
uassert(
ErrorCodes::FailedToParse,
@@ -309,9 +311,9 @@ std::unique_ptr<DocumentSourceLookUp::LiteParsed> DocumentSourceLookUp::LitePars
NamespaceString fromNss;
if (!fromElement) {
validateLookupCollectionlessPipeline(pipelineElem);
- fromNss = NamespaceString::makeCollectionlessAggregateNSS(nss.db());
+ fromNss = NamespaceString::makeCollectionlessAggregateNSS(nss.dbName());
} else {
- fromNss = parseLookupFromAndResolveNamespace(fromElement, nss.db());
+ fromNss = parseLookupFromAndResolveNamespace(fromElement, nss.dbName());
}
uassert(ErrorCodes::InvalidNamespace,
str::stream() << "invalid $lookup namespace: " << fromNss.ns(),
@@ -1025,9 +1027,11 @@ void DocumentSourceLookUp::serializeToArray(
std::vector<Value>& array, boost::optional<ExplainOptions::Verbosity> explain) const {
// Support alternative $lookup from config.cache.chunks* namespaces.
+ //
+ // Do not include the tenantId in serialized 'from' namespace.
auto fromValue = (pExpCtx->ns.db() == _fromNs.db())
? Value(_fromNs.coll())
- : Value(Document{{"db", _fromNs.db()}, {"coll", _fromNs.coll()}});
+ : Value(Document{{"db", _fromNs.dbName().db()}, {"coll", _fromNs.coll()}});
MutableDocument output(
Document{{getSourceName(), Document{{"from", fromValue}, {"as", _as.fullPath()}}}});
@@ -1208,7 +1212,7 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceLookUp::createFromBson(
}
if (argName == kFromField) {
- fromNs = parseLookupFromAndResolveNamespace(argument, pExpCtx->ns.db());
+ fromNs = parseLookupFromAndResolveNamespace(argument, pExpCtx->ns.dbName());
continue;
}
@@ -1241,7 +1245,7 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceLookUp::createFromBson(
if (fromNs.ns().empty()) {
validateLookupCollectionlessPipeline(pipeline);
- fromNs = NamespaceString::makeCollectionlessAggregateNSS(pExpCtx->ns.db());
+ fromNs = NamespaceString::makeCollectionlessAggregateNSS(pExpCtx->ns.dbName());
}
uassert(ErrorCodes::FailedToParse, "must specify 'as' field for a $lookup", !as.empty());
diff --git a/src/mongo/db/pipeline/document_source_lookup_test.cpp b/src/mongo/db/pipeline/document_source_lookup_test.cpp
index a565fb60c26..82a0b6bbd61 100644
--- a/src/mongo/db/pipeline/document_source_lookup_test.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup_test.cpp
@@ -87,7 +87,7 @@ public:
// confirms that variables defined in the ExpressionContext are captured by the $lookup stage.
TEST_F(DocumentSourceLookUpTest, PreservesParentPipelineLetVariables) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -110,7 +110,7 @@ TEST_F(DocumentSourceLookUpTest, PreservesParentPipelineLetVariables) {
TEST_F(DocumentSourceLookUpTest, AcceptsPipelineSyntax) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -128,7 +128,7 @@ TEST_F(DocumentSourceLookUpTest, AcceptsPipelineSyntax) {
TEST_F(DocumentSourceLookUpTest, AcceptsPipelineWithLetSyntax) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -153,7 +153,7 @@ TEST_F(DocumentSourceLookUpTest, AcceptsPipelineWithLetSyntax) {
TEST_F(DocumentSourceLookUpTest, LookupEmptyPipelineDoesntUseDiskAndIsOKInATransaction) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -175,7 +175,7 @@ TEST_F(DocumentSourceLookUpTest, LookupEmptyPipelineDoesntUseDiskAndIsOKInATrans
TEST_F(DocumentSourceLookUpTest, LookupWithOutInPipelineNotAllowed) {
auto ERROR_CODE_OUT_BANNED_IN_LOOKUP = 51047;
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
ASSERT_THROWS_CODE(
@@ -208,19 +208,19 @@ TEST_F(DocumentSourceLookUpTest, LiteParsedDocumentSourceLookupContainsExpectedN
<< "as"
<< "lookup1"));
- NamespaceString nss("test.test");
+ NamespaceString nss(boost::none, "test.test");
std::vector<BSONObj> pipeline;
auto liteParsedLookup = DocumentSourceLookUp::LiteParsed::parse(nss, stageSpec.firstElement());
auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
- ASSERT_EQ(1ul, namespaceSet.count(NamespaceString("test.namespace1")));
- ASSERT_EQ(1ul, namespaceSet.count(NamespaceString("test.namespace2")));
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(boost::none, "test.namespace1")));
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(boost::none, "test.namespace2")));
ASSERT_EQ(2ul, namespaceSet.size());
}
TEST_F(DocumentSourceLookUpTest, RejectLookupWhenDepthLimitIsExceeded) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -241,7 +241,7 @@ TEST_F(DocumentSourceLookUpTest, RejectLookupWhenDepthLimitIsExceeded) {
TEST_F(ReplDocumentSourceLookUpTest, RejectsPipelineWithChangeStreamStage) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -257,7 +257,7 @@ TEST_F(ReplDocumentSourceLookUpTest, RejectsPipelineWithChangeStreamStage) {
TEST_F(ReplDocumentSourceLookUpTest, RejectsSubPipelineWithChangeStreamStage) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -275,7 +275,7 @@ TEST_F(ReplDocumentSourceLookUpTest, RejectsSubPipelineWithChangeStreamStage) {
TEST_F(DocumentSourceLookUpTest, AcceptsLocalFieldForeignFieldAndPipelineSyntax) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -302,7 +302,7 @@ TEST_F(DocumentSourceLookUpTest, AcceptsLocalFieldForeignFieldAndPipelineSyntax)
TEST_F(DocumentSourceLookUpTest, AcceptsLocalFieldForeignFieldAndPipelineWithLetSyntax) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -335,7 +335,7 @@ TEST_F(DocumentSourceLookUpTest, AcceptsLocalFieldForeignFieldAndPipelineWithLet
TEST_F(DocumentSourceLookUpTest, RejectsLocalFieldForeignFieldWhenLetIsSpecified) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -358,7 +358,7 @@ TEST_F(DocumentSourceLookUpTest, RejectsLocalFieldForeignFieldWhenLetIsSpecified
TEST_F(DocumentSourceLookUpTest, RejectsInvalidLetVariableName) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -410,7 +410,7 @@ TEST_F(DocumentSourceLookUpTest, RejectsInvalidLetVariableName) {
TEST_F(DocumentSourceLookUpTest, ShouldBeAbleToReParseSerializedStage) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -468,7 +468,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldBeAbleToReParseSerializedStage) {
TEST_F(DocumentSourceLookUpTest, ShouldBeAbleToReParseSerializedStageWithFieldsAndPipeline) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -535,7 +535,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldBeAbleToReParseSerializedStageWithFieldsA
// be round tripped.
TEST_F(DocumentSourceLookUpTest, LookupReParseSerializedStageWithFromDBAndColl) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("config", "cache.chunks.test.foo");
+ NamespaceString fromNs(boost::none, "config", "cache.chunks.test.foo");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -573,7 +573,7 @@ TEST_F(DocumentSourceLookUpTest, LookupReParseSerializedStageWithFromDBAndColl)
// can be round tripped.
TEST_F(DocumentSourceLookUpTest, LookupWithLetReParseSerializedStageWithFromDBAndColl) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("config", "cache.chunks.test.foo");
+ NamespaceString fromNs(boost::none, "config", "cache.chunks.test.foo");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -612,7 +612,7 @@ TEST_F(DocumentSourceLookUpTest, LookupWithLetReParseSerializedStageWithFromDBAn
// Tests that $lookup with 'collation' can be round tripped.
TEST_F(DocumentSourceLookUpTest, LookupReParseSerializedStageWithCollation) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -661,7 +661,7 @@ TEST_F(DocumentSourceLookUpTest, LookupReParseSerializedStageWithCollation) {
// config.cache.chunks*.
TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadDBAndColl) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -678,7 +678,7 @@ TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadDBAndColl) {
// "cache.chunks.*" but "db" is not "config".
TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadColl) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("config", "coll");
+ NamespaceString fromNs(boost::none, "config", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -695,7 +695,7 @@ TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadColl) {
// not "cache.chunks.*".
TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadDB) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "cache.chunks.test.foo");
+ NamespaceString fromNs(boost::none, "test", "cache.chunks.test.foo");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -713,7 +713,7 @@ TEST_F(DocumentSourceLookUpTest, RejectsPipelineFromDBAndCollWithBadDB) {
// syntax.
TEST_F(DocumentSourceLookUpTest, FromDBAndCollDistributedPlanLogic) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("config", "cache.chunks.test.foo");
+ NamespaceString fromNs(boost::none, "config", "cache.chunks.test.foo");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -738,7 +738,7 @@ TEST_F(DocumentSourceLookUpTest, FromDBAndCollDistributedPlanLogic) {
// $lookup with from: <string> syntax.
TEST_F(DocumentSourceLookUpTest, LookupDistributedPlanLogic) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -844,7 +844,7 @@ private:
TEST_F(DocumentSourceLookUpTest, ShouldPropagatePauses) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -895,7 +895,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldPropagatePauses) {
TEST_F(DocumentSourceLookUpTest, ShouldPropagatePausesWhileUnwinding) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -950,7 +950,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldPropagatePausesWhileUnwinding) {
TEST_F(DocumentSourceLookUpTest, LookupReportsAsFieldIsModified) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -973,7 +973,7 @@ TEST_F(DocumentSourceLookUpTest, LookupReportsAsFieldIsModified) {
TEST_F(DocumentSourceLookUpTest, LookupReportsFieldsModifiedByAbsorbedUnwind) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "foreign");
+ NamespaceString fromNs(boost::none, "test", "foreign");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1007,7 +1007,7 @@ BSONObj sequentialCacheStageObj(const StringData status = "kBuilding"_sd,
TEST_F(DocumentSourceLookUpTest, ShouldCacheNonCorrelatedSubPipelinePrefix) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1036,7 +1036,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldCacheNonCorrelatedSubPipelinePrefix) {
TEST_F(DocumentSourceLookUpTest,
ShouldDiscoverVariablesReferencedInFacetPipelineAfterAnExhaustiveAllStage) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1078,7 +1078,7 @@ TEST_F(DocumentSourceLookUpTest,
TEST_F(DocumentSourceLookUpTest, ExprEmbeddedInMatchExpressionShouldBeOptimized) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1118,7 +1118,7 @@ TEST_F(DocumentSourceLookUpTest, ExprEmbeddedInMatchExpressionShouldBeOptimized)
TEST_F(DocumentSourceLookUpTest,
ShouldIgnoreLocalVariablesShadowingLetVariablesWhenFindingNonCorrelatedPrefix) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1154,7 +1154,7 @@ TEST_F(DocumentSourceLookUpTest,
TEST_F(DocumentSourceLookUpTest, ShouldInsertCacheBeforeCorrelatedNestedLookup) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1192,7 +1192,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldInsertCacheBeforeCorrelatedNestedLookup)
TEST_F(DocumentSourceLookUpTest,
ShouldIgnoreNestedLookupLetVariablesShadowingOuterLookupLetVariablesWhenFindingPrefix) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1226,7 +1226,7 @@ TEST_F(DocumentSourceLookUpTest,
TEST_F(DocumentSourceLookUpTest, ShouldCacheEntirePipelineIfNonCorrelated) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1259,7 +1259,7 @@ TEST_F(DocumentSourceLookUpTest, ShouldCacheEntirePipelineIfNonCorrelated) {
TEST_F(DocumentSourceLookUpTest,
ShouldReplaceNonCorrelatedPrefixWithCacheAfterFirstSubPipelineIteration) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1333,7 +1333,7 @@ TEST_F(DocumentSourceLookUpTest,
TEST_F(DocumentSourceLookUpTest,
ShouldAbandonCacheIfMaxSizeIsExceededAfterFirstSubPipelineIteration) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1399,7 +1399,7 @@ TEST_F(DocumentSourceLookUpTest,
TEST_F(DocumentSourceLookUpTest, ShouldNotCacheIfCorrelatedStageIsAbsorbedIntoPlanExecutor) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -1426,5 +1426,56 @@ TEST_F(DocumentSourceLookUpTest, ShouldNotCacheIfCorrelatedStageIsAbsorbedIntoPl
ASSERT_VALUE_EQ(Value(subPipeline->writeExplainOps(kExplain)), Value(BSONArray(expectedPipe)));
}
+using DocumentSourceLookUpServerlessTest = ServerlessAggregationContextFixture;
+
+TEST_F(DocumentSourceLookUpServerlessTest,
+ LiteParsedDocumentSourceLookupContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+
+ auto stageSpec =
+ BSON("$lookup" << BSON("from"
+ << "namespace1"
+ << "pipeline"
+ << BSON_ARRAY(BSON(
+ "$lookup"
+ << BSON("from"
+ << "namespace2"
+ << "as"
+ << "lookup2"
+ << "pipeline"
+ << BSON_ARRAY(BSON("$match" << BSON("x" << 1))))))
+ << "as"
+ << "lookup1"));
+
+ NamespaceString nss(expCtx->ns.dbName(), "testColl");
+ std::vector<BSONObj> pipeline;
+ auto liteParsedLookup = DocumentSourceLookUp::LiteParsed::parse(nss, stageSpec.firstElement());
+ auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(expCtx->ns.dbName(), "namespace1")));
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(expCtx->ns.dbName(), "namespace2")));
+ ASSERT_EQ(2ul, namespaceSet.size());
+}
+
+TEST_F(DocumentSourceLookUpServerlessTest, CreateFromBSONContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ ASSERT(expCtx->ns.tenantId());
+
+ NamespaceString fromNs(expCtx->ns.dbName(), "coll");
+ expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
+ {fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
+
+ auto docSource = DocumentSourceLookUp::createFromBson(
+ BSON("$lookup" << BSON("from"
+ << "coll"
+ << "pipeline" << BSON_ARRAY(BSON("$match" << BSON("x" << 1))) << "as"
+ << "as"))
+ .firstElement(),
+ expCtx);
+ auto lookupStage = static_cast<DocumentSourceLookUp*>(docSource.get());
+ ASSERT(lookupStage);
+ ASSERT_EQ(lookupStage->getFromNs(), NamespaceString(expCtx->ns.dbName(), "coll"));
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_merge.cpp b/src/mongo/db/pipeline/document_source_merge.cpp
index a9ae914c2c0..ae413052ddf 100644
--- a/src/mongo/db/pipeline/document_source_merge.cpp
+++ b/src/mongo/db/pipeline/document_source_merge.cpp
@@ -257,7 +257,7 @@ BSONObj extractMergeOnFieldsFromDoc(const Document& doc, const std::set<FieldPat
* explicitly specified, it will be defaulted to 'defaultDb'.
*/
DocumentSourceMergeSpec parseMergeSpecAndResolveTargetNamespace(const BSONElement& spec,
- StringData defaultDb) {
+ const DatabaseName& defaultDb) {
NamespaceString targetNss;
DocumentSourceMergeSpec mergeSpec;
@@ -278,7 +278,7 @@ DocumentSourceMergeSpec parseMergeSpecAndResolveTargetNamespace(const BSONElemen
// target namespace collection is empty, we'll use the default database name as a target
// database, and the provided namespace value as a collection name.
targetNss = {defaultDb, targetNss.ns()};
- } else if (targetNss.db().empty()) {
+ } else if (targetNss.dbName().db().empty()) {
// Use the default database name if it wasn't specified explicilty.
targetNss = {defaultDb, targetNss.coll()};
}
@@ -316,7 +316,7 @@ std::unique_ptr<DocumentSourceMerge::LiteParsed> DocumentSourceMerge::LiteParsed
typeName(spec.type())),
spec.type() == BSONType::String || spec.type() == BSONType::Object);
- auto mergeSpec = parseMergeSpecAndResolveTargetNamespace(spec, nss.db());
+ auto mergeSpec = parseMergeSpecAndResolveTargetNamespace(spec, nss.dbName());
auto targetNss = mergeSpec.getTargetNss();
uassert(ErrorCodes::InvalidNamespace,
@@ -451,7 +451,7 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceMerge::createFromBson(
"{} only supports a string or object argument, not {}"_format(kStageName, spec.type()),
spec.type() == BSONType::String || spec.type() == BSONType::Object);
- auto mergeSpec = parseMergeSpecAndResolveTargetNamespace(spec, expCtx->ns.db());
+ auto mergeSpec = parseMergeSpecAndResolveTargetNamespace(spec, expCtx->ns.dbName());
auto targetNss = mergeSpec.getTargetNss();
auto whenMatched =
mergeSpec.getWhenMatched() ? mergeSpec.getWhenMatched()->mode : kDefaultWhenMatched;
diff --git a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
index 61b2aef0c97..f01b449025d 100644
--- a/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
+++ b/src/mongo/db/pipeline/document_source_merge_cursors_test.cpp
@@ -71,7 +71,7 @@ const std::vector<HostAndPort> kTestShardHosts = {HostAndPort("FakeShard1Host",
HostAndPort("FakeShard2Host", 12345),
HostAndPort("FakeShard3Host", 12345)};
-const NamespaceString kTestNss = NamespaceString("test.mergeCursors"_sd);
+const NamespaceString kTestNss = NamespaceString(boost::none, "test.mergeCursors"_sd);
const HostAndPort kTestHost = HostAndPort("localhost:27017"_sd);
const CursorId kExhaustedCursorID = 0;
diff --git a/src/mongo/db/pipeline/document_source_merge_spec.cpp b/src/mongo/db/pipeline/document_source_merge_spec.cpp
index 41926a7cfd9..7f8356435bf 100644
--- a/src/mongo/db/pipeline/document_source_merge_spec.cpp
+++ b/src/mongo/db/pipeline/document_source_merge_spec.cpp
@@ -41,6 +41,7 @@
namespace mongo {
using namespace fmt::literals;
+// TODO SERVER-66708 Ensure the correct tenantId is passed when deserializing the merge target nss.
NamespaceString mergeTargetNssParseFromBSON(const BSONElement& elem) {
uassert(51178,
"{} 'into' field must be either a string or an object, "
@@ -67,7 +68,7 @@ NamespaceString mergeTargetNssParseFromBSON(const BSONElement& elem) {
void mergeTargetNssSerializeToBSON(const NamespaceString& targetNss,
StringData fieldName,
BSONObjBuilder* bob) {
- bob->append(fieldName, BSON("db" << targetNss.db() << "coll" << targetNss.coll()));
+ bob->append(fieldName, BSON("db" << targetNss.dbName().db() << "coll" << targetNss.coll()));
}
std::vector<std::string> mergeOnFieldsParseFromBSON(const BSONElement& elem) {
diff --git a/src/mongo/db/pipeline/document_source_merge_test.cpp b/src/mongo/db/pipeline/document_source_merge_test.cpp
index e7d87707900..8404baa69e5 100644
--- a/src/mongo/db/pipeline/document_source_merge_test.cpp
+++ b/src/mongo/db/pipeline/document_source_merge_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/exec/document_value/document_value_test_util.h"
#include "mongo/db/pipeline/aggregation_context_fixture.h"
#include "mongo/db/pipeline/document_source_merge.h"
+#include "mongo/db/pipeline/document_source_mock.h"
#include "mongo/db/pipeline/process_interface/non_shardsvr_process_interface.h"
namespace mongo {
@@ -96,7 +97,7 @@ public:
TEST_F(DocumentSourceMergeTest, CorrectlyParsesIfMergeSpecIsString) {
const auto& defaultDb = getExpCtx()->ns.db();
- const auto& targetColl = "target_collection";
+ const std::string targetColl = "target_collection";
auto spec = BSON("$merge" << targetColl);
auto mergeStage = createMergeStage(spec);
ASSERT(mergeStage);
@@ -106,7 +107,7 @@ TEST_F(DocumentSourceMergeTest, CorrectlyParsesIfMergeSpecIsString) {
TEST_F(DocumentSourceMergeTest, CorrectlyParsesIfIntoIsString) {
const auto& defaultDb = getExpCtx()->ns.db();
- const auto& targetColl = "target_collection";
+ const std::string targetColl = "target_collection";
auto spec = BSON("$merge" << BSON("into" << targetColl));
auto mergeStage = createMergeStage(spec);
ASSERT(mergeStage);
@@ -116,8 +117,8 @@ TEST_F(DocumentSourceMergeTest, CorrectlyParsesIfIntoIsString) {
TEST_F(DocumentSourceMergeTest, CorrectlyParsesIfIntoIsObject) {
const auto& defaultDb = getExpCtx()->ns.db();
- const auto& targetDb = "target_db";
- const auto& targetColl = "target_collection";
+ const std::string targetDb = "target_db";
+ const std::string targetColl = "target_collection";
auto spec = BSON("$merge" << BSON("into" << BSON("coll" << targetColl)));
auto mergeStage = createMergeStage(spec);
ASSERT(mergeStage);
@@ -950,5 +951,58 @@ TEST_F(DocumentSourceMergeTest, FailsToParseIfOnFieldHaveDuplicates) {
ASSERT_THROWS_CODE(createMergeStage(spec), AssertionException, 31465);
}
+using DocumentSourceMergeServerlessTest = ServerlessAggregationContextFixture;
+
+TEST_F(DocumentSourceMergeServerlessTest,
+ LiteParsedDocumentSourceLookupContainsExpectedNamespacesInServerless) {
+ const std::string targetColl = "target_collection";
+
+ auto tenantId = TenantId(OID::gen());
+ NamespaceString nss(tenantId, "test", "testColl");
+ std::vector<BSONObj> pipeline;
+
+ auto stageSpec = BSON("$merge" << targetColl);
+ auto liteParsedLookup = DocumentSourceMerge::LiteParsed::parse(nss, stageSpec.firstElement());
+ auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(tenantId, "test", "target_collection")));
+
+ // TODO SERVER-66708 Add a test case once IDL parsed objects have access to tenantId.
+}
+
+TEST_F(DocumentSourceMergeServerlessTest, CreateFromBSONContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ ASSERT(expCtx->ns.tenantId());
+
+ const std::string targetColl = "target_collection";
+
+ // Pass collection name as a string.
+ auto spec = BSON("$merge" << targetColl);
+ auto mergeStage = DocumentSourceMerge::createFromBson(spec.firstElement(), expCtx);
+ auto mergeSource = static_cast<DocumentSourceMerge*>(mergeStage.get());
+ ASSERT(mergeStage);
+ ASSERT(mergeSource->getOutputNs().tenantId());
+ ASSERT_EQ(*mergeSource->getOutputNs().tenantId(), *expCtx->ns.tenantId());
+
+ // Assert the tenantId is not included in the serialized namespace.
+ auto serialized = mergeSource->serialize().getDocument();
+ auto expectedDoc = Document{{"db", expCtx->ns.dbName().db()}, {"coll", targetColl}};
+ ASSERT_DOCUMENT_EQ(serialized["$merge"][kIntoFieldName].getDocument(), expectedDoc);
+
+ // Pass collection name as an object.
+ spec = BSON("$merge" << BSON("into" << BSON("coll" << targetColl)));
+ mergeStage = DocumentSourceMerge::createFromBson(spec.firstElement(), expCtx);
+ mergeSource = static_cast<DocumentSourceMerge*>(mergeStage.get());
+ ASSERT(mergeSource);
+ ASSERT(mergeSource->getOutputNs().tenantId());
+ ASSERT_EQ(*mergeSource->getOutputNs().tenantId(), *expCtx->ns.tenantId());
+
+ // Assert the tenantId is not included in the serialized namespace.
+ serialized = mergeSource->serialize().getDocument();
+ ASSERT_DOCUMENT_EQ(serialized["$merge"][kIntoFieldName].getDocument(), expectedDoc);
+
+ // TODO SERVER-66708 Add a test case once IDL parsed objects have access to tenantId.
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_out.cpp b/src/mongo/db/pipeline/document_source_out.cpp
index 43e55d5da91..cafce106b28 100644
--- a/src/mongo/db/pipeline/document_source_out.cpp
+++ b/src/mongo/db/pipeline/document_source_out.cpp
@@ -76,7 +76,7 @@ DocumentSourceOut::~DocumentSourceOut() {
}
NamespaceString DocumentSourceOut::parseNsFromElem(const BSONElement& spec,
- const StringData& defaultDB) {
+ const DatabaseName& defaultDB) {
if (spec.type() == BSONType::String) {
return NamespaceString(defaultDB, spec.valueStringData());
} else if (spec.type() == BSONType::Object) {
@@ -85,7 +85,7 @@ NamespaceString DocumentSourceOut::parseNsFromElem(const BSONElement& spec,
str::stream() << "If an object is passed to " << kStageName
<< " it must have exactly 2 fields: 'db' and 'coll'",
nsObj.nFields() == 2 && nsObj.hasField("coll") && nsObj.hasField("db"));
- return NamespaceString(nsObj["db"].String(), nsObj["coll"].String());
+ return NamespaceString(defaultDB.tenantId(), nsObj["db"].String(), nsObj["coll"].String());
} else {
uassert(16990,
"{} only supports a string or object argument, but found {}"_format(
@@ -98,7 +98,7 @@ NamespaceString DocumentSourceOut::parseNsFromElem(const BSONElement& spec,
std::unique_ptr<DocumentSourceOut::LiteParsed> DocumentSourceOut::LiteParsed::parse(
const NamespaceString& nss, const BSONElement& spec) {
- NamespaceString targetNss = parseNsFromElem(spec, nss.db());
+ NamespaceString targetNss = parseNsFromElem(spec, nss.dbName());
uassert(ErrorCodes::InvalidNamespace,
"Invalid {} target namespace, {}"_format(kStageName, targetNss.ns()),
targetNss.isValid());
@@ -113,7 +113,8 @@ void DocumentSourceOut::initialize() {
// to be the target collection once we are done.
// Note that this temporary collection name is used by MongoMirror and thus should not be
// changed without consultation.
- _tempNs = NamespaceString(str::stream() << outputNs.db() << ".tmp.agg_out." << UUID::gen());
+ _tempNs = NamespaceString(str::stream()
+ << outputNs.dbName().toString() << ".tmp.agg_out." << UUID::gen());
// Save the original collection options and index specs so we can check they didn't change
// during computation.
@@ -138,7 +139,7 @@ void DocumentSourceOut::initialize() {
cmd.appendElementsUnique(_originalOutOptions);
pExpCtx->mongoProcessInterface->createCollection(
- pExpCtx->opCtx, _tempNs.db().toString(), cmd.done());
+ pExpCtx->opCtx, _tempNs.dbName().toString(), cmd.done());
}
CurOpFailpointHelpers::waitWhileFailPointEnabled(
@@ -203,12 +204,14 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceOut::create(
boost::intrusive_ptr<DocumentSource> DocumentSourceOut::createFromBson(
BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
- auto targetNS = parseNsFromElem(elem, expCtx->ns.db());
+ auto targetNS = parseNsFromElem(elem, expCtx->ns.dbName());
return create(targetNS, expCtx);
}
Value DocumentSourceOut::serialize(boost::optional<ExplainOptions::Verbosity> explain) const {
- return Value(DOC(kStageName << DOC("db" << _outputNs.db() << "coll" << _outputNs.coll())));
+ // Do not include the tenantId in the serialized 'outputNs'.
+ return Value(
+ DOC(kStageName << DOC("db" << _outputNs.dbName().db() << "coll" << _outputNs.coll())));
}
void DocumentSourceOut::waitWhileFailPointEnabled() {
diff --git a/src/mongo/db/pipeline/document_source_out.h b/src/mongo/db/pipeline/document_source_out.h
index 64dda167eb3..88883cb6231 100644
--- a/src/mongo/db/pipeline/document_source_out.h
+++ b/src/mongo/db/pipeline/document_source_out.h
@@ -115,7 +115,7 @@ private:
const boost::intrusive_ptr<ExpressionContext>& expCtx)
: DocumentSourceWriter(kStageName.rawData(), std::move(outputNs), expCtx) {}
- static NamespaceString parseNsFromElem(const BSONElement& spec, const StringData& defaultDB);
+ static NamespaceString parseNsFromElem(const BSONElement& spec, const DatabaseName& defaultDB);
void initialize() override;
diff --git a/src/mongo/db/pipeline/document_source_out_test.cpp b/src/mongo/db/pipeline/document_source_out_test.cpp
index aef9be321f0..abeb2ebca8d 100644
--- a/src/mongo/db/pipeline/document_source_out_test.cpp
+++ b/src/mongo/db/pipeline/document_source_out_test.cpp
@@ -114,5 +114,66 @@ TEST_F(DocumentSourceOutTest, SerializeToString) {
ASSERT_EQ(reSerialized["$out"]["coll"].getStringData(), "some_collection");
}
+using DocumentSourceOutServerlessTest = ServerlessAggregationContextFixture;
+
+TEST_F(DocumentSourceOutServerlessTest,
+ LiteParsedDocumentSourceLookupContainsExpectedNamespacesInServerless) {
+ auto tenantId = TenantId(OID::gen());
+ NamespaceString nss(tenantId, "test", "testColl");
+ std::vector<BSONObj> pipeline;
+
+ auto stageSpec = BSON("$out"
+ << "some_collection");
+ auto liteParsedLookup = DocumentSourceOut::LiteParsed::parse(nss, stageSpec.firstElement());
+ auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(tenantId, "test", "some_collection")));
+
+ // The tenantId for the outputNs should be the same as that on the expCtx despite outputting
+ // into different dbs.
+ stageSpec = BSON("$out" << BSON("db"
+ << "target_db"
+ << "coll"
+ << "some_collection"));
+ liteParsedLookup = DocumentSourceOut::LiteParsed::parse(nss, stageSpec.firstElement());
+ namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(tenantId, "target_db", "some_collection")));
+}
+
+TEST_F(DocumentSourceOutServerlessTest, CreateFromBSONContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ ASSERT(expCtx->ns.tenantId());
+ auto defaultDb = expCtx->ns.dbName();
+
+ const std::string targetColl = "target_collection";
+ auto spec = BSON("$out" << targetColl);
+ auto outStage = DocumentSourceOut::createFromBson(spec.firstElement(), expCtx);
+ auto outSource = static_cast<DocumentSourceOut*>(outStage.get());
+ ASSERT(outSource);
+ ASSERT_EQ(outSource->getOutputNs(), NamespaceString(defaultDb, targetColl));
+
+ // Assert the tenantId is not included in the serialized namespace.
+ auto serialized = outSource->serialize().getDocument();
+ auto expectedDoc = Document{{"db", expCtx->ns.dbName().db()}, {"coll", targetColl}};
+ ASSERT_DOCUMENT_EQ(serialized["$out"].getDocument(), expectedDoc);
+
+ // The tenantId for the outputNs should be the same as that on the expCtx despite outputting
+ // into different dbs.
+ const std::string targetDb = "target_db";
+ spec = BSON("$out" << BSON("db" << targetDb << "coll" << targetColl));
+ outStage = DocumentSourceOut::createFromBson(spec.firstElement(), expCtx);
+ outSource = static_cast<DocumentSourceOut*>(outStage.get());
+ ASSERT(outSource);
+ ASSERT(outSource->getOutputNs().tenantId());
+ ASSERT_EQ(*outSource->getOutputNs().tenantId(), *expCtx->ns.tenantId());
+ ASSERT_EQ(outSource->getOutputNs().dbName().db(), targetDb);
+
+ // Assert the tenantId is not included in the serialized namespace.
+ serialized = outSource->serialize().getDocument();
+ expectedDoc = Document{{"db", targetDb}, {"coll", targetColl}};
+ ASSERT_DOCUMENT_EQ(serialized["$out"].getDocument(), expectedDoc);
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_union_with.cpp b/src/mongo/db/pipeline/document_source_union_with.cpp
index 435cd0b1c6e..e232fe41d75 100644
--- a/src/mongo/db/pipeline/document_source_union_with.cpp
+++ b/src/mongo/db/pipeline/document_source_union_with.cpp
@@ -127,16 +127,16 @@ std::unique_ptr<DocumentSourceUnionWith::LiteParsed> DocumentSourceUnionWith::Li
NamespaceString unionNss;
boost::optional<LiteParsedPipeline> liteParsedPipeline;
if (spec.type() == BSONType::String) {
- unionNss = NamespaceString(nss.db(), spec.valueStringData());
+ unionNss = NamespaceString(nss.dbName(), spec.valueStringData());
} else {
auto unionWithSpec =
UnionWithSpec::parse(IDLParserErrorContext(kStageName), spec.embeddedObject());
if (unionWithSpec.getColl()) {
- unionNss = NamespaceString(nss.db(), *unionWithSpec.getColl());
+ unionNss = NamespaceString(nss.dbName(), *unionWithSpec.getColl());
} else {
// If no collection specified, it must have $documents as first field in pipeline.
validateUnionWithCollectionlessPipeline(unionWithSpec.getPipeline());
- unionNss = NamespaceString::makeCollectionlessAggregateNSS(nss.db());
+ unionNss = NamespaceString::makeCollectionlessAggregateNSS(nss.dbName());
}
// Recursively lite parse the nested pipeline, if one exists.
@@ -185,16 +185,16 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceUnionWith::createFromBson(
NamespaceString unionNss;
std::vector<BSONObj> pipeline;
if (elem.type() == BSONType::String) {
- unionNss = NamespaceString(expCtx->ns.db().toString(), elem.valueStringData());
+ unionNss = NamespaceString(expCtx->ns.dbName(), elem.valueStringData());
} else {
auto unionWithSpec =
UnionWithSpec::parse(IDLParserErrorContext(kStageName), elem.embeddedObject());
if (unionWithSpec.getColl()) {
- unionNss = NamespaceString(expCtx->ns.db().toString(), *unionWithSpec.getColl());
+ unionNss = NamespaceString(expCtx->ns.dbName(), *unionWithSpec.getColl());
} else {
// if no collection specified, it must have $documents as first field in pipeline
validateUnionWithCollectionlessPipeline(unionWithSpec.getPipeline());
- unionNss = NamespaceString::makeCollectionlessAggregateNSS(expCtx->ns.db());
+ unionNss = NamespaceString::makeCollectionlessAggregateNSS(expCtx->ns.dbName());
}
pipeline = unionWithSpec.getPipeline().value_or(std::vector<BSONObj>{});
}
diff --git a/src/mongo/db/pipeline/document_source_union_with_test.cpp b/src/mongo/db/pipeline/document_source_union_with_test.cpp
index ec2749edd0f..04f440fa91a 100644
--- a/src/mongo/db/pipeline/document_source_union_with_test.cpp
+++ b/src/mongo/db/pipeline/document_source_union_with_test.cpp
@@ -175,7 +175,7 @@ TEST_F(DocumentSourceUnionWithTest, UnionsWithNonEmptySubPipelines) {
TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithPipeline) {
auto expCtx = getExpCtx();
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{nsToUnionWith.coll().toString(), {nsToUnionWith, std::vector<BSONObj>()}}});
auto bson =
@@ -195,7 +195,7 @@ TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithPipeline) {
TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithoutPipeline) {
auto expCtx = getExpCtx();
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{nsToUnionWith.coll().toString(), {nsToUnionWith, std::vector<BSONObj>()}}});
auto bson = BSON("$unionWith" << nsToUnionWith.coll());
@@ -214,7 +214,7 @@ TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithoutPipeline) {
TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithoutPipelineExtraSubobject) {
auto expCtx = getExpCtx();
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{nsToUnionWith.coll().toString(), {nsToUnionWith, std::vector<BSONObj>()}}});
auto bson = BSON("$unionWith" << BSON("coll" << nsToUnionWith.coll()));
@@ -233,7 +233,7 @@ TEST_F(DocumentSourceUnionWithTest, SerializeAndParseWithoutPipelineExtraSubobje
TEST_F(DocumentSourceUnionWithTest, ParseErrors) {
auto expCtx = getExpCtx();
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{nsToUnionWith.coll().toString(), {nsToUnionWith, std::vector<BSONObj>()}}});
ASSERT_THROWS_CODE(
@@ -395,7 +395,7 @@ TEST_F(DocumentSourceUnionWithTest, DependencyAnalysisReportsReferencedFieldsBef
TEST_F(DocumentSourceUnionWithTest, RespectsViewDefinition) {
auto expCtx = getExpCtx();
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{nsToUnionWith.coll().toString(),
{nsToUnionWith, std::vector<BSONObj>{fromjson("{$match: {_id: {$mod: [2, 0]}}}")}}}});
@@ -427,8 +427,8 @@ TEST_F(DocumentSourceUnionWithTest, RespectsViewDefinition) {
TEST_F(DocumentSourceUnionWithTest, ConcatenatesViewDefinitionToPipeline) {
auto expCtx = getExpCtx();
- NamespaceString viewNsToUnionWith(expCtx->ns.db(), "view");
- NamespaceString nsToUnionWith(expCtx->ns.db(), "coll");
+ NamespaceString viewNsToUnionWith(expCtx->ns.dbName(), "view");
+ NamespaceString nsToUnionWith(expCtx->ns.dbName(), "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{viewNsToUnionWith.coll().toString(),
{nsToUnionWith, std::vector<BSONObj>{fromjson("{$match: {_id: {$mod: [2, 0]}}}")}}}});
@@ -465,7 +465,7 @@ TEST_F(DocumentSourceUnionWithTest, ConcatenatesViewDefinitionToPipeline) {
TEST_F(DocumentSourceUnionWithTest, RejectUnionWhenDepthLimitIsExceeded) {
auto expCtx = getExpCtx();
- NamespaceString fromNs("test", "coll");
+ NamespaceString fromNs(boost::none, "test", "coll");
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
@@ -591,5 +591,60 @@ TEST_F(DocumentSourceUnionWithTest, StricterConstraintsFromSubSubPipelineAreInhe
StageConstraints::UnionRequirement::kAllowed);
ASSERT_TRUE(unionStage.constraints(Pipeline::SplitState::kUnsplit) == expectedConstraints);
}
+
+using DocumentSourceUnionWithServerlessTest = ServerlessAggregationContextFixture;
+
+TEST_F(DocumentSourceUnionWithServerlessTest,
+ LiteParsedDocumentSourceLookupContainsExpectedNamespacesInServerless) {
+ auto tenantId = TenantId(OID::gen());
+ NamespaceString nss(tenantId, "test", "testColl");
+ std::vector<BSONObj> pipeline;
+
+ auto stageSpec = BSON("$unionWith"
+ << "some_coll");
+ auto liteParsedLookup =
+ DocumentSourceUnionWith::LiteParsed::parse(nss, stageSpec.firstElement());
+ auto namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(tenantId, "test", "some_coll")));
+
+ stageSpec = BSON("$unionWith" << BSON("coll"
+ << "some_coll"
+ << "pipeline" << BSONArray()));
+ liteParsedLookup = DocumentSourceUnionWith::LiteParsed::parse(nss, stageSpec.firstElement());
+ namespaceSet = liteParsedLookup->getInvolvedNamespaces();
+ ASSERT_EQ(1, namespaceSet.size());
+ ASSERT_EQ(1ul, namespaceSet.count(NamespaceString(tenantId, "test", "some_coll")));
+}
+
+TEST_F(DocumentSourceUnionWithServerlessTest,
+ CreateFromBSONContainsExpectedNamespacesInServerless) {
+ auto expCtx = getExpCtx();
+ ASSERT(expCtx->ns.tenantId());
+
+ NamespaceString unionWithNs(expCtx->ns.tenantId(), "test", "some_coll");
+ expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
+ {unionWithNs.coll().toString(), {unionWithNs, std::vector<BSONObj>()}}});
+
+ auto spec = BSON("$unionWith"
+ << "some_coll");
+ auto unionWithStage = DocumentSourceUnionWith::createFromBson(spec.firstElement(), expCtx);
+ auto pipeline =
+ Pipeline::create({DocumentSourceMock::createForTest(expCtx), unionWithStage}, expCtx);
+ auto involvedNssSet = pipeline->getInvolvedCollections();
+ ASSERT_EQ(involvedNssSet.size(), 1UL);
+ ASSERT_EQ(1ul, involvedNssSet.count(unionWithNs));
+
+ spec = BSON("$unionWith" << BSON("coll"
+ << "some_coll"
+ << "pipeline" << BSONArray()));
+ unionWithStage = DocumentSourceUnionWith::createFromBson(spec.firstElement(), expCtx);
+ pipeline =
+ Pipeline::create({DocumentSourceMock::createForTest(expCtx), unionWithStage}, expCtx);
+ involvedNssSet = pipeline->getInvolvedCollections();
+ ASSERT_EQ(involvedNssSet.size(), 1UL);
+ ASSERT_EQ(1ul, involvedNssSet.count(unionWithNs));
+}
+
} // namespace
} // namespace mongo