Mongoose append to string while update - mongodb

I try to append something to a string with findOneAndUpdate
My DB values
{ _id: 'myId', name: 'Name', createdAt: ..... }
I try to append something to my name without doing two queries.
await MongoModel.findOneAndUpdate(
{
_id: 'myId',
},
{
'name': currentString + '_someString',
}
)
and my output should look like
{ _id: 'myId', name: 'Name_someString', createdAt: ..... }

You can try update with aggregation pipeline starting from MongoDB 4.2,
$concat to concat current name and new string
await MongoModel.findOneAndUpdate(
{ _id: "myId" },
[{
$set: {
name: {
$concat: ["$name", "_someString"]
}
}
}]
)
Playground

Related

How to update Object's array's objects' value?

Following code is not updating book title, how can achieve my goal of updating book title?
user: {
_id: "123",
books: [{ title: "ABC", pages: 99 }],
}
await model.updateOne(
{
_id: userID,
"books._id": bookID,
},
{ book: { title: "" } }
);
From your scenario, you need arrayFilters.
db.collection.update({
_id: "123" //userID
},
{
$set: {
"books.$[book].title": ""
}
},
{
arrayFilters: [
{
"book._id": "1" //bookID
}
]
})
Sample Mongo Playground
References
How the arrayFilters Parameter Works in MongoDB
try this
await model.updateOne(
{
_id: userID,
"books._id": bookID,
},
{ title: "" }
);

iterate over large mongodb collection for purpose of updating schema

I have a 300k collection of test docs. I want to update all persons firstName and lastName to be lowercase.
const person = new Schema({
firstName: { type: String},
lastName: { type: String }
})
I've added lowecase:true to the schema but how do I update the existing documents?
I tried:
CaseFile
.find({ })
.cursor()
.eachAsync(async function (doc) {
await doc.save()
})
but i get the error
Error: Collection method find is synchronous
I also tried :
CaseFile
.find({ })
.then(docs => {
docs.forEach(doc => {
doc.save()
})
})
which gives the error:
JavaScript heap out of memory
db version v5.0.2
"mongoose": "^6.0.5",
thank you Wernfried Domscheit for the pipeline 🏄 solution:
CaseFile.updateMany({}, [
{
$set:
{
firstName: { $toLower: '$firstName' },
lastName: { $toLower: '$lastName' }
}
}]
)
.then(res => res)
Why on earth "iterate", i.e. line by line?
Use an aggregation pipeline:
db.CaseFile.updateMany({}, [
{ $set:
firstName: { $toLower: "$firstName" },
lastName: { $toLower: "$lastName" }
}
])

How to update multiple documents in mongodb using one query from an array of objects

I have the following array of objects...
[
{
_id: 'dfjkldsjfkldjas',
name: 'will'
},
{
_id: 'fdsfdakjdhfaskh',
name: 'bob'
},
{
_id: 'fdsfdasfdfewrfq',
name: 'tom'
}
]
Is there a way to search a mongodb collection for documents that match these _id's and then $set the name in the document all in one query? Maybe using updateMany?
You can use a bulkWrite to achieve this. First map your array and transform the data like so:
const data = [
{
_id: 'dfjkldsjfkldjas',
name: 'will'
},
{
_id: 'fdsfdakjdhfaskh',
name: 'bob'
},
{
_id: 'fdsfdasfdfewrfq',
name: 'tom'
}
]
const bulkData = data.map(item => (
{
updateOne: {
filter: { _id: item._id },
update: { $set: { name: item.name }}
}
})
);
Then you use the method bulkWrite to save the multiple data.
db.yourCollection.bulkWrite(bulkData);
You also don't have to use the same DB operation for every entry, you can use different ones on the same bulk like deleteOne, replaceOne, insertOne, etc.
You can check more here: https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/
Try this for updating bulk data
const data = [
{
_id: 'dfjkldsjfkldjas',
name: 'will'
},
{
_id: 'fdsfdakjdhfaskh',
name: 'bob'
},
{
_id: 'fdsfdasfdfewrfq',
name: 'tom'
}
]
const bulkData = data.map(item => (
{
updateOne: {
filter: { _id: item._id },
update: { $set: { name: item.name } },
upsert: true
}
})
);
await db1.bulkWrite(bulkData, { ordered: false, upsert: true });

How to find all matched elements from array of objects in mongoose

Schema:
new Schema({
productId: String,
types: [{
productType: String,
lastModified: Date
}]
});
Query:
{
productId: "1",
email: "test#test.com",
productType: "test",
}
I tried this but its returning only first matched element:
const productType = 'test';
const result = await this.model(email)
.find(
{ productId, 'types.productType': productType },
{ 'types.$': productType }
).lean();
with aggregate, it return empty array result:
const result = await this.model(email).aggregate([
{ $match: { productId, 'types.productType': 'productType' } },
{
$project: {
types: {
$filter: {
input: '$types',
as: 'r',
cond: { $eq: ['$$r.productType', productType] }
}
},
_id: 0
}
}
]);
I need to find all matching elements where projection $ returns the first matched

mongoose find and update removes the other fields

I have schema like this:
this.schema = new Schema({
userEmail: String
environments: [
{
envId: String,
appPreference: String,
language: String,
timeZone: String,
summaryNotificationSchedule: {
timeOfTheDay: String
}
}
]
});
Update request:
{
"envId": "u2",
"appPreference": "put2",
"timeZone": "gmt",
"summaryNotificationSchedule.timeOfTheDay": "32400",
}
As you can see, I am not sending "language": "abc", in the update request and in the result I see the language field is removed. I want to update the fields but not remove the other fields
Mongoose find and update call:
await this.model.findOneAndUpdate({ userEmail, 'environments.envId': envId }, { $set: { 'environments.$': setPreferenceFields } }, { new: true });
You can create update object from your request first:
let request = {
"envId": "u2",
"appPreference": "put2",
"timeZone": "gmt",
"summaryNotificationSchedule.timeOfTheDay": "32400",
};
let update = Object.keys(request).reduce((acc, cur) => {
acc[`environments.$.${cur}`] = request[cur];
return acc;
}, {})
console.log(update);
Then pass it to the update:
await this.model.findOneAndUpdate({ userEmail, 'environments.envId': envId }, { $set: update }, { new: true });
You have to specify property with parent key name of an array, it should be like this way,
await this.model.findOneAndUpdate(
{
userEmail,
'environments.envId': envId
},
{
$set: {
'environments.$.envId': "u2",
'environments.$.appPreference': "put2",
'environments.$.timeZone': "gmt",
'environments.$.summaryNotificationSchedule.timeOfTheDay': "32400"
}
},
{ new: true }
)
Another option, update with aggregation pipeline start from MongoDB v4.2, this little lengthy process then above method,
$map to iterate loop of environments array
$cond check condition if envId is equal to matching envId then merge objects update objects and current objects using $mergeObjects otherwise return current object
await this.model.findOneAndUpdate(
{ userEmail },
[
{
$set: {
environments: {
$map: {
input: "$environments",
in: {
$cond: [
{$eq: ["$$this.envId", envId]}, // add update id
{
$mergeObjects: [
"$$this",
setPreferenceFields // your update fields
]
},
"$$this"
]
}
}
}
}
}
],
{new: true}
)