Select all where the months of "date" is December in Mongodb? - mongodb

Do you know if I can do a findAll where the month of Date is December?
I try this request but it's not good:
db.myCollection.aggregate({}, { "Date": { $month: 12 } });
it's similar to a SELECT * FROM table WHERE Months(date)=december?

Consider running an aggregation pipeline that uses the $redact operator as it allows you to incorporate with a single pipeline, a functionality with $project to create a field that represents the month of a date field and $match to filter the documents
which match the given condition of the month being December.
In the above, $redact uses $cond tenary operator as means to provide the conditional expression that will create the system variable which does the redaction. The logical expression in $cond will check
for an equality of a date operator field with a given value, if that matches then $redact will return the documents using the $$KEEP system variable and discards otherwise using $$PRUNE.
Running the following pipeline should give you the desired result:
db.myCollection.aggregate([
{
"$redact": {
"$cond": [
{ "$eq": [{ "$month": "$Date" }, 12] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
This is similar to a $project +$match combo but you'd need to then select all the rest of the fields that go into the pipeline:
db.myCollection.aggregate([
{
"$project": {
"month": { "$month": "$Date" },
"field1": 1,
"field2": 1,
.....
}
},
{ "$match": { "month": 12 } }
])
With another alternative, albeit slow query, using the find() method and $where as:
db.myCollection.find({ "$where": "this.Date.getMonth() === 11" })

db.collection('mycollection').find({"Date": {$month: 12}})
https://docs.mongodb.com/manual/reference/method/db.collection.find/

This should work for finding records in December for a given year, if that is enough to suit your purposes.
db.myCollection.find({
"Date":{
$gte:new Date("2016-12-01T00:00:00Z"),
$lt:new Date("2017-01-01T00:00:00Z")
}})

Related

How to use calcuted date in a mongodb filter?

I want to filter for documents that are within the last minute. But the following query, with a calculated $gte time, does not work.
db.mycollection.aggregate([
{$match: {
"properties.time": {$gte: {$subtract: [ISODate(), 60000]}}
}}
])
It doesn't return anything, but not because there are no dates within the last minute. As far I can tell from projecting the calculated date in queries that do work, the calculated date is correct. Is there some reason that you cannot use a calculated value in this way?
The following query, with a hard-coded time, does work:
db.mycollection.aggregate([
{$match: {
"properties.time": {$gte: ISODate("2018-12-26T12:00:00Z")}
}}
])
I also considered calculating the age in seconds and then filtering based on that. The following also does work.
db.mycollection.aggregate([
{$project: {
"properties.time": "$properties.time",
"age": {$subtract: [ISODate(), "$properties.time"]}
}},
{$match: {
"age": {$lte: 60000}
}}
])
But that's not a good solution because
really, I want to include the filter in a $geoNear query, and since $geoNear has to be the first step in the pipeline, I can't project the age and then use it in the $geoNear
I want to use the index on "properties.time"
We're using eve, so another workaround would be to convert the requested max_age parameter to a start_time in a hook. But it really seems like that first approach should work.
You are using an aggregation expression inside $match so you have to use $expr operator, try:
db.mycollection.aggregate([
{
$match: {
$expr: {
$gte: [ "$properties.time", { $subtract: [ISODate(), 60000] } ]
}
}
}
])
You can use $redact as a fallback for MongoDB lower than 3.6, try:
db.mycollection.aggregate([
{
$redact: {
$cond: {
if: { $gte: [ "$properties.time", { $subtract: [ISODate(), 60000] } ] },
then: "$$KEEP",
else: "$$PRUNE"
}
}
}
])

Specific date comparison

Lets say I have an document like this:
{
productName: 'soap',
expiredDate: ISO_DATE_FORMAT
}
In normal sql I can query like this:
SELECT * FROM table_name WHERE Month(expiredDate) = 11.
How to achieve it in mongoDB or mongooseJS?
You could use the aggregation framework which has some operators that you can use. Consider the first use case which looks at manipulating the date operators within the $project pipeline and a subsequent $match pipeline to filter documents based on the additional field with the $month expression:
db.collection.aggregate([
{
"$project": {
"productName": 1,
"expiredDate": 1,
"expiredMonth": { "$month": "$expiredDate" }
}
},
{ "$match": { "expiredMonth": 11 } }
])
Another option is using a single pipeline with $redact operator which incorporates the functionality of $project and $match as above and returns all documents which match a specified condition using $$KEEP system variable and discards those that don't match using the $$PRUNE system variable. Keep in mind this operator does a collection scan so the first pipeline option may be optimal:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{
"$eq": [
{ "$month": "$expiredDate" },
11
]
},
"$$KEEP",
"$$PRUNE"
]
}
}
])
You can use aggregation framework
db.collection.aggregate([{$project : {month : {"$month" : $expiredDate},productName : 1,expiredDate:1},{$match : {month : 11}}])

$divide aggregation framework questions

I have this query for the MongoDB aggregation framework. I cannot figure out why I can't get this query to run. I checked the documentation and am still perplexed. Can anyone let me know what is wrong.
db.acquisitions.aggregate([
{ $match: {"acquired_year":{$gte:1999} } },
{ $group: {_id:"$acquired_year", "total_acquisition_amount(BBn)": { $divide :[ {$sum:"$acquistion_price"}, 1000000000 ] } }},
{ $sort : {"acquired_year" : -1} }
])
Read the $group manual page, which also lists all valid "accumulators", which means the operators that must be the first argument to any field property referenced after the _id.
This should then lead you to work out that if you want to $divide on a summed total, you need to place that operation in a separate aggregation pipeline stage with $project:
db.acquisitions.aggregate([
{ "$match": { "acquired_year":{ "$gte": 1999 } }},
{ "$group": {
"_id":"$acquired_year",
"total_acquisition_amount(BBn)": { "$sum": "$acquistion_price" }
}},
{ "$project": {
"total_acquisition_amount(BBn)": {
"$divide": [ "$totatotal_acquisition_amount(BBn)", 1000000000 ]
}
}},
{ "$sort": { "_id": -1 }}
])
The only way you can otherwise use math and other operators is "within" an accumulator like $sum, which does not apply in this case since the division must occur "after" the total has been determined.
Also, as a result of $group, the "acquired_year" field is no longer part of the document emitted, but instead this is the _id value, so you apply the sort on that instead.

Mongo $subtract date doesn't work in aggregation $match block

I am creating a mongo aggregation query which use a $subtract operator in my $match block. As explained in these codes below.
This query doesn't work:
db.coll.aggregate(
[
{
$match: {
timestamp: {
$gte: {
$subtract: [new Date(), 24 * 60 * 60 * 1000]
}
}
}
},
{
$group: {
_id: {
timestamp: "$timestamp"
},
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
timestamp: "$_id.timestamp",
total: "$total",
}
},
{
$sort: {
timestamp: -1
}
}
]
)
However, this second query work:
db.coll.aggregate(
[
{
$match: {
timestamp: {
$gte: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
},
{
$group: {
_id: {
timestamp: "$timestamp"
},
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
timestamp: "$_id.timestamp",
total: "$total",
}
},
{
$sort: {
timestamp: -1
}
}
]
)
I need to use $subtract on my $match block so I can't use the last query.
As of mongodb 3.6 you can use $subtract in the $match stage via the $expr. Here's the docs: https://docs.mongodb.com/manual/reference/operator/query/expr/
I was able to get a query like what you're describing via this $expr and a new system variable in mongodb 4.2 called $$NOW. Here is my query, which gives me orders that have been created within the last 4 hours:
[
{ $match:
{ $expr:
{ $gt: [
"$_created_at",
{ $subtract: [ "$$NOW", 4 * 60 * 60 * 1000] } ]
}
}
}
]
Well you cannot do that and you are not meant to do so either. Another valid thing is that you say to "need" to do this but in reality you really do not.
Pretty much all of the general aggregation operators outside of the pipeline operators are really only valid within a $project or a $group pipeline stage. Mostly within $project but certainly not in others.
A $match pipeline is really the same as a general "query" operation, so the only things valid in there are the query operators.
As for the case for your "need", any "value" that is submitted within an aggregation pipeline and particularly within a $match needs to be evaluated outside of the actual pipeline before the BSON representation is sent to the server.
The only exception is the notation that defines variables in the document, particularly "fieldnames" such a "$fieldname" and then only really in $project or $group. So that means something that "refers" to an existing value of a document, and that is something that cannot be done within any type of "query" document expression.
If you need to work with the value of another field in the document then you work it out with $project first, as in:
db.collection.aggregate([
{ "$project": {
"fieldMath": { "$subtract": [ "$fieldOne", "$fieldTwo" ] }
}},
{ "$match": { "fieldMath": { "$gt": 2 } }}
])
For any other purpose you really want to evaluate the value "outside" the pipeline.
The above answers the question you asked, but this answers the question you didn't ask.
Your pipeline doesn't make any sense since grouping on the "timestamp" alone would be unlikely to group anything since the values are of millisecond accuracy and there is likely not to be more than just a few at best for very active systems.
It appears like you are looking for the math to group by "day", which you can do like this:
db.collection.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$timestamp", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$timestamp", new Date(0) ] },
1000 * 60 * 60 * 24
]}
]
},
"total": { "$sum": "$total" }
}}
])
That "rounds" your timestamp value to a single day and has a much better chance of "aggregating" something than you would otherwise have.
Or you can use the "date aggregation operators" to do much the same thing with a composite key.
So if you want to "query" then it evaluates externally. If you want to work on a value "within the document" then you must do so in either a $project or $group pipeline stage.
The $subtract operator is a projection-operator. It is only available during a $project step. So your options are:
(not recommended) Add a $project-step before your $match-step to convert the timestamp field of all documents for the following match-step. I would not recommend you to do this because this operation needs to be performed on every single document on your database and prevents the database from using an index on the timestamp field, so it could cost you a lot of performance.
(recommended) Generate the Date you want to match against in the shell / in your application. Generate a new Date() object, store it in a variable, subtract 24 hours from it and perform your 2nd query using that variable.

