Home > Software engineering >  Updating field to a sorted nested array field
Updating field to a sorted nested array field

Time:11-12

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",
          
        },
        
      },
      
    }
  }
])

Mongo Playground Sample Execution

  • Related