I've been picking up MongoDB and Mongoose recently in an effort to learn the MEAN stack. Using this course: https://www.edx.org/course/introduction-mongodb-using-mean-stack-mongodbx-m101x I am attempting to create a virtual for my product schema that displays the price in a more user-friendly way. However, when printing out this virtual displayPrice it comes up as undefined unless accessed through toObject or toJSON and even then the USD symbol appears as a question mark. I apologize for any stupidity and obvious overlooks, I am new to this database stuff and can't find many tutorials that explain things simply.
Here is the code:
var mongoose = require("mongoose");
var Category = require("./categoryschema.js");
var productSchema = {
name: {
type: String,
required: true
},
// Pictures must start with http://"
pictures: [{ type: String, match: /^http:\/\//i }],
price: {
amount: {
type: Number,
required: true
},
currency: {
type: String,
enum: ["USD", "EUR", "GBP"],
required: true
}
},
category: Category.categorySchema
};
module.exports = new mongoose.Schema(productSchema);
module.exports.productSchema = productSchema;
var schema = new mongoose.Schema(productSchema);
var Product = mongoose.model("Product", schema);
var currencySymbols = {
"USD": "$",
"EUR": "E",
"GBP": "P"
};
// Make human readable string form of price. "$25" instead of "25 USD"
schema.virtual("displayPrice").get(function() {
return currencySymbols[this.price.currency] + "" + this.price.amount;
});
schema.set("toObject", { virtuals: true });
schema.set("toJSON", { virtuals: true });
var p = new Product({
name: "test product",
price: {
amount: "33.58",
currency: "USD"
}
});
console.log(p.displayPrice);
p.price.amount = 20;
console.log(p.displayPrice);
p.price.currency = "EUR";
console.log(JSON.stringify(p));
var obj = p.toObject();
console.log(obj.displayPrice);
Output:
undefined
undefined
{"name":"test product","_id":"569c39774093336c149eba2c","category":{"ancestors":
[]},"price":{"amount":20,"currency":"EUR"},"pictures":[],"displayPrice":"E20","i
d":"569c39774093336c149eba2c"}
E20
The only thing that seemed to work is assigning the virtual to the price key. I have no idea why this was the solution so any answer would be appreciated. It only seemed to change directly logging the virtual's value.
New productSchema:
var mongoose = require("mongoose");
var Category = require("./categoryschema.js");
var fx = require("./fx.js");
var productSchema = {
name: {
type: String,
required: true
},
// Pictures must start with http://"
pictures: [{ type: String, match: /^http:\/\//i }],
price: {
amount: {
type: String,
required: true,
set: function(v) {
this.internal.approximatePriceUSD = v / (fx()[this.price.currency] || 1);
return v;
}
},
currency: {
type: String,
enum: ["USD", "EUR", "GBP"],
required: true,
set: function(v) {
this.internal.approximatePriceUSD = this.price.amount / (fx()[v] || 1);
return v;
}
}
},
category: Category.categorySchema,
internal: {
approximatePriceUSD: Number
}
};
var schema = new mongoose.Schema(productSchema);
var Product = mongoose.model("Product", schema);
var currencySymbols = {
"USD": "$",
"EUR": "E",
"GBP": "P"
};
// Make human readable string form of price. "$25" instead of "25 USD"
schema.virtual("price.displayPrice").get(function() {
return currencySymbols[this.price.currency] + "" + this.internal.approximatePriceUSD;
});
schema.set("toObject", { virtuals: true });
schema.set("toJSON", { virtuals: true });
var p = new Product({
name: "test product",
price: {
amount: "12.50",
currency: "USD"
},
category: {
name: "test"
}
});
console.log(p.price.displayPrice);
console.log(p.price.currency + ": " + p.internal.approximatePriceUSD);
p.price.currency = "EUR";
console.log(p.price.displayPrice);
console.log(p.price.currency + ": " + p.internal.approximatePriceUSD);
p.price.currency = "GBP";
console.log(p.price.displayPrice);
console.log(p.price.currency + ": " + p.internal.approximatePriceUSD);
module.exports = new mongoose.Schema(productSchema);
module.exports.productSchema = productSchema;
New output:
$12.5
USD: 12.5
E11.363636363636363
EUR: 11.363636363636363
P8.333333333333334
GBP: 8.333333333333334
Related
Hello I'm making an Amazon clone, and I have this error
This is my code:
models/product.js
const mongoose = require("mongoose");
const ratingSchema = require("./rating");
const productSchema = mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: true,
trim: true,
},
images: [
{
type: String,
required: true,
},
],
quantity: {
type: Number,
required: true,
},
price: {
type: Number,
required: true,
},
category: {
type: String,
required: true,
},
ratings: [ratingSchema],
});
const Product = mongoose.model("Product", productSchema);
module.exports = { Product, productSchema };
routes/product.js
const ratingSchema = {
userId: req.user,
rating,
};
product.ratings.push(ratingSchema);
product = await product.save();
res.json(product);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
productRouter.get("/api/deal-of-day", auth, async (req, res) => {
try {
let products = await Product.find({});
products = products.sort((a, b) => {
let aSum = 0;
let bSum = 0;
for (let i = 0; i < a.ratings.length; i++) {
aSum += a.ratings[i].rating;
}
for (let i = 0; i < b.ratings.length; i++) {
bSum += b.ratings[i].rating;
}
return aSum < bSum ? 1 : -1;
});
res.json(products[0]);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
module.exports = productRouter;
I read a lot about this problem and to solve this error it is just adding an S on “exports” but I have it and got this error, so I don't know what I am missing.
For database language I'm using MongoDB, and I edit my user to admin profile and when I did that throw me this error, this is what I mean:
_id
63597582b96d28bbf4fba
name
"test"
email
"test#gmail.com"
password
"$0ZmF.eCD5zmB.etNxmGn22tTQqrCsrXkWMhO"
address
""
type
"admin "
__v
0
I am trying to update deeply nested documents and confusing myself with all of the nesting. Below is my model and code so far. I want to update 'purchased' value of inventory based on the size variable that is passed in. I was reading about arrayFilters but I still cannot figure it out.
model:
const mongoose = require('mongoose');
const inventorySchema = new mongoose.Schema({
size: {
type: String,
},
purchased: {
type: Number,
},
used: {
type: Number,
},
});
const kidsSchema = new mongoose.Schema({
firstName: {
type: String,
trim: true,
minlength: 1,
maxlength: 99,
},
currentChild: {
type: Boolean,
},
brandPreference: {
type: String,
trim: true,
minlength: 1,
maxlength: 99,
},
currentSize: {
type: String,
},
currentSizeLabel: {
type: String,
},
lowAlert: {
type: String,
},
diaperHistory: [diaperHistorySchema],
totalPurchased: {
type: Number,
},
totalUsed: {
type: Number,
},
inventory: [inventorySchema],
});
const KidsRecordSchema = new mongoose.Schema({
kids: [kidsSchema],
});
const KidsRecord = mongoose.model('KidsRecord', KidsRecordSchema);
exports.KidsRecord = KidsRecord;
code:
/**
* #description PUT add diapers to kids inventory
*/
router.put('/update/:size', auth, async (req, res) => {
let id = req.body.user_id;
let kidID = req.body.kids_id;
let size = req.params.size;
let purchased = req.body.purchased;
try {
let record = await KidsRecord.findOne({ user_id: id });
let subRecord = record.kids.id(kidID);
let inventory = subRecord.inventory.filter((x) => x.size == size);
console.log('inventory', inventory);
// inventory give me:
// [{ "size": "0", "purchased": 0, "used": 0, "_id": "625e91571be23abeadbfbee6"}]
// which is what I want to target but then how do I apply $set to it?
// $set... ??
if (!subRecord) {
res.send({ message: 'No kids for this user.' });
return;
}
res.send(inventory);
} catch (error) {
res.send({ message: error.message });
}
});
I can drill down and find the correct inventory object I want to update, but not sure how to actually change in and save.
The code is show below.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const user = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
resetToken: String,
resetExpiration: String,
products: [{type: mongoose.Types.ObjectId, required: true, ref: 'Shop'}],
cart: {
items: [
{
productId: {type: mongoose.Types.ObjectId, ref: 'Shop', required: true},
quantity: {type: Number, required: true},
}
]
},
});
user.methods.addToCart = (product) => {
const itemIndex = this.cart.items.findIndex(prod => {
return prod.productId.toString() === product._id.toString();
});
let newQuantity = 1;
const updatedCartItems = [...this.cart.items];
if(itemIndex >= 0) {
newQuantity = this.cart.items[itemIndex].quantity + 1;
updatedCartItems[itemIndex].quantity = newQuantity;
} else {
updatedCartItems.push({
productId: product,
quantity: newQuantity
});
}
const updatedCart = {
items: updatedCartItems
}
this.cart = updatedCart;
return this.save();
}
const model = mongoose.model('User', user);
module.exports = model;
I am trying to store product in the cart instance method as per above schema, but when i send product from my controller to addToCart it says items is undefined on this.cart.items. I haven't used instance method much in mongoose so, i don't know this issue is it with schema or general problem.
let me know if you need any other information.
It was a silly mistake, actually i was using arrow function. so it wasn't bind to schema.
In this application, I am trying to update a field value, if it is successful then I want to save into a log collection, however, not all the data are saved in the log collection, I am not sure if I am doing it the right way, would appreciate if someone could help out.
here is the query :
// both models(Log & Inventory are imported)
router.get("/add_product/:id/:num/:quantity/:order", (req, res) => {
var id = req.params.id;
var quantity = req.params.quantity;
var order = req.params.order;
// console.log('id----', id);
var num_mod = req.params.num;
var modified_count = parseInt(num_mod) - parseInt(quantity);
console.log("num_mod----", num_mod);
Inventory.findByIdAndUpdate(id, { quantity: parseInt(num_mod) }, { new: true }, function(
err,
inventory
) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log(inventory.name);
const newLog = new Log({
name: inventory.name,
description: inventory.description,
price: parseInt(inventory.price),
quantity: parseInt(inventory.quantity),
modified_quantity: parseInt(modified_count),
itemDest: order //this is not being saved
});
newLog.save(function(err, Log) {
if (err) {
console.log(err);
} else {
console.log("add log success");
res.send(inventory);
}
});
}
});
});
URL from front end :
// order is a string
here is the Log schema :
const mongoose = require("mongoose");
const LogSchema = new mongoose.Schema(
{
// _id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
quantity: { type: Number, required: true },
modified_quantity: { type: Number, required: true },
supplier: String,
taxable: Boolean,
itemDest: String
},
{ timestamps: true }
);
// Create model from the schema
const Log = mongoose.model("Log", LogSchema);
// Export model
module.exports = Log;
and here is the inventory schema
const mongoose = require("mongoose");
//create Schema
const InventorySchema = new mongoose.Schema(
{
// _id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
quantity: { type: Number, required: true },
supplier: String,
taxable: Boolean
},
{ timestamps: true }
);
// Create model from the schema
const Inventory = mongoose.model("Inventory", InventorySchema);
// Export model
module.exports = Inventory;
My issue is with this line "itemDest: order" in the query, I intend to save the value of "order" extracted from "req.params.order" into "itemDest" but it doesn't save.
I have set of products indexed in elasticsearch. I`m searching for "title" on my schema.
When I search "fre" or "fresh" I see a result.
But when I search for "small fresh" I don't see any result.
Is it possible to use wildcard with spaces ?
I added es_indexed: "not_analyzed" but no luck.
This is my Product Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var mongoosastic = require("mongoosastic");
const deepPopulate = require("mongoose-deep-populate")(mongoose);
var Owner = require("./user");
var Category = require("./category");
var Reviews = require("./review");
const ProductSchema = new Schema(
{
category: {
type: Schema.Types.ObjectId,
ref: "Category",
es_indexed: true,
es_type: "nested",
es_include_in_parent: true
},
owner: {
type: Schema.Types.ObjectId,
ref: "User",
es_indexed: true,
es_type: "nested",
es_include_in_parent: true
},
reviews: [
{
type: Schema.Types.ObjectId,
ref: "Review",
es_indexed: true,
es_type: "nested",
es_include_in_parent: true
}
],
image: { type: String, es_indexed: true },
title: {
type: String,
es_indexed: "not_analyzed"
},
description: { type: String, es_indexed: true },
price: { type: Number, es_indexed: true },
crated: { type: Date, default: Date.now, es_indexed: true }
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
}
);
ProductSchema.virtual("averageRating").get(function() {
var rating = 0;
if (this.reviews.length == 0) {
rating = 0;
} else {
this.reviews.map(review => {
rating += review.rating;
});
rating = rating / this.reviews.length;
}
return rating;
});
ProductSchema.plugin(deepPopulate);
ProductSchema.plugin(mongoosastic, {
populate: [{ path: "category" }, { path: "owner" }, { path: "reviews" }]
});
let Model = mongoose.model("Product", ProductSchema);
Model.createMapping(function(err, mapping) {
if (err) {
console.log("error creating mapping (you can safely ignore this)");
console.log(err);
} else {
console.log("mapping created!");
console.log(mapping);
}
});
var stream = Model.synchronize();
var count = 0;
stream.on("data", (err, doc) => {
console.log(doc);
count++;
});
stream.on("close", () => console.log("indexed " + count + " documents!"));
stream.on("error", err => console.log(err));
Model.SyncToAlgolia();
Model.SetAlgoliaSettings({
searchableAttributes: ["title"]
});
module.exports = Model;
This is my Search function
async function search(frompage) {
let fullString = "*" + "small fresh" + "*";
let startsFrom = frompage * 10;
console.log(fullString);
const response = await esClient.search({
index: "products",
type: "product",
from: startsFrom,
body: {
query: {
wildcard: {
title: fullString
}
}
}
});
return response;
}