I have a Node/Express API using Mongoose, where I am trying to update subdocuments within multiple documents.
Essentially I have a user with a profile that has multiple phone numbers that they can share with another contact.
Each contact has a profile.contacts[] section, that has profile.contacts.phones[] section.
When the main user sharing his/her phone number with other users changes his/her number, I want to update the profile.contacts.phones[] section in all documents, where the _id of that phone number matches.
Here is my code:
Profile.update({'contacts.phones._id':req.body._id}, {
Profile.contacts.phones.phone_number:req,body.phone_number,
Profile.contacts.phones.phone_type:req.body.phone_type
}, {multi:true}, function(err, result){
if(err)
res.send(err)
res.json(result);
})
Here is a sample "Profile" document:
{
"_id" : ObjectId("59c09dca981de33d180df943"),
"last_name" : "Sam",
"first_name" : "Sam",
"owner_id" : "59c09dca981de33d180df940",
"contacts" : [
{
"first_name" : "Joe",
"last_name" : "Public",
"_id" : ObjectId("59c09dca981de33d180df944"),
"phones" : [
{
"phone_number" : "2067155803",
"phone_type" : "home",
"_id" : ObjectId("59bca0b55481370985cac29a")
}
]
}
]
"__v" : 0
}
Based on what I see in the documentation, this should work...
Thanks for any insight!!
I think you would need to use the positional operator to update an object in an array $ that matched the query.
However, this cannot be used for nested arrays:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value.
https://docs.mongodb.com/manual/reference/operator/update/positional/
You might have to consider restructuring your documents.
Related
The structure of my document looks like this in MongoDB :-
{
_id : "7464bbuiecdhbjdje"
client : "MJMK"
users : [
{_id : "1234" , name : "first user"}
]
}
I would like to remove the whole document for matching users._id. In this case, for a user with _id 1234,the whole document needs to be removed. I have been unable to find any efficient function that does this in Node using mongoose. Thanks for your help.
You want to use deleteMany.
This will delete all documents containing the matches query, in this case a user with matching _id in the users array. Our query will be utilizing Mongo's dot notation to access the array like so:
Model.deleteMany({ 'users._id': "1234" })
From the shell following works. Isn't it possible to do the same in node?
db.collectionname.find({ "users": { $elemMatch: { "_id": "1234" } } })
db.collectionname.remove({ "users": { $elemMatch: { "_id": "1234" } } })
Try this:
db.collectionName.remove({"users._id":{_id:"1234"}})
I have Review schema having likeBy array contains the id of the users who likes the review...
I have to query it like
case 1: If id exist the pull it.
case 2: If id does not exist then push it.
Is it possible to do in a single query???
Review Schema
{
"_id" : ObjectId("5aa0f4aeb5cbb2313276cccc"),
"__v" : 0,
"likeBy" : [ ]
}
This is how I would have done it
db.people.findAndModify({
query: { likedBy : {idOftheUserinLikedArray: "anyId" }},
$push: { likedBy : {idOftheUserinLikedArray: "anyId" } },
new : true
})
I am not sure if that's what you want. Let me know if that works for you.
What this will do is, if it finds that Review object against the property your are searching for in likedBy array property of the documents it will return you the updated object so you can check the likedBy array if the value is inserted or not.
May be helpful
And don't forget to give it a look
var schema = new Schema({
_id: Schema.ObjectId,
email: {type: String, required: true}
});
Previously email field was unique (with unique constraint)
Now I removed unique constraint, then also it's giving unique validation error. I have restarted mongo then also it's throwing error.
Any Idea?
When you remove the unique constraint in the schema definition, you were supposed to manually remove it from the database as well. You can do this either in mongo shell by using the dropIndex() method or in your application using the native node.js driver's dropIndex() method of the collection.
You may need to confirm this first by checking the indexes currently in the database; you will most probably find the email unique index created when you populated the collection after defining the schema in Mongoose.
You can issue the command in mongo shell, supposing you have a collection named users in your database named test:
> db.users.getIndexes()
which will show the current indexes, you may see in your console an array like the following:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.users"
},
{
"v" : 1,
"unique" : true,
"key" : {
"email" : 1
},
"name" : "email_1",
"ns" : "test.users",
"background" : true,
"safe" : null
}
]
For a solution in your application code, supposing you have a Mongoose model called User that has the defined schema above, you can call the getIndexes() native node.js driver's collection method:
User.collection.getIndexes(function (err, results) {
// Handle errors
});
In mongo shell, you can then go ahead and remove the email index with the dropIndex() method :
> db.users.dropIndex("email_1")
In your application code, you can also issue the command via the Mongoose model for the collection, and call the native node.js driver's dropIndex() method of the collection:
User.collection.dropIndex("email_1", function (err, results) {
// Handle errors
});
You can also follow the below steps.
Delete users collection from database.
Save user model(Schema) file and test.
I am writing code to manage a collection of merchants through a website using the plain MongoDB driver for NodeJS. My trouble occurs when the merchant is trying to confirm their account. It is expected for the code to use $unset to erase the value from database therefore activating the merchant's account. Instead, the query returns true, but the confirm field remains untouched. Here is an example of one of the problematic merchants in the database as printed by db.merchants.find({}) (with added line breaks for better readability):
{ "email" : "test#test.com", "password" : "<hashed password is here>",
"salt" : "<salt is here>", "access" : "merchant",
"created" : 1444000000000, "confirm" : "bKEQD0aiV8aXIQPY4CUxCm7KGSZ2pFJM",
"name" : "Test user",
"contact" : { "name" : "Test User",
"address" : "123 Cedar Ln N",
"city" : "Some City", "state" : "AA",
"zip" : "00000", "country" : "USA",
"phone" : "5555555555" },
"_id" : ObjectId("56133111d30ce5d4736323cb") }
AssumEffectively, this is the JS used in order to insert the item to the database:
db.collection("merchants").insert({
name: "Test user",
email: "test#test.com",
.... more fields....,
confirm: "bKEQD0aiV8aXIQPY4CUxCm7KGSZ2pFJM"
}, someCallback);
...and here is how I attempt to unset the "confirm" field:
db.collection("merchants").findOne({email: "test#test.com"}, function(err, user) {
if(err) return handleError();
if(!user) return sendFailureMessageToClient();
if(client.confirm == user.confirm) {
db.collection("merchants").update({_id: user.id},
{$unset: {confirm: true}}, someCallback);
}
}
I am certain that the update command above is actually being executed.
One of the things I think is interesting about the data above is that the _id field is at the end... is that supposed to normally happen? Would that mess up the $unset for the update command? If that is not the issue, then what is?
Thanks in advance for help.
EDIT: I showed why I am using a separate findOne request and then update.
_id is a special BSON object of ObjectId. Using the id string is not enough. You probably want to use _id as well:
db.collection("merchants").update({_id: ObjectId(user._id)},
You may also want to use findAndModify instead.
You missed the underscore in user.id, should be user._id. Right now, it's trying to fetch an user with undefined _id.
If it's not that, you should do your update on the email field, it'll work just fine (assuming it's unique).
The _id at the end of the document doesn't matter, there is no strict order in JSON/BSON fields. The only moment an order is maintened is inside arrays.
I am using a wrapper called mongoskin to access mongoDB. mongoskin is a simple wrapper around mongoDB javascript api.
But when I write to mongoDB, sometimes _id is converted to ObjectID, sometime is it not. The different behavior causes many problem when I have to compare _id. For example:
The following documents in company collection, "creator" is not converted to ObjectID, but item in "clients" is converted to ObjectID automatically.
> db.company.find()
{ "_id" : ObjectId("53d4b452f5b25900005cb998"), "name" : "Default Company Co.", "clients" : [ ObjectId("53d4b452f5b25900005cb999"), ObjectId("53d4b452f5b25900005cb99a") ] }
{ "_id" : ObjectId("53d4b452f5b25900005cb999"), "name" : "client company for 777 - updated", "creator" : "53d4b452f5b25900005cb998", "ssn" : "12-123-1234" }
This is the nodejs code I used to assign _id for "creator"
clientCompany.creator = req.session.user.company_id;
This is the nodejs code I used to assign _id for "clients"
var updateObj = {$addToSet: {clients:resultClient._id} };
// update creator company.clients
creatorCompany.update(updateObj, function(err, result) { ...}
When I console.log "req.session.user.company_id" and "resultClient._id", they both looks like a string type. How come one end up as ObjectID in MongoDB? If there is an auto conversion, how do I make this behavior consistent?
Thanks!
I'm guessing resultClient is the result of a query and req.session.user.company_id a string from your web application? In that case you need to create an ObjectId from the string:
clientCompany.creator = mongoskin.ObjectID(req.session.user.company_id);