1

I am using Bulk Operations in mongodb. I have a situation where I need to push an object into an array field if the conditions are not met.

Here if there are no items in configs that match itype and prefix, then it should push the object into the configs. How can I do this?

//! Find and push if document found but item does not exist.
            orderedBulkOP.find({
              "cid": newConfig.cid,
              "username": newConfig.username,
              "configs": {
                $elemMatch: {
                  "itype": newConfig.itype, "prefix": newConfig.prefix
                }
              }
            }).update({
              $push: {
                "configs": {
                  "itype": newConfig.itype,
                  "prefix": newConfig.prefix,
                  "count": 0,
                  "createdAt": new Date().toISOString()
                }
              }
            });

The schema is like this:

{
  id: String,
  uniqid: String,
  cid: String,
  username: String,
  configs: [{
    createdAt: Date,
    itype: String,
    prefix: String,
    count: Number
  }
  ]
}
ashwin mahajan
  • 1,654
  • 5
  • 27
  • 50
  • By adding [`$not`](https://docs.mongodb.com/manual/reference/operator/query/not/) around the `$elemMatch`. You could put `$ne` on each field, but `$not `is a bit more expressive. – Neil Lunn Aug 22 '17 at 06:54
  • @NeilLunn If prefix = 'abc' and if any item has prefix= 'zxc', then that does not mean that there is no item that has prefix = 'abc'. Am I right? So it will still be pushed if it is already there. – ashwin mahajan Aug 22 '17 at 06:57
  • What are you asking for? Checking if "prefix" is unique? Or checking if the combination of "prefix" and "itype" are unique? Because `$eleMatch` implies that you are looking for the combination. – Neil Lunn Aug 22 '17 at 07:04
  • Yes, I am checking if the combination is unique. So, itype='mno' and prefix='zxc' will satisfy the condition when itype='mno' and prefix='abc' exists and the item will be pushed. – ashwin mahajan Aug 22 '17 at 07:17

1 Answers1

0

Here if there are no items in configs that match itype and prefix, then it should push the object into the configs

You're probably looking for an upsert operation here.

You can try to update your query as:

orderedBulkOP.find({
          "cid": newConfig.cid,
          "username": newConfig.username,
          "configs": {
            $elemMatch: {
              "itype": newConfig.itype, "prefix": newConfig.prefix
            }
          }
        }).upsert().update({
          $push: {
            "configs": {
              "itype": newConfig.itype,
              "prefix": newConfig.prefix,
              "count": 0,
              "createdAt": new Date().toISOString()
            }
          }
        });

With the upsert option set to true, if no matching documents exist for the Bulk.find() condition, then the update or the replacement operation performs an insert.


If the use case is to only insert if the documents with the combination are not found else not do anything. Then you can make use of setOnInsert within your update in somewhat like:

update({
     "$setOnInsert": {
        "configs": {
          "itype": newConfig.itype,
          "prefix": newConfig.prefix
        }
      }
    });

If an update operation with upsert: true results in an insert of a document, then $setOnInsert assigns the specified values to the fields in the document.

If the update operation does not result in an insert, $setOnInsert does nothing.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • If the item is found, then it will push the item. Wouldn't it? I am looking for something like `{$setOnInsert : { $push: {} }}`. But I know its not possible. – ashwin mahajan Aug 22 '17 at 07:22
  • @ashwinx Are you looking for inserting the item only if the `itype` and `prefix `combination is not found? – Naman Aug 22 '17 at 07:24
  • 1
    Yes, exactly thats what I am looking for. I could use `$set` but then it would update the `count` field. – ashwin mahajan Aug 22 '17 at 07:26
  • In that case you can use [`setOnInsert`](https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/) with upsert. Updating the answer with details.. – Naman Aug 22 '17 at 07:27
  • It creates a new document with the specified values. – ashwin mahajan Aug 22 '17 at 07:51
  • *Here if there are no items in configs that match itype and prefix, then it should push the object into the configs* that is what you meant right? Maybe try detailing the question further with some example else. – Naman Aug 22 '17 at 07:53
  • Yes that is what i meant. But `$setOnInsert` creates a new document. – ashwin mahajan Aug 22 '17 at 07:55
  • @ashwinx That is when the `upsert:true` results in an insert. Meaning the document does not exist already. Which is expected , right? – Naman Aug 22 '17 at 07:57
  • Yes, but consider two conditions, first, if cid and username are there but itype is not there in any object and second, if cid and username itself is not there. That is why I was using bulk operations. Because if we use first case then it will create duplicated document for the same cid and username when the itype is not there. – ashwin mahajan Aug 22 '17 at 09:07