Get document on subarray containing date between aggregation mongodb - mongodb

Provided following collection:
[
{
events: [
{
triggers: [
{
date: "2019-12-12T23:00:00"
}
]
}
]
}
]
I want to be able to pull the documents that have any date in between a range of dates, let's say today and tomorrow.
Using following query:
db.collection.aggregate([
{
$match: {
"events.triggers.date": {
$gte: "2019-12-11T23:00:00.000Z",
$lt: "2019-12-12T23:59:00.000Z"
}
}
}
]);
However, when I do this, the query seems to be looking at any document that has any date greater than and any date lower than but not necessarily in the same "trigger" object.
Anyone got any idea how you can filter in a subarray like this (I do more in my query afterwards so a find will not work) and have the date search be subitem specific?

You are almost there, just some mistakes in your query. This should work:
db.collection.aggregate([
{
'$match': {
'$and': [
{"events.triggers.date": { '$gte': "2019-12-11T23:00:00.000Z" }},
{"events.triggers.date": { '$lt': "2019-12-11T23:00:00.000Z" }}
]
}
}
]);

So I found it eventually.
Those looking for the solution. Here it is:
elemMatch
db.collection.aggregate([
{
$match: {
"events.triggers": {
$elemMatch: {
"date": {
$gte: "2019-12-11T23:00:00.000Z",
$lt: "2019-12-12T23:59:00.000Z"
}
}
}
}
}
]);

Related

MongoDB - Dates between using $match

So I try to use MongoDB $match to get data between 2 dates, but it turns out that the data is not returning a.k.a empty here. What it looks like:
db.collection.aggregate([
{
$match: {
date: {
$gte: new Date("2022-10-23"),
$lt: new Date("2022-10-25"),
},
}
},
{
$group: {
_id: "$title",
title: {
$first: "$title"
},
answer: {
$push: {
username: "$username",
date: "$date",
formId: "$formId",
answer: "$answer"
}
}
}
},
])
Here is the data that I try to run on the Mongo playground:
https://mongoplayground.net/p/jKx_5kZnJNz
I think there is no error with my code anymore... but why it gives an empty return.
Migrate the comment to the answer post for the complete explanation.
Issue 1
The document contains the date field as a string type while you are trying to compare with Date which leads to incorrect output.
Ensure that you are comparing both values in the exact type.
Either that migrate the date value to Date type or
converting the date field to Date type in the query via $toDate.
{
$match: {
$expr: {
$and: [
{
$gte: [
{
$toDate: "$date"
},
new Date("2022-10-23")
]
},
{
$lt: [
{
$toDate: "$date"
},
new Date("2022-10-25")
]
}
]
}
}
}
Issue 2
Since you are using $lt ($lt: new Date("2022-10-25")), it won't include the documents with date: new Date("2022-10-25").
For inclusive end date, you shall use $lte.
Demo # Mongo Playground

MongoDB conditional query depending on possible dates

I have a scenario where I want to pull documents that have a lastAlertSentDate field that's over 30 days old. This will run in a daily cron job. Upon querying, this field will then be reset to NOW. So it's meant to act as a "rotating 30 day window" if you will.
The complication here is that the field won't exist if it hasn't been set yet. In this edge case, we'll then have to use a createdDate field of the document to do the 30-day comparison against.
So effectively, I want something like, "If lastAlertSentDate exists, then get all docs where it's older than 30days from now. ---Otherwise, get all docs where createdDate is older than 30days from now"
So the logic between both fields are the same, it's just the field itself that can be different. Because of this, I was thinking to first USE addFields a dateToUseField and then do a match on the second stage based on this.
[
{
'$addFields': {
'dateToUse': {
'$cond': {
'if': {
'$ne': [
'$lastAlertSentDate', undefined
]
},
'then': '$lastAlertSentDate',
'else': '$createdDate'
}
}
}
}, {
'$match': {
'dateToUse': {
'$lte': '30_DAYS_PRIOR'
}
}
}
]
So the else part doesn't seem to work. It doesn't assign $createdDate to dateToUse.
What am I missing? Also, how can I condense this? I'm sure I don't need the addFields first and I can do everything within the $match
You have two options here:
Use a $or query with two predicates, where each of them is a $and predicate:
Either lastAlertSentDate does not exists and createdDate > n
Or lastAlertSentDate exists and it is > n
Playground Link
db.collection.find({
$or: [
{
$and: [
{
"lastAlertSentDate": {
"$exists": false
}
},
{
"createdDate": {
$gt: 5
}
}
]
},
{
$and: [
{
"lastAlertSentDate": {
"$exists": true
}
},
{
"lastAlertSentDate": {
$gt: 5
}
}
]
}
]
})
Use an aggregation using the $ifNull
Playground Link
db.collection.aggregate([
{
$match: {
$expr: {
$gt: [
{
"$ifNull": [
"$lastAlertSentDate",
"$createdDate"
]
},
5
]
}
}
}
])

