How to use mongoose findById() and populate() in javascript? - arrays

Im trying to create a list of items for each users, which means user can create items and ı want to keep that data for each user.
Which I create two different schemas for that in:
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
userSchema = new Schema({
unique_id: Number,
email: String,
username: String,
password: String,
passwordConf: String,
teamname: String,
items: [{ type: Schema.Types.ObjectId, ref: 'Item' }],
});
(ItemSchema = new Schema({
createrId: { type: Schema.Types.ObjectId, ref: 'User' },
productname: String,
description: String,
price: String,
totalStock: String,
})),
(Item = mongoose.model('Item', ItemSchema));
User = mongoose.model('User', userSchema);
module.exports = { Item, User };
After creating 2 schemas ı used them in index.js that routers everything together.
index.js
router.get('/create_item', function (req, res, next) {
return res.render('items.ejs');
});
router.post(
'/create_item',
function (
req,
res,
next // Try to create items in database
) {
console.log(req.body);
var itemInfo = req.body;
if (
!itemInfo.itemname ||
!itemInfo.itemdsc ||
!itemInfo.itemstock ||
!itemInfo.itemprice
) {
res.send();
} else {
var NewItem = new Item({
createrId: loggedInUser._id,
productname: itemInfo.itemname,
description: itemInfo.itemdsc,
price: itemInfo.itemprice,
totalStock: itemInfo.itemstock,
});
NewItem.save(function (err, Item) {
if (err) console.log(err);
else console.log('Item added succesfully !');
});
}
User.findById(loggedInUser._id)
.populate('items')
.exec(function (err, result) {
if (err) {
console.log(err);
} else {
console.log('Suceess ? : ', result.items);
console.log(loggedInUser._id);
}
});
}
);
I keep the data for loggedInUser in '/login' so it is return me a value.
My output is
"Suceess ? :[]
6379921faf39150008fa1b88
Item added succesfully !
Which ı think the problem is from the output it trying to populate item after Item is created but how can ı solve this problem ?
Thanks

The problem is that your code is not executing sequentially (NewItem.save logs after User.findById).
Try to use async await, also you will need to add the newly created item _id to the corresponding User to add the new reference to the items array:
router.post(
'/create_item',
async function (
req,
res,
next // Try to create items in database
) {
console.log(req.body);
try {
var itemInfo = req.body;
if (
!itemInfo.itemname ||
!itemInfo.itemdsc ||
!itemInfo.itemstock ||
!itemInfo.itemprice
) {
return res.send('Incomplete parameters');
}
const newItem = await Item.create({
createrId: loggedInUser._id,
productname: itemInfo.itemname,
description: itemInfo.itemdsc,
price: itemInfo.itemprice,
totalStock: itemInfo.itemstock,
});
const result = await User.findByIdAndUpdate(loggedInUser._id, {
$push: { items: newItem._id },
}, { new: true })
.populate('items')
.exec();
console.log('Suceess ? : ', result.items);
console.log(loggedInUser._id);
return res.send('Finished');
} catch (e) {
console.log(e);
}
}
);

Related

How to save many-to-many relation in typeorm?

