MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update - node.js

as the title says, I want to perform a find (one) for a document, by _id, and if doesn't exist, have it created, then whether it was found or was created, have it returned in the callback.
I don't want to update it if it exists, as I've read findAndModify does. I have seen many other questions on Stackoverflow regarding this but again, don't wish to update anything.
I am unsure if by creating (of not existing), THAT is actually the update everyone is talking about, it's all so confuzzling :(

Beginning with MongoDB 2.4, it's no longer necessary to rely on a unique index (or any other workaround) for atomic findOrCreate like operations.
This is thanks to the $setOnInsert operator new to 2.4, which allows you to specify updates which should only happen when inserting documents.
This, combined with the upsert option, means you can use findAndModify to achieve an atomic findOrCreate-like operation.
db.collection.findAndModify({
query: { _id: "some potentially existing id" },
update: {
$setOnInsert: { foo: "bar" }
},
new: true, // return new doc if one is upserted
upsert: true // insert the document if it does not exist
})
As $setOnInsert only affects documents being inserted, if an existing document is found, no modification will occur. If no document exists, it will upsert one with the specified _id, then perform the insert only set. In both cases, the document is returned.

Driver Versions > 2
Using the latest driver (> version 2), you'll use findOneAndUpdate as findAndModify was deprecated. The new method takes 3 arguments, the filter, the update object (which contains your default properties, that should be inserted for a new object), and options where you have to specify the upsert operation.
Using the promise syntax, it looks like this:
const result = await collection.findOneAndUpdate(
{ _id: new ObjectId(id) },
{
$setOnInsert: { foo: "bar" },
},
{
returnOriginal: false,
upsert: true,
}
);
const newOrUpdatedDocument = result.value;

Its a bit dirty, but you can just insert it.
Be sure that the key has a unique index on it (if you use the _id it's ok, it's already unique).
In this way if the element is already present it will return an exception that you can catch.
If it isn't present, the new document will be inserted.
Updated: a detailed explanation of this technique on the MongoDB Documentation

Here's what I did (Ruby MongoDB driver):
$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}
It will update it if it exists, and insert it if it doesn't.

Related

MongoDB: Insert a document if not exist, but skip updating in case it already exists

How do I insert a document in mongoose if not exist without doing an update on the existing one if it already exists? I know if I do an upsert it tries to update if exist and inserts a new document if it doesn't exist. However, my situation is that I just want to insert a new document if not exist and leave the existing one without updating it.
You are on the right way.
You should use still use upsert in the update method but also with the $setOnInsert operator. Please, read about this operator here.
In short words, using this operator allows you to perform operation only if it results in inserting new document. In other case it will simply skip.
And yes, you of course can use this with mongoose:
YourModel.update(
{ queryField: 1 }, // query
{ $setOnInsert: { ...newDocument }}, // new doc fields
{ upsert: true } // still use upsert option
);

Mongoose check value exist while save/create and send it through response [duplicate]

as the title says, I want to perform a find (one) for a document, by _id, and if doesn't exist, have it created, then whether it was found or was created, have it returned in the callback.
I don't want to update it if it exists, as I've read findAndModify does. I have seen many other questions on Stackoverflow regarding this but again, don't wish to update anything.
I am unsure if by creating (of not existing), THAT is actually the update everyone is talking about, it's all so confuzzling :(
Beginning with MongoDB 2.4, it's no longer necessary to rely on a unique index (or any other workaround) for atomic findOrCreate like operations.
This is thanks to the $setOnInsert operator new to 2.4, which allows you to specify updates which should only happen when inserting documents.
This, combined with the upsert option, means you can use findAndModify to achieve an atomic findOrCreate-like operation.
db.collection.findAndModify({
query: { _id: "some potentially existing id" },
update: {
$setOnInsert: { foo: "bar" }
},
new: true, // return new doc if one is upserted
upsert: true // insert the document if it does not exist
})
As $setOnInsert only affects documents being inserted, if an existing document is found, no modification will occur. If no document exists, it will upsert one with the specified _id, then perform the insert only set. In both cases, the document is returned.
Driver Versions > 2
Using the latest driver (> version 2), you'll use findOneAndUpdate as findAndModify was deprecated. The new method takes 3 arguments, the filter, the update object (which contains your default properties, that should be inserted for a new object), and options where you have to specify the upsert operation.
Using the promise syntax, it looks like this:
const result = await collection.findOneAndUpdate(
{ _id: new ObjectId(id) },
{
$setOnInsert: { foo: "bar" },
},
{
returnOriginal: false,
upsert: true,
}
);
const newOrUpdatedDocument = result.value;
Its a bit dirty, but you can just insert it.
Be sure that the key has a unique index on it (if you use the _id it's ok, it's already unique).
In this way if the element is already present it will return an exception that you can catch.
If it isn't present, the new document will be inserted.
Updated: a detailed explanation of this technique on the MongoDB Documentation
Here's what I did (Ruby MongoDB driver):
$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}
It will update it if it exists, and insert it if it doesn't.

