简体   繁体   中英

Spring data mongodb application design and data aggregation

I was developing a little application with spring data mongo and angularjs . Here is model :

public class Lease {
        @Id
        private String id;
        private long created;
        private Lessor lessor;
        private Lessee lessee;
      }

public class Payment {
        @Id
        private String id;
        private Integer month;
        private Integer year;
        private Long amount;
        private String leaseId;
     }

I did not embed Payment model as I need it to have an id and edit it in its own form. On the main page of the app it represents a list of leases with payments by month. The year is chosen in a select above the list.

How to better load leases with payments by year by month using rest: may be first load leases then inject ids to payments or there is a better solution? For the moment I chose to use aggregation:

LookupOperation lookupOperation = LookupOperation.newLookup().
                                  from("payment").
                                  localField("leaseId").
                                  foreignField("id").
                                  as("payments");

AggregationOperation match = Aggregation.match(Criteria.where("payments.year").is(year));
Aggregation aggregation = Aggregation.newAggregation(lookupOperation, match);

return mongoOperations.aggregate(aggregation, "lease", LeaseAggregation.class).getMappedResults();

The problem is that match is exclusive here and for example if there are no payments in 2017, list of leases is empty. I saw "addFields" functionality in mongo by it is not yet implemented in spring data mongodb. A better json return for me would be :

{"leases" : [{"id" : 123, "created" : 12324343434, "payments" : [123, 455, 343, 323, 344, null, null, 332, 323, 232, 333, 434}]}

where payments will represent twelve months of a particular year.

How can I obtain this with spring data mongodb?

Any help would be appreciated!

Whenever Spring Data Mongo lacks an AggregationOperation you need (to reproduce $addFields , $redact ...), a workaround (some may say quick and dirty solution) is to pass the raw aggregation to Spring, using directly the com.mongodb.client tools :

String collectionName = mongoTemplate.getCollectionName(Payment.class);
MongoCollection<Document> collection = mongoClient.getDatabase(mongoTemplate.getDb().getName()).getCollection(collectionName);

AggregateIterable<Document> ai = collection.aggregate(Arrays.asList(
    Document.parse(/* { "group" : { ... } } */)))

MongoCollection.aggregate() is passed the aggregation pipeline as List<Document> (in fact List<? extends Bson> in raw form as suggested above using Document.parse(), and you can of course also use new Document() to make it look more like proper OOP code. I tend to use the raw form when the raw aggregation is complex or as many nested components for nested Document are too much verbose for me, but that's a matter of taste.


2020 UPDATE.

Use this workaround instead. With this it's easy to use both AggregationOperation provided by Spring and your own "raw" aggregation stages in the same place.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM