findByIdAndDelete a subdocument with mongodb and mongoose, ( nodejs ) - node.js

I am trying to delete a subdocument that i have in my very deep nested schema...
i am using findByIdAndUpdate method but it is not removing the subdocument.
i have this in my nodejs
//I pretend to delete a subdocument inside the array "items"
storeCtrl.deleteAlmacen = (req, res) => {
storeModel.findByIdAndDelete(req.params.id).then(() => {
res.json('deleted')
}).catch((err) => {
res.json(err)
})
}
And it is my schema
// i did not copy and paste, so maybe has some syntax errors
{
"_id" : ObjectId("5c6cd20f13f0940e0cc214c5"),
"store" : [
{
"_id" : ObjectId("5c6cd20f13f0940e0cc214c6"),
"office" : [
{
"_id" : ObjectId("5c6cd21f13f0940e0cc214c8"),
"items" : [
{
"_id" : ObjectId("5c6cd22e13f0940e0cc214cb"),
"pen" : "3",
"paperId" : "a4"
}
],
"employees" : [ ],
"work" : 1
}
]
}
]
}
Do i have to use other method instead findByIdAndDelete?

Take a look at this solution: Update nested subdocument in Mongoose
Long story short, you should be using separate models, all of which reference each other. It's going to be a pain to change what you have now, but in all reality it's going to be MUCH cleaner.
You should have a Store model that references Office and an Office model that references an Items model

Related

Prevent mongoose "Model.updateOne" from updating ObjectId(_id) of the model when using "$set"

