Mongodb store sales data by month schema - node.js

How should I defined my schema if I want to retrieve/insert monthly sales figure? like this?
sales: [{
Jan:Number,
Feb:Number
}]

You may simply create a schema as following
var monthlySale = new Schema({
sale: Number,
month: Number, //may be string
year: Number
});
var MonthlySale = mongoose.model('MonthlySale', monthlySale);
and you can insert
var sale = new MonthlySale({ sale: 5000, month: 7, year: 2016})
sale.save(function(err, saleObj) {
if(err)
console.log('Unable to create sale')
else
console.log('New sale created')
});
and you may find as following
MonthlySale.findOne({month: 7, year: 2016}, function(err, sale) {
if(err)
console.log('Unable to fetch sale')
else
console.log('Sale : ', sale)
});

You should not use values as field name in mongodb schema, that makes it hard to query on it. It is upto you what data you want to save, you can save date of each month of the year, or save month and year field separately.
Below are two schema I think you can use.
sales:[
{
month_date: {type: Date},
amount: {type: Number}
}
]
sales:[
{
month: {type: String},
year: {type: Number},
amount: {type: Number}
}
]
With these schema you can easily query on these records

You can use joi validation, here is an example... (Note: i'm making it dynamic by exporting it so that you can use anywhere you want to...)
So, you have to import joi module first. See below...
var joi = require('joi');
Now below is the validators of string, number etc...
var string = joi.string().min(2).max(50); //you can also set max,min..
var bool = joi.boolean();
var number = joi.number().min(1).max(999999999).required();
var object = function object(obj) {
return joi.object().keys(obj);
}
var array = function array(ary) {
return joi.array().items(ary).min(1);
}
Now just export these all...
module.exports = {
string: string,
bool: bool,
number: number,
object: object,
array: array
}
And now you can import the file in which you kept it and can use these validators.
var schema = {
firstname: string.label('user firstname'),
lastname: string.label('user lastname'),
gender: bool.label('user gender'),
age: number.label('user age')
};

Related

Mongoose delete records after certain time

I have a mongoose Schema which looks like this:
const USERS_DATA = new Schema({
_id: Number,
name: String,
img: String,
date: Date,
phone: String,
article: String,
createdAt: {
type: Date,
required: true,
default: Date.now,
index: { expires: '3d' }
}
},
{
collection: "users",
_id: false,
}
);
I need to push data to this schema.
const User = mongoose.model("users", USERS_DATA);
function pushToDB() {
const newUser = new User({
name: INPUT.name,
img: INPUT.img,
date: INPUT.date,
phone: INPUT.phone,
article: INPUT.article,
});
newUser.save(function (err) {
mongoose.disconnect();
if (err) return console.log(err);
});
}
This data have to be deleted after 3 days when it was pushed to database. How to implement it in node.js? I found it really confusing and tried lots of code. Any answers are appreciated! Thanks
P.S. I use mongoDb Atlas
You should separate the process to push the data into the database from the process to delete it after 3 days. You have already the first part :).
For the second part, you can write a function deleteOldDocument. This function will query for documents in DB that are created for 3 days or more, and delete them. Then, you can run this function periodically, 1 time per day for example.
The pseudo-code, in case you need it :
async function deleteOldDocument() {
const 3DaysAgo = ...; // here you can subtract 3 days from now to obtain the value
// search for documents that are created from 3 days or more, using $lt operator
const documentToDelete = await User.find({"created_at" : {$lt : 3DaysAgo }});
// delete documents from database
.....
// recall the function after 1 days, you can change the frequence
setTimeOut(async function() {
await deleteOldDocument();
}), 86400);
}
// call deleteOldDocument to start the loop
deleteOldDocument();

Access other fields in getter

I'm trying to make it that every time I get subtotal, it adds 2 other fields, productTotal, and tax.
Here's my code:
const cost = new mongoose.Schema({
payment: {
productTotal: Number,
tax: Number,
subtotal: Number, // (productTotal + tax)
}
});
const Cost = mongoose.model('Cost', cost);
How can I add 2 feilds from the same schema when getting a different field?
You can achieve that in mongoose by creating a virtual field for subtotal. The mongoose reference documents this pretty well here: https://mongoosejs.com/docs/tutorials/virtuals.html.
EDIT:
The code snippet below shows how you can define a cost schema that has a subtotal virtual on the payment subdocument:
const PaymentSchema = new Schema({
productTotal: { type: Number, default: 0 },
tax: { type: Number, default: 0 },
});
PaymentSchema.virtual('subtotal').get(function () { return this.productTotal + this.tax; });
const CostSchema = new Schema({
payment: PaymentSchema,
});
From the above snippet you can get the subtotal from a Cost document instance via cost.payment.subtotal.

How can I merge results into a single JSON while querying data from two different models in node.js and mongoose?

