How to add new property to mongoDb model? - reactjs

Hello I am creating a project to show user changes in the PopulationCount data of all the coins according to their categories.
I created a NodeJS script to scrape the data from a coin website, and I have saved it in my MongoDB cloud database as shown below.
In the Front-End, I have created a React App as shown:
When user clicks on a coin category, it should display all the coins from today
Here is the code that relates to showing all the Coin's Categories in my route:
router.get(
"/categories",
asyncHandler(async (req, res) => {
const categories = await Coin.find().distinct(
"category",
(err, results) => {
res.json(results);
}
);
})
);
I am having the biggest problem/trouble with my life because I need to add new property to this document that calculates the difference of the PopulationCount from Today's Date and Yesterday's Date and then adding this new "Trend" property to the document.
So that I can display the Coin data from Today's date and the calculated "trend" property that determines if it there was a decrease or increase in value.
How can I achieve this? So far I have written this code, but I do not know where to go from here.
router.get(
"/categories/:category",
asyncHandler(async (req, res) => {
const { category } = req.params;
const fullName = category.replace(/-/g, " ");
// todays coins
const today = new Date(Date.now());
today.setHours(0, 0, 0, 0);
const todaysCoins = await Coin.find({
category: {
$regex: new RegExp(fullName, "i"),
},
createdAt: {
$gte: today,
},
}).lean();
res.json(todaysCoins);
// loop thru all todays coins
// compare to yesterdays coins
// loop thru array of today and compare to yesterday and add trend
})
// loop through yesterdays coins
const startYest = new Date(Date.now());
startYest.setHours(0, 0, 0, 0);
const oneDayAgo = startYest.getDate() - 1;
startYest.setDate(oneDayAgo);
const endYest = new Date(Date.now());
endYest.setHours(23, 59, 59);
const endYestDayAgo = endYest.getDate() - 1;
endYest.setDate(endYestDayAgo);
const yesterdayCoins = await Coin.find({
category: {
$regex: new RegExp(fullName, "i"),
},
createdAt: {
$gte: startYest,
$lt: endYest,
}
}).lean()
);

As Sharrzard Gh said, MongoDB doesn't allow you to add a new property to your model after it's been defined. Instead, use a structure like this, to define an initially empty property that you will use later to store the trend data.
const CoinSchema = new Schema({
specName: { type: String, required: true },
fullName: { type: String, required: true },
category:{ type: String, required: true },
coinName: { type: String, required: true },
trend: { type: String, required: true, default: '' } // <- Add this property
});
module.exports = mongoose.model("coins", CoinSchema);
You can change the trend data type to an array or object if you need to store more complex data or a series of data points for historical trend tracking, if you want.
Try something like this:
trend: { type: Object, required: true, default: {} }
or
trend: { type: [String] , required: true, default: [] }

I was able to add new properties in the model even after the creation of the model in Node.js using:
insertMany(table_data, { strict: false });
where table_data container the old data appended with the new properties. The strict: false did the magic.
Order Model in Node:
const mongoose = require ('mongoose');
const OrdersSchema = new mongoose.Schema({
sr_no :{type: Number, required:true},
customer_name :{type: String, required:true},
product_name: String,
codes: String,
code_date:{
type:Date,
default: Date.now()
},
design : String,
design_date :{
type:Date,
default: Date.now()
},
design_approval :{
type:String,
default: ''
},
design_approval_date :{
type:Date,
default: Date.now()
},
send_to_printer :{
type:String,
default: ''
},
send_to_printer_date :{
type:Date,
default: Date.now()
},
proof_approval :{
type:String,
default: ''
},
proof_approval_date :{
type:Date,
default: Date.now()
},
shipping : String,
ship_date :{
type:Date,
default: Date.now()
},
received :{
type:String,
default: ''
},
received_date :{
type:Date,
default: Date.now()
},
completed : String,
notes : String,
printing :{
type:String,
default: ''
},
printing_date :{
type:Date,
default: Date.now()
},
stapling :{
type:String,
default: ''
},
stapling_date :{
type:Date,
default: Date.now()
},
user_id :{type: Number, required:true}
},{strict: false});
OrdersSchema.index({ sr_no: -1 });
const Orders = mongoose.model(
'Orders',
OrdersSchema
);
module.exports = Orders;
In Mongo Compass the first record appeared like:
And another record in the same collection was:

