Aggregation query with $set in findOneAndUpdate doesn't update document - mongodb

I am using node 11.6, mongodb 4.2.5, mongoose 4.3.17. I am trying to update a field by adding a string to the end of it. I first updated to mongo 4.2 which I apparently needed to use aggregation pipelines in updates.
I tried following this post like this:
var update = [{$set: {slug: {$concat: ['$slug', '-rejected']}}}];
Content.findOneAndUpdate({_id: id}, update, {new: true}, (err, doc) => {
//
});
but when I ran it I got no error, no document returned, and it was not updated.
So I removed the outer [], and passed just an object like this:
var update = {$set: {slug: {$concat: ['$slug', '-rejected']}}}
Content.findOneAndUpdate({_id: id}, update, {new: true}, (err, doc) => {
//
});
And I receive this error message:
`Cast to string failed for value "{ '$concat': [ '$slug', '-rejected' ] }" at path "slug"`,
What does this mean? How can I accomplish this? (without two separate calls)
Running the same function but replacing update with:
var update = {slug: 'test-slug'}
successfully updates the slug to 'test-slug'.
But trying to do the same simple thing with an aggregation acts much like my previous attempt, no error or change:
var update = [{$set: {slug: 'test-sluggy'}}]
Using updateOne() instead of findOneAndUpdate() doesn't change anything either.
The only thing I can think that could cause it is the mongoose version, but it seems like there's a lot of changes between 4 and 5 and I don't want to update unless I have to, but I can't find anything that says it would change anything.

The pipeline form requires that the update be an array of pipeline stages.
Try wrapping your existing update in [] like
var update = [{$set: {slug: {$concat: ['$slug', '-rejected']}}}]

we can use something like that
var update = [{ $set: { slug: { $concat: ['$slug', '-rejected'] } } }]
starting from mongo versions > 4.2, the update operations can accept an aggregation pipeline, so we are able to update some field based on its current value:
the whole query may be something like that
Content.findOneAndUpdate(
{ _id: id }, // the find criteria
[{ $set: { slug: { $concat: ['$slug', '-rejected'] } } }], // this is the update aggregation pipeline, see the square brackets
{ multi: true }, // this should be set to true if you have more than one document to update,
// otherwise, only the first matching document will be updated,
// this is in case you use update rather than findOneAndUpdate,
// but here we have only one document to update, so we can ignore it
(err, doc) => {
//
});

Related

Aggregate and update with mongoose

I want to use the aggregate method to make my querys and modify my database by a value, I tried with $set but my database is not modified.
Here is how I do my query:
var filter = req.body.filter
var search = [ { $match: filter }, { $set: {item: "2"} }, { $sample: { size: 1 } }]
const result = await dataModel.aggregate(search)
I know there is also findOneAndUpdate but I would like to keep aggregate because I also want to use $project in my pipelines
thanks in advance !
You can use FindOneAndUpdate for change your db

Mongodb: concat to existing document

I have my collection like this:
{
"_id" : "ID1234",
"read_object" : "sss-ssss",
"expireAt" : ISODate("2020-04-30T22:00:00.000Z")
}
In case he encounters the same ID, I would like to update the read_object field, otherwise create a new document.
I tried to do it like this:
db.collection.update(
{ _id: "ID1234" },
{
$set: { read_object: { $concat: ["$read_object", "test"] } },
},
{ upsert: true }
)
but I get an error every time:
The dollar ($) prefixed field '$concat' in 'read_object.$concat' is not valid for storage.
If I add square brackets before $set, like this:
db.collection.update(
{ _id: "1b1b871493-14a0-4d21-bd74-086442df953c-2020-02" },
[{
$set: { read_object: { $concat: ["$read_object", "test"] } },
}],
{ upsert: true }
)
I get this error:
The dollar ($) prefixed field '$concat' in 'read_object.$concat' is not valid for storage.
Where do I have a mistake?
$concat is an aggregation operator, meaning you can't use it while using the basic update syntax as you can only use update operators on it.
With that said Mongo version 4.2 introduces pipeline updates, which is basically what you're trying to do with the square brackets.
Assuming you are using Mongo version 4.2 heres a working example:
db.test1.update({_id: "ID1234"}, [
{$set: {"read_object": {$concat: [{$ifNull: ["$read_object", ""]}, "test"]}}}
], {upsert: true});
Basically we just need to "replace" read_object if document does not exist as it is undefined in that case.
If you are using Mongo version that's smaller than 4.2 then unfortunately there is no way to do what you want in one operation, you'll have to first read the document and then adjust accordingly.

FindOne followed by updateOne, using a single query

By using the "findOne" method of mongoDb driver (no mongoose), can I use the retrieved document to update it later?
For example:
document.collection('myCollection').findOne({ _id: myId }, (err, foundDoc) => {
// **I need to do some multiple checks in between**, so I don't want to use "findOneAndUpdate()"
// foundDoc is the retrieved document. Can I use it directly for update? (something like "foundDoc.update()")
document.collection('myCollection').updateOne({ _id: myId }, { $set: { name: 'John' } });
});
As you can see I am basically doing a second query by using the "updateOne" method (first it searches for the document and then it updates it). Can I avoid that somehow, and use the foundDoc directly for update?
If you want to update the same document, you don't have to call .findOne() followed by .updateOne() method. By default, upsert option is set to false in .upadateOne(), so it will refuse to insert the document if not found else it will update.
document.collection('myCollection').updateOne({ _id: myId }, { $set: { name: 'John' } });
.updateOne should be sufficient in your case.
Also if you want to add some filter conditions, .updateOne() supports that as below :
db.collection.updateOne(
<filter>, // you can place filters here
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
Link : https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/

Updating the path 'x' would create a conflict at 'x'

This error happens when I tried to update upsert item:
Updating the path 'x' would create a conflict at 'x'
Field should appear either in $set, or in $setOnInsert. Not in both.
I had the same problem while performing an update query using PyMongo.
I was trying to do:
> db.people.update( {'name':'lmn'}, { $inc : { 'key1' : 2 }, $set: { 'key1' : 5 }})
Notice that here I'm trying to update the value of key1 from two MongoDB Update Operators.
This basically happens when you try to update the value of a same key with more than one MongoDB Update Operators within the same query.
You can find a list of Update Operators over here
If you pass the same key in $set and in $unset when updating an item, you will get that error.
For example:
const body = {
_id: '47b82d36f33ad21b90'
name: 'John',
lastName: 'Smith'
}
MyModel.findByIdAndUpdate(body._id, { $set: body, $unset: {name: 1}})
// Updating the path 'name' would create a conflict at 'name'
You cannot have the same path referenced more than once in an update. For example, even though the below would result in something logical, MongoDB will not allow it.
db.getCollection("user").updateOne(
{_id: ...},
{$set: {'address': {state: 'CA'}, 'address.city' : 'San Diego'}}
)
You would get the following error:
Updating the path 'address.city' would create a conflict at 'address'
db.products.update(
{ _id: 1 },
{
$set: { item: "apple" },
$setOnInsert: { defaultQty: 100 }
},
{ upsert: true }
)
Below is the key explanation to the issue:
MongoDB creates a new document with _id equal to 1 from the
condition, and then applies the $set AND $setOnInsert operations to
this document.
If you want a field value is set or updated regardless of insertion or update, use it in $set. If you want it to be set only on insertion, use it in $setOnInsert.
Here is the example: https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#example
Starting from MongoDB 4.2 you can use aggregate pipelines in update:
db.your_collection.update({
_id: 1
},
[{
$set:{
x_field: {
$cond: {
if: {$eq:[{$type:"$_id"} , "missing"]},
then: 'upsert value', // it's the upsert case
else: '$x_field' // it's the update case
}
}
}
}],
{
upsert: true
})
db.collection.bulkWrite() also supports it
With the Ruby library at least, it's possible to get this error if you have the same key twice, once as a symbol and once as a string:
db.getCollection("user").updateOne(
{_id: ...},
{$set: {'name': "Horse", name: "Horse"}}
)
I recently had the same issue while using the query below.
TextContainer.findOneAndUpdate({ blockId: req.params.blockId, 'content._id': req.params.noteId }, { $set: { 'content.note': req.body.note } }, { upsert: true, new: true })
When i have changed 'content.note' to 'content.$.note' it has been fixed. So my final query is :
TextContainer.findOneAndUpdate({ blockId: req.params.blockId, 'content._id': req.params.noteId }, { $set: { 'content.$.note': req.body.note } }, { upsert: true, new: true })

MongoDB update collection's data

I try to update an MongoDB data by using this code:
db.medicines.update({"_id":"586a048e34e5c12614a7424a"}, {$set: {amount:'3'}})
but unfortantly the query does not recognize the selector "_id":"586a048e34e5c12614a7424a", even if its exists.
Its succsed when I change the key to another like: name,rate and etc..
there is a special way to use update with _id parameter?
Thanks a head.
_id will be the unique ObjectId that mongodb generates for every document before inserting it. The query dint work because _id is an ObjectId and "586a048e34e5c12614a7424a" is a String. You need to wrap _id with ObjectId().
If you're using mongodb query
db.medicines.update({
"_id": ObjectId("586a048e34e5c12614a7424a")
}, {
$set: {
amount: '3'
}
});
If you are using mongoose. You can use findByIdAndUpdate
db.medicines.findByIdAndUpdate({
"_id": "586a048e34e5c12614a7424a"
}, {
$set: {
amount: '3'
}
});