Double nested array with multiple nested documents identify wrong value - mongodb

I need to identify which documents have the wrong date string ( $gt:10 characters) from all my collection :
{
"_id": ObjectId("5c05984246a0201286d4b57a"),
f: "x",
"_a": [
{
"_onlineStore": {}
},
{
"_p": {
"s": {
"a": {
"t": [
{
"dateP": "20200-09-20",
"l": "English",
"size": "XXL"
}
]
},
"c": {
"t": [
{
"dateP": "20300-09-20",
"l": "English",
"size": "XXL"
}
]
}
}
}
}
]
}
and output need to be as follow:
{f:"x",dateP:"20200-09-20", t:"c"}
{f:"x",dateP:"20300-09-20", t:"a"}
The last field in the output "t" not compulsory but desirable ...
Please, help ...

We can use $objectToArray for this:
db.collection.aggregate([
{$unwind: "$_a"},
{$project: {_id: 0, f: 1, data: {$objectToArray: "$_a._p.s"}}},
{$unwind: "$data"},
{$unwind: "$data.v.t"},
{$match: {$expr: {$gt: [{$strLenCP: "$data.v.t.dateP"}, 10]}}},
{$project: {f: 1, dateP: "$data.v.t.dateP", t: "$data.k"}}
])
See how it works on the playground example

Related

mongoDB count deeply nested array elements filtered by and condition

I am attempting to count elements in 3x nested array , I have some progress , but struggling to filter based on lang:"EN" condition inside the 2x $reduced 1x filter :
Here is example document:
{
"_id": ObjectId("5c05984246a0201286d4b57a"),
f: "x",
"_a": [
{
"_onlineStore": {}
},
{
"_p": [
{
"pid": 1,
"s": {
"a": {
"t": [
{
id: 1,
"dateP": "20200-09-20",
lang: "EN"
},
{
id: 2,
"dateP": "20200-09-20",
lang: "En"
}
]
},
"c": {
"t": [
{
id: 3,
lang: "en"
},
{
id: 4,
lang: "En"
},
{
id: 5,
"dateP": "20300-09-23"
}
]
}
},
h: "Some data"
}
]
}]
}
And here is my attempt ( just need to filter only the elements with lang:"EN"
db.collection.aggregate([
{
$project: {
res: {
$reduce: {
input: "$_a",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
"$cond": {
"if": {
"$eq": [
{
"$type": "$$this._p"
},
"array"
]
},
"then": {
$reduce: {
input: "$$this._p",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
"$filter": {
"input": {
"$objectToArray": "$$this.s"
},
"as": "f",
"cond": {
"$eq": [
"$$f.k",
"c"
]
}
}
}
]
}
}
},
"else": []
}
}
]
}
}
}
}
},
{
$unwind: "$res"
},
{
$unwind: "$res.v.t"
},
{
$count: "Total"
}
])
I need to count all _a[]._p[].s.c.t[] where lang:"EN","en","En" , note at object s there is multiple nested elements c , a , d , etc , only the c need to be counted where lang:"EN" , I managed to filter only the "c" but struggling to add the lang:"EN","en","En" inside the $filter.cond , can anybody help here?
Expected playground output is:
{count:7}
I can add final $match condition and clear the lang:EN , but I am wondering if there is better option to be done inside the reduce/reduce/objectToArray/cond and avoid the $unwind's?
Playgorund
One option to avoid $unwind is:
db.collection.aggregate([
{$match: {"_a._p.s.c.t": {$elemMatch: {lang: {$in: ["EN", "En", "en"]}}}}},
{$project: {
res: {$reduce: {
input: "$_a._p.s.c.t",
initialValue: [],
in: {$concatArrays: ["$$value", "$$this"]}
}}
}},
{$project: {
res: {$reduce: {
input: "$res",
initialValue: 0,
in: {$sum: [
"$$value",
{$size: {$filter: {
input: "$$this",
as: "inner",
cond: {$in: ["$$inner.lang", ["EN", "En", "en"]]}
}}}
]}
}}
}},
{$group: {_id: 0, count: {$sum: "$res"}}}
])
See how it works on the playground example

How can I get the documents having an id that are attribute values of another document in mongodb?

