Updating a nested subdocument's array - Mongoose - node.js

Currently I have the following structure for one of my documents
Company: {
Buildings: [{
Users: [ { _id: ObjectID, name: String, number: String } ]
}]
}
I'm trying to update the user's name and number and currently have tested and verified the following query in mongo:
db.companies.update(
{ "_id": companyID, "buildings._id": buildingID, "buildings.users._id": userID }
,
{ $set: { "buildings.$.users.0.name": "A new name for the user" } }
);
This query updates correctly however when I run the same exact query using mongoose
Company.findOneAndUpdate(
{ _id: companyID, "buildings._id": buildingID, "buildings.users._id": userID }
,
{ $set: { "buildings.$.users.0.name": newName }})
I get no error but the update is not performed.
Is the updating of a deep nested array not available on Mongoose?

Answer was found in an alternative answer to this question:
Answer: https://stackoverflow.com/a/28952991/1327815

Related

Mongoose FindOne returns all children

Is it ok that findOne returns all subdocument in array? I mean isn't it take a long time to execute that, or is there way to get document with only one subDoc ?
my code :
userModel
.find({
{_id: userId,},
{ contacts: { $elemMatch: { userId: contactId } } }
).select('name contacts');
I cant use userModel.contacts.id() cause I don't have id
How can I add a conversation id to this found document
How can I do
const userData = userModel.findById(userId)
.where('contacts')
.elemMatch({ userId: contactId })
and get the result with only one contact (in order not to search for a contact from the entire array)
and simply change relation like
userData.contacts[0].conversation = conversationId;
await userData.save()
How about this:
db.collection.update({
_id: 1
},
{
$set: {
"contacts.$[x].conversation": 3
}
},
{
arrayFilters: [
{
"x.userId": "John"
}
]
})
playground

MongoDB - update data in array of objects within object

I have a document in mongoDB structured like that
_id: ObjectId("generatedByMongo"),
name: {
required: true,
type: String,
trim: true
},
last: {
required: true,
type: String,
trim: true
},
grades: [{
grade: {
_id: ObjectId(""),
grade: Number,
date: date
}
}]
And to server I send array of objects containing 3 fields
[
{studentId}, {gradeId}, {newGrade}
]
What I'm trying to accomplish is I want to find in within that user collection grade with given gradeId and update it's value to newGrade. As far as I tried to do that I have done this
router.patch('/students/updateGrade',async(req,res) => {
const studentId = req.body.updateGradeArray[0].studentId;
const gradeId = req.body.updateGradeArray[0].gradeId;
const newGrade = req.body.updateGradeArray[0].newGrade;
try {
const student = await Student.find({_id: studentId})
.select({'grades': {$elemMatch: {_id: gradeId}}});
} catch(e) {
console.log(e);
}
}
);
If you intend to update just grade.grade(the number value), try this:
Student.updateOne(
// Find a document with _id matching the studentId
{ "_id": studentId },
// Update the student grade
{ $set: { "grades.$[selectedGrade].grade": newGrade } },
{ arrayFilters: [{ "selectedGrade._id": gradeId }] },
)
Why this should work:
Since you are trying to update a student document, you should be using one of MongoDB update methods not find. In the query above, I'm using the updateOne method. Inside the updateOne, I am using a combination of $set and $[identifier] update operators to update the student grade.
I hope this helps✌🏾

MongoDB Searching if element exist in array

So I have something like Survey Schema (I am using mongoose).
In this Schema, for each voting option, I have votes[] array that contains ObjectIds of Users.
Now I want to check if User can vote again, or if he already voted?
The simple solution is iterating thru votes with .indexOf() and checking if id exists. Now this is a blocking way for Node JS, since this operation is sync.
Is there a way to do this with Mongo aggregate or query? So for each voting option I would get additional field like:
didIVoted: true
My Schema looks like this:
const SurveySchema = new Schema({
title: {
type: String
},
options: [{
value: String,
votes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
}]
}, { timestamps: true })
You can use $addFields and $map to overwrite existing options field. To check if userId exists in votes array you can use $indexOfArray
SurveySchema.aggregate([
{
$addFields: {
options: {
$map: {
input: "$options",
in: {
value: "$$this.value",
votes: "$$this.votes",
didIVote: { $ne: [ { $indexOfArray: [ "$$this.votes", userId ] }, -1 ] }
}
}
}
}
}
])

Mongoose how to auto add _id to objects in array within collection item?

i have a mongo collection that looks like this:
{
name: string
_id: (auto set)
items: array[
name: string
url: string
items: array[
{
name: string,
url: string,
items: []
}
]
]
}
I'm using findByIdAndUpdate (with mongoose) to add an item into the items array:
Menu.findByIdAndUpdate(
req.body.parentid,
{
$push: {
items: {
name: req.body.item.name,
url: req.body.item.url,
items: []
}
}
},
{
safe: true,
upsert: true,
new: true
},
function(err, model) {
if (err !== null) {
console.log(err);
}
}
);
This works fine, but it does not add an _id to each object inserted into the items array. And i really need an id for each one.
I'm guessing it comes from the method used, findByIdAndUpdate as it looks more like an update rather than an insert. If my thinking is correct.
Using mongodb 3.2.10 and mongoose 4.7.6.
Any help would be really appreciated.
Thanks.
EDIT: the _id: (auto set) is not real, it's being automatically added via mongo. But just at the top level objects.
Found the solution in this thread: mongoDB : Creating An ObjectId For Each New Child Added To The Array Field
basically, added
var ObjectID = require('mongodb').ObjectID;
and then forcing the creation:
$push: {
items: {
_id: new ObjectID(),
name: req.body.item.name,
url: req.body.item.url,
items: []
}
}
You dont need to sepcify _id: (auto set) in mongoose schema it will automatically add unique _id with each document.
if you don't define _id in Schema, mongoose automatically add a _id to array item.
for example:
const countrySchema = new Schema({
name: {
type: String
},
cities: [
{
// don't define _id here.
name: String
}
],
});
now when you insert a row, the result is something like this:
{name : 'Iran', cities : [{_id : 6202902b45f0d858ac141537,name :
'Tabriz'}]}

Mongoose updating sub document array's individual element(document)

Schema of group and member are as below:
var group=new Schema({
group_id:Number,
group_name:String,
members:[member]
});
var member=new Schema({
member_id:number,
name:String,
});
Sample document after inserting some record in group collection
[{
_id:55ff7fca8d3f6607114dc57d
group_id:1001,
group_name:"tango mike",
members:[
{
_id:44ff7fca8d3f6607114dc21c
member_id:2001,
member_name:"Bob martin" ,
address:String,
sex:String
},
{
_id:22ff7fca8d3f6607114dc22d
member_id:2002,
member_name:"Marry",
address:String,
sex:String
},
{
_id:44ff7fca8d3f6607114dc23e
member_id:2003,
member_name:"Alice" ,
address:String,
sex:String
}
]
}]
My problem:
I am trying to update record of individual group member(element of subdocument members). While updating I have follwing data group: _id, group_id, members:_id and newdata. I am trying like this; but it is not working
var newData={
member_name:"Alice goda" ,
address:"xyz",
sex:"F"
}
groupModel.findOne({"_id":"55fdbaa7457aa1b9bd7f7cf7","group_id":1001},'members -_id',function(err,groupMembers){
if(err)
{
res.json({
"isError":true,
"error":{
"status":1042,
"message":err
}
});
}
else
{
var mem=groupMembers.id("44ff7fca8d3f6607114dc23e");
mem.member_name=newData.member_name;
mem.address=newData.address;
mem.sex=newData.sex;
mem.save(function(err,data){
if(!err)
//sucessfull updated
});
res.json(groupDetails);
}
});
As I understand from your question details, you would like to update one object from the members array, in accordance with the criteria that you specify.
Thus, in order to accurately run the update query for your use case, you could run the following update operation against your collection:
db.collection.update({ _id: "55ff7fca8d3f6607114dc57d",
group_id:1001,
members: {
$elemMatch: { _id: "44ff7fca8d3f6607114dc23e" }
}
},
{ $set: {
"members.$.member_name": "Alice goda",
"members.$.address": "xyz",
"members.$.sex": "F"
}});
Still, be aware that the $ positional operator only updates the first array item that matches your query.
Unfortunately, there is no possibility of updating all the array elements that match your criteria in a single operation. As you can see on MongoDB Jira, the aforementioned feature is one of the most requested functionality, but it has not yet been directly implemented in MongoDB.

Resources