I'm new to backend and trying to make a shopping cart with NodeJs and MongoDb.
I tried many thing but i can't update the quantity field of a product.
This is the schema
const mongoose = require('mongoose');
const itemsSchema = mongoose.Schema(
{
prodId: String,
name: String,
price: Number,
qty: {
type: Number,
default: 1,
min: 1,
},
},
{
timestamps: true,
}
);
const cartSchema = mongoose.Schema(
{
total: {
type: Number,
},
products: [itemsSchema],
},
{
timestamps: true,
}
);
const Cart = mongoose.model('carts', cartSchema);
module.exports = Cart;
This is the controller
const Cart = require('../models/cart');
exports.postItemToCart = async (req, res) => {
const { prodId } = req.body;
Cart.updateOne(
{ 'products.prodId': prodId },
{ $inc: { 'products.qty': 1 } }
);
return res.status(200).send(Cart);
};
This is the cart
[
{
_id: '60062c7a9b0bfd350b3eb755',
__v: 3,
createdAt: '2021-01-19T00:48:58.006Z',
products: [
{
qty: 1,
_id: '6006333bd00fe96af648d308',
prodId: '1111',
name: 'Product One',
price: 24.22,
updatedAt: '2021-01-19T01:17:47.034Z',
createdAt: '2021-01-19T01:17:47.034Z',
},
{
qty: 1,
_id: '600634337f3eba6b451a26e5',
prodId: '2222',
name: 'Product Two',
price: 24.22,
createdAt: '2021-01-19T01:21:55.417Z',
updatedAt: '2021-01-19T01:21:55.417Z',
},
],
total: 48.44,
updatedAt: '2021-01-21T02:19:49.955Z',
},
];
I can change and update in the Mongoose documentation console, but i can't apply the same code on my project.
Please help :)
positional operator will help you to update the first matching array element.
Cart.updateOne(
{ 'products.prodId': prodId },
{ $inc: { 'products.$.qty': 1 } } //note dollar sign
);
Related
What im trying to do is to push the location and the status of the user in an array in a Schema.
The reason I'm doing that is to store all the user locations in a schema(like a history location)
When I create a location I use the method findOneAndUpdate().
controller/auth.js
exports.addLocation = asyncHandler(async (req, res, next) => {
const {phone, locations, status} = req.body;
const theLocation = await User.findOneAndUpdate(
{ phone },
{ locations, status },
{ new: false }
)
res.status(200).json({
success: true,
msg: "A location as been created",
data: theLocation
})
})
models/User.js
const UserSchema = new Schema({
phone: {
type: String,
unique: true,
},
createdAt: {
type: Date,
default: Date.now
},
code: {
type: Number,
max: 4
},
fullName: {
type:String
},
gender: {
type:String
},
birthdate: {
type:String
},
locations: [
{
location: {
latitude: {type:Number},
longitude: {type:Number},
},
status: {
type: String
},
createdAt: {
type: Date,
default: Date.now,
}
}
],
});
So when a new item is stored, the previous one is deleted.
How can I save the previous item to another MongoDB field?
You can use $push at your query like:
User.findOneAndUpdate(
{ phone: "0912341234" },
{
$push: {
locations: {
location: {
latitude: 10,
longitude: 10,
},
status: "online",
},
},
},
{ new: true },
);
I was trying to create HRM project using Node and Mongodb (Mongoose) with leave management so for the leave I have created two documents 1. for leavetypes i.e anualLeave, maternityLeave and so on and the other one of taking care of the leave requests taken by the employees.
So here is my schemas and api requests.
// leave schema embedded in leaveTypeSchema
const mongoose = require("mongoose");
const Joi = require("joi-browser");
Joi.objectId = require("joi-objectid")(Joi);
const { branchSchema } = require("./branch");
const { employeeSchema } = require("./employee");
const { leaveTypesSchema } = require("./leaveType");
const leaveSchema = mongoose.Schema({
branch: {
type: branchSchema,
required: true,
},
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},
daysRequested: {
type: Number,
required: true,
},
fromDate: {
type: Date,
required: true,
},
endDate: {
type: Date,
required: true,
},
availableDays: {
type: Number,
},
});
const Leave = mongoose.model("leave", leaveSchema);
//validation
function validateLeave(leave) {
const schema = {
branchId: Joi.objectId().required(),
employeeId: Joi.objectId().required(),
leaveType: Joi.object()
.keys({
anualLeave: Joi.object()
.keys({
id: Joi.objectId().required(),
})
.required(),
})
.required(),
daysRequested: Joi.number().required(),
fromDate: Joi.date().required(),
endDate: Joi.date().required(),
};
return Joi.validate(leave, schema);
}
module.exports.Leave = Leave;
module.exports.Validate = validateLeave;
//route to post leave requests from employees
router.post("/", async (req, res) => {
// validate
const { error } = Validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
// check if branch is valid
let branch = await Branch.findById(req.body.branchId);
if (!branch) return res.status(400).send("Invalid Branch");
// check if employee is valid
let employee = await Employee.findById(req.body.employeeId);
if (!employee) return res.status(400).send("Invalid employee");
// check if leaveType is valid
let leaveType = await LeaveType.findById({
id: ObjectID(req.body.leaveType.anualLeave.id),
});
if (!leaveType) return res.status(400).send("invalid leave Type");
// post the leave request
const leave = new Leave({
branch: {
_id: branch._id,
name: branch.name,
},
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
},
jobTitle: employee.jobTitle,
salary: employee.salary,
},
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
daysRequested: req.body.daysRequested,
fromDate: req.body.fromDate,
endDate: req.body.endDate,
});
await leave.save();
res.send(leave);
Your document doesn't abide by the way you have created your schema.
When you are passing data to model, you have made leavetype nested inside employee
const leave = new Leave({
/**/
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
}, <- here
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
});
whereas in the schema your leaveType is a diff. object property.
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},
I have this document in mongo atlas
_id: 5f8939cbedf74e363c37dd86,
firstname: "Person",
lastname: "Person lastname",
sex: "Masculino",
age: "20",
birthDay: 2020-10-07T00:00:00.000+00:00,
vaccines: Array
0:Object
dose: Array
_id: 5f8939cbedf74e363c37dd87
vaccine:5f7023ad96f7ed21e85be521
createdAt:2020-10-16T06:12:27.726+00:00
updatedAt:2020-10-16T06:12:27.726+00:00
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
This is my mongoose schema
const mySchema = new Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
sex: {
type: String,
required: true,
},
age: {
type: String,
required: true,
},
birthDay: {
type: Date,
required: true,
},
vaccines: [
{
type: new Schema(
{
vaccine: {
type: Schema.ObjectId,
ref: "Vaccine",
},
dose: Array,
},
{ timestamps: true }
),
},
],
});
every time I add a new person the vaccines array gets one new object with the timestamp as you can see, in my js file I use this code:
const addPerson = (person) => {
const myPerson= new Model(person);
return myPerson.save();
};
Then when I add a new vaccine for the same person this does not get the timestamp, I'm using this code for that:
const addPersonVaccine = async ({ params, body }) => {
if (!params) return Promise.reject("Invalid ID");
const vaccines = [body];
const foundPerson = await Model.updateOne(
{
_id: params,
},
{
$push: {
vaccines: vaccines,
},
}
);
return foundPerson;
};
This is what my body inside vaccines array has:
[ { vaccine: '5f72c909594ee82d107bf870', dose: 'Primera' } ]
The problem is that I have no results about the next timestamps, as you can see in my mongo atlas document:
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
Is that the best way to implement timestamps in subdocuments or sub schemas?
I will appreciate your answers, thnks 👏
You can use mongoose schema timestamps options to the inner schemas
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
for more Mongoose schema set timestamp on nested document
Im creating a document named 'Sells', each sell has properties such as date, name, total, the id of the user who created it ect. and I have another document named 'Sell_details' where I store the details of each sell with their sell _id. So how do i retrieve all the sells i have in the database with an array of the details which that sell has? I've tried to do it but it doesn't retrieve all the details and among other problems. Here's what i have.
this is the sell model
const mongoose = require('mongoose');
const validator = require('validator');
const uniqueValidator = require('mongoose-unique-validator');
const ventaSchema = new mongoose.Schema({
usuarioId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
clienteId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
tipo_comprobante: {
type: String,
required: true
},
num_comprobante: {
type: Number,
required: true
},
serie_comprobante: {
type: Number,
required: true
},
fecha: {
type: Date,
default: Date.now()
},
impuesto: {
type: Number,
required: true
},
total: {
type: Number,
required: true
},
estado: {
type: String,
default: 'Activo'
},
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
ventas: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Detalle_Venta'
},
detalles: {
type: Array
}
},{ toJSON: { virtuals: true } });
ventaSchema.plugin(uniqueValidator, { message: 'Expected {PATH} to be unique' });
const Venta = mongoose.model('Venta', ventaSchema);
module.exports = Venta;
his is the sell router:
router.get('/ventas', auth, async(req, res) => {
try {
await req.user.populate({
path: 'ventas',
options: {
limit: req.query.limit
}
}).execPopulate();
req.user.ventas.forEach(element => {
const detalles = Detalle_Venta.find({ventaId: element._id});
detalles.then(el => {
el.forEach(detalle => {
if (element.detalles.length > 1) {
element.detalles.forEach(el => {
const isAdded = el._id === detalle._id;
if (isAdded) {
element.detalles.push(detalle);
}
})
} else {
element.detalles.push(detalle);
}
});
});
element.save();
});
res.send(req.user.ventas);
} catch (error) {
res.status(400).send(error);
}
});
and this is the output from postman:
[
{
"fecha": "2020-06-22T18:16:44.175Z",
"estado": "Activo",
"detalles": [
{
"_id": "5ef0fa4e49de0641c46eca0b",
"idArticulo": "5ee825bfb3da101df49822ae",
"cantidad": 30,
"precio": 2,
"descuento": 0,
"ventaId": "5ef0fa4e49de0641c46eca0a",
"__v": 0
},
{
"_id": "5ef0fa4e49de0641c46eca0b",
"idArticulo": "5ee825bfb3da101df49822ae",
"cantidad": 30,
"precio": 2,
"descuento": 0,
"ventaId": "5ef0fa4e49de0641c46eca0a",
"__v": 0
}
],
"_id": "5ef0fa4e49de0641c46eca0a",
"usuarioId": "5ee3b6b50376d7143c476834",
"clienteId": "5ee6b115e43839274cc50ddb",
"tipo_comprobante": "RNC",
"num_comprobante": 1000000,
"serie_comprobante": 20001,
"impuesto": 18,
"total": 10500,
"owner": "5ef0e64083f8c815cc67cd7c",
"__v": 2,
"id": "5ef0fa4e49de0641c46eca0a"
},
{
"fecha": "2020-06-22T18:16:44.175Z",
"estado": "Activo",
"detalles": [
{
"_id": "5ef0fa5a49de0641c46eca0d",
"idArticulo": "5ee825bfb3da101df49822ae",
"cantidad": 30,
"precio": 2,
"descuento": 0,
"ventaId": "5ef0fa5a49de0641c46eca0c",
"__v": 0
},
{
"_id": "5ef0fa5a49de0641c46eca0e",
"idArticulo": "5ee825bfb3da101df49822ae",
"cantidad": 303,
"precio": 2,
"descuento": 0,
"ventaId": "5ef0fa5a49de0641c46eca0c",
"__v": 0
}
],
"_id": "5ef0fa5a49de0641c46eca0c",
"usuarioId": "5ee3b6b50376d7143c476834",
"clienteId": "5ee6b115e43839274cc50ddb",
"tipo_comprobante": "RNC",
"num_comprobante": 1000000,
"serie_comprobante": 20001,
"impuesto": 18,
"total": 10500,
"owner": "5ef0e64083f8c815cc67cd7c",
"__v": 1,
"id": "5ef0fa5a49de0641c46eca0c"
}
]
Before you say i did it, the first array of the 'detalles' array (which stores the sell details of that sell), i added one sell detail and i'm getting two, and the second array of the other sell, i'm getting two sell details but i added 3 and if i add 4 i'm only getting 2 and i don't know why. is there a way to fix it and another best way to do it ?
mongoose has a powerful aggregation operator called populate which you have used.
You should use type ObjectId to the property sell details.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
const storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
Then you create the author and story linking them up
const author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
author.save(function (err) {
if (err) return handleError(err);
const story1 = new Story({
title: 'Casino Royale',
author: author._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// that's it!
});
});
You can thereafter populate
Story.
findOne({ title: 'Casino Royale' }).
populate('author').
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
});
This will ensure you don't get an entry twice..
I commented the line of code with // This Command Does not work where I suspect that it is breaking. In the debug log of mongoose, the output looks like this: But nothing is added to the medicineIds array in the Monday object for the DaysOfWeek schema.
The following is the debug output for DayOfWeek.findOneAndUpdate() where I push back onto the array, and am not seeing the result in my mongo database.
Mongoose: dayofweeks.insertOne({ medicineIds: [], _id: 'Monday', __v: 0 }, { session: null }) // <- response to $push
Mongoose: medicines.insertOne({ times: [ 1, 2 ], dayNames: [ 'Monday' ], _id: ObjectId("5e73d816d54b1202e15bb96b"), nam
e: 'Provolone', count: 23, __v: 0 }, { session: null })
Mongoose: dayofweeks.findOne({ _id: 'Monday' }, { projection: {} })
Mutation
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addDayOfWeek: {
type: DayOfWeekType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) }
},
resolve(parent, args) {
let dayOfWeek = new DayOfWeek({
_id: args.name,
medicineIds: new Array()
});
return dayOfWeek.save();
}
},
addNewMedicine: {
type: MedicineType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
count: { type: new GraphQLNonNull(GraphQLInt) },
times: { type: new GraphQLNonNull(GraphQLList(GraphQLInt))},
dayNames: { type: new GraphQLNonNull(GraphQLList(GraphQLString))}
},
resolve (parent, args) {
let medicine = new Medicine({
name: args.name,
count: args.count,
times: args.times,
dayNames: args.dayNames
});
args.dayNames.forEach((dayId) => {
DayOfWeek.findOneAndUpdate( // This Command Does Not Work:
// medicine._id, dayId are correct at this point of the
//code
{ _id: dayId },
{ $push: { medicineIds: medicine._id }},
{ new: true, useFindAndModify: false }
);
});
return medicine.save();
}
}
}
});
DayOfWeek Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const dayOfWeekSchema = new Schema({
_id: String,
medicineIds: [String] // I am trying to push onto this array
});
module.exports = mongoose.model('DayOfWeek', dayOfWeekSchema);
Medicine Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const medicineSchema = new Schema({
id: String,
count: Number,
name: String,
times: [Number],
dayNames: [String]
});
module.exports = mongoose.model('Medicine', medicineSchema);
await Promise.all(args.dayNames.map(dayName => {
return DayOfWeek.findOneAndUpdate({ _id: dayName }, { $push: { medicineIds: medicine._id }});
})).catch((err) => console.error(err));
return await medicine.save();
I just did that and it works. hmm.