I'm updating the age and name of a character with a specific _id from an array of characters that is inside a document of model Drama.
The document I'm working with:-
{
"_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
"characters" : [
{
"_id" : ObjectId("619fdac5a03c8b10d0b8b13c"),
"age" : "23",
"name" : "Vinay",
},
{
"_id" : ObjectId("619fe1d53810a130207a409d"),
"age" : "25",
"name" : "Raghu",
},
{
"_id" : ObjectId("619fe1d53810a130207a502v"),
"age" : "27",
"name" : "Teju",
}
],
}
So to update the character Raghu I did this:-
const characterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": characterObj,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
This updated the character but it also assigned a new ObjectId to the _id field of the character. So, I'm looking for ways on how to prevent the _id update.
Also, I know I can set the individual fields of character instead of assigning a whole new object to prevent that but it will be very tedious if my character's object has a lot of fields.
//Not looking to do it this way
$set: {
"characters.$.age": characterObj.age,
"characters.$.name": characterObj.name,
},
Thanks.
I found something here, just pre define a schema (a blueprint in a way) that affects the id
var subSchema = mongoose.Schema({
//your subschema content
},{ _id : false });
Stop Mongoose from creating _id property for sub-document array items
Or I would say, when you create a character assign it a custom id from the start, that way it will retain that id throughout.
I'm leaving this question open as I would still like to see a simpler approach. But for now, I did find one easy alternative solution for this issue which I'm will be using for some time now until I find a more direct approach.
In short - Deep merge the new object in the old object using lodash and then use the new merged object to set field value.
For example, let's update the character Raghu from my question document:-
First install lodash(Required for deep merging objects) using npm:
$ npm i -g npm
$ npm i --save lodash
Import lodash:
const _ = require("lodash");
Now update the character Raghu like this:-
const newCharacterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.findById(
{ _id: req.drama._id, "characters._id": characterId },
"characters.$",
function(err, dramaDocWithSpecificCharacter) {
console.log(dramaDocWithSpecificCharacter);
// ↓↓↓ console would log ↓↓↓
// {
// "_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
// "characters" : [
// {
// "_id" : ObjectId("619fe1d53810a130207a409d"),
// "age" : "25",
// "name" : "Raghu",
// }
// ],
// }
const oldCharacterObj = dramaDocWithSpecificCharacter.characters[0];
const mergedCharacterObjs = _.merge(oldCharacterObj, newCharacterObj);
// _.merge() returns a deep merged object
console.log(mergedCharacterObjs);
// ↓↓↓ console would log ↓↓↓
// {
// _id: 619fe1d53810a130207a409d,
// age: "26",
// name: "Dr. Raghu",
// };
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": mergedCharacterObjs,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
Note: We can also use the native Object.assign() or … (spread operator) to merge objects but the downside of it is that it doesn’t merge nested objects which could cause issues if you later decide to add nested objects without making changes for deep merge.
You can pass your payload or request body like this if we provide _id it will prevent update to nested document
"characters" : [
{
"_id" : "619fdac5a03c8b10d0b8b13c",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a409d",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a502v",
"age" : "updated value",
"name" : "updated value",
}
],
It works for me for bulk update in array object

How to update with the multiples records in mongodb using node js

here i want to update with the multiple records so i'm using bulk operations and im trying to set the isenrolledcourse to true but its not updating
what i'm passing wrong here, thanks in advance
idsList = [ 60f6897c3b996238808ca5e2, 60f6f729012963152c7d0a0b ]
const Course = db.courses;
Course.bulkWrite([
{ updateMany :
{
"filter" : { _id : {$in : idsList }},
"update" : { $set: { "isenrolledcourse": true}},
"upsert" : true,
}
}
])
updateMany would do, you do not need bulkWrite, because you are doing single kind of action i.e. update. bulkWrite is used to carry on different kinds of actions together.
Course.updateMany(
{_id : {$in : idsList }},
{$set: { "isenrolledcourse": true}},
{upsert:true}
)

$pull is not working for array in mongoose (MongoDB)

In mongoDB (Through mongoose), I am trying to remove an element in array of a collection and using $pull operator for that, but it is not working.
Rooms collection in mongoDB
{
"_id":"sampleObjectId",
"users": ["email1#example.com", "email2#example.com"]
}
Backend code to remove a user (mongoose using Node.js)
Rooms.update(
{ _id: "sampleRoomId" },
{
$pull: {
users: "email1#exmple.com" }
},
}
)
Noting happens to collection on running this code, no change happens. code runs with no errors and in output it says "nModified":0 .
I have no idea how to remove a user from collection.
//actual code output from the mongo shell. This is working as expected. check if you given //correct value in object_id query parameter.
> db.rooms.find();
{ "_id" : "sampleObjectId", "users" : [ "email1#example.com", "email2#example.com" ] }
> db.rooms.update(
... {_id: "sampleObjectId"},
... {$pull:{users:"email1#example.com"}}
... );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.rooms.find();
{ "_id" : "sampleObjectId", "users" : [ "email2#example.com" ] }
> db.version();
4.2.6
>

How to compare array mongodb query

I am currently building a cart system on my mongodb ecommerce app. I need help on how to query and compare array.
here document of cart:
{
"_id" : ObjectId("5d0531e27c8fa1029017ea20"),
"user" : ObjectId("5d0371319315c715fc34b0b0"),
"active" : true,
"item" : [
{
"product" : ObjectId("5d013eb63a2bdd11a46c8dd3"),
"option" : [
{
"name" : "Ukuran",
"value" : "Biru"
}
],
"quantity" : 1
},
{
"product" : ObjectId("5d013eb63a2bdd11a46c8dd3"),
"option" : [
{
"name" : "Ukuran",
"value" : "Biru"
}
],
"quantity" : 1
}
],
"created_at" : ISODate("2019-06-15T17:58:58.762Z"),
"updated_at" : ISODate("2019-06-15T17:59:13.334Z"),
"__v" : 0
}
I want to compare object of item.option field, so my cart system is if cart on database have same object option i will add quantity, otherwise push new object to item.
so current I am not asking on how to implement my cart system, but I want to compare each item.option object
I've already tried this
const cart = await CartModel.find({
"item.option": option
})
and get error Error: Query filter must be an object, got an array
Solved by myself, after many experiment finally i combine $in and $elemMatch for compare each array of object
// this is will dynamic
const optionArray = [
{
"name": "Warna",
"value": "Biru"
},
{
"name": "Ukuran",
"value": "XL"
}
]
const compareOptionQuery = []
for (let i = 0; i < optionArray.length; i++) {
compareOptionQuery.push({
$elemMatch: {
...option[i]
}
})
}
const cart = await CartModel.aggregate([
{
$and: [
{
_id: cartId,
user: userId
},
{
'item.option': {
$in: [...compareOptionQuery]
}
}
]
}
])
The issue with your implementation is that in CartModel.find("item.option": option) your filter (first parameter) encounters an array instead of an object.
This is because you are trying to call for an object option from item, however option is as element of an array field item.
If you wish to access item from an array field, you must specify conditions on the elements in the array field item using {<array field>: {<operator1>: <value1>}} like so:
CartModel.find({
item: {$option: option}
})

$addToSet in a nested Array item

Considering the following
User collection & sample User document
{
"_id" : ObjectId("575c01f7b8e5999addeb598c"),
"username" : "test.1#gmail.com",
"password" : "<password>",
"firstName" : "Test,
"lastName" : "User"
}
I am trying to run an update request to add an entry in userData.eventData which is meant to be an array
In mongo script I can do
> db.Users.update({_id: ObjectId("575c01f7b8e5999addeb598c")}, {"$addToSet":{"userData.eventData":"My event"}} )
And I have the following result : userData is created as an Object and eventData as a nested Array
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.Users.find({_id: ObjectId("575c01f7b8e5999addeb598c")})
{ "_id" : ObjectId("575c01f7b8e5999addeb598c"), "username" : "test.1#gmail.com", "password" : "<password>", "firstName" : "Test", "lastName" : "User", "userData" : { "eventData" : [ "My event" ] } }
While running the same logic in mongo (using driver version 2.1.21)
// with a properly initialized db object
db.collection("Users").update({"_id" : ObjectId("575c01f7b8e5999addeb598c")}, {"$addToSet": { "userData.eventData": "My Event"}}, function(err, result) {
// do something
});
I receive the following response
result:Object
n:0
nModified:0
ok:1
And indeed the database entry is unchanged.
Is that the way it is meant to behave? I can easily fix this by creating the userData.eventData array but I found disturbing the fact that node's Mongo driver and mongo shell didn't behave the same on this
Thanks in advance for your help & advice
Edit 13/6/16
Mistake was on my side, I missed a 'new' before 'ObjectId(...' in node. With it, it behaves exactly the same as in mongo shell (i.e. 'userData' is created as an Object and it includes 'eventData' array)
No issue, then :)
Update, updates an already existing object in your document.
What you want is insert or use upset which creates a new document when no document matches the query criteria
db.collection.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
);
If you wanted to add an object to your array, you would need $push
// Insert a document in the capped collection
function push (db, collection, search, data, callback) {
db.collection(collection).update(
search, {
$push: data
}, function (err, result) {
console.log("New object pushed");
callback(err);
});
}

Resources