How to find items in collection by string input? - mongodb

In my collection users I have a field registerDate in format ISODate(< string >). I need to send a request to MongoDB. I use vibe.d framework and this one can send only deserialized JSON string. So, the input date can be either "2021-02-28T21:00:00Z" or UNIX timestamp.
In detail:
Works:
db.users.find(
{
"registerDate":{
"$gte": ISODate("2021-02-28T21:00:00Z"),
"$lt": ISODate("2021-10-31T21:00:00Z")
}
})
Doesn't work:
"$gte": {$date: "2021-02-28T21:00:00Z"},
"$lt": {$date:"2021-10-31T21:00:00Z"}
I also try:
db.users.aggregate([
{ "$project": {
"registerDate": {
"$gte": { "$toDate": "2021-01-07T23:39:49.178Z" },
"$lt": { "$toDate": "2021-09-07T23:39:49.178Z" }
}
}
}])
Then, I get the error:
"errmsg" : "Invalid $project :: caused by :: FieldPath field names may not start with '$'."

Try $expr expression operator to use aggregation operator in $match stage,
db.users.aggregate([
{
$match: {
$expr: {
$and: [
{
$gte: [
"$registerDate",
{ $toDate: "2021-02-28T21:00:00Z" }
]
},
{
$lt: [
"$registerDate",
{ $toDate: "2021-10-31T21:00:00Z" }
]
}
]
}
}
}
])
Playground

Related

Query using $dateFromString and $lt and $lge in mongodb

I am trying to return some records for date range from mongodb and I am trying to use the following query to query the collection test for the field StartDate:
db.test.aggregate([
{
$match: {
"StartDate": {
"$gte": [
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
],
"$lt": [
{
"$dateFromString": {
"dateString": "2021-04-01T00:00:00.0000000Z"
}
}
]
}
}
}
])
The above query didn't return anything but I am sure there are some data between the dates. Any ideas what have I missed? Thanks!
The $dateFromString is a aggregation pipeline operator, so it requires to match in $expr expression condition, and expression have different syntax of $gte and $lt,
db.test.aggregate([
{
$match: {
$expr: {
$and: [
{
$gte: [
"$StartDate",
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
]
},
{
$lt: [
"$StartDate",
{
"$dateFromString": {
"dateString": "2021-04-01T00:00:00.0000000Z"
}
}
]
}
]
}
}
}
])
Playground
Second option: You can use $toDate operator alternate of $dateFromString,
Playground
Update as per new requirement:
Field activities.dateOfActivity is an array, it requires to iterate through loop and check each date's conditions,
$map to iterate loop of activities.dateOfActivity array and put both condition inside, it will return true if both condition satisfy otherwise return false
$anyElementTrue will check return array of boolean have any true condition then return document
db.test.aggregate([
{
$match: {
$and: [
{
"$expr": {
$anyElementTrue: {
$map: {
input: "$activities.dateOfActivity",
in: {
$and: [
{
"$gte": [
"$$this",
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
]
},
{
"$lt": [
"$$this",
{
"$dateFromString": {
"dateString": "2021-04-20T00:00:00.0000000Z"
}
}
]
}
]
}
}
}
}
}
]
}
}
])
Playground

How do I query mongodb with aggregration by passing data as a parameter to filter unix time stamp

I'm querying through Metabase which is connected to a Mongodb server. The field which I'm querying is nested and is a Unix timestamp. See below
{
room_data: {
"meta": {
"xxx_unrecognized": null,
"xxx_sizecache": 0,
"id": "Hke7owir4oejq3bMf",
"createdat": 1565336450838,
"updatedat": 1565336651548,
}
}
}
The query I have written is as follows
[
{
$match: {
client_id: "{{client_id}}",
"room_data.meta.createdat": {
$gt: "{{start}}",
$lt: "{{end}}",
}
}
},
{
$group: {
id: "$room_data.recipe.id",
count: {
$sum: 1
}
}
}
]
I do not get any result as the field room_data.meta.createdat is not a date (Aug 20, 2020) which I'm passing in. Here start and end are the parameters (Metabase feature) which I'm passing in the Date format. I need some help in converting those dates into unix timestamp which can then be used to filter out the results between the specific dates
If you're using Mongo version 4.0+ you can then use $toDate in you're aggregation like so:
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{
$eq: [
"$client_id",
{{client_id}}
]
},
{
$lt: [
{
$toDate: "$room_data.meta.createdat"
},
{{end}}
]
},
{
$gt: [
{
$toDate: "$room_data.meta.createdat"
},
{{start}}
]
}
]
}
}
}
])
MongoPlayground
If you're you're on an older Mongo version I recommend you either convert you're database fields to be Date type, or you convert your input into a number timestamp somehow (I'm unfamiliar with metabase).
The last option is to use $subtract as you can subtract a number from a date in Mongo, then check to see whether that date is before or after 1970-01-01T00:00:00Z. the problem with this approach is it does not consider timezones, so if your input's timezone is different than your database one or is dynamic this will be a problem you'll have to account for.
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{
$eq: [
"$client_id",
{{client_id}}
]
},
{
$gt: [
{
"$subtract": [
{{end}},
"$room_data.meta.createdat"
]
},
ISODate("1970-01-01T00:00:00.000Z")
]
},
{
$lt: [
{
"$subtract": [
{{start}},
"$room_data.meta.createdat"
]
},
ISODate("1970-01-01T00:00:00.000Z")
]
}
]
}
}
}
])
MongoPlayground

