Adding object into Array of of Objects, Nodejs, Mongoose - node.js

I'm working on a small app where you can add products into a users cart.
I have a route set up for adding a specified product into a cart and that works fine. However when I add it in, all that shows up the array is the _id of the product, when what I'm looking for is an object containing some information (i.e, product name, product price, etc.). So that I can access it later in my React-Redux App. I've found a few suggestions online, but they either seem to not work for me at all or give me the same thing I've been getting.
Heres the route:
Cart.findOne({ user: req.user.id })
.then(cart => {
Product.findById(req.params.product_id).then(product => {
const newItem = {}
newItem._id = product._id;
newItem.name = product.name;
newItem.price = product.price;
const total =
parseFloat(cart.total) + parseFloat(product.price);
cart.total = Math.round(total*100)/100;
cart.items.push(newItem);
cart.save().then(cart=> res.json(cart));
});
})
.catch(err => res.status(404).json(err));
})
.catch(err => res.status(404).json(err));
Here's the corresponding Schema:
const CartSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
total: {
type: String,
default: 0
},
items: [
{
product: {
type: Schema.Types.ObjectId,
ref: "product"
}
}
]
});
Any help would be much appreciated.
Thank you

Try passing the product as is. Maybe because of the schema a Product is expected by mongoose, not a generic object such as, in your case, newItem
Cart.findOne({ user: req.user.id }).then(cart => {
return Product.findById(req.params.product_id);
}).then(product => {
const total = parseFloat(cart.total) + parseFloat(product.price);
cart.total = Math.round(total*100)/100;
cart.items.push(product);
return cart.save();
}).then(cart => {
return res.json(cart)
}).catch(err => res.status(404).json(err));
NOTE: i also fixed your promise chain structure a bit. This way you avoid callback hell, and only need one catch statement for the whole chain of promises.

How about
const CartSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
total: {
type: String,
default: 0
},
items: [
{
type: Schema.Types.ObjectId,
ref: "product"
}
]
});
And item pushed to array of items should be instance of Product, not just plain object.

Related

how to get comments with their author with mongoose, nodejs

i am creating items inside collections and in each items authors are able to leave a comment. i want retrieve comment with their author . so that i referenced author id inside comment schema and i am only getting author id when i populate author in my get request. So can anyone help to get comments with its author information?
ITEM SCHEMA
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
comments: [
{
owner: { type: Schema.Types.ObjectId, ref: "User" },
text: { type: String },
},
],
owner: { type: Schema.Types.ObjectId, ref: "User" },
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
);
itemSchema.index({ "$**": "text" });
export default model("Item", itemSchema);
GET COMMENT ROUTE
itemRouter.get(
"/:itemId/comments",
JWTAuthMiddleware,
adminAndUserOnly,
async (req, res, next) => {
try {
if (req.params.itemId.length !== 24)
return next(createHttpError(400, "Invalid ID"));
const items = await ItemModal.findById(req.params.itemId).populate("owner");
if (!items)
return next(
createHttpError(
400,
`The id ${req.params.itemId} does not match any items`
)
);
res.status(200).send(items.comments);
} catch (error) {
next(error);
}
}
);
What i am getting is only user id and comment

How to structure POST to MongoDB collection with references to ObjectId's of another collection?

