Using criteria query I am fetching documents from MongoDB. What is my requirement here is I want update a field from parent document value by querying sub document using Criteria from Spring Data Mongo. The parent document is comments and nested documents is replies. I am able to get list of comments along with sub documents using the below code,
Data Set:
{
"_id" : ObjectId("5cb726937a7148376094d393"),
"_class" : "vzi.cpei.Comments",
"text" : "first comment on money control",
"replies" : [
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b223639087",
"text" : "extract the traces",
"status" : true
},
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b153690087",
"text" : "replied deiberate",
"status" : false
},
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b153139087",
"text" : "Bgm",
"status" : true
}],
}
Response DTO:
public class CommentsDTO{
private String id;
private String text;
private List<Replies> replies;
private Integer totalReplies;
}
Code wrote in Spring using Spring data mongo Criteria query,
Query query = new Query();
Criteria criteria =Criteria.where("_id").is(new ObjectId("5efe3d1f8a2ef008249f72d9"));
query.addCriteria(criteria);
List<Comments> comments = mongoOps.find(query,"Comments",
CommentsDTO.class);
return comments;
In the result I want to update the field repliesCount with total number of replies with status true so the expected output should be,
{
"_id" : ObjectId("5cb726937a7148376094d393"),
"_class" : "vzi.cpei.Comments",
"text" : "first comment on money control",
"totalReplies" : 2
"replies" : [
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b223639087",
"text" : "extract the traces",
"status" : true
},
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b153690087",
"text" : "replied deiberate",
"status" : false
},
{
"_id" : "3cfef1cd-e0da-4883-86a4-17b153139087",
"text" : "Bgm",
"status" : true
}],
}
I am totally confused where to do this operation while fetching.
You need to perform the MongoDB aggregation.
pipeline
$match
stage to filter results (similar to .find(query)
)$project
to transform the document structure and include totalReplies
field calculated based on replies
valuesdb.Comments.aggregate([
{
$match: {
"_id": ObjectId("5cb726937a7148376094d393")
}
},
{
$project: {
_class: 1,
replies: 1,
totalReplies: {
$size: {
"$filter": {
input: "$replies.status",
as: "status",
cond: "$$status"
}
}
}
}
}
])
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
...
Aggregation agg = Aggregation.newAggregation(
match(Criteria.where("_id").is(new ObjectId("5cb726937a7148376094d393"))),
project("_id", "_class", "replies").and(ArrayOperators.Size.lengthOfArray(
ArrayOperators.Filter.filter("replies.status").as("filter").by("$$filter")
)).as("totalReplies"));
//System.out.println(agg);
return mongoOps.aggregate(agg, mongoOps.getCollectionName(CommentsDTO.class), CommentsDTO.class);
EDIT: Legacy Spring-boot 1.4.2
project("_id", "_class", "replies")
.and(AggregationFunctionExpressions.SIZE
.of(new BasicDBObject("$filter",
new BasicDBObject("input", "$replies.status")
.append("as", "status")
.append("cond", "$$status"))))
.as("totalReplies")