Calculate custom property based on populate element - node.js

I have two Schemas : recipe and product
var recipeSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
products: [{
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
productWeight: Number,
productPrice: Number
}]
})
var productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
weight: {
type: Number,
required: true
},
price: {
type: Number,
required: true
}
})
And I have addRecipe function
module.exports.addRecipe = function(req, res){
Recipe
.create({
name: req.body.name,
products: req.body.products
}, function(err, recipe){
if(err){
console.log("Error creating recipe");
res
.status(400)
.json(err);
} else {
console.log("Recipe created", recipe);
res
.status(201)
.json(recipe);
}
})
}
I'd like to calculate productPrice for every object in array (productPrice = product.price * productWeight / product.Weight).
My posted JSON.
{
"name": "Cake",
"products": [
{
"product": "59728a3f7765441b503e31bc",
"productWeight": 100
},
{
"product": "59728a3f7765441b503e31bd",
"productWeight": 200
},
{
"product": "59728a3f7765441b503e31be",
"productWeight": 50
},
{
"product": "59728a3f7765441b503e31bf",
"productWeight": 500
}
]
}
I'd like also update productPrice on product or reicpe edit.
Is it possible to do this?
Thanks!

I would use a virtual for this, but I don't think it is possible to use it without introducing another schema.
var RecipeProductSchema = new mongoose.Schema({
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
productWeight: Number
});
// note: product must be populated before calling this property
RecipeProductSchema.virtual('productPrice').get(function() {
return this.product.price * this.productWeight / this.product.weight;
});
var RecipeSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
products: [RecipeProductSchema]
})

Related

query multiple nested objects

I have a problem while querying mongodb with nested multiple objects.
I am trying like this
Project.find()
.then((project) => {
RoadMap.find({
scheduledDate: {
$gte: new Date(req.params.gte), $lt: new
Date(req.params.lt)
}
})
.populate("roadMap", "_id title")
.populate("projectId", "_id title photo ")
.exec((err, roadmap) => {
if (err) {
return res.status(422).json({ error: err });
}
res.json({ project, roadmap });
});
})
.catch((err) => {
return res.status(404).json({ error: "project not found" });
});
I am getting results like this
{
project: {
}
roadmap: [{}{}]
}
but I want to achieve like this
{
project: {
_id:
title:
roadmap: [{},{}]
}
}
this is my schema:
projectShema:
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema.Types;
const projectSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
photo: {
type: String,
required: true,
},
caption: {
type: String,
},
postedBy: {
type: ObjectId,
ref: "User",
},
news: [
{
type: ObjectId,
ref: "News",
},
],
roadMap: [
{
type: ObjectId,
ref: "RoadMap",
},
],
},
{ timestamps: true }
);
mongoose.model("Project", projectSchema);
roadMapSchema:
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema.Types;
const roadmapSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
postedBy: {
type: ObjectId,
ref: "User",
},
projectId: { type: ObjectId, ref: "Project" },
categoryName: { type: String },
status: {
type: String,
default: "created",
},
},
{ timestamps: true }
);
mongoose.model("RoadMap", roadmapSchema);
I am not sure how to achieve the results, do I need to change schema or it is possible here also?
thank you

Mongoose Trying to populate

