Mongoose model schema referencing not working - node.js

Basically I have 3 mongoose models(user,product,cart).I have tried referencing cart model with user model.I've the used populate method in mongoose to populate the referenced property in the model.But it is not working and I'm getting an empty cart array after adding a product to the cart like this on consoling.
{
"_id": "5a0b2dcf726753258002273d",
"password": "12345",
"securityQuestion": "What is your Nick Name?",
"securityAnswer": "rj1",
"__v": 0,
"cart": [], // empty cart here
"mobileNumber": 1234567890,
"email": "rahul#gmail.com",
"lastName": "jhawar",
"firstName": "rahul",
"userName": "rj"
}
I also wanted to know whether my referencing of models are done correctly(I mean the relationship).
I'm new to this concept.Please help me out.
//Cart.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var cartSchema = new Schema({
productName : {type:String,default:''},
productCategory : {type:String,default:''},
productDescription : {type:String,default:''},
productPrice : {type:String,default:''},
sellerName : {type:String,default:''},
});
module.exports=mongoose.model('Cart',cartSchema);
//User.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
userName : {type:String,default:'',required:true},
firstName : {type:String,default:''},
lastName : {type:String,default:''},
email : {type:String,default:''},
mobileNumber : {type:Number,default:''},
password : {type:String},
securityQuestion : {type:String},
securityAnswer : {type:String},
cart : [{
type: Schema.Types.ObjectId,
ref:'Cart'
}]
});
module.exports=mongoose.model('User',userSchema);
///Routes///
const cartRouter = express.Router();
const cartModel = mongoose.model('Cart');
const userModel = mongoose.model('User');
const productModel = mongoose.model('Product');
cartRouter.get('/addtocart/:id',auth.checkLogin,(req,res)=>{
productModel.findOne({'_id':req.params.id},(err,foundProduct)=>{
if(!err){
const newCartProduct = new cartModel({
productName : foundProduct.productName,
productCategory : foundProduct.productCategory,
productDescription : foundProduct.productDescription,
productPrice : foundProduct.productPrice,
sellerName : foundProduct.sellerName
});
newCartProduct.save((err)=>{
if(!err){
userModel.findOne({'_id':req.session.user._id}).populate('cart')
. exec((err,cartproduct)=>{
console.log(JSON.stringify(cartproduct, null, "\t"));
});
console.log('New Product added to cart');
res.redirect('/users/cart_products');
}
});
});
});

You've correctly set up the relationship between the user and the cart models in that the user potentially holds a reference to a number of cart models.
However, when you save the cart, you aren't actually updating the cart array/property of the user. You have to both save your newCartProduct and you also have to push the resultant id into the cart array and then save your user model. If you don't do this the populate method isn't going to work, since it has nothing to populate.
Here's an example of how you might use findByIdandUpdate to update the user with the new cart item.
cartRouter.get('/addtocart/:id',auth.checkLogin,(req,res)=>{
productModel.findOne({'_id':req.params.id},(err,foundProduct)=>{
if(!err){
const newCartProduct = new cartModel({
productName : foundProduct.productName,
productCategory : foundProduct.productCategory,
productDescription : foundProduct.productDescription,
productPrice : foundProduct.productPrice,
sellerName : foundProduct.sellerName
});
newCartProduct.save((err, o)=>{
if(!err){
userModel.findByIdAndUpdate(req.session.user._id,
{$push: {"cart": o._id}},
{safe: true, new : true})
.populate('cart')
.exec((err, model) => {
console.log(model); //This will have the cart array filled in
});
});
});
Other choices would be either to reverse the relationship, to hang the reference to the user from the cart, or to use an embedded subdocument:
cart: [Cart]
If you never need to access cart entries outside of the context of a single user (if you never need aggregate information across users), then it's probably the right thing to do.
Your approach maybe right for your use cases, but then you have to manually maintain both sides of the relationship when items are added and removed from the cart.

Related

How can I updateMany mongodb

I have three chatrooms and I would like to update the document with MongoDB only if the user matches with the members.user_id. I don't understand, it's updating to all the member's devices the same token. If someone has an idea?
It's my Node.js code :
const _id = req.params._id;
const token = req.body.token;
const user_id = req.body.user_id;
try{
const updateDevice = await ChatRoom.updateMany(
{"members.$[].user_id" : user_id},
{$set:{"members.$[].devices":token}})
res.send(updateDevice)
}catch(err){console.log(err)}
const mongoose = require('mongoose');
const chatRoom_schema = new mongoose.Schema({
name:{
type:Array,
name:String,
},
members:{
user_id:String,
name:String,
devices:String,
type:Array,
required:true
},
lastMessage:{
content:String,
createdAt:Date,
type:Array,
send_by:String,
readBy:Array
}
}, {
collection: "chatRoom"
})
module.exports = chatRoom = mongoose.model("ChatRoom", chatRoom_schema);
According to the schema there is a object of members not an array of object so to access the specific elements only dot(.) operator is used. If you want to access any element from the array or want to update the specific object value in array of object then $ is used.
Try this query to solve the problem
ChatRoom.updateMany(
{"members.user_id" : user_id},
{$set:{"members.devices":token}})
Let there be a record like
members=[
{
"user_id":"1",
"name":"DD",
"type":"ADMIN"
},
{
"user_id":"2",
"name":"HH",
"type":"CUSTOMER"
}
]
To update the type of user_id(2) from CUSTOMER to ADMIN then $ operator can be useful.
The query can be
ChatRoom.updateMany({"members.$.user_id" : 2},
{$set:{"members.$.type":"ADMIN"}})

How to push new objectId in array of a objectIds inside a document using FindOneAndUpdate query?

I have a user schema which has orders array :
const userSchema = new mongoose.schema({
name : String , email :String,
orders : [{ type : mongoose.Schema.Types.ObjectId , ref :'Orders' }]
})
what I want to do is to push a new OrderId into user's orders using findOneAndUpdate query
const newUser = await User.findByIdAndUpdate( myUserId ,
{ 'orders' : {$push : newOrderId} } ,
{new:true , runValidators : true })
Please help me solve this issue.

Mongoose returns empty while the same query in mongodb shell works fine

I know maybe this question has been asked quite many times here, I've went through several solutions people came with to similar questions but none of them seemed to help in my case.
I have two collections called users and posts and models for them look like this:
users
var mongoose = require('mongoose').set('debug', true);
var Schema = mongoose.Schema;
var usersSchema = new Schema({
name: {type: String, required: true}
});
var User = mongoose.model('user', usersSchema, 'users');
module.exports = User;
posts
var mongoose = require('mongoose').set('debug', true);
var Schema = mongoose.Schema;
var postsSchema = new Schema({
content: String,
user: {
type: Schema.ObjectId,
ref: 'users',
required: true
}
});
var Post = mongoose.model('post', postsSchema, 'posts');
module.exports = Post;
I'm trying to get the posts of a user using this code:
var Post = require('../models/posts');
...
router.get('/posts/user/:userId', function (req, res, next) {
Post.find({user: req.params.userId}, function (err, posts) {
Post.populate(posts, {path: 'user'}, function(err, posts) {
res.send(posts);
});
});
});
Mongoose debug mode reports that the following query is executed during the request:
posts.find({ user: ObjectId("592e65765ba8a1f70c1eb0bd") }, { fields: {} })
which works perfectly fine in mongodb shell (I'm using Mongoclient) but with Mongoose this query returns an empty array.
The query I run in mongodb shell:
db.posts.find({ user: "592e65765ba8a1f70c1eb0bd" })
The results I get:
{ "_id" : ObjectId("592e66b48f60c03c1ee06445"), "content" : "Test post 3", "user" : "592e65765ba8a1f70c1eb0bd" }
{ "_id" : ObjectId("592e66b98f60c03c1ee06446"), "content" : "Test post 4", "user" : "592e65765ba8a1f70c1eb0bd" }
{ "_id" : ObjectId("592e66bb8f60c03c1ee06447"), "content" : "Test post 5", "user" : "592e65765ba8a1f70c1eb0bd" }
I'm at the very beginning on learning Node.JS and MongoDB, so maybe I've missed something.
Thank you in advance!
As Neil Lunn suggested, I checked the user field type and it was indeed of type String instead of ObjectId so there was a mismatch of types between the data stored in collection and the field type from the query.
I used this code to convert the user field type from String to ObjectId in my collection:
db.getCollection('posts').find().forEach(function (post) {
db.getCollection('posts').remove({ _id : post._id});
tempUserId = new ObjectId(post.user);
post.user = tempUserId;
db.getCollection('posts').save(post);
}
);
Now everything works as expected.

mongoose populate return empty

In mongoose, currently i got 2 schema first one is product detail and the second one is inventory both schema are in seperate files
ProductDetail Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var productDetailSchema = new Schema({
ProductID:{type:String,unique:true, require:true},
ProductName:{type:String, require:true},
Spec:String,
Price:String,
Unit:String,
OwnBrand:Boolean,
Inventory:{ type: Schema.Types.ObjectId, ref: 'inventoryModel'}
}, {collection:'products'});
module.exports = mongoose.model('productDetailModel', productDetailSchema)
Inventory Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var inventorySchema = new Schema({
product:{ type: Schema.Types.ObjectId, ref: 'productDetailModel' },
stockLevel:Number
}, {collection:'inventory'});
module.exports = mongoose.model('inventoryModel', inventorySchema)
In this case I would want to put the inventory stockLevel in to productDetail. Like this
{
ProductID:'P701',
ProductName:'popcorn',
Spec:'Large Pack',
Price:'10.00',
Unit:'packs'
Inventory: 20
}
Therefore I've try to use the mongoose populate method
productDetail.findOne({ProductID:'P701'}).populate('Inventory').exec(function(err, doc){
if(err){
console.log(err)
}else{
console.log(doc)
}
})
This is the data that i've in my "Inventory collection"
{
"_id" : ObjectId("584f663d95dfa29bad337572"),
"ProductID": "P701",
"StockLevel": "20"
}
and the data in "ProductDetail Collection"
{
_id : ObjectId("584a3cea027c1f62da941acf"),
ProductID:'P701',
ProductName:'popcorn',
Spec:'Large Pack',
Price:'10.00',
Unit:'packs'
}
For some reason there are no error shows up but the console.log(doc) still didnt include any Inventory: stockLevel. I know I'm doing something wrong, but i've no idea where it is. What kind of key concept i'm missing inorder for this code to work? Thank you in advance
your inventory in the productDetailModel should be an ObjectId, from what you have shown here is a number(i.e. 20).
{
ProductID:'P701',
ProductName:'popcorn',
Spec:'Large Pack',
Price:'10.00',
Unit:'packs'
Inventory: 20
}
this should be like this:
Inventory : ObjectId('someId');
That's why it is not populating, because it can't find an ObjectId as 20 in the inventoryModel.
EDIT
This productDetail document has no field as inventory. Thus, it is not able to populate it.
{
_id : ObjectId("584a3cea027c1f62da941acf"),
ProductID:'P701',
ProductName:'popcorn',
Spec:'Large Pack',
Price:'10.00',
Unit:'packs'
}
It should have something like this too:
inventory : ObjectId("584f663d95dfa29bad337572");//some `ObjectId` which corresponds to `inventoryModel` document.
You need to create inventory field and save ObjectId of some inventory document in productDetail document while saving it, or may be while updating it.

Updating a Record in Mongo After Retrieving Its ID From Another Record

I am trying to make an API point that would do the following. I submit an Object ID in the path. The record with that ID is found. Then, the program looks into a certain field of this object. The field contains an ObjectID for another entry in the database. At last, I need to pull up that record and increment a certain field in it.
In short, I have a child->parent relationship between certain records and would like the ability of incrementing a certain field within the parent record by submitting the child's id to the API point.
Here is the code I had that did the basic child increment. How can I go about doing it for the parent?
router.get('/today/parent/up/:id', function(req, res){
var collection = db.get('Activity');
collection.update({
_id: req.params.id
},
{
$inc: {
"repetitions.today": 1,
"repetitions.total": 1
}
}, function(err, activity){
if (err) throw err;
res.json(activity);
});
})
First use mongo references, heres documenttion:
https://docs.mongodb.com/manual/reference/database-references/
here's mongoose documentation
http://mongoosejs.com/docs/2.7.x/docs/populate.html
Basically You need to do this:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: Schema.ObjectId, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: Schema.ObjectId, ref: 'Person' }
, title : String
, fans : [{ type: Schema.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
Then you could use .populate() method, and then you could extract your populated model and make changes and save them with .save(), but remember to use it in populated model, not the parent one. For ex. You've got author which contains reference to books, so you make request
author.findOne({'name': 'King'}).populate('books').exec((err, king) => {
let book0 = king.books[0];
book0.title = 'I need to change this one';
book0.save((err, data) => {
console.log('saved referenced object')
}
})

Resources