How to count and sum a field between 2 dates? - node.js

Here is my model file:
income.js
var incomeSchema = new schema({
issuedBy: { type: schema.Types.ObjectId, ref: 'user' },
amount: {type: Number, default: 0},
content: { type: String, default :''},
note: { type: String, default: ''}
}, {timestamps: true});
Now I want to sum the all values of amount between 2 dates and count and export all the data between these dates into a table.
For example: I want to sum all the amount between Feb 12 2017 and Feb 23 2017 and shows it like this:
1.Number of transactions: 20
2.Total: $2500
3.The table that display all the data between 2 dates.
I have tried some answer using
collection.find( {'createdAt': {$gte: Date, $lte: Date}}
but I still can't do it.
Any solution will be appreciated. Thank you.

You can try the below aggregation.
db.collection.aggregate(
[{
$match: {
createdAt: {
$gte: new Date('2017-02-12'),
$lte: new Date('2017-02-23')
}
}
}, {
$group: {
_id: null,
Total: {
$sum: "$amount"
},
NoOfTransactions: {
$sum: 1
}
}
}]
)
You can use $out stage to write to a new collection.

you can do it with simple do like this.
db.collection.find({ "field" : { $gt: value1, $lt: value2 } } ); // value1 < field < value
or
First, in the Schema, you need to define the type of the date field to be:
{date: { type: Date, default: Date.now }}
then when u query for the date range:
db.collection.find({"field": {'$gte': new Date('3/1/2014'), '$lt': new Date('3/16/2014')}});

Related

mongoose - Find by created time irrelevant of date

I have the following mongoose model
const Post = new Schema({
created_at: { type: Date, default: Date.now() },
updated_at: { type: Date },
postName: String,
postContent:String,
promoted: {
isPromoted: { type: Boolean, default: false, required: true },
promotedFrom: { type: Date },
promotedTill: { type: Date },
},
});
Example Document
const Post = new Schema({
created_at: 2020-07-05T16:16:38.139+00:00,
postName: My first Post,
postContent:This is my furst post,
promoted: {
isPromoted: true,
promotedFrom: 2020-11-13T16:14:38.139+00:00,
promotedTill: 2020-11-20T16:14:38.139+00:00,
},
});
With mongose I want to query the documents with the promotedTill time to be between 12.00 hrs to 16.30 hrs irrespective of any date.
Thanks in Advance.
Adjust the gt and lt to include/exclude boundaries:
db.foo.aggregate([
{$match: {$expr: {
$and: [
{$gt: [ {$hour: "$promoted.promotedTill"}, 12 ]},
{$lte: [{$hour: "$promoted.promotedTill"},16]},
{$lt: [{$minute: "$promoted.promotedTill"},30]}
]}
}}
]);
check this example
I've done the query quite simple, without other fields.
The query is this:
db.collection.find({
"$and": [
{
"promoted.promotedFrom": {
"$gte": ISODate("yourdate")
}
},
{
"promoted.promotedTill": {
"$lte": ISODate("yordate")
}
}
]
})
Basically is search a value within a range using $gte (greater than or equal) and $lte (lower than or equal).
Using the $and operator both conditions should be true.

Find last document of an array of documents with different values with Mongoose

I have an array of values that I use to query some data. I need to get the last document of each value in the array. I prefer to explain with some code:
Schema:
const quizResultSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
answeredByUser: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
created: { type: Date, default: Date.now },
levelAnswered: { type: mongoose.Schema.Types.ObjectId, ref: 'QuizLevel' },
});
controller:
QuizResult.find(
{
levelAnswered: { $in: levelIds },
answeredByUser: result.applicant._id,
},
{},
{ sort: { created: -1 } }
)
levelIds is an array of Ids and I use it to return an array of documents. The problem is that I'm getting all the documents for each Id in the array sorted by date. What I need is to get the last created document and not all the documents for each Id.
How can I do that? Is it possible to do it just with Mongoose?
It's possible to do this by grouping and using $last like so:
db.collection.aggregate([
{
$match:{
levelAnswered: { $in: levelIds },
answeredByUser: result.applicant._id,
}
},
{
$group: {
_id: "$levelAnswered",
last: {$last: "$$ROOT"}
}
},
{
$replaceRoot: {
newRoot: "$last"
}
}
])

How can we sort data based on date in mongodb

how can I sort document in ascending or descending order in MongoDB
const contact = await AtHome.aggregate([
{
$project:{
name:1,
partnerName:1,
phone:1,
email:1,
city:1,
guest:1,
desc:1,
requestedOn: { $dateToString: { format: "%d-%m-%Y", date: "$createdAt" } },
weddingDate: { $dateToString: { format: "%d-%m-%Y", date: "$weddingDate" } },
}
},
{ $sort : { createdAt : -1 } }
]);
try {
res.send(contact);
} catch (err) {
res.send('INVALID');
}
I treid to add createdAt:1 to $project object but did not work.
AtHome.js (Schema)
const mongoose = require('mongoose');
const atHomeSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
partnerName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
phone: {
type: String,
required: true
},
weddingDate: {
type: Date,
required: false
},
city: {
type: String,
required: true
},
guest: {
type: Number,
required: true
},
desc: {
type: String,
required: true
},
createdAt:{
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('AtHome', atHomeSchema);
I added AtHome.js which is my schema file
This is what I tried but not working, I am just getting the unsorted document
Please help me
Thank You
As per the Aggregation framework, you have to first sort the data and then project it to desired output. Try the following query:
const contact = await AtHome.aggregate([
{ $sort : { createdAt : -1 } },
{ $project: {
name:1,
partnerName:1,
phone:1,
email:1,
city:1,
guest:1,
desc:1,
requestedOn: { $dateToString: { format: "%d-%m-%Y", date: "$createdAt" } },
weddingDate: { $dateToString: { format: "%d-%m-%Y", date: "$weddingDate" } },
}
}
]);
try {
res.send(contact);
} catch (err) {
res.send('INVALID');
}
As mentioned in the above answers you need to sort the data before project. I can see the in you Schema that you are using default: Date.now() for the createdAt, this will set the value of createdAt to time when Schema is defined, So all the records will have same value for createdAt until next restart of the server. You can use Date.now instead, if you need to set the createdAt to time when the record is created
createdAt:{
type: Date,
default: Date.now
}
You have to include the createdAt field in projection or change the order of pipeline stages $sort then $project.
Each stage transforms the documents as they pass through the pipeline. In your first stage, you have filtered out createdAt so the second stage doesn't take any effect. It is recommended to use $sort before $project stage so $sort stage may use index. This will provide better performance.
// sort order 1 -> asc, -1 ->desc
await AtHome.aggregate([ {$sort : { createdAt : 1 }} ,{$project : {...}}])
I think your first step should be to understand how aggregation pipeline works. In pipeline stages, the data goes from one stage to another.On each stage we do some modifications in data and that modified data is passed on to next pipeline stage.
Here your first stage is project which return the fields you specified:
and this data is then passed on to sorting stage and as the sort stage can not find createdAt field in data from first stage, so unsorted data is returned.
So add
const contact = await AtHome.aggregate([
{
$project:{
name:1,
partnerName:1,
phone:1,
email:1,
city:1,
guest:1,
desc:1,
requestedOn: { $dateToString: { format: "%d-%m-%Y", date: "$createdAt" } },
weddingDate: { $dateToString: { format: "%d-%m-%Y", date: "$weddingDate" } },
createdAt : 1
}
},
{ $sort : { createdAt : -1 } }
]);
which gives createdAt field which sort stage can use:
Using sort as a first stage in pipeline is very bad idea. If millions of records are there then the query will be very slow. So it always good to use sort before the limit stage but after the match stage(so that we have less number of docs to sort).
Use timestamp option for createdAt and updatedAt fields in documents.
const schema = Schema({
//your fields
}, {
timestamps: true }
});
Read More : https://mongoosejs.com/docs/guide.html#timestamps

get count of conditionally matched elements from an array in MongoDB

I want comments with today's date and it should be non-empty and how much comments it has via using mongoose. I have tried a lot. Currently, I am trying to achieve with two methods. both have some problems let me explain. please consider I have only two posts in DB one has no comments like: [], and the other has 2 comments two inside it with today date and the 3 is old.
Method 1 :
in this method, it returns me today comment but it only returns single comment added on today.
and also returning me another object which has no comments
Post.find({ })
.select({
comments: { $elemMatch: { date: { $gt: startOfToday } } },
title: 1,
})
.exec((err, doc) => {
if (err) return res.status(400).send(err);
res.send(doc);
});
the output of above code is :
[{"_id":"5e9c67f0dd8479634ca255b1","title":"sdasd","comments":[]},{"_id":"5e9d90b4a7008d7bf0c4c96a","title":"sdsd","comments":[{"date":"2020-04-21T04:04:11.058Z","votes":
[{"user":"hhhh","vote":1}],"_id":"5e9e70bbece9c31b33f55041","author":"hhhh","body":"xvxgdggd"}]}]
Method 2 :
In this method I am using the same thing above inside the found object like this:
Post.find({ comments: { $elemMatch: { date: { $gt: startOfToday } } } })
.exec((err, doc) => {
if (err) return res.status(400).send(err);
res.send(doc);
});
And it returns me first post with all comments (3 comments) but not second post(that is good) that have empty comment array.
here is the output :
[{"author":{"id":"5e85b42f5e4cb472beedbebb","nickname":"hhhh"},"hidden":false,"_id":"5e9d90b4a7008d7bf0c4c96a","title":"sdsd","body":"dsfdsfdsf","votes":[{"user":"5e85b42f5e4cb472beedbebb","vote":1}],"comments":[{"date":"2020-04-20T12:08:32.585Z","votes":[],"_id":"5e9d90c0a7008d7bf0c4c96b","author":"hhhh","body":"zcxzczxc z zxc"},
{"date":"2020-04-21T04:04:11.058Z","votes":[{"user":"hhhh","vote":1}],"_id":"5e9e70bbece9c31b33f55041","author":"hhhh","body":"xvxgdggd"},
{"date":"2020-04-21T04:56:25.992Z","votes":[],"_id":"5e9e7cf96095882e11dc510c","author":"hhhh","body":"new should appear in feeds"}],"date":"2020-04-20T12:08:20.687Z","createdAt":"2020-04-20T12:08:20.692Z","updatedAt":"2020-04-21T04:56:26.003Z","__v":3}]
This is my post schema :
const postSchema = new Schema(
{
title: {
type: String,
required: true,
unique: 1,
index: true,
},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
nickname: String,
},
body: {
type: String,
required: true,
},
comments: [
{
author: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
date: { type: Date, default: Date.now },
votes: [{ user: String, vote: Number, _id: false }],
},
],
date: { type: Date, default: Date.now },
hidden: {
type: Boolean,
default: false,
},
votes: [{ user: Schema.Types.ObjectId, vote: Number, _id: false }],
},
{ timestamps: true }
);
So, if I have SUM up the things I need today comments and today is 21st April (Two comments) and another comment date is 20. I only need today's comments with its count.
If I forgot something to add please let me know. Thanks
There are couple of changes as $elemMatch would return only the first matching element from array but not all the matching elements in comments array. So it's not useful here, additionally if you want comments for today you need to use $gte instead of $gt for input startOfToday. Finally, You need to use aggregation-pipeline to do this :
db.collection.aggregate([
/** Lessen the no.of docs for further stages by filter with condition */
{
$match: { "comments.date": { $gte: ISODate("2020-04-21T00:00:00.000Z") } }
},
/** Re-create `comments` array by using filter operator with condition to retain only matched elements */
{
$addFields: {
comments: {
$filter: {
input: "$comments",
cond: { $gte: ["$$this.date", ISODate("2020-04-21T00:00:00.000Z")] }
}
}
}
},
{
$addFields: { count: { $size: "$comments" } } // Add count field which is size of newly created `comments` array(Which has only matched elements)
}
]);
Test : mongoplayground

Mongoose aggregate, match data created between two intervals of date [NOT WORKING] [duplicate]

This question already has answers here:
Find objects between two dates MongoDB
(17 answers)
MongoDb aggregation $match error : "Arguments must be aggregate pipeline operators"
(4 answers)
Closed 3 years ago.
I want to aggregate data of my mongoose Schema so that I get data of particular month. I used match with aggregate using gte and lt operators but if only single match operator (gte or lt) given only it works.
My order schema is as follows:
const OrderSchema = new Schema({
_id: Schema.Types.ObjectId,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, required: false },
//....................
productId: { type: Schema.Types.ObjectId, ref: "products" },
productName: { type: String, required: false },
productQuantity: { type: Number, required: false },
totalAmount: { type: Number, required: false },
});
Now, I need to find total top five products sold out this month, on the basis of productQuantity.
I tried using both matching createdAt field with gte with some date and lte with some date. But, if used both at once, result is not returned but when using one at a time, the result is returned.
Order.aggregate([
{
$match: {
createdAt: { //schema contains createdAt field with Date.now()value
$gte: Date("2019-04-30"),
$lt: Date("2019-05-10")
}
}
}])
But using one at a time
Order.aggregate([
{
$match: {
createdAt: {
$gte: Date("2019-04-30")
}
}
$group: {
_id: "$productId",
created: { $first: "$createdAt" },
count: { $sum: "$productQuantity" }
}
}
{
$sort: {
count: -1
}
},
{ $limit: 5 }
}
])
it works.But I need to find the top sold products within a interval of month given the month.
I did this way
Order.aggregate([
{
$match: {
createdAt: {
$gte: startDate
}
},
$group: {
_id: "$productId",
created: { $first: "$createdAt" },
count: { $sum: "$productQuantity" }
}
},
{
$sort: {
count: -1
}
},
{ $limit: 5 }
])
But this gives message": "Arguments must be aggregate pipeline operators" error.
Why am i having such problem?

Resources