0

I have a collection posts which has an array of objects comments. Within this array of objects, I have another array of objects likes.

I am trying to write a query that pulls the most recent 5 comments from a post, and only pulls true or false for likes, based on if the user has liked the comment already.

I have written this so far:

db.posts.aggregate([
    {
        "$match": {
            "_id": postId
        }
    },
    {
        "$project": 
        {
            "comments": {
                "$slice": [ "$comments", -5 ]
            }
        }
    },
    {
        "$project": {
            "comments.content": 1,
            "comments.likes": { 
                "$eq":  [ "comments.likes.$.createdBy.username", username ] 
            } 
        }
    }
])

But this seems to pull false everytime.

Is it possible to do this without having to write a separate query to check if the user has liked?

EDIT: So for the below document: Test

With username = "testusername", and postId = "60fcd335abbe5a73583b69f0"

I would expect output:

[
    {
        "content": "test comment",
        "likes": true
    },
    {
        "content": "another test comment",
        "likes": true
    }
]

And with username = "testusername2" I would expect output

[
    {
        "content": "test comment",
        "likes": true
    },
    {
        "content": "another test comment",
        "likes": false
    }
]

Answer

Thanks to @ray for your help with this one. Condensed Code Here though please see ray's response for the code split out with explanation.

2
  • 1
    would be helpful if you can provide sample data and expected output for everyone here to take a look Commented Jul 25, 2021 at 2:56
  • 1
    another piece of advice: paste the code/sample data in text instead of image; It would be easier for others to copy and replicate it Commented Jul 25, 2021 at 4:02

1 Answer 1

1

You can use $map to process your arrays layer-by-layer.

  1. You can first $map comment to project a boolean for if the likes are liked by user1
  2. Then you can use $anyElementTrue to perform the checking on the projected boolean
db.posts.aggregate([
  {
    "$match": {
      "_id": "p1"
    }
  },
  {
    "$project": {
      "comments": {
        "$slice": [
          "$comments",
          -5
        ]
      }
    }
  },
  {
    "$project": {
      likes: {
        "$map": {
          "input": "$comments",
          "as": "c",
          "in": {
            content: "$$c.content",
            likedByU1: {
              "$map": {
                "input": "$$c.likes",
                "as": "l",
                "in": {
                  $eq: [
                    "$$l.createdBy._id",
                    "u1"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  {
    "$project": {
      likes: {
        "$map": {
          "input": "$likes",
          "as": "l",
          "in": {
            content: "$$l.content",
            likedByU1: {
              "$anyElementTrue": [
                "$$l.likedByU1"
              ]
            }
          }
        }
      }
    }
  }
])

Here is the Mongo playground to show the idea(with some minor modifications to your example). You can modify it to fit your needs.

Sign up to request clarification or add additional context in comments.

3 Comments

This is perfect. Haven't used $map much before so thank you for this, and for bringing Mongo playground to my life.
FYI have played around with it a bit to avoid a second projection: Mongo Playground
@bourkison your new code is cleaner. The reason I split the code into 2 stages is for demonstration purpose so you can run the stages separately for better understanding. I would suggest you supplement your new version in your question to help more people out :)

Your Answer

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.