Save multiples Objects At Once Mongo(Many To Many Mongoose) - node.js

this is my scene:
I have 2 collections(tables):Projects and students(the relation is many to many).
My models are:
Project:
var ProjectSchema = new Schema({
name: {
type: String
},
description: {
type: String
},
user : {
type : Mongoose.Schema.Types.ObjectId,
ref : "User"
}
});
Mongoose.model('Project', ProjectSchema);
User:
var Userchema = new Schema({
name: {
type: String
},
surname: {
type: String
},
project : {
type : Mongoose.Schema.Types.ObjectId,
ref : "Project"
}
});
Mongoose.model('User', UserSchema);
In my view I have 2 inputs where receive name and description of project, then have multiselect where choose 1 or multiples users for this project. When I Click OK should:
Create project, and add to user all users selected as objects in BBDD.
Update user, with project asociation.
I try the next, but only funcion if I choose 1 user:
exports.addUserProject = function(req, res) {
var project=new svmp.Project();
project.name=req.body.name;
project.description=req.body.description;
project.user=req.body.user[0]._id;
project.save(function(err,project){
if (err) {
return res.send(400, {
message : getErrorMessage(err)
});
} else {
res.jsonp(project);
}
})
};
The result in my BBDD is the next:
{ "_id" : ObjectId("57a200a38fae140913ac5413"), "user" : ObjectId("578f41ddb0641d961416c3f5"), "name" : "Project1", "Description" : "Project1 Desc","__v" : 0 }
Thanks for your help

First in your schema definition, since the relationship is many-to-many, you should change the ref to an array of the referenced objects. For example change the user property of projectSchema to an array of object ids like so,
var ProjectSchema = new Schema({
name: {
type: String
},
description: {
type: String
},
user : [{
type : Mongoose.Schema.Types.ObjectId,
ref : "User"
}]
});
Do the same for the project property of the userSchema.
Secondly, on this line project.user=req.body.user[0]._id; you are setting the the _id of only the first selected user as the user while ignoring every other selected users. This is why your code only works for one user. Instead, I will suggest you use a simple loop to push all selected users' _id to the project's user property. You can use a forEach loop as given below.
var selectedUsers = req.body.user;
selectedUsers.forEach(function(u){
project.user.push(u._id)
})
You can also do this with a simple for loop if you wish.
I believe the suggestions above should fix the issues you described.

Related

Insert a document containing a field from another collection

I have these 2 models:
Role model (Role.js)
const mongoose = require ("mongoose");
const roleSchema = mongoose.Schema({
role: {
type : Number,
required : true
},
description : {
type : String,
required : true
}
});
module.exports = mongoose.model("Role", roleSchema);
I populate the Role collection:
db.Roles.insertMany([{_id: 1, description: "Read only"}, { _id: 2, description: "Read, write" }, { _id: 3, description: "Read, write, delete" }])
Admin model (Admin.js)
const mongoose = require ("mongoose");
const Role = require("./Role");
const adminSchema = mongoose.Schema({
firstName : {
type : String,
required : true
},
lastName : {
type : String,
required : true
},
email : {
type : String,
required : true
},
dateCreate : {
type : Date,
default : Date.now
},
role : {
type : mongoose.Schema.Types.ObjectId,
ref : "Role"
}
})
module.exports = mongoose.model("Admin", adminSchema);
As you can see, the Admin model contains a field that is a reference to the Role _id in Role.js
Now I'm trying to insert a document in the Admin collection.
mongoose.connect("mongodb://127.0.0.1:27017/Test")
insertAdmin()
async function insertAdmin() {
try {
const role = Role.findOne({_id: 3});
console.log("Role: " + role)
const admin = await Admin.create({
firstName : "Kate",
lastName : "Eagan",
email : "vvv#dd.com",
role : role._id
})
console.log("Admin added");
} catch (err) {
console.log(err.message)
console.log("Admin NOT added");
}
//console.log(admin);
}
All goes well, the admin is added, but when I do (using mongosh) :
db.admins.find()
all the admins have no role field, it's just not showing. I guess I'm not inserting it correctly in my insertAdmin() function?
I re-created your work here GitHub
it just worked perfectly, seems that the problem is that when you try to assign the founded id of the role to the new admin that you want to create, it comes with **ObjectId**(xxxxxxxxxx) prefix and in that way it don't work as far as I know, you can check, another thing, can anyone knows the difference between
mongoose.Types.ObjectId
and
mongoose.Schema.Types.ObjectId
and one more thing to add is that if the role is required in the admin model you should add the option, so that you can check it with your existing code.

Push new object to nested mongodb document where particular condition matches within a sub document