I am very new to MEAN projects and most of my code is code from various tutorials adapted to my own needs with my limited knowledge of MEAN. Sorry for any misuse of terminology, I'll try to explain my situation the best I can.
I have a MongoDB database with two collections: items and carts.
items has documents that have fields for the Product, Color, Size, and Price of each item.
carts has documents with one field that is an array of items referencing the ObjectId for each item, a field for the Total, and a boolean field called Active.
I'm struggling with the POST request to the cart API that includes references to the ObjectId of multiple items.
item.model.js:
module.exports = mongoose => {
const Item = mongoose.model(
'item',
mongoose.Schema({
product: {
type: String,
required: true
},
color: {
type: String,
required: true
},
size: {
type: String,
required: true
},
price: {
type: Number,
required: true
}
})
);
return Item;
};
cart.model.js:
module.exports = mongoose => {
const Cart = mongoose.model(
'cart',
mongoose.Schema({
items: [{
item: {
type: mongoose.Schema.Types.ObjectID,
ref: 'Item'
}
}],
total: {
type: Number,
required: true
},
active: {
type: Boolean,
required: true
}
})
);
return Cart;
};
cart.controller.js cart creation:
const db = require('../models');
const Cart = db.carts;
// Create and Save a new Cart
exports.create = (req, res) => {
// Validate request
if (!req.body.items) {
res.status(400).send({ message: 'Content can not be empty!' });
return;
}
// Create a Cart
const cart = new Cart({
items: req.body.items,
total: req.body.total,
active: req.body.active ? req.body.active : true
});
// Save Cart in the database
cart
.save(cart)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || 'Some error occurred while creating the Cart.'
});
});
};
POST request to /api/cart/
I figured it out lol.
Formatting the POST request like this successfully added items to the items field array:
{
"items": [
{"item": "616f3f378302f3944caa8fac"},
{"item": "6182ff5cd852c5a4c7315048"},
{"item": "6182ff6ad852c5a4c731504c"}
],
"total": 0,
"active": true
}

Delete all nested items in database when deleting a top item using MongoDB and Express

I have three collections in my mongodb: clients, programs, and data. Data is nested in Programs and Programs are nested in Clients. That is a single client can have multiple programs and every such program can have a number of Data sets. Here is how clients' schema looks like:
const ClientSchema = new Schema({
fullName: { type: String, required: true },
dateEnrolled: { type: Date, required: true },
isCurrent: { type: String, required: true },
Programs: [{ type: Schema.Types.ObjectId, ref: 'Program' }]
});
// and my Programs schema looks like:
const ProgramSchema = new Schema({
programName: { type: String, required: true },
dateStarted: { type: Date, required: true },
dateFinished: { type: Date },
Data: [{ type: Schema.Types.ObjectId, ref: 'Answer' }]
});
Now, when I delete a client from my database I want to delete all the programs that belong to the clients and all the data sets that were created for those programs. Please, help me.
Try something like this:
router.delete('/:clientId', (req, res, next) => {
const id = req.params.id;
Client.findById(id).then((client) => {
client.Programs.forEach((programId) => {
Program.findById(programId).then((program) => {
Answer.deleteMany({ _id: { $in: program.Data } });
});
Program.deleteOne({ _id: programId })
});
Client.deleteOne({ _id: client._id })
})
});
You can register a middleware function for remove in ClientSchema.
ClientSchema.pre('remove', { query: true, document: true }, async function() {
// call deleteMany on ProgramModel
await ProgramModel.deleteMany({ _id: { $in: this.Programs } });
});
You can do the same thing for ProgramSchema if you want to cascade delete Answer.
Nenad, thank you very much, again! You gave me an idea to loop through first program ids, then through programs. My solution comes in two parts:
Part 1:
`router.delete('/:id', (async (req, res, next) => {
const { id } = req.params;
const client = await Client.findById(id);
await client.Programs.forEach(async (element) => {
const programs = [];
const program = await Program.findById(element);
programs.push(program);
programs.forEach(async (program) => {
await Answer.deleteMany({ _id: { $in: program.Data } });
})
});
await Client.findByIdAndDelete(id);
res.redirect('/clients');
}))`
part 2 that goes to Client schema file and makes sure that all the programs are deleted as well:
`ClientSchema.post('findOneAndDelete', async function (doc) {
if (doc) { await Program.deleteMany({ _id: { $in: doc.Programs } }) }
})`
Now, when I delete a client all instances of related programs are deleted and all instances of data-sets related to each program are deleted, too. Thank you, guys, are all awesome!

In Mongoose findOneAndUpdate, how can I make my post request work?

