How to set minimum value great then 0 in mongoose schema? - node.js

I think that it was easy question, but I am puzzled.
How Can I add minimum value to mongoose > 0?
var customer = new Schema({
cash: {
type: Number,
minimum: 0
}
});
this code allow 0 value, but I want to do > 0
I know, I can do this
var customer = new Schema({
cash: {
type: Number,
minimum: 0
},
validate: {
validator: function(value) {
return value > 0;
},
message: 'cash need to be > 0'
},
});
*cash is float, and can be very small
But it's too long, Is there an easier way?

You ca specify the min while defining the schema.
var breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Too few eggs'],
max: 12
},
bacon: {
type: Number,
required: [true, 'Why no bacon?']
},
drink: {
type: String,
enum: ['Coffee', 'Tea'],
required: function() {
return this.bacon > 3;
}
}
});
var Breakfast = db.model('Breakfast', breakfastSchema);
`
var badBreakfast = new Breakfast({
eggs: 2,
bacon: 0,
drink: 'Milk'
});
var error = badBreakfast.validateSync();
assert.equal(error.errors['eggs'].message,
'Too few eggs');
assert.ok(!error.errors['bacon']);
assert.equal(error.errors['drink'].message,
'`Milk` is not a valid enum value for path `drink`.');
badBreakfast.bacon = 5;
badBreakfast.drink = null;
error = badBreakfast.validateSync();
assert.equal(error.errors['drink'].message, 'Path `drink` is required.');
badBreakfast.bacon = null;
error = badBreakfast.validateSync();
assert.equal(error.errors['bacon'].message, 'Why no bacon?');

http://mongoosejs.com/docs/api.html#schema-number-js
Try this:
var customer = new Schema({
cash: {
type: Number,
min: 1
}
});
I assume you are using mongoose.js?

Related

How to update deeply nested documents in mongoose v6.2.2

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.

Mongoose check reference id

I'm a newbie to mongoose and express. Tried to find an answer to my question, but no luck. I have the following schemas:
var Schema = mongoose.Schema;
var Project= new Schema ({
name: {
type: String,
required: true,
min: 5,
max: 255
},
description: {
type: String,
min: 5,
max: 500
},
status: {
type: String,
min: 5,
max: 255
},
priority: {
type: String,
min: 5,
max: 255
},
progress: {
type: String,
min: 5,
max: 255
} ,
_user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
module.exports= mongoose.model('Project', Project);
var Schema = mongoose.Schema;
var IssueSchema= new Schema ({
description: {
type: String,
min: 5,
max: 500,
required: true
},
status: {
type: String,
min: 5,
max: 255,
default: "Open"
},
priority: {
type: String,
min: 5,
max: 255
},
deadline:{
type: Date
},
_project: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project'
}
});
module.exports= mongoose.model('Issue', IssueSchema);
I'm working on create Issue middleware for the route /projectId/issues and have the code
const bodyParser = require('body-parser');
const Issue = require ('../models/Issue');
var Project = require ('../models/Project');
const { handleError,ErrorHandler } = require('../helpers/error');
exports.createIssue = (req, res,next) => {
Project.findById(req.params.id);
if (req.params.id!= null) {
console.log('Project found');
const {description, deadline, priority, status} = req.body;
var issue = new Issue({
description,
deadline,
status,
priority,
_project:req.params.id
});
issue.save();
res.status(200).send("Issue created!")}
else {
err = new Error('Project ' + req.params.id + ' not found');
err.status = 404;
return next(err);
}
};
And it's not working :( What I'm trying to achieve is to check if projecID exists before saving "issue" document. Any ideas on how to implement this?
Project.findById accepts a callback with parameters error and document
Project.findById(req.params.id, (error, project) => {
console.log(project) // project will be null if not found.
// handle project !== null here
})
You are checking whether or not an id was passed to params:
if (req.params.id!= null)
But this does not tell you whether there is a Project associated with that id. You need to check the return value of Project.findById(req.params.id). Some like:
let result = Project.findById(req.params.id)
if(!result.length) console.log('No project found")
In this case, I'm pretty sure the result variable will be an array however, it may also be an object so console.log(result) and see what you get. Then write you conditional statement based on that.
Also, if you want to catch any errors, you need to use a try/catch block instead of if/else.

Person Age Virtual Property in Mongoose

I would like to know if there is a simple way in Mongoose of getting a person's age from the associated birthdate in the schema.
// Define the User schema
const Schema = mongoose.Schema;
const UserSchema = new Schema({
id: { type: Number, required: true, unique: true },
first_name: { type: String, required: true, max: [15, 'Too long name'] },
last_name: { type: String, required: true, max: [15, 'Too long surname'] },
gender: { type: String, required: true, enum: ['M', 'F'] },
image: { data: Buffer, contentType: String }
birthdate: { type: Date, required: true }
}, {
collection: 'Users'
});
How to implement this?
// Virtual for user's age
UserSchema
.virtual('age')
.get(function() {
...
});
You can manipulate the date like bellow :
Short answer :
const birthdate = new Date(687882497000);
// replace birthdate by this.birthdate
const nbYearRounded = Math.floor((Date.now() - birthdate.getTime()) / (1000 * 3600 * 24 * 365));
console.log(nbYearRounded);
Exxplaination :
const date = new Date(687882497000);
const timeInMs = date.getTime();
const diffInMs = Date.now() - timeInMs;
const nbDay = diffInMs / 1000 / 3600 / 24;
const nbYear = nbDay / 365;
const nbYearRounded = Math.floor(nbYear);
console.log(date);
console.log(timeInMs);
console.log(diffInMs);
console.log(nbDay);
console.log(nbYear);
console.log(nbYearRounded);
Here is the documentation about VirtualType.prototype.get().
Given example :
var virtual = schema.virtual('fullname');
virtual.get(function () {
return this.name.first + ' ' + this.name.last;
});
This should work
UserSchema.virtual('age').get(function(){
return Math.floor((Date.now() - this.birthdate.getTime()) / (1000 * 3600 * 24 * 365));
});

Virtual in Mongoose Object is Undefined?

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

How to access a virtual attribute from within another virtual using Mongoose

I have an Invoice model that uses virtual attributes to compute values for tax, subtotal, total etc. The problem I have is that some of the virtual attributes need to be able to reference other virtual attributes.
For example, here is the Mongoose schema for the Invoice:
var InvoiceSchema = Schema({
number: String,
customer: {ref:String, email:String},
invoiceDate: {type: Date, default: Date.now},
dueDate: {type: Date, default: Date.now},
memo: String,
message: String,
taxRate: {type:Number, default:0},
discount: {
value: {type:Number, default:0},
percent: {type:Number, default:0}
},
items: [ItemSchema],
payment: {type: Schema.Types.ObjectId, ref: 'Payment'}
});
InvoiceSchema.virtual('tax').get(function(){
var tax = 0;
for (var ndx=0; ndx<this.items.length; ndx++) {
var item = this.items[ndx];
tax += (item.taxed)? item.amount * this.taxRate : 0;
}
return tax;
});
InvoiceSchema.virtual('subtotal').get(function(){
var amount = 0;
for (var ndx=0; ndx<this.items.length; ndx++) {
amount += this.items[ndx].amount;
}
return amount;
});
InvoiceSchema.virtual('total').get(function(){
return this.amount + this.tax;
});
InvoiceSchema.set('toJSON', { getters: true, virtuals: true });
var ItemSchema = Schema({
product: String,
description: String,
quantity: {type: Number, default: 1},
rate: Number,
taxed: {type: Boolean, default: false},
category: String
});
ItemSchema.virtual('amount').get(function(){
return this.rate * this.quantity;
});
ItemSchema.set('toJSON', { getters: true, virtuals: true });
module.exports = mongoose.model('Invoice', InvoiceSchema);
Now to understand the issue take a look at the virtual definition for 'tax' ...
InvoiceSchema.virtual('tax').get(function(){
var tax = 0;
for (var ndx=0; ndx<this.items.length; ndx++) {
var item = this.items[ndx];
tax += (item.taxed)? item.amount * this.taxRate : 0;
}
return tax;
});
... in this example item.amount, when called inside a virtual, doesn't use the virtual getter for item.amount.
Is there some way to tell Mongoose that I need to use the getter instead of trying to read a property that doesn't exist?
Did you try item.get('amount')?
That seems to be the explicit way of using virtuals.
Got it from this Issue:
https://github.com/Automattic/mongoose/issues/2326
Didn't find anything else related unfortunately.

Resources