Julius Guevarra
Julius Guevarra

Reputation: 888

MongoDB Aggregate - How to check if a specific field value exists in array of documents

I have this LikeSchema for Posts and what i want to achieve is check whether the user id exists from these array of Likes Document's _user_id

Supposed I have this array of Likes document

[
  {
    id: 'a',
    _user_id: 'u1',
    _post_id: 'p1'
  },
  {
    id: 'b',
    _user_id: 'u2',
    _post_id: 'p1'
  }
]

How do I check if user id u1 exists in likes array documents using aggregation?

I have this Like Schema

const LikeSchema = new Schema({
    _post_id: {
        type: Schema.Types.ObjectId,
        ref: 'Post',
        required: true
    },
    _user_id: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
})

And my aggregation

const result = await Post.aggregate([
  {
    $match: { privacy: 'public' }
  }, 
  {
    $lookup: {
      from: 'likes',
      localField: '_id',
      foreignField: '_post_id',
      as: 'likes'
    }
  },
  {
     $addFields: {
        isLiked: {
          $in: [req.user._id, '$likes'] // WONT WORK SINCE I STILL HAVE TO MAP AS ARRAY OF _user_id
        }
     }  
   }
])

The solution I have in mind is to map first the array to only have user id values then perform the $in expression. How do I map the likes array from lookup in aggregation stages so that it will only contain arrays of user id values to make the $in expression match? Or maybe there is a better way to check for value that exists in array of objects?

Upvotes: 2

Views: 3480

Answers (2)

SuleymanSah
SuleymanSah

Reputation: 17858

It seems you can accomplish this in a cleaner way just using one addFields.

const result = await Post.aggregate([
  {
    $match: { privacy: 'public' }
  }, 
  {
    $lookup: {
      from: 'likes',
      localField: '_id',
      foreignField: '_post_id',
      as: 'likes'
    }
  }, 
  {
    $addFields: {
      isLiked: {
        $in: [req.user._id, "$likes._id"]
      }
    }
  }
])

Upvotes: 2

Julius Guevarra
Julius Guevarra

Reputation: 888

Finally found the answer. Turns out, there exists a $map operator in aggregation. This is how I used it

const result = await Post.aggregate([
  {
    $match: { privacy: 'public' }
  }, 
  {
    $lookup: {
      from: 'likes',
      localField: '_id',
      foreignField: '_post_id',
      as: 'likes'
    }
  }, 
  {
    $addFields: {
       likeIDs: { // map out array of like id's
         $map: {
           input: "$likes",
           as: "postLike",
           in: '$$postLike._id'
         }
       }
    }
  },
  {
     $addFields: {
        isLiked: {
          $in: [req.user._id, '$likeIDs'] // it works now
        }
     }  
   }
])

Upvotes: 2

Related Questions