I am trying to make a relation between Posts and Hashtags, here are my both entities,
#Entity('posts')
export class Posts {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ length: 200, nullable: true })
caption: string;
#ManyToMany(() => Hashtags, (hashtags) => hashtags.posts, { eager: true })
#JoinTable({ name: 'posts_hashtags_relation' })
hashtags: Hashtags[];
#ManyToOne(() => User)
#JoinColumn({ name: 'author_id' })
author: User;
}
#Entity('hashtags')
export class Hashtags {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
hashtag: string;
#ManyToMany(() => Posts, (post) => post.hashtags, {
eager: false,
cascade: true,
})
posts: Posts[];
#CreateDateColumn({ name: 'created_at' })
createdAt: Date;
}
By these, typeorm created a database posts_hashtags_relation, with columns postsId and hashtagsId
And the service by which I am saving hashtags in the hashtags table is like this
async createPost(creator : User, body: CreatePostDto) {
if (!body.caption) {
throw new BadRequestException('Post must contain some text');
}
// Extract hashtags from the post caption body
const hashtags = body.caption.match(/\#\w+/g); // hashtags are the array of all hashtags in the post caption
if(hashtags){
for (const hashtag of hashtags) {
const hashtagEntity = await this.hashtagRepo.findOne({ hashtag });
if (!hashtagEntity) {
await this.hashtagRepo.save({ hashtag });
}
}
}
const post = new Posts();
post.author = creator;
post.caption = body.caption;
post.images = body.images;
const resPost = await this.postsRepo.save(post);
return resPost;
}
But how to save the relation in posts_hashtags_relation table ?
As you notice in your post entity you have the column hashtags: Hashtags[]; the same thing in Hashtags...
So you can save the data of the relation in both entities:
with your code we can do:
...
let hashtagsEntites:Array<Hashtags> = [];
if(hashtags){
for (const hashtag of hashtags) {
var hashtagEntity = await this.hashtagRepo.findOne({ hashtag });
if (!hashtagEntity) {
hashtagEntity = await this.hashtagRepo.save({ hashtag });
}
hashtagsEntites.push(hashtagEntity);
}
}
const post = new Posts();
post.author = creator;
post.caption = body.caption;
post.images = body.images;
post.hashtags= hashtagsEntites ; // here's how we save hashtags's post in the table 'posts_hashtags_relation'
const resPost = await this.postsRepo.save(post);

CastError: Cast to ObjectId failed for value "{ _id: ':5ec5cc919efcf581eb692690' }" at path "_id" for model "Posts"

Checking the router on the server side it console logs the right values, only the follow error is popping up in here. Trying to build a counter that should update the value on the backend. But the problem I have is that value will not be stored in there. When using Postman the value will be stored successfully. What is the solution that can fix this issue.
export const incrementProduct = (index, updateAmount, id) => {
return dispatch => {
dispatch(increment(index));
try {
axios.patch(`${API_URL}/:${id}`, {
amount: updateAmount
}).then(res => {
console.log(res.config);
})
} catch(err) {
console.log(err)
}
}
}
const PostSchema = mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
amount: {
type: Number,
required: true
},
editable: {
type: Boolean,
required: true
},
data: {
type: Date,
default: Date.now
}
});
// update
router.patch('/:postId', async(req, res) => {
console.log('update', req.params.postId + 'amount ' + req.body.amount)
try {
const updatedPost = await Post.findByIdAndUpdate(
{_id: req.params.postId}, <--- this cause the console error...
{$set:
{
amount: req.body.amount
},
})
console.log('try')
res.json(updatedPost)
} catch(err) {
console.log(err + 'test ')
res.json({ message: err })
}
})
You need to remove : in the patch url like this:
axios.patch(`${API_URL}/${id}`
Also findByIdAndUpdate requires only the value of _id, so you can only pass the value like this:
await Post.findByIdAndUpdate(req.params.postId, ...
findByIdAndUpdate(id, ...) is equivalent to findOneAndUpdate({ _id: id }, ...).

Problem with adding an item to an array of mongoose reference objects

In my NodeJS and MongoDB app, I have 2 mongoose schemas:
companySchema:
const companySchema = new Schema({
name: {
type: String,
required: true
},
products: [{
type: Schema.Types.ObjectId,
ref: 'Product',
required: false
}]
});
companySchema.statics.addProduct = function (productId) {
let updatedProducts = [...this.products];
updatedProducts.push(productId);
this.products = updatedProducts;
return this.save();
}
module.exports = mongoose.model(‘Company’, companySchema);
productSchema:
const productSchema = new Schema({
name: {
type: String,
required: true
},
quantity: {
type: Number,
required: true
}
});
module.exports = mongoose.model('Product', productSchema);
Every time I add a new product to productSchema, I would like to add the _id of the newly created product to products array in companySchema in order to easily access products later on.
To accomplish this, I wrote:
const Company = require('../models/company');
const Product = require('../models/product ');
exports.postAddProduct = (req, res, next) => {
const name = req.body.name;
const quantity = req.body.quantity;
const product = new Product({
name: name,
quantity: quantity
});
product.save()
.then(product => {
return Company.addProduct(product._id);
})
.then(result => {
res.redirect('/');
})
.catch(err => console.log(err));
}
I am getting an error: TypeError: this.products is not iterable.
You are setting a static method, which is a method on the model rather than the document instance.
Therefore, this refers to the model itself, not the individual document.
Unlike the document, the model doesn’t have an array (iterable) called products so it can’t be spread into a new array.
Try using methods instead of statics:
companySchema.methods.addProduct = function (productId) {
...
}
I hope this helps.

Mongoose: Don't push to array if document exists

In the below code, I'm creating a checkin document which I then reference in the user document, on top with a car reference (in the user's collection). What I'm trying to achieve is to skip the insert to the array if the car already exists.
How to check it? Or should I work with the model, and define a uniqueness reference in the cars[] array?
router.post('/', (req, res, err) => {
var checkin = new Checkin(req.body);
checkin._id = mongoose.Types.ObjectId();
checkin.save()
.then((checkinDoc) => {
return User.findOneAndUpdate(
{ _id: ObjectID(checkin.user) },
{ $push: { checkins: checkinDoc._id, cars: checkin.car } },
{ new: true }
)
})
.catch((err) => {
res.send(err);
});
});
My user.js model
[...]
cars: [{
type: Schema.Types.ObjectId,
ref: 'Car'
}],
[...]

Mongoose/Mongo: Update Not Saving

I'm extremely perplexed by this issue that I'm having with mongo/mongoose. I'm essentially trying to get an array of products, delete a certain product from the array, and then update the shopping chart with the new array that omits the selected product. Here's the snippet of code I'm dealing with:
const remove = (req, res, next) => {
console.log('here is the product id ' + req.body.cart.product)
delete req.body._owner // disallow owner reassignment.
Cart.find({_id: req.user.cartId})
.then((products1) => {
console.log("array of products: " + products1[0].product)
const index = products1[0].product.indexOf(req.body.cart.product)
console.log("index valeu: " + index)
if (index > -1) {
products1[0].product.splice(index, 1)
return products1[0].product
}
return products1[0].product
})
.then((products2) => {
console.log('Second Promise Input: ' + products2)
Cart.update({_id: req.user.cartId}, {$set: {product: products2}})
})
.then(() => res.sendStatus(204))
.catch(next)
}
And here's the output from my server:
Server listening on port 4741
here is the product id 5952b57ea52d092b8d34c6b0
array of products: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
index valeu: 0
Second Promise Input: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
PATCH /carts-decrease/595b037e128cfd37e0c864d7 204 38.773 ms
According to my console.logs, I'm getting the array just the way I want it but it simply does not update the shopping cart with the new array. I've been staring at this code for far too long and I'd appreciate a second set of eyes on this. Thanks.
P.S. Ignore the fact that the product ids are all the same, its just a testing variable
Cart Schema:
'use strict'
const mongoose = require('mongoose')
const cartSchema = new mongoose.Schema({
product: {
type: Array,
required: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false
}
}, {
timestamps: true,
toJSON: {
virtuals: true,
transform: function (doc, ret, options) {
const userId = (options.user && options.user._id) || false
ret.editable = userId && userId.equals(doc._owner)
return ret
}
}
})
const Cart = mongoose.model('Cart', cartSchema)
module.exports = Cart
Product Schema:
'use strict'
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
}
}, {
toJSON: {
virtuals: true
}
})
const Product = mongoose.model('Product', productSchema)
module.exports = Product
Show request:
const show = (req, res) => {
const product = {}
product.array = []
// console.log(req.cart.product)
const promises = []
Promise.all(req.cart.product.map(function (id) {
return Product.find({_id: ObjectId(id)})
})).then(function (products) {
console.log(products)
req.cart.product = products
return res.json({
cart: req.cart.toJSON({virtuals: true, user: req.user})
})
}).catch(function (err) {
console.log(err)
return res.sendStatus(500)
})
}
I would recommend you to slightly modify your cartSchema and store products in the form of an array of embedded documents:
const cartSchema = new mongoose.Schema({
products: [{
name: { type: String },
price: { type: Number }
...
}]
...
});
If you do this you can simply use the $pull update operator to remove products from your cart:
{ $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
In your case the query should then look like this:
Cart.update(
{ _id: req.user.cartId },
{ $pull: { products: { '_id': req.body.cart.product } }}
);
As the embedded documents will have their own ObjectId there will only be one document matching the query.

Resources