I have two mongoose models in my app:
var UsersSchema = new Schema({
username: {type: String},
facebook_username: {type: String},
display_name: {type: String}
}
and
var CommentsSchema = new Schema({
user_id: {type: String, required: true},
text_content: {type: String},
photo_content_url: {type: String}
comment_date: {type: Date, default: Date.now},
created_at: {type: Date, default: Date.now},
}
currently each single comment contains user_id - a direct link to its author.
I created a node.js endpoint that takes POST query and returns JSON with all details about comments:
commentsRoutes.post('/comments', function (req, res) {
var startDate = req.body.startDate;
var endDate = req.body.endDate;
var query= {};
query.$and = [];
// and condition on start date
if(startDate != undefined) {
var startDate = new Date(req.param('startDate'));
var endDate = new Date(req.param('endDate'));
query.$and.push({"comment_date":{$gte: startDate}});
query.$and.push({"comment_date":{$lte: endDate}});
}
var finalquery = Comments.find(query)
finalquery.exec(function(err, comments){
if(err) {
res.send(err);
return;
}
res.json(comments);
});
});
This endpoint returns me a JSON with all the comments, however - I need to attach to each JSON details about its author - username, facebook_username and display_name (fetched based on user_id from UsersSchema). Can you tell me how could I do it?
user_id in CommentsSchema is a mongoose id of a specific user from UsersSchema
====== EDIT
Like #Antonio suggested in his answer below, I used populate in my case, it worked well and I saw merged JSON at the end. All user details were added, it looked like:
{
"text_content": "blah",
"photo_content_url": "http://www...",
"comment_date": "some date",
"created_at": "some date",
"user_id": { "username": "someUsername",
"facebook_username": "fbUsername",
"display_name": "sth" }
}
however - is there a way to include POST parameters in my endpoint that will apply to the user_id json?
Currently I'm sending a POST parameters startDate and endDate. But if I send also facebook_username - how can I include it in a query and find only comments that were written by this author?
I tried adding a simple condition:
var fb_username = req.body.facebookUsername;
query.$and.push({"facebook_username": fb_username});
but it didn't work because there's no such field facebook_username in CommentsSchema...
So how can I include condition attached to fields from UsersSchema that will limit results from CommentsSchema?
Since you have a reference to the corresponding user you could use populate.
Taking into account that try this:
Comments
.find({
comment_date: {
$gte: startDate,
$lte: endDate
}
})
.populate('user_id')
.exec(function(err, comments) {
if(err) {
return res.send(err);
}
res.json(comments);
});
By the way, not related to the main question, but since you are not doing any change in the server I think a GET would be a better option.
I also abbreviated your query, $and is not necessary here.
EDIT
You can add filtering to populate, in your case:
.populate({
path: 'user_id',
match: {
facebook_username: fb_username
}
})

Mongoose get Leagues which has upcoming matches

I've created a db structure as below. in this structure i would like to retrieve leagues which has matches from currentDate - 7 days and forward. My question is then whether this is possible with the current structure of my database or i need to change that before it is possible?
League schema
var leagueSchema = new Schema({
name: String,
league: Number
});
Match schema
var matchSchema = new Schema({
matchId: Number,
leagueId: Number,
date: Date,
homeName: String,
awayName: String,
});
Your design could use some changes where you create references to documents in other collections for population to be effective. Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s)
In your matchSchema, the league field is set to a number _id, where the ref option is what tells Mongoose which model to use during population, in this case the League model. The following example demonstrates the schema changes:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var leagueSchema = new Schema({
_id: Number,
name: String,
league: Number
});
var matchSchema = new Schema({
_id: Number,
league: { type: Number, ref: 'League' },
date: Date,
homeName: String,
awayName: String
});
var League = mongoose.model('League', leagueSchema);
var Match = mongoose.model('Match', matchSchema);
When you are saving the documents, you simple assign the _id value on your refs, as in the following example:
var premierLeague = new League({ _id: 0, name: 'Premiership', league: 100 });
premierLeague.save(function (err) {
if (err) return handleError(err);
var matchDay = new Date(2015, 10, 7);
var match = new Match({
_id: 1,
date: matchDay,
league: premierLeague._id, // assign the _id from the league
homeName: "Manchester United",
awayName: "West Brom"
});
match.save(function (err) {
if (err) return handleError(err);
// thats it!
});
});
Now on to your query to retrieve leagues which has matches from currentDate - 7 days and forward, you populate your match's league using the query builder:
var oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
Match
.find({ "date": { "$gte": oneWeekAgo } })
.populate('league')
.exec(function (err, matches) {
if (err) return handleError(err);
console.log(matches);
});

Upserting subdocument in for loop

Not sure why this isn't working, I've tried a few different variations of syntax, but no go. I'm also unsure if having an update inside a for loop is a good idea.
Basically I'm updating a user's inventory by passing an object that contains two arrays (item[], and item_qty[]) that resembles as such:
var itemset = {}
itemset.item['Gold','Silver','Bronze']
itemset.item_qty[1,3,5]
itemset.qty = itemset.item.length
and the arrays can have varying lengths as such:
var itemset = {}
itemset.item['Platinum','Bronze']
itemset.item_qty[1,1]
itemset.qty = itemset.item.length
The goal is to keep adding to a user's inventory item quantity if the item exists, or add it (along with the quantity) if it doesn't exist. So using the two updated examples, the user will have Platinum(1), Gold(1), Silver(3) and Bronze(6) after they have both passed through the function.
The schema for the USER is:
var UserSchema = new mongoose.Schema({
name: {type: String, required: true},
mail: {type: String, required: true},
inv: {
item: {type: String},
iqty: {type: Number}
}
})
And here is the function:
function addInv(userId,itemset) {
for(i=0;i<itemset.qty;i++){
console.log('Updating:' + itemset.item[i])
db.User.update(
//Criteria
{
_id: userId,
inv: {item: itemset.item[i]}
},
{
$set:
{
inv:
{
'$item': itemset.item[i],
$inc: {'$iqty': itemset.item_count[i]}
}
}
},
{upsert:true},
function(err,i)
{
console.log('Error: ' + err)
console.log('Db: ' + i)
}) //End Update()
}
}
This works, in the sense of syntax, but it never updates the data...
I'd also like to see if there's a way to do this without having the for loop & multiple db calls.

Resources