mongodbspring-datamongotemplate

Not able to pull from nested array and query return sub-document using MongoTemplate


I am using mongodb in springboot. And here is a part of my data:

{
  "topic": [
    {
      "_topicId": "5e5e4d4bb431502946c15342",
      "name": "testName0",
      "username": "test0",
      "date": 1583238474961,
      "reply": [
        {
          "_replyId": "38d29dcb-1a79-4788-b721-5fbe700cc99d",
          "username": "test0",
          "content": "reply0",
          "date": 1583240780072
        },
        {
          "_replyId": "07a0293a-22a1-45fb-9aa2-775fa24e9915",
          "username": "test1",
          "content": "reply1",
          "date": 1583240955561
        }
      ]
    },
    {
      "_topicId": "5e5e4d4bb431502946c15343",
      "name": "testName1",
      "username": "test1",
      "date": 1583238475241,
      "reply": []
    }
  ]
}

I have two problems:

(1) I try to pull a reply(a object of in java) from a topic, I try these queries:

Query query = Query.query(Criteria.where("_topicId").is(topicId));
Update update = new Update().pull("reply.$._replyId", topicReplyId);
mongoTemplate.updateFirst(query, update, "colletionName");

And I got a error The positional operator did not find the match needed from the query

Query query = Query.query(Criteria.where("_topicId").is(topicId));
Update update = new Update().pull("reply._replyId", topicReplyId);
mongoTemplate.updateFirst(query, update, "colletionName");

And I got a error Cannot use the part (_replyId) of (reply._replyId) to traverse the element

Then I decide to use the third way:

Query query = Query.query(Criteria.where("_topicId").is(topicId));
Update update = new Update().pull("reply", replyEntity);
mongoTemplate.updateFirst(query, update, "colletionName");

I try to new a ReplyEntity replyEntity, and I got my second problem:

(2) How can I get the subdocument from a document?

Query query = Query.query(Criteria.where("_topicId").is(topicId).and("reply._replyId").is(replyId));
TopicEntity t = mongoTemplate.findOne(query, TopicEntity.class, "colletionName");

I used the query but I get the outer-document(topic), include two reply on the example above of topic1. I just want the reply,how can make it? Thanks a lot.


Solution

  • (1) Update (pull) reply array element:

    This code will update the document; that is removes the specific element (sub-document) from the reply array:

    // Query criteria for topic and reply
    String topicId = "5e5e4d4bb431502946c15342";
    String topicReplyId = "07a0293a-22a1-45fb-9aa2-775fa24e9915";
    
    MongoOperations mongoTemplate = new MongoTemplate(MongoClients.create(), "test");
    Query query = Query.query(Criteria
                                   .where("topic._topicId").is(topicId)
                                   .and("topic.reply._replyId").is(topicReplyId));
    Update update = new Update().pull("topic.$.reply", new Document("_replyId", topicReplyId));
    mongoTemplate.updateFirst(query, update, "topics"); // "topics" is the collection name
    



    [ EDIT ADD ]

    (2) Aggregation query to get the reply document:

    db.topics.aggregate( [
      { $unwind: "$topic" },
      { $match: { "topic._topicId": topicId } },
      { $unwind: "$topic.reply" },
      { $match: { "topic.reply._replyId": topicReplyId } },
      { $project: { _id: 0, reply: "$topic.reply" } }
    ] ).pretty()
    

    This returns:

    {
            "reply" : {
                    "_replyId" : "07a0293a-22a1-45fb-9aa2-775fa24e9915",
                    "username" : "test1",
                    "content" : "reply1",
                    "date" : 1583240955561
            }
    }