I am new to Nodejs and Mongodb. I am building Message Chat Functionality in my project. I have done with Message Schema.
This is my User Model Schema:
var UserSchema = new mongoose.Schema({
name : {
type : String
},
email : {
type : String
},
password : {
type : String
},
chats : [{
chatId : String,
messages : [{
type : mongoose.Schema.Types.ObjectId,
ref : 'Message'
}]
}]
});
I want to add all the new message objects to the subdocument where chatId is matched. So,that I can easily use it on front end (which is Angular) with structured
I have tried this:
var msg = new Message({
senderId : req.user._id,
to : toUser,
from : user,
message : req.body.msg,
datetime : new Date()
})
msg.save();
const chatFr = await User.findOneAndUpdate(
{ 'chats.chatId' : req.user._id },
{ $push : { 'chats' : msg }}
)
It is creating a separate object.I don't know how to use 'where' in sub-documents here. I am coming from Sql background. Please help how to execute conditional queries in sub documents in mongodb/mongoose
Try this:
await User.findOneAndUpdate(
{ 'chats.chatId' : req.user._id },
{ $addToSet: { 'chats.$.messages' : msg }}}
)
You need to insert using the $ position operator. You can also utilize $addToSet.

mongoose find by ObjectId

I'm defining a mongoose schema like this
var accountPostSchema = new mongoose.Schema({
account: {
id: { type: mongoose.Schema.Types.ObjectId, ref: 'Account' }
},
post: {
id: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }
}
});
app.db.model('AccountPost', accountPostSchema);
When a user(account holder) create a post, I save the post in a Post schema and get the 'postId'. Then I save the 'postId' and the 'accountId'
in the above accountPostSchema like this
var fieldsToSet = {
post: {
id: postId
},
account: {
id: accountId
}
};
db.models.AccountPost.create(fieldsToSet, function(err, accountPost) {
if (err) {
// handle error
}
// handle success
});
After entering few postId's and accountId's, I see the following results in the mongo shell
> db.accountposts.find({})
{ "_id" : ObjectId("5835096d63efc04da96eb71e"), "post" : { "id" : ObjectId("5835096d63efc04da96eb71d") }, "account" : { "id" : ObjectId("5833c920c868d7264111da69") }, "__v" : 0 }
{ "_id" : ObjectId("583509e12052c7a2a93c4027"), "post" : { "id" : ObjectId("583509e12052c7a2a93c4026") }, "account" : { "id" : ObjectId("5833c920c868d7264111da69") }, "__v" : 0 }
Now how do I find all the matching 'Posts' given an accountId? (not the postId's)
For example if I the accountId is 583509e12052c7a2a93c4026, I need to find Posts with Post._id=5835096d63efc04da96eb71d and Post._id=583509e12052c7a2a93c4026
What is the query I should run to get the matching Posts?
I think, you should follow this way to get all the posts associated with particular accountid.
db.accountposts.find({'account.id' : accountId})
.populate('post.id')
.exec();
First, I would suggest changing your Schema to the following
var accountPostSchema = new mongoose.Schema({
account: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Account'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
This actually makes more sense, especially when you try to populate the subdocuments. Actually, I would say this Schema is useless. Why don't you define your Post schema like the following?
var PostSchema = new mongoose.Schema({
poster: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Account'
},
message: String
});
If you use the latter code, you could execute the following query to get all posts by a particular user:
db.posts.find({poster: accountId}, function(dbErr, userPosts) {
if(dbErr) {
// Handle the error.
}
// Do something with the posts by accountId in the array userPosts.
});
The advantages of removing the id field from poster becomes clear once you try to populate poster. If you defined poster as an object with the field id and try to populate it, you will need to access data about the poster as such:
posterName = retrievedPost.poster.id.name;
Alternatively, by just making the poster field an ObjectId directly, you can access the populated user more directly:
posterName = retrievedPost.poster.name;

mongodb mongoose nodejs express4 why insert a _id in Object Array field automatically?

It might be conceptual question about _id in mongodb.
I understand mongodb will insert a _id field automatically if you don't set key field in document.In my case, I defined a field as Object Array, I don't know why it always create a _id in each Object in Array of this field.
I do appreciate if someone could clarify it for me.
Mongoose Model Scheme definition:
module.exports = mongoose.model("Application", {
Name: String,
Description: String,
Dependency: [
{
App_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Application'
},
Priority: Number
}
]
});
This is an Update operation, request data is:
{ _id: '571953e33f33c919d03381b5',
Name: 'A Test Utility (cmd)',
Description: 'A Test Utility (cmd)'
Dependency:
[ { App_id: '571953e33f33c919d03381b6', Priority: true },
{ App_id: '571953e33f33c919d03383da', Priority: 0 } ]
}
I use this code to update it
var id = req.body._id;
Application.findOneAndUpdate({ _id: id }, req.body, function (err, app) {
if (err)
res.send(err);
res.json(app);
});
The update is successful.But the document in mongodb is:
{
"_id" : ObjectId("571953e33f33c919d03381b5"),
"Name" : "A Test Utility (cmd)",
"Description" : "A Test Utility (cmd)",
"Dependency" : [
{
"Priority" : 1,
"App_id" : ObjectId("571953e33f33c919d03381b6"),
"_id" : ObjectId("571a7f552985372426509acb")
},
{
"Priority" : 0,
"App_id" : ObjectId("571953e33f33c919d03383da"),
"_id" : ObjectId("571a7f552985372426509aca")
}
]
}
I just don't understand how come the _id in the "Dependency" Array?
Thanks.
When you use [{..}] that means inside it act as a sub schema and you know that MongoDB insert a _id field automatically if you don't set key field in document. So you need to force to insert document without _id field.
Need use {_id:false} for your Dependency array schema to insert without _id
var ApplicationSchema = new mongoose.Schema({
Name: String,
Description: String,
Dependency: [
{
App_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Application'
},
Priority: Number,
_id: false
}
]
});
module.exports = mongoose.model("Application", ApplicationSchema);

