Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$Push if Object doesn't already exist

I have an array of students in my Documents VirtualClass and my goal is to create a roster with no duplication of students.

I want to match the VirtualClass with the teacher and then $push the student into an array.

Virtual Class Schema

const VirtualClassSchema = new Schema({
  name: { type: String, required: true },
  descriptionHeading: { type: String, required: true },
  room: { type: String },
  teacher: {
    userId: { type: Schema.Types.ObjectId, ref: "User" },
    name: { type: String },
    profile_picture: { type: String }
  },
  students: [
    {
      userId: { type: Schema.Types.ObjectId, ref: "User" },
      name: { type: String },
      profile_picture: { type: String }
    }
  ],
...

My current query is as follows

      VirtualClass.aggregate([
        { $match: { "teacher.userId": user._id } },
        {
          $group: {
            _id: null,
            students: { $addToSet: "$students" }
          }
        }
      ])

Which returns:

[
    {
        "_id": null,
        "students": [
            [
                {
                    "_id": "5e84d1a1ab3ebf54283b8cb2",
                    "userId": "5dd27452592f600900235945",
                    "name": "student  zero",
                    "profile_picture": "https://productionstemulistorage.blob.core.windows.net/stemuli/profile-picture-6e609f3b-44cb-44c0-888a-1b6767e3072d"
                }
            ],
            [
                {
                    "_id": "5e84d1a1ab3ebf54283b8cb4",
                    "userId": "5dd27452592f600900235945",
                    "name": "student  zero",
                    "profile_picture": "https://productionstemulistorage.blob.core.windows.net/stemuli/profile-picture-6e609f3b-44cb-44c0-888a-1b6767e3072d"
                }
            ]
        ]
    }
]

Expected results:

_id: null,
"students":
[
     {
       "_id": "5e84d1a1ab3ebf54283b8cb4",
        "userId": "5dd27452592f600900235945",
        "name": "student  zero",
        "profile_picture": "https://productionstemulistorage.blob.core.windows.net/stemuli/profile-picture-6e609f3b-44cb-44c0-888a-1b6767e3072d"
    }
]

Thank you!

like image 940
Snoopy Avatar asked Apr 02 '20 16:04

Snoopy


People also ask

How do you check if an item already exists in an array?

The indexof() method in Javascript is one of the most convenient ways to find out whether a value exists in an array or not. The indexof() method works on the phenomenon of index numbers. This method returns the index of the array if found and returns -1 otherwise.

How do you find if an element exists in an array in TypeScript?

To check if a TypeScript array contains an object:Pass a function to the Array. find() method. Check whether the identifier of the object is equal to a specific value and return true if it is. Array.


2 Answers

You could use exclusion to remove the id from the document before sending it to $addToSet. Also your student is array so you would need $unwind.

Something like

VirtualClass.aggregate([
  {$match:{"teacher.userId": user._id}},
  {$project:{"students._id": 0}},
  {$unwind: "$students"},
  {$group:{
      _id: null,
      students: {$addToSet:"$students"}
  }}
])

example here - https://mongoplayground.net/p/zULrmihM03z

Output

[
  {
    "_id": null,
    "students": [
      {
        "name": "student  zero",
        "profile_picture": "https://productionstemulistorage.blob.core.windows.net/stemuli/profile-picture-6e609f3b-44cb-44c0-888a-1b6767e3072d",
        "userId": "5dd27452592f600900235945"
      }
    ]
  }
]

Another alternative would be to set _id to false so mongoose will not generate any id for student.

Something like

 students: [
    {
      userId: { type: Schema.Types.ObjectId, ref: "User" },
      name: { type: String },
      profile_picture: { type: String },
      _id:false
    }
  ],

You can then use aggregation query without the exclusion.

VirtualClass.aggregate([
  {$match:{"teacher.userId": user._id}},
  {$unwind: "$students"},
  {$group:{
      _id: null,
      students: {$addToSet:"$students"}
  }}
])
like image 65
s7vr Avatar answered Oct 22 '22 06:10

s7vr


You need to $unwind and then $project to remove _id before $group in your aggregate function.

Code:

[
  {
    "$match": {
      "teacher.userId": 1
    }
  },
    {
    "$unwind": "$students"
  },
    {
    "$project": {
      "students._id": 0
    }
  },
  {
    "$group": {
      "_id": null,
      "students": {
        "$addToSet": "$students"
      }
    }
  }

]

Example code and Pipeline Explanation

like image 20
Nafeo Joy Avatar answered Oct 22 '22 06:10

Nafeo Joy