mongodbmongodb-querynestjs

Sort by field of an object in array where id matches the specific element object id in the array NestJS , Mongodb


I have following result data which is not properly sorted according to my needs

 [
 {
    "_id": "defmongodb_objectId",
    "categories": [
      {
        "category": "123mongodb_objectId" //reference Id
        "positionAccordingToCategory": 2,
      },
      {
        "category": "789mongodb_objectId" //reference Id
        "positionAccordingToCategory": 1,
      },
    ],
  },
  {
    "_id": "abcmongodb_objectId",
    "categories": [
      {
        "category": "789mongodb_objectId" //reference Id
        "positionAccordingToCategory": 2,
      },
    ],
  },
 {
    "_id": "0mongodb_objectId",
    "categories": [
      {
        "category": "789mongodb_objectId" //reference Id
        "positionAccordingToCategory": 0,
      },
    ],
  },

]

EXPECTED RESULT

   [
     
      {
        "_id": "abcmongodb_objectId",
        "categories": [
          {
            "category": "789mongodb_objectId" //reference Id
            "positionAccordingToCategory": 2,
          },
        ],
      },
      {
        "_id": "defmongodb_objectId",
        "categories": [
          {
            "category": "123mongodb_objectId" //reference Id
            "positionAccordingToCategory": 2,
          },
          {
            "category": "789mongodb_objectId" //reference Id
            "positionAccordingToCategory": 1,
          },
        ],
      },
     {
    "_id": "0mongodb_objectId",
    "categories": [
      {
        "category": "789mongodb_objectId" //reference Id
        "positionAccordingToCategory": 0,
      },
    ],
  },
]

I want to sort the result by that positionAccordingToCategory which the given id matches with the category.

EXAMPLE The id provided is 789mongodb_objectId, and it is the second element of the categories array, so I want the result by the positionAccordingToCategory of category, 789mongodb_objectId.

ISSUE The issue is that it sorts the result by matching the **first** element of the categories array no matter what category id is provided. Basically I want to show the same document at different positions in different categories ,according to category.

MY CURRENT QUERY

await this.myModel
      .find({
        // other filters
        categories: { $elemMatch: { category: 'category._id '} },
      })
      .sort({ 'categories.positionAccordingToCategory': -1 })

This query only works when there's only single object element in categories It's not like query throws any error when there are more than one element, but the issue is it disturbs the order in the particular category list. As in the above example the element should be visible at 1st index but instead it goes to 2nd index, and if there are another element of that category with the same index as 2 than it moves to 3rd index

YOU CAN PROVIDE ANY AGGREGATION QUERY SOLUTION WHICH I WILL USE AT THE LAST OPTION, BUT I WON'T PREFER ANY AGGREGATION HERE.


Solution

  • This is just one way to do this using an aggregation. There must be infinite possibilities, but this one's tested and it works (I've change the category ids into names, but it's the same concept).

    Notice that I've added an extra first $match stage that you might not need, so feel free to remove it, and the last $project stage is just clean up, so you might be able to get rid of that one as well.

    [
      {
        '$match': {
          'categories': {
            '$elemMatch': {
              'category': 'category_789'
            }
          }
        }
      }, {
        '$project': {
          '_id': 1, 
          'categories': 1, 
          'sortCategory': {
            '$filter': {
              'input': '$categories', 
              'as': 'c', 
              'cond': {
                '$eq': [
                  '$$c.category', 'category_789'
                ]
              }
            }
          }
        }
      }, {
        '$sort': {
          'sortCategory.positionAccordingToCategory': -1
        }
      }, {
        '$project': {
          '_id': 1, 
          'categories': 1
        }
      }
    ]