1

using mongodb shell, I am able to perform an aggregation query that retrieves the whole document. In order to do that I use the $$ROOT variable.

db.reservations.aggregate([
   { $match : { hotelCode : "0360" } },
   { $sort : { confirmationNumber : -1 , timestamp: -1 } },
   { $group : {
       _id : "$confirmationNumber",
       timestamp :{$first : "$timestamp"},
       fullDocument :{$first : "$$ROOT"}
   }}
])

It retrieves objects whose content is confirmationNumber, timestamp, fullDocument. fullDocument is the whole document.

I am wondering if it is possible to do the same with Spring-Data and the aggregation framework.

My java code is:

TypedAggregation<ReservationImage> aggregation = newAggregation(
   ReservationImage.class,
   match(where("hotelCode").is(hotelCode)),
   sort(Direction.DESC,"confirmationNumber","timestamp"),
   group("confirmationNumber").
     first("timestamp").as("timestamp").
     first("$$ROOT").as("reservationImage"));
  List<myClass> items = mongoTemplate.aggregate(
    aggregation, 
    myClass.class).getMappedResults();

the error is : org.springframework.data.mapping.PropertyReferenceException: No property $$ found for type myClass

Do you have any ideas?

Thanks.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Chessman
  • 87
  • 1
  • 8

2 Answers2

10

We created https://jira.spring.io/browse/DATAMONGO-954 to track the support for accessing System Variables from MongoDB Pipeline expressions.

Once that is in place, you should be able to write:

Aggregation agg = newAggregation( //
   match(where("hotelCode").is("0360")), //
   sort(Direction.DESC, "confirmationNumber", "timestamp"), //
   group("confirmationNumber") //
      .first("timestamp").as("timestamp") //                
      .first(Aggregation.ROOT).as("reservationImage") //
);
Thomas Darimont
  • 1,356
  • 11
  • 14
  • Could you please guide me here: https://stackoverflow.com/questions/61056287/java-lang-illegalargumentexception-expected-unique-result-or-null-but-got-more ? – PAA Apr 06 '20 at 17:46
5

I've seen this sort of thing before and it is not just limited to variable names such as $$ROOT. Spring data has it's own ideas about how to map "properties" of the document in the pipeline. Another common problem is simply projecting a new or calculated field that essentially has a new "property" name to it that does not get recognized.

Probably the best approach is to "step down" from using the helper classes and methods and construct the pipeline as BSON documents. You can even get the underlying collection object and the raw output as a BSON document, yet still cast to your typed List at the end.

Mileage may vary to your actual approach,but essentially:

    DBObject match = new BasicDBObject(
        "$match", new BasicDBObject(
            "hotelCode", "0360"
        )
    );

    DBObject sort = new BasicDBObject(
        "$sort", new BasicDBObject(
        "cofirmationNumber", -1
        ).append("timestamp", -1)
    );

    DBObject group =  new BasicDBObject(
        "$group", new BasicDBObject(
            "_id", "confirmationNumber"
        ).append(
            "timestamp", new BasicDBObject(
                "$first", "$timestamp"
            )
        ).append(
            "reservationImage", new BasicDBObject(
                "$first", "$$ROOT"
            )
        )
    );

    List<DBObject> pipeline = Arrays.asList(match,sort,group);

    DBCollection collection = mongoOperation.getCollection("collection");
    DBObject rawoutput = (DBObject)collection.aggregate(pipeline);

    List<myClass> items = new AggregationResults(List<myClass>, rawoutput).getMappedResults();

The main thing is moving away from the helpers that are getting in the way and constructing the pipeline as it should be free of the imposed restrictions.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • thanks for you comments. "Another common problem is simply projecting a new or calculated field that essentially has a new "property" name to it that does not get recognized." Do you have an example at hand for what you'd like to be able to do with Spring Data MongoDB? – Thomas Darimont Jun 13 '14 at 12:53
  • @ThomasDarimont I have come across a few. My Bedtime right now. Bu if there is a place to submit a JIRA issue or whatever bug tracking then let me know. I'll dig up a case and test, against trunk if required. – Neil Lunn Jun 13 '14 at 13:58
  • you can find the JIRA for Spring Data MongoDB here: https://jira.spring.io/browse/DATAMONGO – Thomas Darimont Jun 17 '14 at 07:05