Using $push for null array

Viewed 1605

I have a mongo document which has array field called "events".

This field can sometimes be null.

I'm using $push to add an element to an "events" array. The problem is, it can't work when "events" field is null

For example, if in database it looks like this:

{
"_id" : ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id" : "714638ba-2e08-2168-2b99-00002f3d43c0",
"events" : null
 }

and I run Update with $push:

{ $push : { "events" : { "profile" : 10, "data" : "X"}}};

it will fail because events is null.

I can fix this using 2 steps of querying the database. But, can I do it in 1 query?

I want to add the event to the array if the array fields is not null, but if it is null, first, create the "events" empty array, and later, add the event to the array.

2 Answers

Staring with MogoDB 4.2 you can use Updates with Aggregation Pipeline. This allows to perform the update as a single operation.

var OBJ_TO_ADD = { "profile": 10, "data": "X" }

db.test.updateOne(
  { "_id" : ObjectId("4d2d8deff4e6c1d71fc29a07") }, 
  [ 
      { 
          $set: { 
              events: { 
                  $ifNull: [ 
                      { $concatArrays: [ "$events", [ OBJ_TO_ADD ] ] }, 
                      [ OBJ_TO_ADD ] 
                  ] 
              }
          }
      } 
  ] 
)

Do not write null values for fields that do not have data. Then you'll be able to use $push in the expected manner:

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({a:1})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.update({a:1},{$push:{b:[1]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find()
{ "_id" : ObjectId("5f0b481dd9c782d95389a1ce"), "a" : 1, "b" : [ [ 1 ] ] }

If you insist on writing nulls to the database, you can use aggregation pipeline update with $set, $ifNull, $concatArrays and $merge:

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({a:2,b:null})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find()
{ "_id" : ObjectId("5f0b4bfbd9c782d95389a1d2"), "a" : 2, "b" : null }
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.aggregate([{$match:{a:2}},{$set:{b:{$ifNull:[{$concatArrays:['$b',[4]]},[4]]}}},{$merge:'foo'}])
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find()
{ "_id" : ObjectId("5f0b4bfbd9c782d95389a1d2"), "a" : 2, "b" : [ 4 ] }
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.aggregate([{$match:{a:2}},{$set:{b:{$ifNull:[{$concatArrays:['$b',[5]]},[5]]}}},{$merge:'foo'}])
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find()
{ "_id" : ObjectId("5f0b4bfbd9c782d95389a1d2"), "a" : 2, "b" : [ 4, 5 ] }
Related