smartmouse
smartmouse

Reputation: 14404

Mongodb version 3+: how to insert new document only if it doesn't exist and get the result

I have tried dozens of solutions, and none of these worked and most of them are deprecated.

I have a collection with documents like this:

    _id: 5d99ef3285c93711828cd15d
    code: 1234
    name: "Foo"
    surname: "Bar"
    address: "That street"
    phone: 1234567

I would like to insert new document only if there isn't any document with the same code.

My last try was this:

const result = await db.collection('users').findOneAndUpdate(
    { code: user.code },
    {
        $setOnInsert: user,
    },
    {
        upsert: true,
    }
);

but I get E11000 duplicate key error collection...

updateOne() returns the same error; update() is deprecated...

So, how to add only new document and get the result (true if document has been added or false if it already exists)?

Thank you.

Upvotes: 0

Views: 214

Answers (6)

niranjan_harpale
niranjan_harpale

Reputation: 2274

As far as my knowledge is,

with $set and $setOnInsert, we can not update/insert the same field.

i.e. $set and $setOnInsert should be mutually exclusive.

It works if the document is being updated, but throws an exception if document is being inserted.

In case of update, $setOnInsert will be ignored.

In case of insertion, both will be executed.

I think the solution would be,

use returnNewDocument and have one field in the schema isUpdated defaults to false.

Note:

make sure whenever you use "insert" operation on the collection, you don't add isUpdated which will be set to false then or set it to false.

form a query like

db.collection('users').findOneAndUpdate(
    { code: user.code },
    {
        $set: user,          // which has user.isUpdated set to true
    },
    {
        upsert: true,
        returnNewDocument: false, // (by default it is false only)
    }
);

With this logic,

So let's go step by step,

  1. If the document doc1 is not present, it will be inserted, and mongo will return the response null. You will know, it is Inserted.

  2. If the document doc2 is present(considering this logic is not applied on the previously inserted document doc2 before and isUpdated field is not present in doc2), it will execute $set so in returned cursor, this field not present i.e. undefined, so you know from this, it is updated.

  3. let's say we fire the same query for doc1 again (which is present and we have applied our new logic), then there are two cases

    a. it will be updated and in the cursor, we have isUpdated equal to false.

    b. it will be updated and in the cursor, we have isUpdated equal to true.

    In both case you know it is Updated

I think this approach should solve your problem.

Please share if this helps you, or you find any other solution.

UPDATE

ACTUALLY

You dont even need another field isUpdated, without this fiels this should work with the same logic.

i.e. If cursor is null then its inserted, if not null then its updated.

Upvotes: 1

Ankit Kumar Rajpoot
Ankit Kumar Rajpoot

Reputation: 5600

Try this one

   db.collection("users").findOne({ code: user.code }, (err, data) => {
      if (data) {
        return res.send(false);
      } else {
        // a document
        var user = new User({
          code: code,
          name: "Foo",
          surname: "Bar",
          address: "That street",
          phone: 1234568
        });

        // save model to database
        user.save(function(err, book) {
          if (err) return console.error(err);
          return res.send(true);
        });
      }
    });

Upvotes: 0

Sumit Yadav
Sumit Yadav

Reputation: 44

 Users.findOneAndUpdate({code:user.code}, {upsert: true, new: true, setDefaultsOnInsert: true }, function(error, result) { if (error) return; // do something with the document }).

I think it would work. Let us know if you have any questions.

Upvotes: -1

Michael G
Michael G

Reputation: 582

You could use findOne to see if a user with that code exists first

const result = await db.collection('users')
    .findOne({ code: user.code });

if( result ){ 
  return res
      .status(400)
      .json({ errors: [{ msg: 'User already exists' }] });
}
//create 
user = new User({
     code = code, 
     name = name, 
     foo = foo
)}
//save
user.save();   
res.json(user);

Upvotes: 0

Thomas Rückert
Thomas Rückert

Reputation: 106

You can simply wrap the request with a try/catch block to catch the Error. Then return false when this exception occured.

Upvotes: 0

Ümit Yayla
Ümit Yayla

Reputation: 38

You can still run a query like this;

document = db.collection('users').findOne({code:userCode});
if(document == null){
     //document  doesn't exists so you can use insertOne
}
else{
// document exists, sou you can update
}

it won't be efficient but it'll do the work.

Upvotes: 0

Related Questions