3

I have a MongoDB collection with a unique index. I am trying to insert or update an array of documents into that collection.

If there is NO existing document in the collection matching the unique index of the document, that new document should be inserted into the collection.

However, if there IS already a document in the collection with that unique index, it should be updated with the fields of the new document. Any fields that are NOT present in the new document should be left untouched.

This is what I have currently which is working for inserting (but NOT for updating).

const mongojs = require('mongojs');
const db = mongojs('mongodb://username:password@address.mlab.com:37230/database');

             // items is an array of documents
db.items.insert(items, (err, task) => {
    if (err) {
      console.log(err);
    }
  })

I understand this is wrong and it currently gives this error:

E11000 duplicate key error index: database.items.$upc_1 dup key:

What is the proper query for this?

etayluz
  • 15,920
  • 23
  • 106
  • 151

3 Answers3

5

You can try using mongodb bulkWrite api:

var ops = []

items.forEach(item => {
    ops.push(
        {
            updateOne: {
                filter: { _id: unique_id },
                update: {
                    $set: { fields_to_set_if_exists },
                    $setOnInsert: { fileds_to_insert_if_does_not_exist }
                },
                upsert: true
            }
        }
    )
})

db.collections('collection_name').bulkWrite(ops, { ordered: false });
lee shin
  • 894
  • 11
  • 6
  • 1
    even fields not exist $set operator performs its operations, your $set operator descripton is wrong : https://docs.mongodb.com/manual/reference/operator/update/set/ – Serhat Ates Sep 10 '18 at 11:21
4

I don't believe that you can update an entire array of documents at the same time. Therefore, you would have to update each item in the array individually.

items.forEach((item) => {
   db.items.update({_id: item._id}, item, {upsert: true}, (err, task) => {
           if (err) {
             console.log(err);
           }
   });
}

The {upsert: true} option will update if a record exists and insert if not.

Tyler Kirby
  • 253
  • 1
  • 10
  • 1
    Thank Tyler. I'm currently using mLab which is a remote hosted service. If I have ten documents, with this approach I have to make ten separate calls. Is there no better way? – etayluz Mar 24 '17 at 03:12
  • If you could also take at this question: https://stackoverflow.com/questions/42990854/mongodb-insert-multiple-documents-into-collection-with-unique-index-even-if-som – etayluz Mar 24 '17 at 03:23
  • Not to my knowledge. There is an updateMany function, but it will update all documents that match the given query selector. I don't believe this is what you are looking for. – Tyler Kirby Mar 24 '17 at 03:26
  • This answer appears to ignore that the question is about using the already-defined unique index on the collection, *not* the _id. – coatesap Jun 10 '20 at 12:03
0

What are you looking for is an upsert, not an insert. It can be done by the following code:

db.collection.update(
   <query>,
   <updates>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>,
     collation: <document>
   }
)

Query will search for a document using the parameters of the query, if it finds, it will update the fields mentioned in . If it doesn't find, it will insert a new document with the fields and values of .

The last object (with multiple fields), contains a field to say if an upsert is desired and one called "multi" to say if an update on multiple documents is desired.

For example:

db.items.update({name:"item1"},{$set:{price:20}},{upsert:true}) 

This will search for a document with the name "item1" and set its price to 20. If it doesn't find, it will create a new one with price 20.

One thing to be noticed though is:

If you don't use the tag $set on the fields, it will substitute the whole document.

Supposing you have a document like this:

{_id:"1234",name:"item1",price:10}

If you run the following two queries:

db.items.update({name:"item1"},{$set:{price:20}},...)

and

db.items.update({name:"item1"},{price:20},...)

it will yeld different results:

First one:

{_id:"1234",name:"item1",price:20}

Second one:

{_id:"1234",price:20}

If you don't call $set, it will change the whole document.

More information on the manual:

https://docs.mongodb.com/manual/reference/method/db.collection.update/

Hope my answer was helpful

Israel Zinc
  • 2,713
  • 2
  • 18
  • 30