Update specific object in array document mongodb

Hi There,

I need help to update specific object in array.

I have two documents in my mongodb

{
   "_id":{
      "$oid":"62e932661c0f2e018fe6be47"
   },
   "buCode":"toto",
   "clientNumber":"111111",
   "audiences":[
      {
         "audience":{
            "audienceId":"2",
            "name":"test mdia",
            "createdDate":null,
            "updatedDate":null,
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":20,
            "category":"LOYALTY",
            "description":"",
            "buCode":"titi"
         },
         "startDate":{
            "$date":"2022-08-01T10:15:10.000Z"
         },
         "remainingNumberOfUse":1,
         "lastEntryDate":{
            "$date":"2022-08-02T14:19:19.106Z"
         }
      }
   ]
}
-----------------------------------------------------------
{
   "_id":{
      "$oid":"6321ac546402366af853592c"
   },
   "buCode":"toto",
   "clientNumber":"22222",
   "audiences":[
      {
         "audience":{
            "_id":{
               "$oid":"62c7f6a1b5824a6a312ed5e6"
            },
            "buCode":"toto",
            "category":"LOYALTY",
            "audienceId":"2",
            "name":"Test MDIA",
            "description":"prova1",
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":30,
            "updatedDate":{
               "$date":"2022-08-23T13:46:53.763Z"
            }
         },
         "startDate":{
            "$date":"2022-08-01T00:00:00.000Z"
         },
         "remainingNumberOfUse":1,
         "lastEntryDate":{
            "$date":"2022-09-13T09:19:35.897Z"
         }
      },
      {
         "audience":{
            "buCode":"toto",
            "category":"LOYALTY",
            "audienceId":"2",
            "name":"Test MDIA 2",
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":30,
            "createdDate":{
               "$date":"2022-09-08T14:52:46.000Z"
            },
            "updatedDate":{
               "$date":"2022-09-08T10:04:19.167Z"
            }
         },
         "startDate":{
            "$date":"2022-09-13T00:00:00.000Z"
         },
         "lastEntryDate":{
            "$date":"2022-09-13T12:32:44.791Z"
         },
         "remainingNumberOfUse":1
      },
      {
         "audience":{
            "buCode":"toto",
            "category":"LOYALTY",
            "audienceId":"3",
            "name":"Test MDIA 3",
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":30,
            "createdDate":{
               "$date":"2022-09-09T14:52:46.000Z"
            },
            "updatedDate":{
               "$date":"2022-09-09T10:04:19.167Z"
            }
         },
         "startDate":{
            "$date":"2022-09-13T00:00:00.000Z"
         },
         "lastEntryDate":{
            "$date":"2022-09-13T12:32:44.791Z"
         },
         "remainingNumberOfUse":1
      },
      {
         "audience":{
            "buCode":"toto",
            "category":"LOYALTY",
            "audienceId":"4",
            "name":"Test MDIA 4",
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":30,
            "createdDate":{
               "$date":"2022-09-09T14:52:46.000Z"
            },
            "updatedDate":{
               "$date":"2022-09-09T10:04:19.167Z"
            }
         },
         "startDate":{
            "$date":"2022-09-13T00:00:00.000Z"
         },
         "lastEntryDate":{
            "$date":"2022-09-13T12:32:44.791Z"
         },
         "remainingNumberOfUse":1
      },
      {
         "audience":{
            "buCode":"toto",
            "category":"LOYALTY",
            "audienceId":"5",
            "name":"Test MDIA  5",
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":60,
            "createdDate":{
               "$date":"2022-09-09T14:52:46.000Z"
            },
            "updatedDate":{
               "$date":"2022-09-09T10:04:19.167Z"
            }
         },
         "startDate":{
            "$date":"2022-09-13T00:00:00.000Z"
         },
         "lastEntryDate":{
            "$date":"2022-09-13T12:32:44.791Z"
         },
         "remainingNumberOfUse":1
      }
   ]
}

The first document content one audience but the second has 4 audiences.
And i want to add these fields (isdeleted = true and deletedDate = new Date()) in audiences array.

{
   "_id":{
      "$oid":"62e932661c0f2e018fe6be47"
   },
   "buCode":"LMIT",
   "clientNumber":"92111403",
   "audiences":[
      {
         "audience":{
            "audienceId":"42003",
            "name":"OFFER_MDIA LMIT",
            "createdDate":null,
            "updatedDate":null,
            "maximumNumberOfUseDuringMembership":1,
            "membershipDuration":20,
            "category":"LOYALTY",
            "description":"",
            "buCode":"LMIT"
         },
         "startDate":{
            "$date":"2022-08-01T10:15:10.000Z"
         },
         "remainingNumberOfUse":1,
         "lastEntryDate":{
            "$date":"2022-08-02T14:19:19.106Z"
         },
         "deletedDate":{
            "$date":"2022-09-19T13:57:46.666Z"
         },
         "isDeleted":true
      }
   ]
}

