One to Many Relationship MongoDb Using Mongoose - node.js

I am creating a relationship between a User and Address. User can have multiple Address. Here is my schema:
userSchema.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const Address = require('./addressSchema')
let userSchema = mongoose.Schema({
name :String,
age : Number,
cohort :String,
addresses : [{type : Schema.Types.ObjectId, ref : 'Address'}]
})
let User = mongoose.model('User',userSchema)
module.exports = User
addressSchema.js
const mongoose = require('mongoose')
let addressSchema = mongoose.Schema({
city : String,
state :String
})
let Address = mongoose.model('Address',addressSchema)
module.exports = Address
Adding New User and Address (app.js)
let user = new User({name : 'Mary', age : 36, cohort : 2019})
let address = new Address({ city : 'Houston', state : 'TX'})
user.addresses.push(address)
user.save(function(error,newUser){
console.log(newUser)
})
Fetching the users and their addresses
// fetch all users
User.find(function(error,users){
users.forEach((user) => {
console.log('addresses')
user.addresses.forEach((address) => {
console.log(address.city) // prints undefined
})
})
})
Any ideas what I am doing wrong?

It depends on what you are trying to accomplish: storing the addresses as embedded documents in user OR storing references to address documents in user. The example code above has blended both approaches which, as you have noticed, does not work.
Embedded:
Combine the schemas:
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const addressSchema = Schema({
city : String,
state :String
})
const userSchema = Schema({
name :String,
age : Number,
cohort :String,
addresses : [addressSchema]
});
const User = mongoose.model('User',userSchema);
And the rest of the code can remain the same.
References:
The existing schema and model declarations remain the same.
Then when the data is stored, the address also needs to be stored:
const user = new User({name : 'Mary', age : 36, cohort : 2019})
const address = new Address({ city : 'Houston', state : 'TX'})
user.addresses.push(address)
Promise.all([
user.save(),
address.save()
]).then(([newUser, newAddress]) => {
console.log({newUser, newAddress});
});
And finally when fetching the document, if the address is meant to be included then it needs to be populated:
User.find().populate('addresses').exec().then((users) => {
users.forEach((user) => {
user.addresses.forEach((address) => {
console.log({user, address});
});
});
});

Related

How do I update the existing documents' schema?

I'm using mongoose to do some MongoDB operations.
At the beginning the category was number,
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const sampleSchema = new Schema({
category: {
type: Number,
}
})
module.exports = mongoose.model("SampleSchema", sampleSchema);
Now the category changed to String, So I changed the model like this
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const sampleSchema = new Schema({
category: {
type: String,
}
})
module.exports = mongoose.model("SampleSchema", sampleSchema);
The problem is, I have already inserted 200 records into this collection. Is there any way to update the category value with a string and change its type to string?
Please get All data by query and update it one by one in loop.
Like:
db.tableName.find( { 'status' : { $type : 1 } } ).forEach( function (val) {
val.status = new String(val.status);
db.tableName.save(val);
});
I changed the category to mixed, that's working fine with numbers and string.
Thanks for the help #prasad_

How to save same document in MongoDB?

My Schema
const mongoose = require('mongoose');
const test_schema = new mongoose.Schema({
Name: {
type: String
}
});
const chatting = mongoose.model('chat', test_schema);
module.exports = chatting;
Getting model of above give the schema
const chat = require('./models/chatting.js');
Save Variables
const one = new chat({
Name : "John"
})
const two = new chat({
Name : "John"
})
Now Saving
await chat.insertMany([one, two])
but i got duplicate name key error
You provided a wrong schema to us. (test_schema != chatting_schema). check your chatting schema and see if it is structured somewhat like this:
const chatting_schema = new mongoose.Schema({
Name: {
type: String
unique: true
}
});
if so, remove the unique property.
also, when you already created a document you can just use the .save() function like
await one.save();

MongoDB updating child document does not affect the original document and vise versa

I'm using mongoose and have these schema:
var mongoose = require('mongoose');
mongoose.connect(/*...*/)
const usersSchema = new Schema({
email: String,
name: String
})
const ordersSchema = new Schema({
user: usersSchema, // <--- ref to usersSchema
createdOn: Date,
// ...
})
const UsersModel = mongoose.model('Users', usersSchema );
const OrdersModel = mongoose.model('Orders', ordersSchema );
The problem is when I insert an entity into Users collection and put that entity reference into the Orders.user field, It seems that Mongo does clone that object in the Orders collection.
// insert user:
var savedUser = await(new UsersModel({
_id: mongoose.Types.ObjectId(),
email: 'AAA#example.com'
})).save();
// insert order with user reference:
var savedOrder = await(new OrdersModel({
_id: mongoose.Types.ObjectId(),
createdOn: new Date(),
user: savedUser // <--- ref to users
})).save();
Now I modify the user document:
// update original user:
var userDocToModify = await UsersModel.findById(savedUser._id);
userDocToModify.email = "BBB#example.com";
await userDocToModify.save();
Assertion will fail in the below line:
var orderDoc = await OrdersModel.findById(savedOrder._id);
assert(orderDoc.user.email == userDocToModify.email, 'email not changed in the orderDoc.user.email!');
Actually what you are doing here, is not referencing userSchema. You are embedding that schema into orders schema. If you want to reference user you should do as below:
const ordersSchema = new Schema({
user: {type: mongoose.Schema.Types.ObjectId, ref: 'Users'}
createdOn: Date,
// ...
})
In this case you just store the user id in parent document.
Now if you want to get orders and the user within it, you can either use mongoose population or $lookup in mongoose aggregation.

