Mongo query using aggregation for dates - mongodb

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
}
}])

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

How to find all the the ['id'] by month in pymongo?

I have a huge dataset consisting of collections with fields like this
{"id":"f3fd1b6c",
"originalVersion":"v2",
"rotation":[{"0.5"},{"-0.5"},{"-0.5"},{"-0.5"}],
"scale":[{"1.0"},{""1.0"},{""1.0"}],
"translation":[{"-2.8820719718933105"},{"11.548246383666992"},{"0.0"}],
"timestamp":"2020-03-27T13:28:09.883+00:00"
I want to get all the field ids that were created in same month.
So far I have tried using "find" with exact timestamp query
db.collection.find({'timestamp':date})
But I want to get all the elements that were created in same month,
If you are going to search records by a given month, you can do a simple find with $month
db.collection.find({
$expr: {
$eq: [
{
$month: "$timestamp"
},
3
]
}
})
Here is the Mongo playground for your reference.
If you want to group by month and group the ids together, you can do like this.
db.collection.aggregate([
{
$group: {
_id: {
"$month": "$timestamp"
},
idsToFetch: {
"$push": "$id"
}
}
}
])
Here is the Mongo playground for your reference.

MongoDB get only the last documents per grouping based on field

I have a collection "TokenBalance" like this holding documents of this structure
{
_id:"SvVV1qdUcxNwSnSgxw6EG125"
balance:Array
address:"0x6262998ced04146fa42253a5c0af90ca02dfd2a3"
timestamp:1648156174658
_created_at:2022-03-24T21:09:34.737+00:00
_updated_at:2022-03-24T21:09:34.737+00:00
}
Each address has multiple documents like of structure above based on timestamps.
So address X can have 1000 objects with different timestamps.
What I want is to only get the last created documents per address but also pass all the document fields into the next stage which is where I am stuck. I don't even know if the way I am grouping is correctly done with the $last operator. I would appreciate some guidance on how to achieve this task.
What I have is this
$group stage (1st stage)
{
_id: '$address',
timestamp: {$last: '$timestamp'}
}
This gives me a result of
_id:"0x6262998ced04146fa42253a5c0af90ca02dfd2a3"
timestamp:1648193827320
But I want the other fields of each document as well so I can further process them.
Questions
1) Is it the correct way to get the last created document per "address" field?
2) How can I get the other fields into the result of that group stage?
Use $denseRank
db.collection.aggregate([
{
$setWindowFields: {
partitionBy: "$address",
sortBy: { timestamp: -1 },
output: { rank: { $denseRank: {} } }
}
},
{
$match: { rank: 1 }
}
])
mongoplayground
I guess you mean this:
{ $group: {
_id: '$address',
timestamp: {$last: '$timestamp'},
data: { $push: "$$ROOT" }
} }
If the latest timestamp is also the last sorted by _id you can use something like this:
[{$group: {
_id: '$_id',
latest: {
$last: '$$ROOT'
}
}}, {$replaceRoot: {
newRoot: '$latest'
}}]

Is there a way to use find and aggregate together in MongoDB?

I m a MongoDB begginer and I have the following problem:
I have a document format(sorry for lack of definition) as follows in MongoDB:
And I want to query the top 10 albums of the worst genre of a decade I choose.
Firstly I did an aggregate that gave me in the last stage the worst genre of the decade I choose to use as comparison later (BDA1 being my database and album my collection I want to aggregate and find on):
BDA1.album.aggregation(
[
{
$addFields: {
release_date: {
$toDate: "$release_date"
}
}
},
{
$addFields: {
sales_amount: {
$convert: {
input: "$sales_amount",
to: "int"
}
}
}
},
{
$match: {
"release_date": {
$gte: new ISODate("2009-01-01"),
$lt: new ISODate("2021-01-01")
}
}
},
{
$unwind: {
path: "$band.band_genre",
}
},
{
$group: {
_id: "$band.band_genre",
total: {
$sum: "$sales_amount"
}
}
},
{
$sort: {
total: 1
}
},
{
$limit: 1
}
])
(Sorry for the lack of good formatting but I took the code from a pipeline I used to do the aggregation in MongoDB Compass.)
That resulted in:
But my question now is: how do I do to use that aggregate result in what I can only assume is a find command where band.band_genre equals to the genre I just calculated in the aggregation?
I have been searching SO for a while with no results and google as well.
Any suggestions?
(Anything that I have forgot to mention that u feel is important to understand the problem please say and I will edit it in)

Get document on subarray containing date between aggregation 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"
}
}
}
}
}
]);