Delete item from MongoDB with Nodejs - node.js

I'm trying to delete a user id from all collections that have a reference to it. I'm bringing a user id across from the form and want to remove every reference to it in every business collection. I know the below query doesn't work but it shows my current approach.
db.collection('business', function (err, allBus){
allBus.update({}, { $pull: {followers: { userID } } } );
});
Here is my data, any ideas?
{
"_id" : ObjectId("55355d0ab063708c0b73809e"),
"address" : "Donegal",
"businessName" : "burkes shoes",
"email" : "info#burkes.ie",
"followers" : [
ObjectId("55300f5208224af428d1beaf"),
ObjectId("553129666252d2fc0a4634e4")
],
"gpsLat" : "55.1763595",
"gpsLong" : "-7.7923",
"homeDomain" : "www.burkes.ie",
"imgpath" : "\\images\\uploads\\57461Burkes_logo_1429560586607.jpg",
"password" : "1",
"role" : "business"
}

If userID is a string you will need to cast it first to ObjectID before using it in your query. Something like this should do the magic:
var ObjectID = require("mongodb").ObjectID,
userID = new ObjectId("55300f5208224af428d1beaf");
/*
if userID is a string then this will work
var userID = new ObjectId(userID);
*/
db.business.update(
{"followers": userID},
{
"$pull": { "followers": userID }
},
{ multi: true }
);
The query above will have better performance than an update without a query as it first filters documents that have in their followers array an element with the userID value and then updates the matched documents by pulling the ObjectID value from the array.

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

Create Rating if Not Rated Yet, Update User Rating if Rating Exists

I am creating a user experience where a user will be able to rate items from different vendors. My initial thought is for each User schema to have an array which stores all the items that the user has rated. The rated item would include the unique vendor item ID and a numerical rating value.
User Model
const userSchema = new mongoose.Schema({
...
userType: String,
ratedItems: Array,
...
});
Controller
exports.postUpdateRatedItem = (req, res, next) => {
User.findById(req.user.id, (err, user) => {
if (err) { return next(err); }
user.update(
{ $push: {ratedItems : {
vendorItem : req.body.itemID,
rating : req.body.rating
}}},
function (err) {
res.send(200);
});
});
}
Current Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "3" },
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" }
]
}
This approach works great in adding new object to the array, but only adds and does not update. Instead, every time a user updates a rating, a new object is added to the array. What approach would allow to check for the unique vendorItem id? How do I go about checking the user rated items? If found, update the rating value, if not found, push to the array.
Thank you in advance, still learning MongoDB/Mongoose.
Edit
Below is what I expect the outcome. For each object in the array, the 'rating' is updated when the user changes the rating. The ratedItems array will eventually have many many vendorItem with unique IDs and ratings.
Expected Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" },
// additional rated items all with unique IDs
{ "vendorItem" : "5c9bcc14d5161c38a4581e28", "rating" : "2" },
{ "vendorItem" : "5c9407d143cd0f20d758acdb", "rating" : "11" }
]
}
It sounds like you are looking for "upsert" functionality. The Mongoose model API provides findByIdAndUpdate and other similar methods for this.
Make sure you set the new and upsert options to true. This will create the object if it doesn't exist and return the modified document if it is updated.
Your use case would look something like this:
const update = {
$push: {
ratedItems: {
vendorItem: req.body.itemID,
rating: req.body.rating
}
}
};
const options = {'new': true, upsert: true};
User.findByIdAndUpdate(req.user.id, update, options, function(err, user) {
// ...
});

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

mongodb update upsert creating new records

I have a record like this
{
"_id" : ObjectId("57025c35e31f7274c1195c26"),
"token" : "0ffed58b-57ed-4a2a-bb09-97c64f0f2bd2",
"key" : "silly key",
"user" : ObjectId("55e3f8fcc78dc516096dc3e2"),
"twlprofile" : "Test Blog"
}
It is the only one in the collection.
I am trying to update and need the update to upsert if it can't find anything, so I have this code
var id = "55e3f8fcc78dc516096dc3e2";
var tokendoc = {
"token": "05c50aa0-5a8c-4fad-b27f-db64a0355b4f",
"key": "silly key",
"user": "55e3f8fcc78dc516096dc3e2",
"twlprofile": "Test Blog"
}
var tokenrecord = tokensCollection.update(
{ where: { user: id } },
tokendoc,
{ upsert: true },
function(err,tokenres){
//this part not important right now
});
which if I run it ends up inserting another document with the same user property. I have also tried using BSON.ObjectID with the id variable and it doesn't help.
How do I get this to work like a real upsert?

Resources