0

I have this data and I want to sort it by two fields: first by specific address (details.address), for example 'Tel Aviv'. second by regular sort, by details.cost field. here is my data:

[{
    "_id": "123",
    "details": [{
        "_id": "1",
        "address": "Ramat Gan",
        "cost": "50"
    }, {
        "_id": "2",
        "address": "Tel Aviv",
        "cost": "30"
    }]
},
{
    "_id": "456",
    "details": [{
        "_id": "4",
        "address": "Modi'in",
        "cost": "40"
    }, {
        "_id": "5",
        "address": "Tel Aviv",
        "cost": "20"
    }]
}
]

and I want to get this data after the two sorting:

[{
    "_id": "456",
    "details": [{
        "_id": "5",
        "address": "Tel Aviv",
        "cost": "20"
    }, {
        "_id": "4",
        "address": "Modi'in",
        "cost": "40"
    }, {
        "_id": "123",
        "details": [{
            "_id": "2",
            "address": "Tel Aviv",
            "cost": "30"
        }, {
            "_id": "1",
            "address": "Ramat Gan",
            "cost": "50"
        }]
    }]
}]

actually, I want to sort by my specific value address' (in this case - 'Tel Aviv') cost

3
  • Could you show us the code that gives you this data? Commented Jun 7, 2022 at 13:30
  • db.<collectionName>.insertMany( [{ "_id": "123", "details": [{ "_id": "1", "address": "Ramat Gan", "cost": "50" }, { "_id": "2", "address": "Tel Aviv", "cost": "30" }] }, { "_id": "456", "details": [{ "_id": "4", "address": "Modi'in", "cost": "40" }, { "_id": "5", "address": "Tel Aviv", "cost": "20" }] } ]) Commented Jun 7, 2022 at 13:44
  • Is it enough for you, please? Commented Jun 7, 2022 at 13:54

2 Answers 2

1

If you want both splitting and sorting by cost you can expand @BuzzMoschetti's solution $group part to use $cond:

db.collection.aggregate([
  {$unwind: "$details"},
  {$sort: {"details.cost": 1}},
  {
    $group: {
      _id: "$_id",
      top: {
        $push: {
          $cond: [{$eq: ["$details.address", "Tel Aviv"]}, "$details", "$$REMOVE"]
        }
      },
      bottom: {
        $push: {
          $cond: [{$ne: ["$details.address", "Tel Aviv"]}, "$details", "$$REMOVE"]
        }
      }
    }
  },
  {$project: {details: {$concatArrays: ["$top", "$bottom"]}}}
])

See how it works on the playground example both

In case you to just order by specific address first:

db.collection.aggregate([
  {
    $project: {
      top: {
        $filter: {
          input: "$details",
          as: "item",
          cond: {$eq: ["$$item.address", "Tel Aviv"]}
        }
      },
      bottom: {
        $filter: {
          input: "$details",
          as: "item",
          cond: {$ne: ["$$item.address", "Tel Aviv"]}
        }
      }
    }
  },
  {
    $project: {
      details: {$concatArrays: ["$top", "$bottom"]}
    }
  }
])

See how it works on the playground example top-city

Sign up to request clarification or add additional context in comments.

1 Comment

Clever use of $$REMOVE here to "not push" something.
0

Pretty straightforward: $unwind then re-$group. When sorting arrays of things across document boundaries you pretty much have no choice but to use $unwind to let $sort work properly.

db.foo.aggregate([
    {$unwind: '$details'}
    ,{$sort: {'details.address':-1,'details.cost':1}}

    // Rebuild the original doc; $push will *preserve* the sorted
    // order of address+cost following from the stage above:
    ,{$group: {_id:'$_id', details: {$push: '$details'}}}
]);

3 Comments

Thanks Buzz Moschetti. but I don't want to sort my address ascending or descending. I only want to top specific value for address, for example, I want to see all the records of 'Tel Aviv's at the top of their object, as like as the example. Do you know hoe can I do it?
I am confused; the answer matches your indicated target output. Can you edit the output in the question to make this "top specific value for address" more clear? What is a "top specific value"?
Is it more clear? I want to see all the objects with the specific value field as the firsts in their array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.