Mongoose, Nodejs - replace many documents in one I/O?

I have an array of objects and I want to store them in a collection using only one I/O operation if it's possible. If any document already exists in the collection I want to replace it, or insert it otherwise.
These are the solutions that I found, but doesn't work exactly as I want:
insertMany(): this doesn't replace the document that already exists, but throws exception instead (This is what I found in the Mongodb documentation, but I don't know if it's the same as mongoose).
update() or ‎updateMany() with upsert = true: this doesn't help me as well, because here I have to do the same updates to all the to stored documents.
‎There is no replaceMany() in mongodb or mongoose.
Is there anyone how knows any optimal way to do replaceMany using mongoose and node.js
There is bulkWrite (https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/), which makes it possible to execute multiple operations at once. In your case, you can use it to perform multiple replaceOne operations with upsert. The code below shows how you can do it with Mongoose:
// Assuming *data* is an array of documents that you want to insert (or replace)
const bulkData = data.map(item => (
{
replaceOne: {
upsert: true,
filter: {
// Filter specification. You must provide a field that
// identifies *item*
},
replacement: item
}
}
));
db.bulkWrite(bulkData);
You need to query like this:
db.getCollection('hotspot').update({
/Your Condition/
}, {
$set: {
"New Key": "Value"
}
}, {
multi: true,
upsert: true
});
It fulfils your requirements..!!!

MongoDB ignore <query> on upsert

I'm trying to upsert a document into MongoDB using the main Node driver. I want to query by _id, and if that _id doesn't exist, then create a new document with a normal ObjectId. However, from the MongoDB docs:
The update creates a base document from the equality clauses in the query parameter, and then applies the update expressions from the update parameter.
(https://docs.mongodb.com/manual/reference/method/db.collection.update/#upsert-behavior)
Meaning that it will try to use whatever I compared the _id to in the query section as the document's new id. The problem is that, if the document doesn't exist yet, I'm comparing _id to null, so when it creates the new document, it sets the _id as null. Is there a way to avoid this behavior? I want to query by _id, but not use whatever I compare it to in the upsert. Here's my code so far:
dbm.collection('orders').findOneAndUpdate(
{
_id: order._id
},
{
$set: order,
$setOnInsert: {_id: ObjectId()}//tried with and without this
},
{
upsert: true,
returnOriginal: false
}).then().catch()
Here, order, is an object with a few fields. And for my purposes, I need to query by _id, not some other indexed field.

What is the difference between update and updateMany method in mongo DB?

I am trying to update multiple documents on mongo db. which is better update method with multi = true or updateMany method? and what is the difference between them?
To expand on Blakes Seven's answer, all the updateOne method does is simply set the multi option to false as seen here (github).
And in turn all the updateMany method does is set the multi opion to true as seen here (github).
So there is no "better" method to call, just pick whichever you feel suits the situation.
The difference is that update() by default, modifies only one document matching the specified filter. However, you can make it modify all the documents by adding the modifier {multi: true}. updateMany on the other hand, modifies all the documents matching a specified filter.
“updateOne” and “updateMany” are newer APIs and should be used if possible instead of “update”.
In the book “Mongo Definitive Guide” (while talking about “updateOne”):
“The update document must contain update operators. Previous versions of the CRUD API did not catch this type of error. Earlier update methods would simply complete a whole document replacement in such situations. It is this type of pitfall that led to the creation of a new CRUD API.”
— MongoDB: The Definitive Guide: Powerful and Scalable Data Storage by Shannon Bradshaw, Eoin Brazil, et al.
updateMany() and updateOne()
updateMany(query, update, options) => Modifies ALL the doc's which matches the query.
updateOne(query, update, options) => Modifies a SINGLE first doc which matches the query.
updateMany() and updateOne() options:
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
update():
update(query, update, options) => Modifies a SINGLE first doc which matches the query.
(DEFAULT behaviour. But you could used as a "updateMany()" or "updateOne()"
by changing the "multi: <boolean>" inside options)
update() options:
{
multi: <boolean>, // multi: true => became => updateMany()
// multi: false => became => updateOne()
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
One difference which I found missing in previous answers is update() does not require $set to be used and w/o $set it actually erases all the existing data in document and replaces it with the data you provide.
While updateMany() & updateOne() actually need $set to be specified w/o $set you will get an error like uncaught exception: Error: the update operation document must contain atomic operators
It totally depends on your use case though.
updateOne() --> update only one document in a collection.
updateMany() --> update many document in a collection with given filter.

Resources