From f1760b0f8530a7aea9804669b1d5d77d34258325 Mon Sep 17 00:00:00 2001 From: jmarkerink Date: Fri, 27 Feb 2026 19:55:30 +0100 Subject: [PATCH] fix: #231 lookup across collections --- .../aggregation/stage/LookupStage.java | 2 +- .../aggregation/stage/LookupStageTest.java | 16 ++++ .../backend/AbstractAggregationTest.java | 87 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStage.java b/core/src/main/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStage.java index c8d110dfe..ff8dd77a3 100644 --- a/core/src/main/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStage.java +++ b/core/src/main/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStage.java @@ -48,7 +48,7 @@ public Stream apply(Stream stream) { } private Document resolveRemoteField(Document document) { - Object value = Utils.getSubdocumentValue(document, localField); + Object value = Utils.getSubdocumentValueCollectionAware(document, localField); List documents = lookupValue(value); Document result = document.clone(); result.put(as, documents); diff --git a/core/src/test/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStageTest.java b/core/src/test/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStageTest.java index f5abe98bf..d76cb26f8 100644 --- a/core/src/test/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStageTest.java +++ b/core/src/test/java/de/bwaldvogel/mongo/backend/aggregation/stage/LookupStageTest.java @@ -148,6 +148,22 @@ public void testLookupWithNestedField() throws Exception { ); } + @Test + public void testLookupWithNestedFieldArray() throws Exception { + prepareAuthorsCollectionMock(); + + LookupStage lookupStage = buildLookupStage("from: 'authors', 'localField': 'author.id', foreignField: '_id', as: 'author'"); + configureAuthorsCollection("_id: 3", "_id: 3, name: 'Uncle Bob'"); + configureAuthorsCollection("_id: 4", "_id: 4, name: 'Aunt Bobby'"); + Document document = json("title: 'Clean Code', author: [{ id: 3 }, {id: 4}]"); + + Stream result = lookupStage.apply(Stream.of(document)); + + assertThat(result).containsExactly( + json("title: 'Clean Code', author: { id: 3 }, author: [{_id: 3, name: 'Uncle Bob'}, {_id: 4, name: 'Aunt Bobby'}]") + ); + } + private LookupStage buildLookupStage(String jsonDocument) { return new LookupStage(json(jsonDocument), database); } diff --git a/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractAggregationTest.java b/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractAggregationTest.java index 208cdd95a..f5cbc65b1 100644 --- a/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractAggregationTest.java +++ b/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractAggregationTest.java @@ -1484,6 +1484,93 @@ void testAggregateWithLookupAndUncorrelatedSubqueries() { ); } + @Test + void testLookupPersonWithSubscription() { + MongoCollection personCollection = db.getCollection("Person"); + MongoCollection subscriptionCollection = db.getCollection("Subscription"); + + personCollection.insertOne(json(""" + "_id": 666, + "personId": 666, + "Relation": [ + { + "organizationId": "org1", + "active": true + }, + { + "organizationId": "org3", + "active": false + } + ] + """)); + + subscriptionCollection.insertOne(json(""" + "_id": 123, + "organizationId": "org1", + "Subscription": [ + { + "Id": 1, + "Name": "Netflix" + }, + { + "Id": 12, + "Name": "Disney+" + } + ] + """)); + subscriptionCollection.insertOne(json(""" + "_id": 456, + "organizationId": "org2", + "Subscription": [ + { + "Id": 2, + "Name": "HBO Max" + }, + { + "Id": 11, + "Name": "SkyShowtime" + } + ] + """)); + + List pipeline = jsonList( + "$match: { 'personId': 666 }", + "$lookup: {from: 'Subscription', localField: 'Relation.organizationId', foreignField: 'organizationId', as: 'Subscriptions'}" + ); + + assertThat(personCollection.aggregate(pipeline)) + .containsExactly(json(""" + "_id": 666, + "personId": 666, + "Relation": [ + { + "organizationId": "org1", + "active": true + }, + { + "organizationId": "org3", + "active": false + } + ], + "Subscriptions": [ + { + "_id": 123, + "organizationId": "org1", + "Subscription": [ + { + "Id": 1, + "Name": "Netflix" + }, + { + "Id": 12, + "Name": "Disney+" + } + ] + } + ] + """)); + } + @Test void testAggregateWithReplaceRoot() { List pipeline = jsonList("$replaceRoot: { newRoot: '$a.b' }");