This is an exemple:
{"_id": 1,
"attr1": "x",
"class":"vehicle"}
{"_id": 2,
"attr1": "xf",
"class":"vehicle"}
{"_id": 3,
"attr1": "xz",
"class":"vehicle"}
{"_id": 4,
"attr1": "xddz",
"class":"vehicle"}
{"_id": 11,
"attr1": "xy",
"class":"seat",
"vehicle":[1,2]}
{"_id": 12,
"attr1": "exy",
"class":"seat",
"vehicle":[3,2]}
I would like to know what query or aggregate to use in order to get the document with "class" vehicle having their ids in the array of attribute value of the documents with class "seat".
In our example I need to have those documents returned:
{"_id": 1,
"attr1": "x",
"class":"vehicle"}
{"_id": 2,
"attr1": "xf",
"class":"vehicle"}
{"_id": 3,
"attr1": "xz",
"class":"vehicle"}
Assuming every document is in the same collection, you could do something like this :
db.collection.aggregate([
{
"$group": {
"_id": null,
"root": {
"$push": "$$ROOT"
},
}
},
{
$project: {
_id: 0,
res: {
"$filter": {
"input": "$root",
"as": "values",
"cond": {
$in: [
"$$values._id",
{
"$reduce": {
input: "$root.vehicle",
initialValue: [],
in: {
"$setUnion": [
"$$value",
"$$this"
]
},
}
},
]
}
}
}
}
},
{
"$unwind": "$res"
},
{
"$replaceRoot": {
"newRoot": "$res"
}
}
])
You can check on https://mongoplayground.net/p/iYG8o0ZOde5

mongodb aggregate subtract by key

DB : mongoDB
i have following query result.
(ex1)
[
{
"array": [
{
"_id": 1,
"createdAt": NumberLong(1675408092026),
"pType": "A"
},
{
"_id": 2,
"createdAt": NumberLong(1675408097462),
"pType": "B"
},
{
"_id": 3,
"createdAt": NumberLong(1675408101259),
"pType": "A"
},
{
"_id": 4,
"createdAt": NumberLong(1675408104682),
"pType": "B"
}
]
}
]
OR
(ex2)
[
{
"array": [
{
"_id": 1,
"createdAt": NumberLong(1675408092026),
"pType": "A"
},
{
"_id": 2,
"createdAt": NumberLong(1675408095026),
"pType": "A"
},
{
"_id": 3,
"createdAt": NumberLong(1675408097462),
"pType": "B"
},
{
"_id": 4,
"createdAt": NumberLong(1675408101259),
"pType": "A"
},
{
"_id": 5,
"createdAt": NumberLong(1675408104682),
"pType": "B"
},
{
"_id": 6,
"createdAt": NumberLong(1675408108682),
"pType": "B"
},
{
"_id": 7,
"createdAt": NumberLong(1675408118682),
"pType": "A"
}
]
}
]
I want to subtract the 'createdAt' value of pType 'A' from the 'createdAt' value of 'B'
And I want to add up the subtracted value.
(ex2)
1675408097462(_id:2) - 1675408092026(_id:1)
+
1675408104682(_id:4) - 1675408101259(_id:3)
(ex2)
1675408097462(_id:3) - 1675408095026(_id:2)
+
1675408104682(_id:5) - 1675408101259(_id:4)
i want to following result using with mongodb 'aggregate'
please help me.
The expected result is...
(ex1)
{
"sum_of_diff": "8859"
}
(ex2)
{
"sum_of_diff": "5859"
}
thank you
One option is to use $reduce with $mergeObjects:
db.collection.aggregate([
{$project: {
sum_of_diff: {$reduce: {
input: "$array",
initialValue: {lastA: {_id: -1}, sum: 0},
in: {$mergeObjects: [
"$$value",
{$cond: [
{$eq: ["$$this.pType", "A"]},
{lastA: "$$this"},
{$cond: [
{$eq: [{$subtract: ["$$this._id", 1]}, "$$value.lastA._id"]},
{sum: {
$add: [
"$$value.sum",
{$subtract: ["$$this.createdAt", "$$value.lastA.createdAt"]}
]
}},
{}
]}
]}
]}
}}
}},
{$project: {sum_of_diff: "$sum_of_diff.sum", _id: 0}}
])
See how it works on the playground example

Mongodb Aggregations - Group by date including condition

