Skip to content

Commit 05fdd7e

Browse files
committed
Support aggregation with $merge as a string (#768)
JAVA-4258
1 parent 240e712 commit 05fdd7e

File tree

4 files changed

+86
-17
lines changed

4 files changed

+86
-17
lines changed

driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/AggregatePublisherImpl.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,19 @@ private MongoNamespace getOutNamespace() {
214214
+ "is not a string or namespace document");
215215
}
216216
} else if (lastPipelineStage.containsKey("$merge")) {
217-
BsonDocument mergeDocument = lastPipelineStage.getDocument("$merge");
218-
if (mergeDocument.isDocument("into")) {
219-
BsonDocument intoDocument = mergeDocument.getDocument("into");
220-
return new MongoNamespace(intoDocument.getString("db", new BsonString(databaseName)).getValue(),
221-
intoDocument.getString("coll").getValue());
222-
} else if (mergeDocument.isString("into")) {
223-
return new MongoNamespace(databaseName, mergeDocument.getString("into").getValue());
217+
if (lastPipelineStage.isString("$merge")) {
218+
return new MongoNamespace(databaseName, lastPipelineStage.getString("$merge").getValue());
219+
} else if (lastPipelineStage.isDocument("$merge")) {
220+
BsonDocument mergeDocument = lastPipelineStage.getDocument("$merge");
221+
if (mergeDocument.isDocument("into")) {
222+
BsonDocument intoDocument = mergeDocument.getDocument("into");
223+
return new MongoNamespace(intoDocument.getString("db", new BsonString(databaseName)).getValue(),
224+
intoDocument.getString("coll").getValue());
225+
} else if (mergeDocument.isString("into")) {
226+
return new MongoNamespace(databaseName, mergeDocument.getString("into").getValue());
227+
}
228+
} else {
229+
throw new IllegalStateException("Cannot return a cursor when the value for $merge stage is not a string or a document");
224230
}
225231
}
226232

driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/AggregatePublisherImplTest.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ void shouldBuildTheExpectedOperationsForDollarOutAsDocument() {
215215
assertOperationIsTheSameAs(expectedOperation, executor.getWriteOperation());
216216
}
217217

218-
@DisplayName("Should build the expected AggregateOperation for $merge")
218+
@DisplayName("Should build the expected AggregateOperation for $merge document")
219219
@Test
220-
void shouldBuildTheExpectedOperationsForDollarMerge() {
220+
void shouldBuildTheExpectedOperationsForDollarMergeDocument() {
221221
String collectionName = "collectionName";
222222
List<BsonDocument> pipeline = asList(BsonDocument.parse("{'$match': 1}"),
223223
BsonDocument.parse(format("{'$merge': {into: '%s'}}", collectionName)));
@@ -293,6 +293,38 @@ void shouldBuildTheExpectedOperationsForDollarMerge() {
293293
assertOperationIsTheSameAs(expectedOperation, executor.getWriteOperation());
294294
}
295295

296+
@DisplayName("Should build the expected AggregateOperation for $merge string")
297+
@Test
298+
void shouldBuildTheExpectedOperationsForDollarMergeString() {
299+
String collectionName = "collectionName";
300+
MongoNamespace collectionNamespace = new MongoNamespace(NAMESPACE.getDatabaseName(), collectionName);
301+
List<BsonDocument> pipeline = asList(BsonDocument.parse("{'$match': 1}"),
302+
BsonDocument.parse(format("{'$merge': '%s'}", collectionName)));
303+
304+
TestOperationExecutor executor = createOperationExecutor(asList(getBatchCursor(), getBatchCursor(), getBatchCursor(), null));
305+
AggregatePublisher<Document> publisher =
306+
new AggregatePublisherImpl<>(null, createMongoOperationPublisher(executor), pipeline, AggregationLevel.COLLECTION);
307+
308+
AggregateToCollectionOperation expectedOperation = new AggregateToCollectionOperation(NAMESPACE, pipeline,
309+
ReadConcern.DEFAULT,
310+
WriteConcern.ACKNOWLEDGED);
311+
312+
// default input should be as expected
313+
Flux.from(publisher).blockFirst();
314+
315+
WriteOperationThenCursorReadOperation operation = (WriteOperationThenCursorReadOperation) executor.getReadOperation();
316+
assertEquals(ReadPreference.primary(), executor.getReadPreference());
317+
assertOperationIsTheSameAs(expectedOperation, operation.getAggregateToCollectionOperation());
318+
319+
FindOperation<Document> expectedFindOperation =
320+
new FindOperation<>(collectionNamespace, getDefaultCodecRegistry().get(Document.class))
321+
.filter(new BsonDocument())
322+
.batchSize(Integer.MAX_VALUE)
323+
.retryReads(true);
324+
325+
assertOperationIsTheSameAs(expectedFindOperation, operation.getReadOperation());
326+
}
327+
296328
@DisplayName("Should handle error scenarios")
297329
@Test
298330
void shouldHandleErrorScenarios() {

driver-sync/src/main/com/mongodb/client/internal/AggregateIterableImpl.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,19 @@ private MongoNamespace getOutNamespace() {
237237
+ "is not a string or namespace document");
238238
}
239239
} else if (lastPipelineStage.containsKey("$merge")) {
240-
BsonDocument mergeDocument = lastPipelineStage.getDocument("$merge");
241-
if (mergeDocument.isDocument("into")) {
242-
BsonDocument intoDocument = mergeDocument.getDocument("into");
243-
return new MongoNamespace(intoDocument.getString("db", new BsonString(namespace.getDatabaseName())).getValue(),
244-
intoDocument.getString("coll").getValue());
245-
} else if (mergeDocument.isString("into")) {
246-
return new MongoNamespace(namespace.getDatabaseName(), mergeDocument.getString("into").getValue());
240+
if (lastPipelineStage.isString("$merge")) {
241+
return new MongoNamespace(namespace.getDatabaseName(), lastPipelineStage.getString("$merge").getValue());
242+
} else if (lastPipelineStage.isDocument("$merge")) {
243+
BsonDocument mergeDocument = lastPipelineStage.getDocument("$merge");
244+
if (mergeDocument.isDocument("into")) {
245+
BsonDocument intoDocument = mergeDocument.getDocument("into");
246+
return new MongoNamespace(intoDocument.getString("db", new BsonString(namespace.getDatabaseName())).getValue(),
247+
intoDocument.getString("coll").getValue());
248+
} else if (mergeDocument.isString("into")) {
249+
return new MongoNamespace(namespace.getDatabaseName(), mergeDocument.getString("into").getValue());
250+
}
251+
} else {
252+
throw new IllegalStateException("Cannot return a cursor when the value for $merge stage is not a string or a document");
247253
}
248254
}
249255

driver-sync/src/test/unit/com/mongodb/client/internal/AggregateIterableSpecification.groovy

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class AggregateIterableSpecification extends Specification {
194194
.comment('this is a comment'))
195195
}
196196

197-
def 'should build the expected AggregateToCollectionOperation for $merge'() {
197+
def 'should build the expected AggregateToCollectionOperation for $merge document'() {
198198
given:
199199
def executor = new TestOperationExecutor([null, null, null, null, null, null, null])
200200
def collectionName = 'collectionName'
@@ -331,6 +331,31 @@ class AggregateIterableSpecification extends Specification {
331331
.comment('this is a comment'))
332332
}
333333

334+
def 'should build the expected AggregateToCollectionOperation for $merge string'() {
335+
given:
336+
def executor = new TestOperationExecutor([null, null, null, null, null, null, null])
337+
def collectionName = 'collectionName'
338+
def collectionNamespace = new MongoNamespace(namespace.getDatabaseName(), collectionName)
339+
def pipeline = [new BsonDocument('$match', new BsonDocument()), new BsonDocument('$merge', new BsonString(collectionName))]
340+
341+
when:
342+
new AggregateIterableImpl(null, namespace, Document, Document, codecRegistry, readPreference, readConcern, writeConcern, executor,
343+
pipeline, AggregationLevel.COLLECTION, false)
344+
.iterator()
345+
346+
def operation = executor.getWriteOperation() as AggregateToCollectionOperation
347+
348+
then:
349+
expect operation, isTheSameAs(new AggregateToCollectionOperation(namespace, pipeline, readConcern, writeConcern,
350+
AggregationLevel.COLLECTION))
351+
352+
when:
353+
operation = executor.getReadOperation() as FindOperation<Document>
354+
355+
then:
356+
operation.getNamespace() == collectionNamespace
357+
}
358+
334359
def 'should build the expected AggregateToCollectionOperation for $out as a document'() {
335360
given:
336361
def cannedResults = [new Document('_id', 1), new Document('_id', 2), new Document('_id', 3)]

0 commit comments

Comments
 (0)