Sample code to write custom query in reactive mongo

 This is a sample code snippet to demostrate how to write a custom aggregation with spring data reactive mongo.

Here are the sample collection structures:

Collection: UserData

{
   "userId":"123456",
   "profilePhotoAssetId":"abcd1234"
}

Collection: Comment

{
   "authorId":"123456",
   "body":"This is a sample comment",
   "userPhotoId": null
}

userPhotoId is a transient field. It'll be set from the aggregation result.

I want to get all comments and inside the comment, I want the profilePhotoAssetId from UserData in the userPhotoId field. 

For the above example, the sample output should be:

{
   "authorId":"123456",
   "body":"This is a sample comment",
   "userPhotoId":"abcd1234"
}


What are the steps?

1. First step is to write a lookup operation that'll join the two collection based on comment.authorId and userData.userId. After this step, the output will be:

{
   "authorId":"123456",
   "body":"This is a sample comment",
   "userData":[
      {
         "userId":"123456",
         "profilePhotoAssetId":"abcd1234"
      }
   ]
}

2. Next step is add a unwind operation so that userData becomes an inner object inside the query result. So after that, the query result becomes the following:

{
   "authorId":"123456",
   "body":"This is a sample comment",
   "userData":{
      "userId":"123456",
      "profilePhotoAssetId":"abcd1234"
   }
}

3. Our next step is to create a new property to set the userData.profilePhotoAssetId inside the root object. I'm doing this because the userData may contain many other fields which I don't need. So I'm creating a new field and copying the required parameter only. Obviously, this step is optional.

After this step, the query result becomes:

{
   "authorId":"123456",
   "body":"This is a sample comment",
   "userPhotoId":"abcd1234",
   "userData":{
      "userId":"123456",
      "profilePhotoAssetId":"abcd1234"
   }
}

4. In my last step I'll remove the userData property from the query result because I've already copied the required property to the main object. This step is also optional and can be skipped. I've to write a custom operation for the addFields

So, that's all. Here is a sample code that does the above steps.

public Flux<Comment> getComments(String threadId) {
// unwind
LookupOperation lookupOperation = lookup("userData", "authorId", "userId", "userData");
UnwindOperation unwindOperation = unwind("userData", true);
AggregationOperation addFieldOperation = new AggregationOperation() {
@Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("$addFields", new Document("userPhotoId", "$userData.profilePhotoAssetId"));
}
};
ProjectionOperation projectionOperation = Aggregation.project().andExclude("userData");

Aggregation aggregation = Aggregation.newAggregation(lookupOperation, unwindOperation, addFieldOperation, projectionOperation);
return mongoOperations.aggregate(aggregation, Comment.class, Comment.class);
}
 

Comments

Popular posts from this blog

Run tasks in background in Spring

Conditional field inclusion in Jackson and Spring Boot

How to configure Wildfly 10 to use MySQL