Hi all so I am trying to make a post request that increments a value if it already exists and if not it should create a new item.
router.post('/', auth, async (req, res) => {
try {
const { name, price, image } = req.body;
var query = { name },
update = { $inc: { count: 1 } },
options = { upsert: true, new: true,};
await CartItem.findOneAndUpdate(query, update, options, function (
err,
data
) {
if (err) {
const newItem = new CartItem({
user: req.user.id,
name: name,
price: price,
image: image,
});
const item = newItem.save();
res.json(item);
} else {
res.json(data);
}
});
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CartItemSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
count: {
type: Number,
},
image: {
type: String,
required: true,
},
});
module.exports = CartItem = mongoose.model('cartItem', CartItemSchema);
So there are two problems here that I cannot wrap my head around(Pretty new with MongoDb, did do my research).
I can get the count to increment, but it increments with 2 or even more instead of 1. (I know other users also experienced this)
If the item is already in the cart(name matches) I want a new item to be added which does happen, but it only adds the name, count and Id. I want it to add the user, name, price and image.
Would appreciate some assistance.
you should create your document with a default value equals to 0.
define count at your schema like the following:
count: {
type: Number,
default: 0
}
then use { $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }.
link to docs: https://docs.mongodb.com/manual/reference/operator/update/inc/

add a item inside a nested schema mongoose with addToSet

I know populating schemas is not a new question but I am having a little trouble following the logic on this in regards to multiple schemas. I am working with
"mongoose": "^4.8.5",
"express": "^4.15.0",
I have a schema with a collection of caffeine drinks. When a user selects a drink i would like for that drink to be assigned to the user.
** If at any point I am missing something simple in the architecture please let me know. This project has been my intro to mongodb.
I am reading through populating on the mongoose documentation http://mongoosejs.com/docs/populate.html.
Essentially, if I am to assign the drinks to the list it looks like I want to add them as a reference in an array. This was my approach with caffeine_list
const SelectedDrinks = require('./userDrinks');
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
caffeine_list: caffeine_list: [ // attempting to reference selected drinks
{
type: mongoose.Schema.Types.ObjectId,
ref: 'SelectedDrinks'
}
]
})
SelectedDrinks comes from the schema below. I added a reference to the user as the creator below
const User = require('./user');
let userDrinkSchema = new mongoose.Schema({
creator : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
caffeine: Number,
mgFloz: Number,
name: String,
size: Number,
updated_at: {
type: Date,
default: Date.now()
}
});
This is where I start to get confused. I initially tried populate but could not get it going. If that was correct please let me know.
In regards to my task of adding a selected drink to the user I used addToSet. I was hoping that this would give me the drink info. I did my set up like so....
const User = require('../../models/user');
const UserDrinks = require('../../models/userDrinks');
router.post('/addDrink', (req, res, next) => {
let newDrink = new UserDrinks({
creator: req.body.creator,
caffeine: req.body.caffeine,
mgFloz: req.body.mgFloz,
name: req.body.name,
size: req.body.size,
updated_at: req.body.updated_at
});
newDrink.save( (err) => {
if(err) {
res.send(err);
} else {
User.findOne({ _id: newDrink.creator}, (err, user) => {
user.caffeine_list.addToSet(newDrink)
user.save( function (err) {
if(err) {
console.log(err);
}else {
res.status(201).json(newDrink);
}
})
})
}
})
});
However, after i do a post in postman I check caffeine_list and the result is
"caffeine_list" : [
ObjectId("58d82a5ff2f85e3f21822ab5"),
ObjectId("58d82c15bfdaf03f853f3864")
],
Ideally I would like to have an array of objects being passed with the caffeine info like so
"caffeine_list" : [
{
"creator": "58d6245cc02b0a0e6db8d257",
"caffeine": 412,
"mgFloz": 218.7,
"name": "1.95 Perfect Drink!",
"size": 42.93,
"updated_at": "2017-03-24T18:04:06.357Z"
}
]
Change your else part with below code instead of findOne and save use update
User.update(
{ _id: newDrink.creator},
{ $addToSet:{
caffeine_list: newDrink
}}).exec(function (err, updatedrink){
if(err) {
console.log(err);
}else {
res.status(201).json(updatedrink);
}
})
Although I am not sure this is the best approach I did find this to be give me the result that I was desiring. I had to make two small changes and I was able to get the caffeine_list to give me the desired response
I had to access the schema for selected drinks
const SelectedDrinks = require('./userDrinks').schema; //** need schema
Afterwards I was able to change
caffeine_list: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'UserDrinks' // name of the file
}
]
to
caffeine_list: [SelectedDrinks]
Now that I have the schema I am able to add the drinks directly into the caffeine_list on the UserSchema.

Resources