7

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
  • 2
    Can you not have "events" an empty array instead of null? This should be more consistent. Commented Jul 12, 2020 at 12:02
  • the problem is that I can't know if "events" field might not exists or can also be null sometimes :/ it's not a mendatory field.. although I do agree the best way to handle this, is to just always create it , even if it is empty Commented Jul 12, 2020 at 13:38

3 Answers 3

6

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 ] 
                  ] 
              }
          }
      } 
  ] 
)
Sign up to request clarification or add additional context in comments.

Comments

1

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 ] }

Comments

0

You can use $cond to conditionally check if the field is null, and if so set the value to an array with one element, else use $concatArrays to add the new element.

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

db.collection.update(
{ "key": "key"},
[
  {
    "$set": {
      "events": {
        "$cond": {
          "if": {"$eq": ["$events", null]},
          "then": [OBJ_TO_ADD],
          "else": {"$concatArrays": ["$events", [OBJ_TO_ADD]]}
        }
      }
    }
  }
])

See this example how it works for null, empty array and array with elements: https://mongoplayground.net/p/vlvJ5xj0dIc

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.