I create a script in nodejs like this

var dbo = db.db("myDatabase");

dbo.collection("customer").aggregate(
  [
    {
      $unwind: "$audiences"
    },
    {
      $addFields: {
        qualify: {
          $cond: {
            if: {
              $lte: [
                {
                  $dateAdd: {
                    startDate: "$audiences.startDate",
                    unit: "day",
                    amount: "$audiences.audience.membershipDuration"
                  }
                },
                "$$NOW"
              ]
            },
            then: true,
            else: false
          }
        }
      }
    },
    { $match: 
        {$and: [
                {qualify: true}
               ]
        }
    }
  ]
)
.forEach(function(doc) {
  dbo.collection("customer").updateOne({ _id: doc._id, audiences : doc.audiences }, 
    { $set: { 'audiences.$[0].isDeleted':true, 'audiences.$[].deletedDate':new Date() } }, 
    { multi: true },
    function(err, obj){
    console.log(JSON.stringify(doc.audiences))
    db.close();
  });
  });
});

but when the audiences array content 2 items, the update is not working.
Someone can help me please ?

Mohamed

A few things.

1 - You should try to $match first before altering the documents. You have better chance to hit an index if any exists.

2 - I understand your $match involves a computed field from $addFields, but you should compute that field directly in your $match without altering the documents.

3 - You can $match into an array without $unwind.

4 - Rather than calling updateOne for each documents you should use bulkWrite to reduce the round trips to the server.

5 - You could leverage the flexible schema nature of mongo by combining isDeleted and deletedDate into a single field. deletedDate null or missing means it is not deleted, that would save storage.

6 - Take a look at $map which allows you to modify all elements of an array. You would need an expression that uses $mergeObjects. The last stage of your aggregation could then be a $merge to write back the documents into the original collection without even downloading any documents on the client.

2 Likes

Hi @steevej,

thanks for your reply.
I tried with bulkwrite, map and mergeObjects is not working

Can you help me please, i’m new in mongodb

  var dbo = db.db("audienceSharing");
  
 
dbo.collection("customer").aggregate(
  [
    {
      $match:
      {
          "clientNumber":"111111"
      }
    },
    {
      $unwind: "$audiences"
    },
    {
      $addFields: {
        qualify: {
          $cond: {
            if: {
              $lte: [
                {
                  $dateAdd: {
                    startDate: "$audiences.startDate",
                    unit: "day",
                    amount: "$audiences.audience.membershipDuration"
                  }
                },
                "$$NOW"
              ]
            },
            then: true,
            else: false
          }
        }
      }
    },
    { $match: 
        {$and: [
                {qualify: true}
               ]
        }
    }
  ]
)
.forEach(function(doc){
  dbo.collection('customer').bulkWrite(
    [
      {
        updateOne:
        {
          filter: doc._id,
          update: 
          {
            $set: 
            { 
                'audiences.isDeleted':true, 
                'audiences.deletedDate':new Date() 
            } 
          },
          "upsert": false
          //arrayFilters:doc.audiences
        }
      }
    ],
    function(err,obj){
      console.log(JSON.stringify(doc));
      db.close();
    }
    )
})});

I used $map and $mergeObject is not working when the arrays contents more one items.

// Connection URI
const url = process.env.uri;

MongoClient.connect(url, function(err, db) {
  if (err) throw err;
  var dbo = db.db("audienceSharing");
  
 
dbo.collection("customer").aggregate(
  [
    {$match:{"clientNumber":"22222"}},
    {
      $unwind: "$audiences"
    },
    {
      $addFields: {
        qualify: {
          $cond: {
            if: {
              $lte: [
                {
                  $dateAdd: {
                    startDate: "$audiences.startDate",
                    unit: "day",
                    amount: "$audiences.audience.membershipDuration"
                  }
                },
                "$$NOW"
              ]
            },
            then: true,
            else: false
          }
        }
      }
    },
    { $match: 
        {$and: [
                {qualify: true}
               ]
        }
    }
  ]
)
.forEach(function(doc){
  dbo.collection('customer').updateOne(
  {
    _id:doc._id, audiences:doc.audiences
  },
  [
    {
      $set:
      {
        "audiences":
        {
          $map:
          {
            "input":"$audiences",
            as:"audience",
            in:
            /*{
              $cond:
              [
                {
                  $eq: [ "$$audience", doc.audiences]
                },*/
                {
                  $mergeObjects:
                  [
                    "$$audience",
                    {
                      "isDeleted" : true,
                      "deletedDate": new Date()
                    }
                  ]
                }
              //]
            //}
          }
        }
      }
    }
  ],
  function(err, obj){
    console.log(doc.audiences)
  }
  )
})
})

I need your help

You still $unwind despite

You still $addFields despite

You still use

despite