I have some issues
Cant populate CartProduct, just show the ObjectId.
There is way to make every time that CartProduct create, add automatically to? cart.
is this the right way of schemas structure?
Cart
const CartSchema = new Schema({
active: { type: Boolean, required: true, default: true },
createAt: { type: Date, default: Date.now },
client: { type: Schema.Types.ObjectId, ref: "User", required: true },
products: [{ type: Schema.Types.ObjectId, ref: "CartProduct" }],
});
const Cart = model("Cart", CartSchema);
Cart Product
const CartProductSchema = new Schema({
item: { type: Schema.Types.ObjectId, ref: "Product", required: true },
cart: { type: Schema.Types.ObjectId, ref: "Cart", required: true },
quantity: { type: Number, required: true },
totalPrice: { type: Number, required: true },
});
const CartProduct = model("CartProduct", CartProductSchema);
Product
const ProductSchema = new Schema({
name: { type: String, required: true },
price: { type: Number, required: true },
image: { type: String, required: true },
category: { type: Schema.Types.ObjectId, ref: "Category", require: true },
});
const Product = model("Product", ProductSchema);
Cart Controller
router.post("/", async (req, res) => {
try {
const { userId } = req.body;
const cart = await Cart.findOne({ client: userId
}).populate("CartProduct");
if (cart === null) {
const newCart = new Cart({
client: userId,
});
await newCart.save();
return res.status(201).send({ cart: newCart });
}
res.status(200).send({ cart });
} catch (error) {
res.status(500).send(error);
}
});
Add Product to Cart
router.post("/addProductToCart", async (req, res) => {
try {
const { item, cart, quantity, price } = req.body;
const newProduct = new CartProduct({
item,
cart,
quantity,
totalPrice: price * quantity,
});
await newProduct.save();
await Cart.findOneAndUpdate(
{ _id: cart },
{ $push: { products: newProduct } },
{
new: true,
}
);
res.status(201).send({ message: "New Product Added To Cart" });
} catch (error) {
res.status(500).send(error);
}
});
adding product to cart does working,
but populate not working
adding the output
{
"cart": {
"active": true,
"products": [
"602bc081daf867167c2eb5da"
],
"_id": "602aab802f625d1654805ef0",
"client": "601c50211c94cf5d642c67fb",
"createAt": "2021-02-15T17:12:32.997Z",
"__v": 0
}
}
your missing { in cartSchema
const CartSchema = new Schema({
active: { type: Boolean, required: true, default: true },
createAt: { type: Date, default: Date.now },
client: { type: Schema.Types.ObjectId, ref: "User", required: true },
products: [{ type: Schema.Types.ObjectId, ref: "CartProduct" }],
});
export the the models like this
module.exports = Cart
There is way to make every time that CartProduct create, add automatically to? cart
there is not a automatic way for adding new _id of CartProduct to collection, so you should use findOneAndUpdate() or find() and push in to products array and save()
is this the right way of schemas structure
yes, It is.
so for populate you can try:
let resultCarts = await Cart.find(filter).populate("products")
let resultProducts = await Product.find(filter).populate("category")
so change the CartProduct to products because you should pass name of field as a argument not name of schema
await Cart.findOne({ client: userId
}).populate("products");

How can I update some fields of an embedded object using mongoose's findOneAndUpdate method and not lose the other fields?

router.put('/experience/update/:exp_id',
auth,
async (req, res) => {
const {
title,
company,
location,
from,
to,
current,
description
} = req.body;
const newExp = {};
newExp._id = req.params.exp_id;
if (title) newExp.title = title;
if (company) newExp.company = company;
if (location) newExp.location = location;
if (from) newExp.from = from;
if (to) newExp.to = to;
if (current) newExp.current = current;
if (description) newExp.description = description;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//UPDATE Experience
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
res.json(profile);
} catch (error) {
console.log(error.message);
res.status(500).send('Internal Server Error');
}
}
)
I am using the findOneAndUpdate method to update the experience field inside a profile mongoose model.
After accesssing the endpoint, I put the updated details, for eg. company and location. But I lose all the other fields. So how can I update only select fields while others remain unchanged ?
Below is the profile schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date,
required: true
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedIn: {
type: String,
},
instagram: {
type: String,
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
There are some problems in your code.
You are passing only one argument to findOneAndUpdate. Ideally the syntax is findOneAndUpdate(filter, update). So basically you need to pass update query as 2nd argument.
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
In below code you are modifying the profile object and saving it. Which is not required. And this is also the reason why you are losing fields.
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
Solution-
We need to figure out the update part of findOneAndUpdate(filter, update).
Here is the update query -
db.collection.update({
"user": "5f96dc85ac5ae03160a024a8",
"experience._id": "5f9826c3a3fa002ce0f11853"
},
{
"$set": {
"experience.$": {
"current": false,
"_id": "5f9826c3a3fa002ce0f11853",
"title": "Senior developer",
"company": "Morgan Stanley",
"location": "Pune",
"from": "2017-04-30T18:30:00.000Z",
"to": "2020-07-08T18:30:00.000Z",
"description": "testing"
}
}
})
Try it here
Trying Mongoose way :
const filter = { user: req.user.id, "experience._id": req.params.exp_id }
const update = { $set: { "experience.$": newExp } }
profile = await Profile.findOneAndUpdate(filter,update);

Populate() Mongoose is not returning joined data

Well, I´am trying to retrieve Name and Lastname from user who created the document but it´s not working, it still returning Mongo´s Id
This is my areas model
var mongo = require('mongoose'),
validator = require('mongoose-unique-validator'),
Schema = mongo.Schema
var model = new Schema({
NAME: { type: String, required: true, unique: true, max: 50, min: 3 },
STATUS: { type: String, default: 'active' },
ADDED_BY: { type: Schema.Types.ObjectId, ref: 'users' },
ADDED_DATE: { type: Date, default: Date.now }
}, {collection :'areas'})
model.plugin( validator, { message: 'The {PATH} is not valid or duplicated' } )
module.exports = mongo.model('Area', model )
This is the user model
var mongo = require('mongoose'),
validator = require('mongoose-unique-validator'),
Schema = mongo.Schema
var userSchema = new Schema({
PERSONAL_DATA: {
NAME: { type: String, required: [ true, 'The name is necessary' ], max: 50 },
LAST_NAME: { type: String, required: [ true, 'the lastname is necessary' ], max: 100 },
PHOTO: { type: String, max: 100 },
BIRTHDAY: { type: Date },
MARITIAL_STATUS: { type: Schema.Types.ObjectId, ref: 'maritial_statuses' },
GENDER: { type: String, max: 1 },
EMAIL: { type: String, required: true },
},
COMPANY_DATA: {
JOB: { type: Schema.Types.ObjectId, ref: 'jobs' },
AREA: { type: Schema.Types.ObjectId, ref: 'areas' },
ROLE: { type: Schema.Types.ObjectId, ref: 'roles' },
BOSS: { type: Schema.Types.ObjectId, ref: 'users' },
}
}, { collection: 'users' } )
model.plugin( validator, { message: 'The {PATH} is not valid or duplicated' } )
module.exports = mongo.model('User', userSchema )
And this is my areas route
var express = require('express'),
model = require('../../models/catalogs/areas'),
app = express()
app.get('/:from', (req, res) => {
var from = parseInt( req.params.from )
model.find()
.sort('NAME').populate({ path: 'users', select: 'NAME LAST_NAME'})
.limit(10).skip(from)
.exec((error, data) => {
if (error) {
return res.status(500).json({
success: false,
error
})
}
res.status(200).json({
success: true,
data
})
})
})
module.exports = app
The response is
{
"success": true,
"data": [
{
"STATUS": "active",
"_id": "5c547f4adadf433914f72c8c",
"NAME": "Contabilidad y Finanzas",
"ADDED_BY": "5c4f562deec6f4defeea759b",
"ADDED_DATE": "2019-02-01T17:18:02.680Z",
"__v": 0
},
{
"STATUS": "active",
"_id": "5c547f3edadf433914f72c8b",
"NAME": "Tecnologías",
"ADDED_BY": "5c4f562deec6f4defeea759b",
"ADDED_DATE": "2019-02-01T17:17:50.579Z",
"__v": 0
}
]
}
As you seen, ADDED_BY is a field joined to Users and I want to retrieve that information. I don´t know what is wrong with my code.

Accessing object in array of arrays using mongoose

I have the following structure and am trying to remove an object in participants (league.division.participants).
var participantSchema = new mongoose.Schema({
player: { type: mongoose.Schema.Types.ObjectId, ref: 'Player' },
record: { type: mongoose.Schema.Types.ObjectId, ref: 'ParticipantRecord' },
events: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Event' } ]
});
var divisionSchema = new mongoose.Schema({
name: String,
participants: [ participantSchema ]
});
var leagueSchema = new mongoose.Schema({
name: String,
startDate: { type: Date, default: Date.now },
endDate: Date,
locked: Boolean,
leagueType: { type: mongoose.Schema.Types.ObjectId, ref: 'LeagueType' },
game: { type: mongoose.Schema.Types.ObjectId, ref: 'Game' },
divisions: [ divisionSchema ],
});
mongoose.model('League', leagueSchema);
var _RemoveDivisionParticipant = function(participantId)
{
return new Promise((resolve,reject) =>{
Models.League.findOne({'divisions.participants._id':participantId})
.populate('divisions')
.populate('divisions.participants')
.exec((err, league) => {
if (err) {return reject(err)}
league.divisions(XXXXX).participants(participantId).remove();
console.log(league.divisions[0].participants[0])
})
})
}
This is what i have so far, but obviously it returns the league object, and i have no way of getting to the participants since I don't know which division the participant is in (Shown by XXXXX in the sample). Any pointers as to what I should do?
You can use $pull to remove an array element based on a condition :
League.update({
'divisions.participants._id': participantId
}, {
$pull: {
'divisions.$.participants': {
"_id": participantId
}
}
}, { multi: true }, function(err, res) {
console.log(res);
});

Resources