user data includes :
"_id" : 4
"username" : "smith"
likedVideos :[
"videoId" : 10
"title" : "something"
"speaker" : "something"
"description" : "something"
]
i have a collection with userId and a array of liked videos lists.liked videos(Array) includes videoId and video details. so i need to check that is the user is already liked this video or not. so how i match userId and videoId from the collection without using unwind ?
i tried :
const data = await userModel.findOne({ _id : userId,likedVideos: { $elemMatch: { videoId : videoId } } })
but it returns all the data of that user.const alreadyLiked = await userModel.aggregate([ { $match: { '_id' : userId, 'likedVideos.videoId' : videoId, }, }, ]);
this also not working as expected.
I need a perfect solution to match a element inside array without using unwind (My boss said that unwind is a costly operation it will effect the app performance). can you please help me to solve this.
CodePudding user response:
Both of your queries are valid. It will return all the users that match your query. You are matching a specific user and a movie. So if a user is returned, it means that the user has already liked the video.
Nevertheless $elemMatch
is useful when you have multiple conditions to apply to an object. So it's better to use your second solution with
{
"_id": userId,
"likedVideos.videoId": videoId
}
If you want to keep only a given element in likedVideos
, you can use $filter
in an aggregate.
For example
db.collection.aggregate([
{
$match: {
"_id": 1
}
},
{
$project: {
list: {
$filter: {
input: "$likedVideos",
as: "item",
cond: {
$eq: [
"$$item.videoId",
1
]
}
}
}
}
}
])
it will only return the movies in likedVideos
with id=1
try it here
CodePudding user response:
The best way to filter elements in a subarray is by using an Aggregation with $match
and $project
.
Example:
[{
$match: {
_id: 'userId',
likedVideos.videoId: 'videoId'
}
}, {
$project: {
'likedVideos': {
$filter: {
input: '$likedVideos',
as: 'item',
cond:
{$eq: ["$$item.videoId","videoId"]}
}
}
}
}]