Populating the cart doesnt work (mongoose)

I have been trying to populate cart products in user's cart. Below is my code.
const express = require('express')
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({extended:false}))
const Schema = mongoose.Schema;
var userSchema = new Schema({
userName : {
type : String,
default : 'UserName'
},
cart : [ { type : mongoose.Schema.Types.ObjectId , ref : 'Cart' } ]
})
var cartSchema = new Schema({
productNameofUser : {
type : String,
default : 'Product'
}
})
var UserModel = mongoose.model('User' , userSchema)
var CartModel = mongoose.model('Cart' , cartSchema)
app.get('/users',(req,res)=>{
UserModel.find().populate('cart').exec((err,result)=>{
res.send(result)
})
})
app.post('/createUser',(req,res)=>{
let newUser = new UserModel()
newUser.save((err,result)=>{
console.log(result)
res.send(result)
})
})
app.get('/products',(req,res)=>{
CartModel.find((err,result)=>{
res.send(result)
})
})
app.post('/createProduct',(req,res)=>{
let newProduct = new CartModel()
newProduct.save((err,result)=>{
res.send(result)
})
})
app.listen(3000,()=>{
console.log('running')
mongoose.connect('mongodb://127.0.0.1:27017/populate' , { useNewUrlParser : true })
})
what I am trying to do is, There is a carts collection and there is another collection of users. Every user has a cart which i am trying to populate from the cart collections.
If you want to test the code, first create the user using the post link 'http://localhost:3000/createUser/' via postman and the create cart Products using 'http://localhost:3000/createProduct/' via postman. Then whenever i tried to fetch the users 'http://localhost:3000/users/' , the cart array stays empty no matter what i try.
Please figure where i am going wrong
Update your Schema defination as:
const Schema = mongoose.Schema;
var userSchema = new Schema({
userName : {
type : String,
default : 'UserName'
},
cart : [ { type : Schema .ObjectId , ref : 'Cart' } ]
})
And try this again:
app.get('/users',(req,res)=>{
UserModel.find().populate('cart').exec((err,result)=>{
res.send(result)
})
})
Let me know, if this works now.

How to get a list of available Mongoose Discriminators?

Given a situation where you have a User Scheme that you use to create a base model called User. And then for user roles, you use mongoose discriminators to create inherited models called Admin, Employee and Client. Is there a way to programmatically determine how many discriminations/inheritances/roles of the User model are available, as well as the available names?
My question in terms of code:
File: models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var options = {discriminatorKey: 'role'};
var userSchema = mongoose.Schema({
name: String,
email: String,
password: String,
},options);
var User = mongoose.model('User', userSchema);
var Client = User.discriminator("Client", mongoose.Schema({
Address : String,
Tax_identification : String,
Phone_number : String,
Internal_Remarks : String,
CRM_status : String,
Recent_contact : String,
}));
var Employee = User.discriminator("Employee",mongoose.Schema({
Staff_Id: String,
}));
module.exports = {User: User, Client: Client, Employee: Employee };
File: controllers/usersController.js
var User = require('../models/user.js').User;
module.exports = {
registerRoutes: function(app){
app.get('user/create',this.userCreateCallback)
},
userCreateCallback: function(req,res){
//Get Available User Roles - The function below doesn't exist,
//Just what I hypothetically want to achieve:
User.geAvailableDiscriminators(function(err,roles){
res.render('user/create',{roles:roles})
});
}
};
I hope I managed to express what I want to do. Alternative approaches are also welcome.
Since v4.11.13, mongoose model has model.discriminators which is an array of models, keyed on the name of the discriminator model.
In your case if you do console.log(User.discriminators) you will get:
{
Client: {
....
},
Employee: {
}
}
As far as I can see, this is not documented anywhere.
Line 158 in lib.helpers.model.discriminators.js is where this is created.
I think you want to fetch the names and values of all the discriminators as for the names you can simply use
User.discriminators
but for finding values you can use this
return Promise.all(Object.keys(discriminators).map(i =>
discriminators[i].find({ userId: this._id }))
).then(promiseResults =>
promiseResults.reduce((arr, el) => arr.concat(el), [])
);
you need to put userId under each discriminators for that.

Resources