mongoose - Find by created time irrelevant of date - node.js

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.

Related

How to query for sub-document in an array with Mongoose

I have a Schema of Project that looks like this:
const ProjectSchema = new mongoose.Schema({
name: {
type: String,
Required: true,
trim: true
},
description: {
type: String,
},
devices: [{
name: {type: String, Required: true},
number: {type: String, trim: true},
deck: {type: String},
room: {type: String},
frame: {type: String}
}],
cables: {
type: Array
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
adminsID: {
type: Array
},
createdAt: {
type: Date,
default: Date.now
}
I want to query an object from array of "devices".
I was able to add, delete and display all sub-documents from this array but I found it really difficult to get single object that matches _id criteria in the array.
The closest I got is this (I'm requesting: '/:id/:deviceID/edit' where ":id" is Project ObjectId.
let device = await Project.find("devices._id": req.params.deviceID).lean()
console.log(device)
which provides me with below info:
[
{
_id: 6009cfb3728ec23034187d3b,
cables: [],
adminsID: [],
name: 'Test project',
description: 'Test project description',
user: 5fff69af08fc5e47a0ce7944,
devices: [ [Object], [Object] ],
createdAt: 2021-01-21T19:02:11.352Z,
__v: 0
}
]
I know this might be really trivial problem, but I have tested for different solutions and nothing seemed to work with me. Thanks for understanding
This is how you can filter only single object from the devices array:
Project.find({"devices._id":req.params.deviceID },{ name:1, devices: { $elemMatch:{ _id:req.params.deviceID } }})
You can use $elemMatch into projection or query stage into find, whatever you want it works:
db.collection.find({
"id": 1,
"devices": { "$elemMatch": { "id": 1 } }
},{
"devices.$": 1
})
or
db.collection.find({
"id": 1
},
{
"devices": { "$elemMatch": { "id": 1 } }
})
Examples here and here
Using mongoose is the same query.
yourModel.findOne({
"id": req.params.id
},
{
"devices": { "$elemMatch": { "id": req.params.deviceID } }
}).then(result => {
console.log("result = ",result.name)
}).catch(e => {
// error
})
You'll need to use aggregate if you wish to get the device alone. This will return an array
Project.aggregate([
{ "$unwind": "$devices" },
{ "$match": { "devices._id": req.params.deviceID } },
{
"$project": {
name: "$devices.name",
// Other fields
}
}
])
You either await this or use .then() at the end.
Or you could use findOne() which will give you the Project + devices with only a single element
Or find, which will give you an array of object with the _id of the project and a single element in devices
Project.findOne({"devices._id": req.params.deviceID}, 'devices.$'})
.then(project => {
console.log(project.devices[0])
})
For now I worked it around with:
let project = await Project.findById(req.params.id).lean()
let device = project.devices.find( _id => req.params.deviceID)
It provides me with what I wanted but I as you can see I request whole project. Hopefuly it won't give me any long lasting troubles in the future.

I wanna fetch all users who have most sparks in last 7 days in mongodb and nodejs

I wanna fetch all users who has most sparks in last 7 days
Sparks are something like friends
can anyone help in this problem
Thanks in advance
//create schema for users
const UserSchema = new Schema({
password: {
type: String,
default: "",
},
account_type: {
type: String,
default: "",
},
account_name: {
type: String,
default: "",
},
firstName: {
type: String,
default: "",
},
lastName: {
type: String,
default: "",
},
image: {
type: String,
default: "",
},
sparks: [
{
user: {
type: Schema.Types.ObjectId,
ref: "users",
},
status: {
type: Number,
required: true,
enum: STATUS,
},
createdAt: {
type: Date,
default: Date.now,
},
},
],
createdAt: {
type: Date,
default: Date.now,
},
});
This is my schema
router.get("/leaderboard/get/sparks", async (req, res) => {
let newDate = moment().subtract(7, "day");
console.log(
Date("2020-06-26T12:49:29.324Z") < Date("2020-06-24T12:49:29.563+00:00")
);
let topUsers = await User.aggregate([
{
$match: {
"sparks.createdAt": { $eq: new Date("2020-06-24T12:49:29.563+00:00") },
},
},
]);
res.status(200).json({ date: topUsers });
});
This is what I tried but did not work for me
I tried so many ways but faild
I was searching but not found anthing useful for my problem
You can do the following aggreagtion
User.aggregate([
{
'$project': {
'_id': 1,
'numberOfSparks': {
'$cond': {
'if': {
'$isArray': '$sparks'
},
'then': {
'$size': '$sparks'
},
'else': 'NA'
}
}
}
}, {
'$sort': {
'numberOfSparks': -1 //sort in descending order
}
}, {
'$limit': 10 // get top 10 users
}
])
Now this will work fine but if this aggregation is used frequently I would advice you a different approach. Aggregation is quite compute intensive it has to check all the users, then generate all the computed field, numberOfSparks, then sort. So as the number of users and sparks grow it could slow your server down.
Instead you could make a sparkCount field in your user schema. Then every time a spark is pushed to your User, you could increment a sparkCount field. Simply index the sparkCount field. Now you can easily query users with top sparkCount with a single query;
User.find({}).sort({sparkCount:-1}).limit(10)
This query will only read the 10 user documents with top sparkCount. No additional computation. And its also very scalable. Number of users and sparks will not effect performance.
Hope it helped

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?

How to count and sum a field between 2 dates?

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')}});

Resources