Home > Mobile >  How to add a sub-document field with a condition in MongoDB?
How to add a sub-document field with a condition in MongoDB?

Time:09-21

I am trying to add a new subField with a condition.
In the case the field already exists, I don't overwrite it.
In the case the condition is not fulfilled, I don't want to add the parent object.

Here is my collection :

{type: "A", object: {a: "", b: "foo"}},
{type: "A", object: {a: ""}},
{type: "A"},
{type: "B"}

Here is my aggregate :

{
  $addFields: {
    "object.b": {
      $cond: {
        if: {$eq: ["$type","A"]},
        then: {$ifNull: ["$object.b", "bar"]},
        else: "$DROP"
      }
    }
  }
}

$DROP is not an aggregate command, but in the else case I don't want to add the new field.
It will not create the b field, but the parent object remains.

Here is my current result :

{type: "A", "object": {a: "", b: "foo"}},
{type: "A", "object": {a: "", b: "bar"}},
{type: "A", "object": {b: "bar"}},
{type: "B", "object": {}},

Here is what I want :

{type: "A", object: {a: "", b: "foo"}},
{type: "A", object: {a: "", b: "bar"}},
{type: "A", object: {b: "bar"}},
{type: "B"}

Your help will be highly appreciated.

CodePudding user response:

This aggregate query will give you the desired result:

db.collection.aggregate([
{
    $addFields: {
        object: {
            $cond: {
                if: { $eq: [ "$type", "A" ] },
                then: { 
                    $mergeObjects: [ 
                        "$object",  
                        { b: { $ifNull: [ "$object.b", "bar" ] } } 
                    ] 
                },
                else: "$$REMOVE"
            }
        }
    }
}
])

Note the $$REMOVE is a aggregation system variable.

CodePudding user response:

When a $set adds a path, all path is added, even if you end up to $$REMOVE this will affect only the last of the path, the rest would be already added see example

Query

  • $set with switch case starting from object
  • if object doesn't exist
    • if type A add {object :{"b" : "bar"}}
    • else $$REMOVE
  • $type = "A" AND (not-value "$object.b")
    add {"b" : "bar"} (this case covers also the case object:null)
  • else
    keep old value (another type, or b had value)

*Maybe it could be smaller but we check many things(see the example for all cases of data)

  • object exists/null/not exists
  • type A/not
  • b exists/null/value

Test code here

db.collection.aggregate([
  {
    "$set": {
      "object": {
        "$switch": {
          "branches": [
            {
              "case": {
                "$eq": [
                  {
                    "$type": "$object"
                  },
                  "missing"
                ]
              },
              "then": {
                "$cond": [
                  {
                    "$eq": [
                      "$type",
                      "A"
                    ]
                  },
                  {
                    "b": "bar"
                  },
                  "$$REMOVE"
                ]
              }
            },
            {
              "case": {
                "$and": [
                  {
                    "$eq": [
                      "$type",
                      "A"
                    ]
                  },
                  {
                    "$or": [
                      {
                        "$eq": [
                          "$object.b",
                          null
                        ]
                      },
                      {
                        "$eq": [
                          {
                            "$type": "$object.b"
                          },
                          "missing"
                        ]
                      }
                    ]
                  }
                ]
              },
              "then": {
                "$mergeObjects": [
                  "$object",
                  {
                    "b": "bar"
                  }
                ]
              }
            }
          ],
          "default": "$object"
        }
      }
    }
  }
])
  • Related