Mongoose fail to set ref -Schema.Types.ObjectId- to other document

I'm trying to save a document with mongoose according to the doc http://mongoosejs.com/docs/populate.html
First I call parent.save and inside the callback of parent.save I use child.save.
But when I check parent.childs I can see that no child has been added.
The parent schema is Home :
var HomeSchema = new Schema({
password : String,
draft : { type: Boolean, default: true },
edited : { type: Boolean, default: false },
guests : [{type : Schema.Types.ObjectId, ref : 'Guest'}],
_event : {type : Schema.Types.ObjectId, ref : 'Event'}
});
the child schema is Guest :
var GuestSchema = new Schema({
_home : {type : Schema.Types.ObjectId, ref : 'Home'},
firstname : String,
lastname : String,
coming : { type: String, default: 'dnk-coming' },
phone : String,
email : String,
address : String,
edited : { type: Boolean, default: false },
draft : { type: Boolean, default: true }
});
To avoid any misunderstanding, you have to know that this two Schema are included in my user schema :
var userSchema = mongoose.Schema({
homes:[homeSchema.HomeSchema],
events:[eventSchema.EventSchema],
guests:[eventSchema.guestSchema],
});
Now you should have all the required informations to completly understand the execution :
UserModel.findById(user._id, function(err, userFound) {
if (!err) {
/* cleaning draft*/
userFound.homes = that.clean(userFound.homes);
/* setting draft */
var HomeModel = mongoose.model("Home");
var homeModel = new HomeModel();
homeModel.draft = true;
if (userFound.homes === null) {
userFound.homes = [];
}
homeModel.save(function(err) {
if (!err) {
var GuestModel = mongoose.model("Guest");
var guestModel = new GuestModel();
guestModel._home = homeModel._id;
guestModel.save(function(err) {
if (!err) {
// #ma08 : According to the doc this line should'nt be required
//homeModel.guests.push(guestModel._id); so when I use this obviously the id is correctly set but when I try a populate after saving the populate do not work
userFound.homes.push(homeModel);
userFound.guests.push(guestModel);
userFound.save(function(err) {
if (!err) {
successCallback();
}
else {
errorCallback();
}
});
}
});
}
});
This treatement doesn't result in any error. But it doesn't work as intended when I stringify the user.guests I get :
guests:
[ { coming: 'dnk-coming',
edited: false,
draft: true,
_id: 53dcda201fc247c736d87a95,
_home: 53dce0f42d5c1a013da0ca71,
__v: 0 }]
wich is absolutly fine I get the _home id etc...
Then I stringify the user.homes and I get :
homes:
[ { draft: true,
edited: false,
guests: [],
_id: 53dce0f42d5c1a013da0ca71,
__v: 0 } ]
According to the doc guests should be setted, but it's not <-- this is my issue. Please help me to figure out what I'm doing wrong. I could set it manualy but according to the doc it's not suppose to work this way I think.
guestModel.save(function(err) {...
this is wrong because you are embedding the guests in the userSchema.
So skip the guestModel.save and just push the guestModel in userFound
An embedded document can never a reference. You can't point to it without obtaining the parent document. So you can't do both embedding and keeping a ref to the embedded document. You should choose between either embedding or adding a ref.
My suggestion would be to design your schemas like this. Store guests in a separate collection. Store the ref to guest in user and home schemas. If you want to store some relationship data you can store along with the ref like [{guestId:{type:Schema.Types.ObjectId,ref:'Guest'},field1:{type:...},field2:{...}..] and if you just want the ref [{type:Schema.Types.ObjectId,ref:'Guest'}]

Resources