Mongodb query specific month|year not date

How can I query a specific month in mongodb, not date range, I need month to make a list of customer birthday for current month.
In SQL will be something like that:
SELECT * FROM customer WHERE MONTH(bday)='09'
Now I need to translate that in mongodb.
Note: My dates are already saved in MongoDate type, I used this thinking that will be easy to work before but now I can't find easily how to do this simple thing.
With MongoDB 3.6 and newer, you can use the $expr operator in your find() query. This allows you to build query expressions that compare fields from the same document in a $match stage.
db.customer.find({ "$expr": { "$eq": [{ "$month": "$bday" }, 9] } })
For other MongoDB versions, consider running an aggregation pipeline that uses the $redact operator as it allows you to incorporate with a single pipeline, a functionality with $project to create a field that represents the month of a date field and $match to filter the documents
which match the given condition of the month being September.
In the above, $redact uses $cond tenary operator as means to provide the conditional expression that will create the system variable which does the redaction. The logical expression in $cond will check
for an equality of a date operator field with a given value, if that matches then $redact will return the documents using the $$KEEP system variable and discards otherwise using $$PRUNE.
Running the following pipeline should give you the desired result:
db.customer.aggregate([
{ "$match": { "bday": { "$exists": true } } },
{
"$redact": {
"$cond": [
{ "$eq": [{ "$month": "$bday" }, 9] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
This is similar to a $project +$match combo but you'd need to then select all the rest of the fields that go into the pipeline:
db.customer.aggregate([
{ "$match": { "bday": { "$exists": true } } },
{
"$project": {
"month": { "$month": "$bday" },
"bday": 1,
"field1": 1,
"field2": 1,
.....
}
},
{ "$match": { "month": 9 } }
])
With another alternative, albeit slow query, using the find() method with $where as:
db.customer.find({ "$where": "this.bday.getMonth() === 8" })
You can do that using aggregate with the $month projection operator:
db.customer.aggregate([
{$project: {name: 1, month: {$month: '$bday'}}},
{$match: {month: 9}}
]);
First, you need to check whether the data type is in ISODate.
IF not you can change the data type as the following example.
db.collectionName.find().forEach(function(each_object_from_collection){each_object_from_collection.your_date_field=new ISODate(each_object_from_collection.your_date_field);db.collectionName.save(each_object_from_collection);})
Now you can find it in two ways
db.collectionName.find({ $expr: {
$eq: [{ $year: "$your_date_field" }, 2017]
}});
Or by aggregation
db.collectionName.aggregate([{$project: {field1_you_need_in_result: 1,field12_you_need_in_result: 1,your_year_variable: {$year: '$your_date_field'}, your_month_variable: {$month: '$your_date_field'}}},{$match: {your_year_variable:2017, your_month_variable: 3}}]);
Yes you can fetch this result within date like this ,
db.collection.find({
$expr: {
$and: [
{
"$eq": [
{
"$month": "$date"
},
3
]
},
{
"$eq": [
{
"$year": "$date"
},
2020
]
}
]
}
})
If you're concerned about efficiency, you may want to store the month data in a separate field within each document.