Related

NodeJS/MongoDB - Match Value in Array with a Value from a Logged In User

I want Users to be able to Report a File/Video only once. By pushing the users ID to a Array, then check if the user has already reported the file.
So far I am able to find the current user, then the VideoID, increment by 1 and then push it to the reportedBy array. But I am pretty lost, when it comes to checking if the current user has already reported the file. Any tips/help will be much appreciated! :-)
MY ROUTE
router.get('/report/:videoLink', function(req, res, next){
async.waterfall([
function(callback){
User.findOne({_id: req.user._id}, function(err, foundUser){
if(err) return next(err)
callback(err, foundUser)
})
},
function(foundUser, callback){
Video.findOne({videoLink: req.params.videoLink}, function(err, reportVideo){
reportVideo.reports++;
reportVideo.reportedBy.push(foundUser);
if (reportVideo.reports > 4) {
reportVideo.remove();
}
reportVideo.save(function(err){
if(err) return next();
});
res.redirect('/');
});
}
]);
});
MY SCHEMA
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var random = require('mongoose-simple-random');
var fragSchema = new Schema({
title: String,
videoLink: {type: String, unique: true, required: true},
category: String,
upVotes: {type: Number, default: 0},
downVotes: {type: Number, default: 0},
voted: {type: Boolean, default: false},
reports: {type: Number, default: 0},
reportedBy: [{ type: Schema.Types.ObjectId, ref: 'User'}],
ownByUser: {type: Schema.Types.ObjectId, ref: 'User'},
date: { type: Date, default: Date.now }
});
fragSchema.plugin(random);
module.exports = mongoose.model('Frag', fragSchema);
This should find the video only if it was reported by that user before:
Video.findOne({
videoLink: req.params.videoLink,
reportedBy: foundUser._id,
}, ...
and this should find the video only if it was not reported before:
Video.findOne({
videoLink: req.params.videoLink,
reportedBy: {$nin: [foundUser._id]},
}, ...
There are many ways to achieve that but you get the idea.
If you want to find the video anyway an then test if it was already reported by that user then something like this inside of your Video.findOne callback:
if (reportVideo.reportedBy.indexOf(foundUser._id) < 0) {
// not reported by that user yet
} else {
// already reported by that user
}
If you're using lodash then you can use:
if (_.includes(reportVideo.reportedBy, foundUser._id)) {
// already reported by that user
} else {
// not reported by that user yet
}
Make sure that you have the IDs as strings, maybe you will need to use .toString() or something like that before the comparisons.

Mongoose: Validation not being executed when saving

I've defined this Mongoose schema:
// validator function
var arrayWithAtLeastFiveElements = function (a) {
return (a !== undefined && a.length >= 5);
};
var orderSchema = new Schema({
user: {
type: Schema.ObjectId,
ref: User,
required: true
},
products: [{
type: Schema.ObjectId,
ref: Product,
required: true,
validate: [arrayWithAtLeastFiveElements, 'Order needs to have at least five products']
}]
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
}
});
When I try to save it, the validation is not executed if products is undefined, null or an empty array, and it saves the new order with an empty array of products in each case. The validations are only run when products is an array with at least one element. Any clue what's going on? It there a way to make the validation run in all cases? Also, what does require do in this case? I don't see any change in validations if I define products array as required or not...
Define it with:
products: {
type: [Schema.ObjectId],
required: true,
}

Custom Validate an object Push to an array

I am using something similar to the following schema.
By visiting the Item page I can add related items to the Item's Related Items array field.
I would like to custom validate the object I am pushing to the Item's Related Items field, to test if a similar object exists in the array already - so that I do not get a duplicate.
In my code below, the custom validation does not fire. I expect this may be because custom validation cannot be applied to a type: [object], and should be applied to the properties of the object - but then I am unable to test the object as a whole.
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
custom: function () {
let queryData = { docId: this.docId, related: this.value }
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.name':{
type: String,
label:'Name',
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});
I figured out the way to handle this.
The custom validation should not be on the [object], but rather one of the properties of the object - in this case 'source' or 'code'.
Inside one of the object properties you can call this.siblingField(otherField); But it means you have to rebuild the object.
In my case :-
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
},
'related.$.name':{
type: String,
label:'Name',
custom: function () {
//---------------------------
//this is the important bit
//---------------------------
let queryData = {
docId: this.docId,
related: {
name:this.value,
code:this.siblingField('code').value,
}
}
//---------------------------
//end of important bit
//---------------------------
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});