I have a series of documents gathered by aggregation grouping. This is the result for one document:
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"time": {
"time": "2022-10-19T11:35:44.655Z",
"tz": "-120",
"_id": "634fe1100a011986b7137da0"
}
},
{
"page": "/2",
"time": {
"time": "2022-10-19T12:14:29.536Z",
"tz": "-120",
"_id": "634fea257acb264f23d421f1"
}
},
{
"page": "/",
"time": {
"time": "2022-10-19T15:37:30.002Z",
"tz": "-120",
"_id": "634fea266001ea364eeb38ea"
}
},
],
"visitedPages": 3,
"createdAt": "2022-10-19T11:35:44.920Z"
},
I want to get this (in this case 2 documents as the time difference between array position 2 and 3 is greater than 2 hours):
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"durationInMinutes": "39",
"time": {
"time": "2022-10-19T11:35:44.655Z",
"tz": "-120",
"_id": "634fe1100a011986b7137da0"
}
},
{
"page": "/2",
"durationInMinutes": "2",
"time": {
"time": "2022-10-19T12:14:29.536Z",
"tz": "-120",
"_id": "634fea257acb264f23d421f1"
}
}
],
"visitedPages": 2,
},
{
"_id": {
"ip": "79.xxx.xxx.117",
"myDate": "2022-10-19"
},
"date": "2022-10-19",
"allVisitedPages": [
{
"page": "/",
"durationInMinutes": "2",
"time": {
"time": "2022-10-19T15:37:30.002Z",
"tz": "-120",
"_id": "634fea266001ea364eeb38ea"
}
},
],
"visitedPages": 1,
},
I want to get a new grouping document if the time between an array position and the following array position is greater than 2 hours. On the last array position it show always show "2".
I tried $divide and $datediff. But this is not possible on the group stage as it's an unary operator. An approach I tried is to calculate the sum of start and end time by dividing. But how to execute this on an array level on the group stage? Maybe someone could point me in the right direction if possible at all?
You can group and then reduce, but another option is to use $setWindowFields to calculate your grouping index before grouping:
db.collection.aggregate([
{$setWindowFields: {
partitionBy: {$concat: ["$ip", "$date"]},
sortBy: {"time.time": 1},
output: {prevtime: {
$push: "$time.time",
window: {documents: [-1, "current"]}
}}
}},
{$addFields: {
minutesDiff: {
$toInt: {
$dateDiff: {
startDate: {$first: "$prevtime"},
endDate: {$last: "$prevtime"},
unit: "minute"
}
}
}
}},
{$addFields: {deltaIndex: {$cond: [{$gt: ["$minutesDiff", 120]}, 1, 0]}}},
{$setWindowFields: {
partitionBy: {$concat: ["$ip", "$date"]},
sortBy: {"time.time": 1},
output: {
groupIndex: {
$sum: "$deltaIndex",
window: {documents: ["unbounded", "current"]}
},
duration: {
$push: "$minutesDiff",
window: {documents: ["current", 1]}
}
}
}
},
{$set: {
duration: {
$cond: [
{$and: [
{$eq: [{$size: "$duration"}, 2]},
{$lte: [{$last: "$duration"}, 120]}
]},
{$last: "$duration"},
2
]
}
}},
{$group: {
_id: {ip: "$ip", myDate: "$date", groupIndex: "$groupIndex"},
date: {$first: "$date"},
allVisitedPages: {$push: {page: "$page", time: "$time", duration: "$duration"}},
visitedPages: {$sum: 1}
}},
{$unset: "_id.groupIndex"}
])
See how it works on the playground example

How to get or return only the matching object from an nested array using mongoose

{
"_id": "6339f99ee18b2481a04b4fe8",
"userId": "60a8a51cf2229813a45d2238",
"array1": [
{
"someId1": "6339f99ee18b2481a04b4fe9",
"customIndex": 2,
"array2": [
{
"someId2": "6339f99ee18b2481a04b4fea",
"startDate": 2022-10-10T19:56:26.000+00:00,
"endDate": 2022-10-12T19:56:26.000+00:00,
}
]
},
{
"someId1": "6345ca40112b743fd8172be0",
"customIndex": 4,
"array2": [
{
"someId2": "6345ca40112b743fd8172be1",
"startDate": 2022-10-10T19:56:26.000+00:00,
"endDate": 2022-10-27T19:56:26.000+00:00,
}
]
}
]
}
I have above structure in mongoDB and want to get only that object from array1 which matches the conditions of endDate > 2022-10-17
Here's what I try to do:
result= await Collection.find({
userId: { '$in': userIdList},
'array1.array2.endDate': { "$gte": 2022-10-17}
})
But above return the both objects from array1 even though the endDate for one object is less than 2022-10-17
How can I get the the response like below? Also, Am I using the right Mongoose calls to achieve what I am trying to achieve.
Expected response that I am trying to achieve:
{
"_id": "6339f99ee18b2481a04b4fe8",
"userId": "60a8a51cf2229813a45d2238",
"array1": [
{
"someId1": "6345ca40112b743fd8172be0",
"customIndex": 4,
"array2": [
{
"someId2": "6345ca40112b743fd8172be1",
"startDate": 2022-10-10T19:56:26.000+00:00,
"endDate": 2022-10-27T19:56:26.000+00:00,
}
]
}
]
}
If array1 can contain several such items, and array2 contain several such items, one option is using $reduce with $filter and $mergeObjects for this:
db.collection.aggregate([
{$match: {userId: {'$in': userIdList}}}
{$project: {
userId: 1,
array1: {
$reduce: {
input: "$array1",
initialValue: [],
in: {$concatArrays: [
"$$value",
[{$mergeObjects: [
"$$this",
{array2: {
$filter: {
input: "$$this.array2",
as: "innerItem",
cond: {$gte: [
"$$innerItem.endDate",
{$dateFromParts: {year: 2022, month: 10, day: 17}}
]}
}
}}
]}]
]}
}
}
}},
{$project: {
userId: 1,
array1: {$filter: {
input: "$array1",
cond: {$gt: [{$size: "$$this.array2"}, 0]}
}}
}}
])
See how it works on the playground example