I have a document of the collection Topic that has the following nested array
[
{
_id: ObjectId("618c6718c6a8cc3a84a0b37f"),
subscribers: [
{userId: ObjectId("618c643545f4ecd43e9b20f7"), points: {daily: 50, total: 100} },
{userId: ObjectId("618c641345f4ecd43e9b20f0"), points: {daily: 10, total: 80} },
{userId: ObjectId("618c648c45f4ecd43e9b20fc"), points: {daily: 20, total: 80} }
],
quizzes: [],
leaderboards: {
daily: [],
total: []
},
}
]
Is there anyway to sort the subscribers list by daily points and set the results to the nested daily array in leaderboards field?
I.e I wish my document will look like this after the query
[
{
_id: ObjectId("618c6718c6a8cc3a84a0b37f"),
subscribers: [
{userId: ObjectId("618c643545f4ecd43e9b20f7"), points: {daily: 20, total: 70} },
{userId: ObjectId("618c641345f4ecd43e9b20f0"), points: {daily: 10, total: 60} },
{userId: ObjectId("618c648c45f4ecd43e9b20fc"), points: {daily: 15, total: 50} }
],
quizzes: [],
leaderboards: {
daily: [
{userId: ObjectId("618c643545f4ecd43e9b20f7"), points: {daily: 20, total: 70} },
{userId: ObjectId("618c648c45f4ecd43e9b20fc"), points: {daily: 15, total: 50} }
{userId: ObjectId("618c641345f4ecd43e9b20f0"), points: {daily: 10, total: 60} },
],
total: []
},
}
]
I was able to find the following code
db.collection.aggregate(
{ $unwind: "$memberships" },
{ $sort: { "memberships.price": 1 }},
{ $group: {
_id: "$_id",
"memberships": { $push: "$memberships" }
}
})
from Mongoose sort nested array of object not working
This aggregation works, but it sorts subscribers rather than using the resulting array to update the daily nested field.
CodePudding user response:
You are actually on the right track. You just need to use $merge
to update back to the collection.
db.Topic.aggregate([
{
$unwind: "$subscribers"
},
{
$sort: {
"subscribers.points.daily": -1
}
},
{
$group: {
_id: "$_id",
"daily": {
$push: "$subscribers"
}
}
},
{
$project: {
leaderboards: {
daily: "$daily"
}
}
},
{
"$merge": {
"into": "Topic",
"on": "_id",
"whenMatched": "merge"
}
}
])
Here is the Mongo playground for your reference
CodePudding user response:
@ray answer is correct and will work just fine.
I would rewrite the query using a single $set
stage and the $function
operator to sort the nested fields.
db.collection.aggregate([
{
"$set": {
"leaderboards.daily": {
"$function": {
"body": "function(arr) {return arr.sort((a,b) => (a.points.daily < b.points.daily) ? 1 : ((b.points.daily > a.points.daily) ? -1 : 0));}",
"args": [
"$subscribers"
],
"lang": "js",
},
},
}
}
])