How can I manipulate a date field in an aggregation pipeline? - mongodb

I'm trying to set the time from a Date field to the start of the day
function getDate(date){ return new Date(date.getYear(), date.getMonth(),
date.getDate(), 0,0,0); }
...
{"$project" : {
"_id" : getDate("$dt"),
...
If I send "$dt" I get TypeError: date.getYear is not a function as I'm passing a string,
If remove the quotation marks, I get Error: "$dt is not defined",
but if I set "$dt" as value for "_id" I get a correct ISO Date.
So how do I pass the date object to the function?

MongoDB's aggregation pipeline does not support JavaScript. To manipulate date values for results in the aggregation pipeline you need to use Date Aggregation Operators.
For example :
db.date.aggregate([
{ $project: {
_id: { $dateToString: { format: "%Y%m%d", date: "$dt" }}
}}
])
Assuming you have a document with a field called dt with a date value of ISODate("2017-07-01T10:01:23.344Z"), the result would look like:
{
"result": [
{
"_id": "20170701"
}
],
"ok": 1
}
Note: if you have multiple documents for the same day, this approach will create duplicate _id values in your results. You may want to project to a different field name or perhaps use a $group stage instead of $project if your intent is to combine values for the same day.

Related

Mongodb - Using a long number to filter a Date type field in aggregation

I'm trying to filter a collection on a date type field using a long number (number of ms since epoch) and can't seem to get it to work in a regular find() or an aggregation. What am I missing ?
TIA!
db.testColl.insertOne({_id:"test", createDate: ISODate("2022-12-26T01:00:00.000+0000")})
db.testColl.find( {createDate: {$gte : {$toDate:1608947820333} }} )
I think you can't use aggregate expression $toDate in find/match. Instead, try using it with $expr.
db.testColl.find({
$expr: {
$gte: [
"$createDate",
{
$toDate: 1608947820333
}
]
}
})

How to convert string to ISODate in mongodb and compare it with $$NOW

Here, In my mongo collection, the date type column data is stored as string. I have a view where I need to compare this date with current date and time $$NOW. Since my date is stored as string. The query is getting executed but not getting compared. I tried to convert this string to date type but the data changed like this 2022-05-23 1:28:36 quotes were gone. Tried to run same query, but getting an error.
sample data:
[{oldDate:"2022-05-23 1:28:36"}]
My query:
db.collection.aggregate([
{
"$match": {
$expr: {
$lt: [
"$oldDate",
"$$NOW"
]
}
}
}
])
If the data in db is in the form of ISODate("2022-05-23 1:28:36") then this query is working. But the actual data in my db for this column is in the form of string. Can anyone help me to convert this to ISODate() through code itself and make comparison work.
Use $toDate operator to convert date string to date.
db.collection.aggregate([
{
"$match": {
$expr: {
$lt: [
{
$toDate: "$oldDate"
},
"$$NOW"
]
}
}
}
])
Sample Mongo Playground

Filter dates stored as int on MongoDB

I have several documents on MongoDB collection that were created with an invalid date value and have it currently as:
"UpdateDate " : Date(-62135596800000)
I want to filter those documents, but my query is not returning any data:
db.MyCollection.find({UpdateDate : Date(-62135596800000)})
How can I filter my collection to retrieve those documents?
The problem with your query is that you need to build a proper Date object. This may be different depending on what languaje or drivers are you using to query, but in JS just using new Date() should work.
db.MyCollection.find({UpdateDate : new Date(-62135596800000)})
If you cant reproduce what the value need to be , luckily the date when you have inserted the document for the first time you can extract and update from the document _id as follow:
db.getCollection('MyCollection').update({ UpdateDate : new Date(-62135596800000)
},
[
{ "$set": { "UpdateDate": { $toLong: { $toDate: "$_id" }}}}
],
{ "multi" : true}
)
This is with the assumption that you havent customized the default _id

I am building a mongoose aggregate for search mongodb documents

I built an aggregate for searching according to a filter. The things that the user can search for are optional, So is there any way to make the match optional - for example the user can select a date, If he didn't select, I want the aggregate function not to use match date
db.articles.aggregate(
[ {
$match : { date : userSelectedDate }, { else : elseSelection}
} ]
);
If no date is selected => cancel the date matching and match anothers
I would try to build the query document dynamically.
var query = []
if (userSelectedDate){
query.push({ $match : { date : userSelectedDate }})
db.articles.aggregate(query)
The solution was in mongoose documentation using: Aggregate#append(ops)
Appends new operators to this aggregate pipeline
after having the aggregate
aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
so now I can use if(condition) before appending
mongoose documentation for append

Why are dates in match aggregate query being ignored?

I'm trying to run an aggregation statement in my mongo db. I have a document whose structure is (at least) as follows:
{
"_id": ObjectId,
"date": ISODate,
"keywordGroupId": NumberLong,
"ranking": NumberLong,
}
I would like to run an aggregation statement that aggregates the 'ranking' field for a given 'keywordGroupId' and a given 'date' interval.
I have been trying with the following aggregate command:
{
aggregate : "KeywordHistory",
pipeline : [
{ $match: { keywordGroupId: 75 , "$date": {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2013-02-01T00:00:00.0Z")}} },
{ $group: { _id: { null }, count: { $sum: "$ranking" } } }
]
}
This command executes without errors and returns a result. If I try to change the value for the 'keywordGroupId' field, the command returns a different value, so I assume that the $match statement works for that field (NumberLong). Though, if I change the 'date' range and I specify a time interval for which I don't have any data in the database, it still returns a result (I would actually expect an empty result set). So I have to assume that the $match statement is ignoring the date interval specified.
Can anyone help me with this point?
Remove the $ prefix on the $date field of your $match:
{ $match: {
keywordGroupId: 75,
date: {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2013-02-01T00:00:00.0Z")}
}},
You only use the $ prefix when the field name is used in a value, not as a key.
Sometimes ISodate does not works . so in Case if you want to match date using only "one" date the best way is:---
ex:-- Let a schema be:---
var storeOrder = new Schema({
store_name:{type:String, required:true},
date :{type:Date ,default:moment(new Date()).format('YYYY-MM-DD')},
orders : [{
vegetable : String,
quantity : Number,
price:Number
}]
});
mongoose.model('storeorder',storeOrder);
now to aggregate by matching date :--
storeOrder.aggregate([$match:{date :new Date("2016-12-26T00:00:00.000Z")} ])
**It is must to use new Date("2016-12-26T00:00:00.000z") instead of Date("2016-12-26T00:00:00.000z") because Date(your_date) !== new Date(your_date).
THANK YOU
The aggregate expects a Javascript Date Object and doesn't work otherwise.
new Date();
new Date(year, month, day);
Please note the month start with 0 and not 1 (Your January is 0 and December 11)