Mirko
Mirko

Reputation: 137

How to do aggregate queries with accumulators using the MongoDB driver in Java

I'm quite new to MongoDB and its interaction with Java. I'm using this driver

<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>

and I want to perform this aggregation query:

db.getCollection('COLLECTION').aggregate(
[
  {"$match": {"val.elem.0001": {"$exists":true}}},
  {"$project": {"FIELD_PATH": "$val.elem.0001"}},
  {$group: {_id:{"FIELD": {"$literal": "0001"}},
  "PATH": {"$addToSet": "$FIELD_PATH"}}}
]                                          
);

The Java code I've written is the following, however I'm not sure if I correctly used the addToSet method:

AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
new Document("$match", new Document("val.elem.0001",new Document("$exists",true))),
new Document("$project", new Document("FIELD_PATH","$val.elem.0001")),
new Document("$group", new Document("_id",new Document("FIELD", new Document("$literal", "0001"))
    .append("PATH", Accumulators.addToSet("$PATH", "$FIELD_PATH"))))));

It is correct? Because I can't print the result on screen if I add the "append" part. The error that returns is Can't find a codec for class com.mongodb.client.model.BsonField.

So, to summarize and make more readable and comprehensive what I've asked:

Upvotes: 0

Views: 2651

Answers (1)

s7vr
s7vr

Reputation: 75994

Your usage of the API is incorrect.

Change your aggregation to the one below (stick with Document for expressions).

AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
       new Document("$match", new Document("val.elem.0001", new Document("$exists", true))),
       new Document("$project", new Document("FIELD_PATH", "$val.elem.0001")),
       new Document("$group", new Document("_id", new Document("FIELD", new Document("$literal", "0001"))).append("PATH", new Document("$addToSet", "$FIELD_PATH")))));

BsonField is a helper class primarily built to providea data holder for Accumulators which return key and Bson value pair. So it is not meant to be used as value type. When used with helper methods, it is converted into Document and serialized using the document codec.

You can rework your aggregation to use helper methods (Filters, Projections Accumulators & Aggregates).

AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
       Aggregates.match(Filters.exists("val.elem.0001")),
       Aggregates.project(Projections.computed("FIELD_PATH", "$val.elem.0001")),
       Aggregates.group( new Document("FIELD", new Document("$literal", "0001")), Accumulators.addToSet("PATH", "$FIELD_PATH"))));

You can further simplify the aggregation by using static imports.

import static com.mongodb.client.model.Accumulators.addToSet;
import static com.mongodb.client.model.Aggregates.group;
import static com.mongodb.client.model.Aggregates.match;
import static com.mongodb.client.model.Aggregates.project;
import static com.mongodb.client.model.Projections.computed;
import static java.util.Arrays.*;

AggregateIterable<Document> output = collection.aggregate(asList(
       match(Filters.exists("val.elem.0001")),
       project(computed("FIELD_PATH", "$val.elem.0001")),
       group( new Document("FIELD", new Document("$literal", "0001")), addToSet("PATH", "$FIELD_PATH"))));

See the documentation for more information.

Upvotes: 2

Related Questions