简体   繁体   中英

Aggregation query on MongoDB based on multiple criteria in subdocuments

I have several hundred records in this format:

{
 "_id" : ObjectId( "51ac1356c59151b66c0c9b6b" ),
 "name" : "SomeString",
 "subject" : [
    "_id" : ObjectId( "51ac1356c59151b66c0c912d" ),
    "subjectName": "SomeString",
    "prerequisite": [ "subject1", "subject2"]
    ]
}

Here's what the data looks like:

[
  {
    "_id": "123456",
    "name": "StudentB",
    "subject": [
      {
        "_id": "78901",
        "subjectName": "Politics 101",
        "prerequisite": []
      },
      {
        "_id": "10987",
        "subjectName": "Geography 500",
        "prerequisite": []
      },
      {
        "_id": "78901",
        "subjectName": "Science 500",
        "prerequisite": [
          "Science 204",
          "Math 100"
        ]
      },
      {
        "_id": "54321",
        "subjectName": "Maths 600",
        "prerequisite": [
          "Science 400",
          "Math 400"
        ]
      }
    ]
  },
  {
    "_id": "654321",
    "name": "StudentA",
    "subject": [
      {
        "_id": "78901",
        "subjectName": "Music 101",
        "prerequisite": []
      },
      {
        "_id": "78901",
        "subjectName": "History 500",
        "prerequisite": [
          "History 200"
        ]
      },
      {
        "_id": "54321",
        "subjectName": "Maths 600",
        "prerequisite": [
          "Science 400",
          "Math 400"
        ]
      },
      {
        "_id": "10987",
        "subjectName": "Geography 500",
        "prerequisite": []
      }
    ]
  }
]

I am trying to construct a query to get the below result:

[
  {
    "_id": "654321",
    "name": "StudentA",
    "subject": [
      {
        "_id": "78901",
        "subjectName": "History 500",
        "prerequisite": [
          "History 200"
        ]
      },
      {
        "_id": "54321",
        "subjectName": "Maths 600",
        "prerequisite": [
          "Science 400",
          "Math 400"
        ]
      },
      {
        "_id": "10987",
        "subjectName": "Geography 500",
        "prerequisite": []
      },
      {
        "_id": "78901",
        "subjectName": "Music 101",
        "prerequisite": []
      }
    ]
  },
  {
    "_id": "123456",
    "name": "StudentB",
    "subject": [
      {
        "_id": "54321",
        "subjectName": "Maths 600",
        "prerequisite": [
          "Science 400",
          "Math 400"
        ]
      },
      {
        "_id": "78901",
        "subjectName": "Science 500",
        "prerequisite": [
          "Science 204",
          "Math 100"
        ]
      },
      {
        "_id": "10987",
        "subjectName": "Geography 500",
        "prerequisite": []
      },
      {
        "_id": "78901",
        "subjectName": "Politics 101",
        "prerequisite": []
      }
    ]
  }
]

Here's what I have tried so far:

studentDetails.aggregate([
    {
        $project: {
            '_id': 1,
            'name': 1,
            'subject.subjectName': 1,
            'subject.prerequisite': 1
        }
    },
    {
        $unwind: '$subject'
    },
    {
        $sort: {
            'subject.prerequisite': -1
        }
    },
    {
        $group: {
            _id: '$_id',
            subject: { $push: '$subject' },
            name: { $first: '$name' },
        }
    }
]);

The above aggregation returns the subdocuments sorted in reverse alphabetical order where the prerequisites are ordered from ZA and the empty arrays are at the end of the list.

I need to sort the documents in the DB according to:

  1. The name of the students (AZ)
  2. Name of the subject where the subjects with prerequisites are returned at the beginning of the resulting query

It is totally difficult to do both sorting together, because

  • when sort with subject.subjectName: 1 it will sort ascending order
  • then sort with subject.prerequisite: -1 it will sort descending order but it reset above sort

You can try this experiment,

  $unwind: ... ,  // skipped
  • sort by subjectName ascending order
  {
    $sort: {
      "subject.subjectName": 1
    }
  },
  • add new field viewOrder in subject array
  • check condition if prerequisite array size greater then 0 then set 1 otherwise 0
  {
    $addFields: {
      "subject.viewOrder": {
        $cond: {
          if: {
            $size: "$subject.prerequisite"
          },
          then: 1,
          else: 0
        }
      }
    }
  },
  • sort by viewOrder descending order
  {
    $sort: {
      "subject.viewOrder": -1
    }
  },
  • no longer needed viewOrder field
  {
    $project: {
      "subject.viewOrder": 0
    }
  },
  $group: ... ,  // skipped
  • sort by name ascending order
  {
    $sort: {
      name: 1
    }
  }

Here you have achieved your desired result.

Playground: https://mongoplayground.net/p/syg2w_d0j_b

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM