mongoDB Not Returning Query When Trying to Filter By Date - mongodb

I am trying to find all documents that are created greater than or equal to a month ago.
But when I query the DB it returns nothing when doing the following code:
console.log(moment().add(-31, "days").toDate()) // this logs 2022-09-30T07:27:26.373Z
let filter = {
companyId: companyId,
userId: userId,
_created_at: {$gte: moment().add(-31, "days").toDate()}
};
db.collection("Users")
.find(filter)
.count()
.then(count => {
if(!count){
return resolve({result: [], count: 0});
} else {
db.collection("Users")
.find(filter)
.sort({_created_at: -1})
.limit(parseInt(limit))
.skip(parseInt(page) * parseInt(limit))
.toArray()
.then(result => {
resolve({result: result, count: count});
})
.catch(error => {
console.log(error);
reject(error);
})
}
});
However when I do the following filter it works and returns the documents:
let filter = {
companyId: companyId,
userId: userId,
_created_at: {$gte: moment().add(-31, "days").format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]") }
};
The only thing i changed is the format of the date and i am specifiying exactly how it should be formatted to equal the DB but i do not want to do something hard coded. Although the moment().add(-31, "days").toDate() matches the same format in my DB.
Why am i not getting any results from the query?

toDate() returns a JS Date object, try with format() to return an ISO formatted date:
let filter = {
companyId: companyId,
userId: userId,
_created_at: { $gte: moment().add(-31, 'days').format() },
};

Related

Mongoose find between dates, order by ID

So I am trying to find all documents in a database between 'X' and 'X' dates and then order those by userID. This is what I have so far:
await Expense.find(
{'date' :{'$gte': new Date(startDate), '$lte': new Date(endDate)}}),{sort: {_id: 1}}.exec(function(err, data){
if(err){
console.log('Error Fetching Model');
console.log(err);
}
console.log(JSON.stringify(data, null));
expenseArray = data;
console.log(expenseArray);
But it keeps giving me "TypeError: {(intermediate value)}.exec is not a function"
For added clarification I am trying to write this in mongoose:
"SELECT employeeName, SUM(amount)
FROM reimbursements
WHERE d8 BETWEEN '$startDate' AND '$endDate'
GROUP BY employeeName
ORDER BY employeeName;";
What am I doing wrong? Thank you in advance :D
Your query has few syntax issues, Please try this :
Update :
Below old code will work, but it would be better if you try this way :
try {
let data = await Expense.find(
{ 'date': { '$gte': new Date(startDate), '$lte': new Date(endDate) } }).sort({ _id: 1 })
/** .find() will not return null, it will either return [] or [with matched docs] */
if (data.length) { // checks data != []
console.log(data)
} else { // data == []
console.log('Empty - no docs found')
}
} catch (error) {
console.log('Error Fetching Model');
console.log(error);
}
Old :
await Expense.find(
{ 'date': { '$gte': new Date(startDate), '$lte': new Date(endDate) } }).sort({ _id: 1 }).exec(function (err, data) {
/** sort is not an option for .find() not like aggregate, it has to be on cursor which is result of .find() & .exec() should be at end which is either .find() or .sort() */
if (err) {
console.log('Error Fetching Model');
console.log(err);
}
console.log(JSON.stringify(data, null));
expenseArray = data;
console.log(expenseArray)
})
Sample : mongooseModel.find().sort().exec()
Ref : cursor.sort

How to project updated values only using findOneAndUpdate in embedded array Mongoose?

Currently my User model looks like:
{
_id: 'SomeId'
firstName: 'John',
lastName: 'Cena',
books: [
{
_id: 'xyz',
title: 'a',
author:'b',
ratings:[
{source:'source1', value:"8"},
{source:'source2', value:"9"}]
},
{
_id: 'abc',
title: 'c',
author:'d',
ratings:[
{source:'source3', value:"7"},
{source:'source4', value:"5"}]
}
]
}
After making an findOneAndUpdate query to update rating=>value of 1st book object(_id: "xyz") from 8 to 10 for a given source(say "source1"):
let up={
'books.$[book].ratings.$[rating].value':10
}
let filter={
new:true,
'books.rating':1, //I just want rating array of updated objects in it
arrayFilters:[
{ 'book._id':'xyz'},
{ 'rating.source': 'source1'}
]
}
User.findOneAndUpdate({'_id':'userId','books._id':'xyz'},up,filter).select('books.rating').exec((err,doc)=> {
if (err) throw err;
console.log(doc);
}
My code updates the books=>rating=>value correctly but I can't get that updated rating of that book.
This gives me rating of all books with both updated and non updated values in it. Looks like:-
{
books: [{ ratings:[{source:'source1', value:"10"},{source:'source2', value:"9"}] },
{ ratings:[{source:'source3', value:"7"},{source:'source4', value:"5"}] }]
}
I think the data of 2nd book shouldn't be there at all according to my code. I expect the follwing output:
{
books: [{ ratings:[{source:'source1', value:"10"}] }
}
Please help me to write findOneAndUpdate query correctly!
you can use array.find() like this:
const updatebookSource = (sourceId, userId, bookId) => {
User.findOneAndUpdate({ _id: userId, "books._id": bookId }, up, filter).exec(
(err, doc) => {
if (err) throw err;
let res = doc.books[0].ratings.find(rating => {
return rating.source === sourceId;
});
console.log(JSON.stringify(res, null, 1));
}
);
};
This returns the updated object. Let me know if it works.

get first and last values for each condition

I have a collection like this:
{
_id: ObjectId('534343df3232'),
date: ISODate('2016-01-08T00:00:00Z'),
item_type: "book",
book_id: ObjectId('534343df3232fdf'),
user_id: ObjectId('534343df3232fdf23'),
rating: 6
},
{
_id: ObjectId('534343df3232'),
date: ISODate('2016-01-05T00:00:00Z'),
item_type: "movie",
movie_id: ObjectId('534343df3232fdf'),
user_id: ObjectId('534343df3232fdfa'),
rating: 5
},
{
_id: ObjectId('534343df3232'),
date: ISODate('2016-01-010T00:00:00Z'),
item_type: "song",
song_id: ObjectId('534343df3232fdf'),
user_id: ObjectId('534343df3232fdf13'),
rating: 9
}
There can be only one rating per item per user per day.
I would like to check how the ratings evolve between a period of time for a selection of users and items. I need only the first and the last rating for each book/movie/song.
I have no idea on how I could do this the most efficient way.
As for now, I'm retrieving all the ratings for all the users, and then parsing them with PHP.
db.ratings.find({user_id:{$in:[...]}, $or:[book_id:{$in:[...]}, song_id:{$in:[...]}, movie_id:{$in:[...]}, ], date:{$gte:.., $lte..} });
This is obviously unefficient but I don't know how to handle this case.
You can do it with mongodb mapReduce. So at first you need to filter your data on date range, selection of users and selection of items(query part). Then group by item(map part) and for each item select first and last days with corresponding ratings(reduce part).
Try the following query:
var query = {
user_id: {$in:[...]}
date: { $gte: dateFrom, $lt:dateTo},
$or: [
{book_id: {$in:[...]}},
{song_id:{$in:[...]}},
{movie_id:{$in:[...]}}
]
}
var map = function () {
emit(this.item_type, {
first : {rating: this.rating, date: this.date},
last: {rating: this.rating, date: this.date}
})
}
var reduce = function (key, values) {
var res = values[0];
for (var i=1; i<values.length; i++ ) {
if (values[i].first.date < res.first.date)
res.first = values[i].first;
if (values[i].last.date > res.last.date)
res.last = values[i].last;
}
return res;
}
db.collection.mapReduce( map , reduce , { out: { inline : true }, query: query } )

Mongoose: Populate field in collection from other collection

I have 2 Mongoose collections: ExpenseCategory and Expense
var ExpenseCategorySchema = new Schema({
name: String,
totalSpentInThisMonth: Number
});
mongoose.model('ExpenseCategory', ExpenseCategorySchema);
var ExpenseSchema = new Schema({
expenseCategoryId: {type: Schema.Types.ObjectId, ref: 'ExpenseCategory'},
amount: Number,
date: Date
});
mongoose.model('Expense', ExpenseSchema);
There is a GET api call written in Node.js to return all ExpenseCategory items.
appRouter.route('/expensecatgories')
.get(function(req, res){
ExpenseCategory.find({}, function (expenseCategories) {
res.json(expenseCategories);
});
});
In the above GET method I want to populate field totalSpentInThisMonth in each expenseCategories item before returning. This field needs to be calculated as a sum of all expense.amount where expense.expenseCategoryId matched the expenseCategory.id and expense.date is in current month.
How can I populate the field totalSpentInThisMonth before returning expenseCategories?
Use the .aggregate() method from the aggregation framework for this. You would need to first construct dates to use as your date range query for documents whose date falls within the current month, thus you need to calculate
the first and last days of the month date objects. These dates would be used in the $match pipeline to filter out the documents that are not in the current month.
The next pipeline stream would be the $group stage which groups the incoming documents by the expenseCategoryId key so that you may calculate the total spent in the current month using the
accumulator operator $sum.
The following code implements the above:
appRouter.route('/expensecatgories').get(function(req, res){
var today = new Date(), y = today.getFullYear(), m = today.getMonth();
var firstDay = new Date(y, m, 1);
var lastDay = new Date(y, m + 1, 0);
var pipeline = [
{
"$match": {
"date": { "$gte": firstDay, "$lt": lastDay }
}
},
{
"$group": {
"_id": "$expenseCategoryId",
"totalSpentInThisMonth": { "$sum": "$amount" }
}
}
];
Expense.aggregate(pipeline, function (err, result){
if (err) throw err;
var categories = result.map(function(doc) { return new ExpenseCategory(doc) });
Expense.populate(categories, { "path": "expenseCategoryId" }, function(err, results) {
if (err) throw err;
console.log(JSON.stringify(results, undefined, 4 ));
res.json(results);
});
});
});

How can I query for documents where date is greater than a provided date?

I have a collection of events that contain the date of the event stored in this format: YYYY-MM-DD. I want to query for all events that are greater than a provided date. This is what I have so far:
var eventSchema = mongoose.Schema({
title : String,
details : String,
start_date : String,
company: {
type: String,
ref: 'Company'
}
});
eventSchema.methods.getUpcomingEvents = function(company_id, cb) {
var date = utils.dateToday(); // returns e.g., '2015-02-26'
return this.model('Event')
.find({ company: company_id, start_date : {$gte: date} })
.sort({start_date: 'asc'})
.exec(function (err, data) {
if (err) {
console.log('ERROR = ' + err);
cb(false, err);
} else {
cb(null, data);
}
})
};
The problem is that this query is return events that occur before the provided 'date'. What am I doing wrong?
start_date is String, make it Date if you want to perform date comparisons.
change the schema to something like this;
var eventSchema = mongoose.Schema({
title : String,
details : String,
start_date : Date,
company: {
type: String,
ref: 'Company'
}
});