MongoDB - match multiple fields same value

So I am trying to do something where I can group MongoDB fields for a check.
Given I have following data structure:
{
//Some other data fields
created: date,
lastLogin: date,
someSubObject: {
anotherDate: date,
evenAnotherDate: date
}
On these I want to do a check like this:
collection.aggregate([
{
$match: {
"created": {
$lt: lastWeekDate
},
"someSubObject.anotherDate": {
$lt: lastWeekDate
},
"lastLogin": {
$lt ...
is there a possibility to group the fields and do something like
$match: {
[field1, field2, field3]: {
$lt: lastWeekDate
}
}
You need $expr to use $map to generate an array of boolean values and then $allElementsTrue to apply AND condition
db.collection.find({
$expr: {
$allElementsTrue: {
$map: {
input: [ "$field1", "$field2", "$field3" ],
in: { $lt: [ "$$this", lastWeekDate ] }
}
}
}
})
EDIT: if you need that logic as a part of aggregation you can use $match which is an equivalent of find
db.collection.aggregate([
{
$match: {
$expr: {
$allElementsTrue: {
$map: {
input: [ "$field1", "$field2", "$field3" ],
in: { $lt: [ "$$this", lastWeekDate ] }
}
}
}
}
}
])

Can mongodb find's lt operator take the document field as argument

I have a document which looks something like this
{
"_id":ObjectId("5bcef414b4305f4054571305"),
"timestamp": ISODate("2018-10-23T10:12:36.755 Z"),
"config" : {
"expiry_duration" : NumberLong(10000)
}
}
I need to find documents which are expired ,i.e, whose $datediff(time.Now(), $timestamp) > config.expiry_duration
I am not clear if I need to use aggregate or if I can achieve this using find itself
You can do it using .find() method but you need $expr operator (which is available in MongoDB 3.6):
db.collection.find({
$expr: {
$gt: [ { $subtract: [ ISODate("2018-10-23T16:39:06.266Z"), "$timestamp" ] }, "$config.expiry_duration" ]
}
})
To get current date you can type new Date() in Mongo shell
If you need a solution for MongoDB < 3.6 you can use .aggregate() and $redact pipeline stage:
db.col.aggregate({
$redact: {
$cond: {
if: { $gt: [ { $subtract: [ ISODate("2018-10-23T16:39:06.266Z"), "$timestamp" ] }, "$config.expiry_duration" ] },
then: "$$KEEP",
else: "$$DESCEND"
}
}
})

How can I compare array length in $match stage?

funding_rounds below is an array, I'm trying to do the following query and get an error $size needs a number, what is the correct way to use $size with $gte in aggregation?
db.companies.aggregate([
{
$match: {
$and: [
{"founded_year": 2004},
{"funding_rounds": {$size: {$gte: 5}}}
]
}
}
])
$size operator should be used inside projections, so you need to transform your document before matching:
db.companies.aggregate([
{
"$project": {
"_id": 1,
"founded_year": 1,
"funding_rounds": 1,
"funding_rounds_size": { "$size": "$funding_rounds" }
}
},
{
"$match": {
"$and": [
{"founded_year": 2004},
{"funding_rounds_size": { "$gte": 5 }}
]
}
}
])
There's also much shorter way to compare array length: you can check if fifth element exists:
db.companies.find(
{
"founded_year": 2004,
"funding_rounds.4": { $exists: true }
}
)