how to use $elemMatch on array specifying an upper field as part of the query

I'd like to retrieve for a specific user, his chats with unread messages.
Lets say I have a simplified chat model like that :
{
lastMessageAt: Date,
participants: [
{
user: String(id),
lastReadAt: Date
}
]
}
How can I achieve my query ?
I have tried several thing like with $elemMatch, but lastMessageAt is unknown at this level...
ChatDB.find({
'participants': {
$elemMatch: { user: '12345', lastReadAt: { $lt: '$lastMessageAt' } }
}
}
Thanks in advance for your help ! :)
$elemMatch operator will find those documents in ChatDB collection that have at least 1 element in participants that matches your criteria. Also my research ended with the conslusion that it is not yet possible to access other document field in $elemMatch operator. Anyway, if this is your goal, then you can use this query:
ChatDB.aggregate([
{
$match: {
"participants.user": "12345",
$expr: {
$lt: [
"$participants.lastReadAt",
"$lastMessageAt"
]
}
}
}
])
Mongo playground
If you also want to filter participants that really matched the criteria, then you need to add a projection stage:
ChatDB.aggregate([
{
$match: {
"participants.user": "12345",
$expr: {
$lt: [
"$participants.lastReadAt",
"$lastMessageAt"
]
}
}
},
{
$project: {
participants: {
$filter: {
input: "$participants",
as: "participant",
cond: {
$and: [
{
$eq: [
"$$participant.user",
"12345"
]
},
{
$lt: [
"$$participant.lastReadAt",
"$lastMessageAt"
]
}
]
}
}
}
}
}
])
Mongo playground
I have found the solution witch is to use the aggregator with the $unwind operator.
await ChatDB.aggregate([
{
$unwind: '$participants'
},
{
$match: {
'participants.user': '12345',
$expr: {
$lt: [
'$participants.lastReadAt',
'$lastMessageAt'
]
}
}
}]);
Hope this will be usefull

Mongo query using aggregation for dates

In the following query I'm trying to find entries in my articles collection made in the last week, sorted by the number of votes on that article. The $match doesn't seem to work(maybe I dont know how to use it). The following query works perfectly, so its not a date format issue,
db.articles.find(timestamp:{
'$lte':new Date(),
'$gte':new Date(ISODate().getTime()-7*1000*86400)}
})
But this one doesn't fetch any results. Without the $match it also fetches the required results(articles sorted by votecount).
db.articles.aggregate([
{
$project:{
_id:1,
numVotes:{$subtract:[{$size:"$votes.up"},{$size:"$votes.down"}]}}
},
{
$sort:{numVotes:-1}
},
{
$match:{
timestamp:{
'$lte':new Date(),
'$gte':new Date(ISODate().getTime()-7*1000*86400)}
}
}
])
You are trying to match at the end of your pipeline, which supposes you have projected timestamp field, and you haven't done that.
I believe what you want is to filter data before aggregation, so you should place match at the top of your aggregation array.
Try this:
db.articles.aggregate([{
$match: {
timestamp: {
'$lte': new Date(),
'$gte': new Date(ISODate().getTime() - 7 * 1000 * 86400)
}
}
}, {
$project: {
_id: 1,
numVotes: {
$subtract: [{
$size: "$votes.up"
}, {
$size: "$votes.down"
}]
}
}
}, {
$sort: {
numVotes: -1
}
}])

MongoDB $gt not working

Below query gives me no results, though there are fields matching the geo location and time stamp.
I have tried changing the $gt to $ne and it returns the sum of all $values.
I have even tried checking timeStamp greater than 0. still no result. Here timestamp is a double datatype.
db.csv2.aggregate([{
$match: {
$and: [{
loc: {
$geoWithin: {
$box: [
[-128.232422, 26.382028],
[-109.6875, 43.624147]
]
}
}
}, {
timeStamp: {
$gt: 1432201420790
}
}]
}
}, {
$group: {
_id: null,
total: {
$sum: "$value"
}
}
}]);
The error given to you is explicit. Read the doc to see how to implement the $gt aggregation function : documentation
Basically, the condition is an array
{ $gt: ["$timeStamp", 1432201420790] }
[EDIT]
Your issue probably comes from the syntax with the dollar sign before the timestamp field which is missing in your code. Try this
{ $gt: ["$timeStamp", 1432201420790] }
instead of
{ timeStamp: { $gt: 1432201420790 }}