0

I need to paginate over documents contained in arrays of the root documents. For example, to view the first 10 orders with an open status across customers.

Customer structure

{
 "_id": ObjectId("cust-1"),
 "name": "John Doe",
 "orders": [
 {
 "_id": ObjectId("order-1"),
 "status": "open" 
 },
 {
 "_id": ObjectId("order-2"),
 "status": "closed" 
 }
 ]
},
{
 "_id": ObjectId("cust-2"),
 "name": "Jane Doe",
 "orders": [
 {
 "_id": ObjectId("order-3"),
 "status": "open" 
 },
 {
 "_id": ObjectId("order-4"),
 "status": "open" 
 },
 {
 "_id": ObjectId("order-5"),
 "status": "open" 
 },
 ]
}

A page request for page 0, limit 3 should result in John Doe's customer record containing order-1 and Jane Doe's record containing order-3 and order-4. For example

[{
 "_id": ObjectId("cust-1"),
 "name": "John Doe",
 "orders": [
 {
 "_id": ObjectId("order-1"),
 "status": "open" 
 }
 ]
},
{
 "_id": ObjectId("cust-2"),
 "name": "Jane Doe",
 "orders": [
 {
 "_id": ObjectId("order-3"),
 "status": "open" 
 },
 {
 "_id": ObjectId("order-4"),
 "status": "open" 
 },
 ]
}]

To do this, I unwind orders and count, apply page request, group results by _id and project orders and name.

My code

// Operations
UnwindOperation unwindOperation = Aggregation.unwind("orders");
CountOperation countOperation = Aggregation.count().as("total");
SkipOperation skipOperation = Aggregation.skip(pageable.getOffset());
LimitOperation limitOperation = Aggregation.limit(pageable.getPageSize());
GroupOperation groupOperation = Aggregation.group("_id")
 .push("orders").as("orders")
 .push("name").as("name");
ProjectionOperation projectionOperation = Aggregation.project()
 .andInclude("orders")
 .andInclude("name");
// Facet
FacetOperation facetOperation = Aggregation
 .facet(
 unwindOperation,
 countOperation)
 .as("metadata")
 .and(
 unwindOperation,
 skipOperation, 
 limitOperation,
 groupOperation,
 projectionOperation)
 .as("data");
// Aggregation
Aggregation aggregation = Aggregation.newAggregation(
 Aggregation.match(criteria),
 facetOperation
);
AggregationResults<CaseReportFacetResult> result = mongoTemplate.aggregate(aggregation, Customer.class, CustomerFacetResult.class);
CustomerFacetResult fetchResult = result.getUniqueMappedResult();

Problem This works, but in the result set, the name field of the customer is duplicated for each open order. For example Jane Doe's name is returned as "name": "Jane Doe, Jane Doe".

I have removed name from the grouping, but then it is not able to be projected. If removed from both, then the data is not returned at all.

asked Jun 11 at 19:22

1 Answer 1

1

Instead of push("name") ({ $push: "$name" }) which adding the name value into an array, you should use first("name") to get the first/single name value only.

GroupOperation groupOperation = Aggregation.group("_id")
 .push("orders").as("orders")
 .first("name").as("name");
answered Jun 12 at 10:41
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I see now that it was coming back as an array, didn't notice that before.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.