I am trying to compare two fields of collection mongo itself to get rows, but not able to get it.
[{
"date_created" : ISODate("2022-06-24T05:01:15.370+0000"),
"date_modified" : ISODate("2022-06-29T05:01:15.370+0000"),
},
{
"date_created" : ISODate("2022-06-24T05:01:15.370+0000"),
"date_modified" : ISODate("2022-06-19T05:01:15.370+0000"),
},
{
"date_created" : ISODate("2022-06-24T05:01:15.370+0000"),
}]
Query
db.getCollection("collection_name").aggregate([
{
$match: {
status: '1',
$or: [
{
date_modified: { $gt: ISODate('$date_created') }
},
{
date_modified: {
"$exists": false,
},
},
],
},
},
]);
Expected result:
[{
"date_created" : ISODate("2022-06-24T05:01:15.370+0000"),
"date_modified" : ISODate("2022-06-29T05:01:15.370+0000"),
},{
"date_created" : ISODate("2022-06-24T05:01:15.370+0000"),
}]
Current result: date_created is not defined
You need to use $expr for comparison between fields. Use $ifNull to cater your requirement for checking the existence for the date_created field.
db.collection.find({
$expr: {
$or: [
{
$eq: [
{
$ifNull: [
"$date_modified",
null
]
},
null
]
},
{
$gt: [
"$date_modified",
"$date_created"
]
}
]
}
})
Here is the Mongo Playground for your reference.
Related
Below is the document which has an array name datum and I want to filter the records based on group by year and filter by the types and max date.
{
"_id" : ObjectId("5fce46ca6ac9808276dfeb8c"),
"year" : 2018,
"datum" : [
{
"Type" : "1",
"Amount" : NumberDecimal("100"),
"Date" : ISODate("2018-05-30T00:46:12.784Z")
},
{
"Type" : "1",
"Amount" : NumberDecimal("300"),
"Date" : ISODate("2023-05-30T00:46:12.784Z")
},
{
"Type" : "2",
"Amount" : NumberDecimal("340"),
"Date" : ISODate("2025-05-30T00:46:12.784Z")
},
{
"Type" : "3",
"Amount" : NumberDecimal("300"),
"Date" : ISODate("2021-05-30T00:46:12.784Z")
}
]
}
The aggregate Query I tried.
[{$group: {
_id :"$year",
RecentValue :
{
$sum: {
$reduce: {
input: '$datum',
initialValue: {},
'in': {
$cond:
[
{
$and:
[
{$or:[
{ $eq: [ "$$this.Type", '2' ] },
{$eq: [ "$$this.Type", '3' ] }
]},
{ $gt: [ "$$this.Date", "$$value.Date" ] },
]
}
,
"$$this.Amount",
0
]
}
}
}
}
}}]
the expected output would be which having the max date "2025-05-30T00:46:12.784Z"
{
_id :2018,
RecentValue : 340
}
Please let me know what mistake I did in the aggregate query.
You can get max date before $group stage,
$addFields to get document that having max date from, replaced $or with $in condition and corrected return value
$group by year and sum Amount
db.collection.aggregate([
{
$addFields: {
datum: {
$reduce: {
input: "$datum",
initialValue: {},
"in": {
$cond: [
{
$and: [
{ $in: ["$$this.Type", ["2", "3"]] },
{ $gt: ["$$this.Date", "$$value.Date"] }
]
},
"$$this",
"$$value"
]
}
}
}
}
},
{
$group: {
_id: "$year",
RecentValue: { $sum: "$datum.Amount" }
}
}
])
Playground
I want to count the number of resetPassword (subdocument in Users schema) codes that are currently active. For a code to be active it's expiry date must be greater than the current date.
Here is my users schema. If someone requests to reset there password, we'll push a new { code: X, expiresAt, createdAt } Object to the array.
id: { type: String, unique: true },
resetPassword: [
{
code: String,
expiresAt: Date,
createdAt: Date,
},
],
I'm having an issue trying to $sum the total number of active reset codes. Here is the query I'm running that returns an empty array...note that if I were to remove the resetPassword.expiresAt: { $gt: nowDateInMilliseconds() } match section, it will return all the codes. I've tried moving this match statement out of the intial $match stage then doing an unwind & a match on the expiresAt but this didn't work either.
[
{
$match: {
"id": userId,
'resetPassword.expiresAt': {
$gt: nowDateInMillisec(),
},
},
},
{
$group: {
_id: '$id',
totalValidResetCodes: {
$sum: {
$size: '$resetPassword',
},
},
},
},
]
This returns an empty array, even though I've got the expiry dates set to a date in the future.
I also tried the following with the same result (notice how I added $unwind and another $match to the pipeline)
[
{
$match: {
"id": userId,
},
},
{
$unwind: '$resetPassword',
},
{
$match: {
'resetPassword.expiresAt': {
$gt: nowDateInMillisec(),
},
}
},
{
$group: {
_id: '$id',
totalValidResetCodes: {
$sum: {
$size: '$resetPassword',
},
},
},
},
]
nowDateInMillisec() - This simply returns the current date in milliseconds from epoch.
What am I doing wrong?
You can try $reduce in $project, instead of your all process, you need to return ISOdate from this nowDateInMillisec(),
db.collection.aggregate([
{ $match: { id: 1 } },
{
$project: {
totalValidResetCodes: {
$reduce: {
input: "$resetPassword",
initialValue: 0,
in: {
$add: [
"$$value",
{
$cond: [
{ $gt: ["$$this.expiresAt", nowDateInMillisec()] },
// If you really want to pass timestamp then try below line
// { $gt: ["$$this.expiresAt", { $toDate: nowDateInMillisec() }] },
1,
0
]
}
]
}
}
}
}
}
])
Playground
Below is my research, date format when stored in mongodb format should work for milliseconds as well. The below test is for up to a minute.
> db.users13.find().pretty();
{
"_id" : ObjectId("5f4e03768379a4e3f957641d"),
"id" : "johnc",
"resetPassword" : [
{
"code" : "abc",
"expiresAt" : ISODate("2020-09-02T09:11:18.394Z"),
"createdAt" : ISODate("2020-09-01T08:11:18.394Z")
},
{
"code" : "mno",
"expiresAt" : ISODate("2020-08-25T09:26:18.394Z"),
"createdAt" : ISODate("2020-08-25T08:11:18.394Z")
}
]
}
{
"_id" : ObjectId("5f4e06938379a4e3f957641f"),
"id" : "katey",
"resetPassword" : [
{
"code" : "j2c",
"expiresAt" : ISODate("2020-09-02T08:48:18.394Z"),
"createdAt" : ISODate("2020-09-01T08:11:18.394Z")
},
{
"code" : "rml",
"expiresAt" : ISODate("2020-09-01T08:26:18.394Z"),
"createdAt" : ISODate("2020-09-01T08:11:18.394Z")
}
]
}
> db.users13.aggregate([
{$unwind:"$resetPassword"},
{$match:{"resetPassword.expiresAt":{$gt:ISODate()}}}
]).pretty();
{
"_id" : ObjectId("5f4e03768379a4e3f957641d"),
"id" : "johnc",
"resetPassword" : {
"code" : "abc",
"expiresAt" : ISODate("2020-09-02T09:11:18.394Z"),
"createdAt" : ISODate("2020-09-01T08:11:18.394Z")
}
}
{
"_id" : ObjectId("5f4e06938379a4e3f957641f"),
"id" : "katey",
"resetPassword" : {
"code" : "j2c",
"expiresAt" : ISODate("2020-09-02T08:48:18.394Z"),
"createdAt" : ISODate("2020-09-01T08:11:18.394Z")
}
}
> ISODate()
ISODate("2020-09-01T08:31:37.059Z")
>
#Joe answered my question in the comments. He hinted that using a milliseconds since epoch time in the match filter wouldn't work since I'm using a Date type in my Mongoose schema.
So instead of doing this: $gt: nowDateInMillisec(), I simply used a Date type like so: $gt: new Date(),
I'm trying to write a bit complicated query to the MongoDB and I honestly don't know how to write it correctly. In SQL pseudocode it's something like:
SELECT *
FROM Something
WHERE
(IF Item.NestedItem THEN NULL ELSE Item.NestedItem.Id) == 'f4421f9e-5962-4c68-8049-a45600f36f4b'
OR (IF Item.NestedItem THEN NULL ELSE Item.NestedItem.OtherId) == 'd6b799dc-f464-4919-8435-a7b600cc408a'
I succeeded in writing IF as
{ "$cond" : [{ "$eq" : ["$Item.NestedItem", null] }, null, "$Item.NestedItem.Id"] }
{ "$cond" : [{ "$eq" : ["$Item.NestedItem", null] }, null, "$Item.NestedItem.OtherId"] }
But now I'm not sure how to compare them to constants and then join. I'm trying to write a translator from SQL-like language to MongoDb and this is example of query I cannot translate. I want something like
{ "$and" :
{ "$eq" : { "$cond" : [{ "$eq" : ["$Item.NestedItem", null] }, null, "$Item.NestedItem.Id"] }, "f4421f9e-5962-4c68-8049-a45600f36f4b" },
{ "$eq" : { "$cond" : [{ "$eq" : ["$Item.NestedItem", null] }, null, "$Item.NestedItem.OtherId"] }, "d6b799dc-f464-4919-8435-a7b600cc408a" } }
But it's an invalid query as I get it
The $cond is a aggregation expression, you need to use $expr before $and operation, in your SQL query you have user OR so just updating in query $and to $or
corrected $eq syntax after $or
db.collection.aggregate([
{
$match: {
$expr: {
$or: [
{
$eq: [
{
$cond: [
{ $eq: ["$Item.NestedItem", null] },
null,
"$Item.NestedItem.Id"
]
},
"f4421f9e-5962-4c68-8049-a45600f36f4b"
]
},
{
$eq: [
{
$cond: [
{ $eq: ["$Item.NestedItem", null] },
null,
"$Item.NestedItem.OtherId"
]
},
"d6b799dc-f464-4919-8435-a7b600cc408a"
]
}
]
}
}
}
])
Playground
I am trying to use the MongoDB $lookup with the Uncorrelated Subqueries.
Using MongoDB 3.6.12 (support began on 3.6)
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#join-conditions-and-uncorrelated-sub-queries
The following pipeline step is working, however if I swap out the first "userB" with the second, no results are returned.
{
from: 'friendships',
let: { requestuser: ObjectId("5c0a9c37b2365a002367df79"), postuser: ObjectId("5c0820ea17a69b00231627be") },
pipeline: [
{
$match : {
$or : [
{
"userA" : ObjectId("5c0820ea17a69b00231627be"),
"userB" : ObjectId("5c0a9c37b2365a002367df79")
// "userB" : '$$requestuser'
},
{
"userA" : ObjectId("5c0a9c37b2365a002367df79"),
"userB" : ObjectId("5c0820ea17a69b00231627be")
}
],
"accepted" : true
}
},
{
$project: {v: 'true'}
}
],
as: "match"}
Results with hard coded ObjectId:
"match" : [
{
"_id" : ObjectId("5d6171dd319401001fd326bf"),
"v" : "true"
}
]
Results using variable:
"match" : [
]
I feel like ObjectIds need special treatment. All the examples I could find are using simple variables like strings.
To verify the '$$requestUser' contained a value, I tested it on the projection:
"match" : [
{
"_id" : ObjectId("5d6171dd319401001fd326bf"),
"v" : ObjectId("5c0a9c37b2365a002367df79")
}
]
When you use un co-related sub queires, you need to use $expr to pass a variable.
You can try something like following.
{
$match: {
$expr: {
$or: [
{
$and:[
{
$eq: [ "userA", ObjectId("5c0820ea17a69b00231627be") ]
},
{
$eq: [ "userB", ObjectId("5c0a9c37b2365a002367df79") ]
},
{
$eq: [ "userB", "$$requestuser" ]
}
]
},
{
$and:[
{
$eq: [ "userA", ObjectId("5c0a9c37b2365a002367df79") ]
},
{
$eq: [ "userB", ObjectId("5c0820ea17a69b00231627be") ]
}
]
}
]
},
"accepted": true,
}
}
I have created a sample demo to show how $expr works inside the lookup : Sample demo for Uncorrelated Subquery
Below are the sample collection.
col1:
"_id" : ObjectId("5ec293782bc00b43b463b67c")
"status" : ["running"],
"name" : "name1 ",
"dcode" : "dc001",
"address" : "address1",
"city" : "city1"
col2:
"_id" : ObjectId("5ec296182bc00b43b463b68f"),
"scode" : ObjectId("5ec2933df6079743c0a2a1f8"),
"ycode" : ObjectId("5ec293782bc00b43b463b67c"),
"city" : "city1",
"lockedDate" : ISODate("2020-05-20T00:00:00Z"),
"_id" : ObjectId("5ec296182bc00b43b463688b"),
"scode" : ObjectId("5ec2933df6079743c0a2a1ff"),
"ycode" : ObjectId("5ec293782bc00b43b463b67c"),
"city" : "city1",
"lockedDate" : ISODate("2020-05-20T00:00:00Z"),
"_id" : ObjectId("5ec296182bc00b43b44fc6cb"),
"scode" :null,
"ycode" : ObjectId("5ec293782bc00b43b463b67c"),
"city" : "city1",
"lockedDate" : ISODate("2020-05-20T00:00:00Z"),
problemStatement:
I want to display name from col1 & count of documents from col2 according to ycode where scode is != null
Tried attempt:
db.col1.aggregate([
{'$match':{
city:'city1'
}
},
{
$lookup:
{
from: "col2",
let: {
ycode: "$_id",city:'$city'
},
pipeline: [
{
$match: {
scode:{'$ne':null},
lockedDate:ISODate("2020-05-20T00:00:00Z"),
$expr: {
$and: [
{
$eq: [
"$ycode",
"$$ycode"
]
},
{
$eq: [
"$city",
"$$city"
]
}
]
},
},
},
], as: "col2"
}
},
{'$unwind':'$col2'},
{'$count':'ycode'},
{
$project: {
name: 1,
status: 1,
}
},
])
now problem with this query is it either displays the count or project the name & status i.e if i run this query in the current format it gives {} if I remove {'$count':'ycode'} then it project the values but doesn't give the count and if I remove $project then i do get the count {ycode:2} but then project doesn't work but I want to achieve both in the result. Any suggestions
ORM: mongoose v>5, mongodb v 4.0
You can try below query :
db.col1.aggregate([
{ "$match": { city: "city1" } },
{
$lookup: {
from: "col2",
let: { id: "$_id", city: "$city" }, /** Create local variables from fields of `col1` but not from `col2` */
pipeline: [
{
$match: { scode: { "$ne": null }, lockedDate: ISODate("2020-05-20T00:00:00Z"),
$expr: { $and: [ { $eq: [ "$ycode", "$$id" ] }, { $eq: [ "$city", "$$city" ] } ] }
}
},
{ $project: { _id: 1 } } // Optional, But as we just need count but not the entire doc, holding just `_id` helps in reduce size of doc
],
as: "col2" // will be an array either empty (If no match found) or array of objects
}
},
{
$project: { _id: 0, name: 1, countOfCol2: { $size: "$col2" } }
}
])
Test : mongoplayground