I have 2 collections, users and tracks. When I fetch the user profile I want to get his tracks.
The user object has tracks
as an array of track IDs, and the second collection is of the tracks.
Now I am running this code below:
users
.aggregate([
{
$match: { _id: ObjectId(userId) },
},
{
$lookup: {
from: "tracks",
localField: "tracks",
foreignField: "_id",
as: "tracks",
},
},
{
$unwind: {
path: "$tracks",
preserveNullAndEmptyArrays: true,
},
},
{
$group: {
_id: ObjectId(userId),
username: { $first: "$username" },
profileImg: { $first: "$profileImg" },
socialLinks: { $first: "$socialLinks" },
tracks: {
$push: {
_id: "$tracks._id",
name: "$tracks.name",
categoryId: "$tracks.categoryId",
links: "$tracks.links",
mediaId: isLogged ? "$tracks.mediaId" : null,
thumbnailId: "$tracks.thumbnailId",
views: { $size: { $ifNull: ["$tracks.views", []] } },
downloads: { $size: { $ifNull: ["$tracks.downloads", []] } },
uploadedDate: "$tracks.uploadedDate",
},
},
},
},
])
In case the user does not have tracks or there are no tracks, the $ifNull
statement returns an object only with these fields so it looks like that:
user: {
// user data
tracks: [{ views: 0, downloads: 0, mediaId: null }]
}
There are no tracks found so "tracks.views"
cannot be read so I added the $ifNull statement, how can I avoid it from returning an empty data? also, the API call knows whether the user is logged or not (isLogged
), I set the mediaId
to null
.
If there are no tracks found, why does the code still add these 3 fields only? no tracks to go through them...
Edit:
Any track has downloads
and views
containing user IDs whom downloaded / viewed the track, the track looks like that
{
"name": "New Track #2227",
"categoryId": "61695d57893f048528d049e5",
"links": {
"youtube": "https://www.youtube.com",
"soundcloud": null,
"spotify": null
},
"_id": "616c90651ab67bbd0b0a1172",
"creatorId": "61695b5986ed44e5c1e1d29d",
"mediaId": "616c90651ab67bbd0b0a1171",
"thumbnailId": "616c90651ab67bbd0b0a1170",
"plays": [],
"status": "pending",
"downloads": [],
"uploadedDate": 1634504805
}
When the fetched user doesn't have any track post yet, I receive an array with one object that contains the fields mentioned above.
What I expect to get when the user has no tracks is an empty array, as you can see the response above, the track object isn't full and contains only the conditional keys with zero value and null.
Bottom line I want the length of views
and downloads
only if there is a track or more, also for the mediaId
which I want to hide it in case the user isn't logged. If there are no tracks I don't understand why it returns these 3 fields
expected result when the user has one track or more
user: {
// user data
tracks: [
{
name: "New Track #2227",
categoryId: "61695d57893f048528d049e5",
links: {
youtube: "https://www.youtube.com",
soundcloud: null,
spotify: null,
},
_id: "616c90651ab67bbd0b0a1172",
creatorId: "61695b5986ed44e5c1e1d29d",
mediaId: "616c90651ab67bbd0b0a1171",
thumbnailId: "616c90651ab67bbd0b0a1170",
plays: 0,
status: "pending",
downloads: 0,
uploadedDate: 1634504805,
},
];
}
expected result when the user has no tracks
user: {
// user data
tracks: [];
}
CodePudding user response:
Append this stage to you pipeline:
{
$set: {
tracks: {
$filter: {
input: "$tracks",
cond: { $ne: [ { $type: "$$this._id" }, "missing" ] }
}
}
}
}
CodePudding user response:
You could also modify your query to look something like this: (check out a live demo here)
This query uses a conditional push via $cond
coupled with the $and
operator to search for more than one condition. If tracks.downloads
or tracks.plays
are not greater than 0, we use the $$REMOVE
variable (which just ignores that document and returns an empty array, like you are looking for).
Query
db.users.aggregate([
{
$match: {
_id: ObjectId("616c80793235ab5cc26dbaff")
},
},
{
$lookup: {
from: "tracks",
localField: "tracks",
foreignField: "_id",
as: "tracks"
}
},
{
$unwind: {
path: "$tracks",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: ObjectId("616c80793235ab5cc26dbaff"),
username: {
$first: "$username"
},
profileImg: {
$first: "$profileImg"
},
socialLinks: {
$first: "$socialLinks"
},
tracks: {
$push: {
// "IF" plays and downloads > 0
$cond: [
{
$and: [
{
$gt: [
"$tracks.plays",
0
]
},
{
$gt: [
"$tracks.downloads",
0
]
},
]
},
// "THEN" return document
{
_id: "$tracks._id",
name: "$tracks.name",
categoryId: "$tracks.categoryId",
links: "$tracks.links",
mediaId: "$tracks.mediaId",
thumbnailId: "$tracks.thumbnailId",
plays2: {},
plays: "$tracks.plays",
downloads: "$tracks.downloads",
uploadedDate: "$tracks.uploadedDate"
},
// "ELSE" remove
"$$REMOVE"
]
}
}
}
}
])