mongoose update with push operations on array and set operation on object

I have this mongoose schema
var ContactSchema = module.exports = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: Number,
required: true,
},
messages: [
{
title: {type: String, required: true},
msg: {type: String, required: true}
}],
address:{ city:String,
state:String
}
});
I have initially the collection set with name and phone field. I need to update the collection with new messages into messages array and new address into address object. the function must also need to handle any single operation, ie in some case i have only update to messages array or updates to both name and address. so how i can i do all operations in a single function.
var messages= {
title: req.body.title,
msg: req.body.msg
}
Model.findOneAndUpdate({'_id': req.body.id,},{$push: {messages:message}},{upsert: true}, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});
You could try use both the $set and $push operators in your update object. Suppose, for example, you want to update both name and address fields in one single operation, use the $set on the name field and a $push operation to the address array:
var messages= {
title: req.body.title,
msg: req.body.msg
},
query = {'_id': req.body.id},
update = {
$set: {name: req.body.name},
$push: {messages: message}
},
options = {upsert: true};
Model.findOneAndUpdate(query, update, options, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});

Properties with default values in Mongoose schema are not persisting

I have the following schema I've written using Mongoose:
var querySchema = mongoose.Schema({
quoteId: { type: String, default: '' },
zipcode: { type: String, default: '' },
email: { type: String, default: '' },
type: {type: String, default: ''},
isEmailChecked: { type: Boolean, default: true },
});
I provide values for only 3 properties in the querySchema assuming that the result of the fields will take default values when a new instance of query object is persisted:
var query = {};
query.quoteId = "1414775421426";
query.email = "myself#somewhere.com";
query.type = "Foo";
But following document is what I see as the result in the collection:
{
"_id" : ObjectId("5453c27d0e4c3f2837071856"),
"email" : "myself#somewhere.com",
"type" : "Foo",
"quoteId" : "1414775421426",
"__v" : 0
}
Should isEmailChecked and zipcode not be assigned their default values when a new instance of query object is persisted to the MongoDB database?
Following is how I am persisting an instance of the query object using ExpressJS/NodeJS:
app.post('/api/queries', function (req, res) {
QuoteQuery.create({
quoteId: req.body.query.quoteId,
type: req.body.query.type,
zipcode: req.body.query.zipcode,
email: req.body.query.email,
isEmailChecked: req.body.query.isEmailChecked,
}, function (err, query) {
if (err) {
res.send(err);
}
res.json(query);
});
});
Could somebody help me understand that why I got the isEmailChecked and zipcode properties in the resulting document in the MongoDB database?
I am using NodeJS, AngularJS and ExpressJS in my application along with MongoDB.
When you set mongoose model field it not use default value.
As workaround you can use underscore to extend mongoose model object with keys which exists in your query object like this:
_.extend(dbQueryObject, query);
Here is complete example:
var mongoose = require('mongoose');
var querySchema = mongoose.Schema({
quoteId: { type: String, default: '' },
zipcode: { type: String, default: '' },
email: { type: String, default: '' },
type: {type: String, default: ''},
isEmailChecked: { type: Boolean, default: true }
});
var db = mongoose.createConnection('mongodb://localhost:27017/stackoverflow',
{ server: { auto_reconnect: true } },
function(err) {
var QuerySchema = db.model('test', querySchema);
var query = {};
query.quoteId = "1414775421426";
query.email = "myself#somewhere.com";
query.type = "Foo";
QuerySchema.create({
quoteId: query.quoteId,
type: query.type,
zipcode: query.zipcode,
email: query.email,
isEmailChecked: query.isEmailChecked
}, function (err, query) {
process.exit(0);
});
});
Here is what in db:
{
"_id" : ObjectId("5453ce3c9f7e0d13c52abf61"),
"type" : "Foo",
"email" : "myself#somewhere.com",
"quoteId" : "1414775421426",
"__v" : 0
}

Resources