Mongodb Pymongo using $set to create an array/list/collection

Question:

I’m trying to use $set to create an array/list/collection (not sure which is proper terminology), and I’m not sure how to do it. For example:

I have a document inserted into my database that looks like this:

"_id": (unique, auto-generated id)
"Grade": Sophomore

I want to insert a collection/list/array using update. So, basically I want this:

"_id": (unique, auto-generated id)
"Grade": Sophomore
"Information"{
           "Class_Info": [
                        {"Class_Name": "Math"}
                         ]

What I’ve been doing so far is using .update and dot notation. So, what I was trying to do was use $set like this:

collection.update({'_id': unique ID}, {'$set': {'Information.Class_Info.Class_Name': 'Math}})

However, what that is doing is making Class_Info a document and not a list/collection/array, so it’s doing:

"_id": (unique id)
"Grade": Sophomore
"Information"{
           "Class_Info": {
                        "Class_Name": "Math"
                         }

How do I specify that I want Class_Info to be a list? IF for some reason I absolutely cannot use $set to do this, it is very important that I can use dot notation because of the way the rest of my program works, so if I’m supposed to use something other than $set, can it have dot notation to specify where to insert the list? (I know $push is another option, but it doesn’t use dot notation, so I can’t really use it in my case).

Thanks!

Asked By: Vandexel

||

Answers:

If you want to do it with only one instruction but starting up from NOT having any key created yet, this is the only way to do it ($set will never create an array that’s not explicit, like {$set: {"somekey": [] }}

db.test.update(
  { _id: "(unique id)" },
  { $push: {
    "Information.Class_Info": { "Class_Name": "Math" }
  }}
)

This query does the trick, push to a non-existing key Information.Class_Info, the object you need to create as an array. This is the only possible solution with only one instruction, using dot notation and that works.

Answered By: Maximiliano Rios

There is a way to do it with one instructions, $set and dot notation, as follows:

db.test.updateOne(
  { _id: "my-unique-id" },
  { $set: { 
    "Information.Class_Info": [ { "Class_Name": "Math" } ] 
  }}
)

There is also a way to do it with two instructions and the array index in the dot notation, allowing you to use similar statements to add more array elements:

db.test.updateOne(
  { _id: "my-unique-id" },
  { $set: { "Information.Class_Info": [] }}
)
db.test.updateOne(
  { _id: "my-unique-id" },
  { $set: { 
    "Information.Class_Info.0": { "Class_Name": "Math" },
    "Information.Class_Info.1": { "Class_AltName": "Mathematics" } 
  }}
)

Deviating from these options has interesting failure modes:

  • If you try to combine the second option into a single updateOne() call, which is usually possible, MongoDB will complain that "Updating the path ‘Information.Class_Info.0’ would create a conflict at ‘Information.Class_Info’"

  • If you try to use dot the notation with the array index ("Information.Class_Info.0.Class_Name": "Math") but without creating an empty array first, then MongoDB will create an object with numeric keys ("0", "1", …). It really refuses to create array except when told explicitly using […] (as also told in the answer by @Maximiliano).

Answered By: tanius