MongoDB ignore <query> on upsert - node.js

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.

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
);

mongodb get list of subdocuments that matched with list of values

my document schema goes like this
_id: kkj33h2kjkjh32jk34
events: [
{
_id: k234j3lk4k2j3h4j3j4
},
{
_id: k234j3lk4k2j3h4j3j4
},
{
_id: k234j3lk4k2j3h4j3j4
}
]
here is my query, I have a list of _ids of the subdocuments of events field and I need to get all the matched subdocuments as the response from the event field I have tried to use $in and many but failed can anyone suggest me how to do this
tried this
subarr=['fh576hgfu658uyg7h','k234j3lk4k2j3h4j3j4']
model.findOne({
clgid: req.query.clgid,
'events._id': {$in:subarr}
},{"events.$":1});
but the problem with the above code is that it is fetching the first matching subdocument. but I need all the matching subdocuments.
suggest me the right way to do this query so that I get all the matched subdocuments that match from array
The issue of your query matching only the first subdocument is the use of {"events.$":1} in your projection.
I'm not sure what you are actually intending to do.
{"events.$":1} will limit to the first (sub)document matching your query, as per the documentation of the $ operator.
Maybe you're trying only to get the _id of the subdocuments and then, please try the following:
subarr=['fh576hgfu658uyg7h','k234j3lk4k2j3h4j3j4']
model.findOne({
clgid: req.query.clgid,
'events._id': {$in:subarr}
},{"events._id":1});

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: best way to upsert data defined in conditions array

I trying to find best way for insert/update mongodb documents within one query, without calling save foreach element in result. Is it possible?
This code create document only with setData object.
conditions.idUser = {$in: uids};
db.Data.update(conditions, {$set: setData}, { multi: true, upsert: true }, function(err) {
But I need to save multiple documents, like {idUser : uid1, ..}, {idUser: uid2, ...}

MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update

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.

Resources