Get Comments Aggregates.lookup syntax issues

I’m struggling to get Aggregates.lookup to work. The code below has no syntax errors, and all types match accordingly, but when executed gives me the error: “A pipeline stage specification object must contain exactly one field”, and I know it’s pointing somewhere in the lookup phase, maybe something to do with an array or list… but I’m stuck!

The code I’m using here for lookupStage is the $lookup phase generated from Compass. It correctly gives me the result when executing this pipeline in Compass.

All three test cases fail.

List<Bson> pipeline = new ArrayList<>();

// match stage to find movie
Bson match = Aggregates.match(Filters.eq("_id", new ObjectId(movieId)));

// lookup stage to generate movie comments, List<Bson> ... also does not work
List<? extends Bson> lookupStage = new ArrayList<>(
        Arrays.asList(new Document("let", new Document("id", "$_id"))
                .append("pipeline", Arrays.asList(
                        new Document("$match", 
                                new Document("$expr", 
                                        new Document("$eq", Arrays.asList("$movie_id", "$$id")))), 
                        new Document("$sort", new Document("date", -1L)))))
);

Bson lookup = Aggregates.lookup("comments", lookupStage,"comments");

pipeline.add(match);
pipeline.add(lookup);

Document movie = moviesCollection.aggregate(pipeline).first();

The syntax I’m trying to match comes from Aggregates.java documentation and the implementation looks like this. All my types match, but something still isn’t lining up.

// This implementation returns a function

public static Bson lookup(String from, List<? extends Bson> pipeline, String as) {
    return lookup(from, (List)null, (List)pipeline, as);
}

// This implementation returns the lookup stage with different parameters: from, let, pipeline, as

public static <TExpression> Bson lookup(String from, @Nullable List<Variable<TExpression>> let, List<? extends Bson> pipeline, String as) {
    return new Aggregates.LookupStage(from, let, pipeline, as);
}

https://mongodb.github.io/mongo-java-driver/4.2/apidocs/mongodb-driver-core/com/mongodb/client/model/Aggregates.html#lookup(java.lang.String,java.util.List,java.lang.String)

There are not many examples on the web for using Aggregates.lookup with this syntax implementation. Any help in debugging this is greatly appreciated!

Hi, Ian
Don’t rely on compass generated code. I remember getting a similar issue.
Most com.mongodb.client.model.Aggregates.* static helpers methods generate a Bson which can be transformed toBsonDocument which is a bson document :grimacing:.
Now any BsonDocument can be printed using a toString for debugging purpose
by running

Bson lookup = Aggregates.lookup("comments", lookupStage,"comments");
System.out.println(lookup.toBsonDocument());

I get

{
   "$lookup":{
      "from":"comments",
      "pipeline":[
         {
            "let":{
               "id":"$_id"
            },
            "pipeline":[
               {
                  "$match":{
                     "$expr":{
                        "$eq":[
                           "$movie_id",
                           "$$id"
                        ]
                     }
                  }
               },
               {
                  "$sort":{
                     "date":-1
                  }
               }
            ]
         }
      ],
      "as":"comments"
   }
}

can you spot the error? great.
moving on,
when you have some variables (those defined within let), use the following helper method:

 lookup(String from, @Nullable List<Variable<TExpression>> let, List<? extends Bson> pipeline, String as)

i.e.

import static com.mongodb.client.model.Aggregates.*;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Sorts.*;
import static java.util.Arrays.asList;
List<Bson> lookupPipeline = asList(match(expr(new Document("$eq", asList("$movie_id", "$$id")))),
                                                          sort(descending("date")));
Bson lookup = lookup("comments", asList(new Variable<>("id", "$_id")), lookupPipeline,"comments");

You can ignore the helper methods and create your pipeline using org.bson.Document
for example

Bson match = new Document("$match",new Document("$expr",new Document("$eq", asList("$movie_id","$$id"))));
Bson sort = new Document("$sort",new Document("date", new BsonInt32(-1)));
Bson lookupStage = new Document("$lookup",
                new Document("from","comments").
                        append("let",new Document("id","$_id")).
                        append("pipeline",asList(match,sort)).
                        append("as","comments"));

good luck,

1 Like

Thanks for the quick reply Imad_Bouterra, I found the error in my logic. I was able to solve the errors and pass the tests!

Though… I can’t print out the match or lookup using toBsonDocument() like you’ve showed in your code:

Bson lookup = Aggregates.lookup("comments", lookupStage, "comments");
System.out.println(lookup.toBsonDocument()); 

The error underneath the parenthesis immediately after .toBsonDocument reads:

‘toBsonDocument(java.lang.Class , org.bson.codecs.configuration.CodecRegistry)’ in ‘org.bson.conversions.Bson’ cannot be applied to ‘()’

I tried this after implementing the function to display the Bson for your code suggestions, and for my original code, but the error remains. Importing the org.bson.BsonDocument class has no effect. I saw on the docs that the class and codec are arguments optional, but am still unsure why the error is present. A quick Google search doesn’t yield any good help.

http://mongodb.github.io/mongo-java-driver/4.2/apidocs/bson/org/bson/BsonDocument.html#toBsonDocument(java.lang.Class,org.bson.codecs.configuration.CodecRegistry)

Hi Ian,
that’s because the default implementation of toBsonDocument was added in the version 4.2 of mongodb-driver-sync.
you have to upgrade the version of your driver, or add a Codec registry.
you can use the default one

System.out.println(
            lookup.toBsonDocument(BsonDocument.class, 
                                  MongoClientSettings.getDefaultCodecRegistry()));

gives:

{ "$lookup" : { "from" : "comments", "let" : { "id" : "$_id" }, "pipeline" : [{ "$match" : { "$expr" : { "$eq" : ["$movie_id", "$$id"] } } }, { "$sort" : { "date" : -1 } }], "as" : "comments" } }

versions:

<maven-compiler-plugin.source>8</maven-compiler-plugin.source>
<maven-compiler-plugin.target>8</maven-compiler-plugin.target>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<mongodb-driver-sync.version>3.9.1</mongodb-driver-sync.version>

Regerdes,

3 Likes