Formatting dates with $dateToString in nested arrays - arrays

I have a collection of documents where each document represents a location of charging stations. I'm trying to use $map and $dateToString to transform each last_updated field into a string and drop the milliseconds portion of the datetime. There can be up to 3 last_updated fields on a location. A top level last_updated, a last_updated for each evse within an evses array, and a last_updated field within each connector of each evse. It is also possible for the evses array to not be present.
Here is an example of 2 locations:
[
{
"id" : "A",
"name" : "Meades Park",
"evses" : [
{
"uid" : "B",
"connectors" : [
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
},
{
"uid" : "D",
"connectors" : [
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-12-14T23:42:06.000Z")
},
{
"id" : "F",
"name" : "5th Avenue",
"last_updated" : ISODate("2022-01-12T13:12:01.000Z")
}
]
I have my query working for the top level last_updated field and the evses.last_updated field, but I am having trouble modifying the evses.connectors.last_updated field. This is my query:
db.collection.aggregate([
{
$addFields: {
last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$last_updated' } },
evses: {
$map: {
input: '$evses',
as: 'evse',
in: {
{
$mergeObjects: [
'$$evse',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$evse.last_updated' } } }
]
}
}
}
}
}
}
])
Is it possible to do another $map within the $map.in object? I was able to get this working by turning in into an array, but this changed the way the data was returned.
Here is an example of my query using $map.in as an array:
db.collection.aggregate([
{
$addFields: {
last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$last_updated' } },
evses: {
$map: {
input: '$evses',
as: 'evse',
in: [
{
$mergeObjects: [
'$$evse',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$evse.last_updated' } } }
]
},
{
$map: {
input: '$$evse.connectors',
as: 'connector',
in: {
$mergeObjects: [
'$$connector',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$connector.last_updated' } } }
]
}
}
}
]
}
}
}
])
In this case the returned documents look like this:
[
{
"id" : "A",
"name" : "CHARGEPOINT WVCC 1684DELL 1",
"evses" : [
[
{
"uid" : "B",
"connectors" : [
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000+0000")
}
],
"last_updated" : "2021-02-18T23:54:56Z"
},
[
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : "2021-02-18T23:54:56Z"
}
]
],
[
{
"uid" : "D",
"connectors" : [
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-19T22:15:43.000+0000")
}
],
"last_updated" : "2021-02-19T22:15:43Z"
},
[
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : "2021-02-19T22:15:43Z"
}
]
]
],
"last_updated" : "2021-12-14T23:42:06Z"
},
{
"id" : "F",
"name" : "5th Avenue",
"last_updated" : "2022-01-12T13:12:01Z",
evses: null
}
]
Is it possible to perform a $map within another $map without duplicating the evses.connectors array and nesting them in another array?

Your query should be as below:
Remove the [] from in for the $map.
Merge objects for $$evse, last_updated document and connectors document.
db.collection.aggregate([
{
$addFields: {
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$last_updated"
}
},
evses: {
$map: {
input: "$evses",
as: "evse",
in: {
$mergeObjects: [
"$$evse",
{
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$$evse.last_updated"
}
}
},
{
connectors: {
$map: {
input: "$$evse.connectors",
as: "connector",
in: {
$mergeObjects: [
"$$connector",
{
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$$connector.last_updated"
}
}
}
]
}
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground

Related

Nested Query on Array in MongoDB collection

I have below User collection and I want find/update query to update nested array elements.
{
_id:"000-0000-0001",
Roles :{
0000-0000-0011: //EngagementId
["0000-0000-0111", "0000-0000-0112", "0000-0000-3333"],//RoleId
"0000-0000-0012" :
["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002",
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112", "0000-0000-3333"]
}
}
Requirement: I want to pull RoleId 0000-0000-3333 if the array have combination of 0000-0000-3333 and 0000-0000-0112
Below is expected result :
{
_id:"000-0000-0001",
Roles :{
"0000-0000-0011" : ["0000-0000-0111", "0000-0000-0112"],
"0000-0000-0012" : ["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002"
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"]
}
}
Note : Find/update will work on Key:value or if it is nested then key.key:value, but in above example we have key.value.[values]:$pull(value) and that's the challaenge.
The data model necessitates a complicated update with a pipeline.
Here' one way to do it.
db.collection.update({
"Roles": {"$exists": true}
},
[
{
"$set": {
"Roles": {
"$arrayToObject": {
"$map": {
"input": {"$objectToArray": "$Roles"},
"as": "roleKV",
"in": {
"k": "$$roleKV.k",
"v": {
"$cond": [
{
"$and": [
{"$in": ["0000-0000-0112", "$$roleKV.v"]},
{"$in": ["0000-0000-3333", "$$roleKV.v"]}
]
},
{
"$filter": {
"input": "$$roleKV.v",
"cond": {"$ne": ["$$this", "0000-0000-3333"]}
}
},
"$$roleKV.v"
]
}
}
}
}
}
}
}
],
{"multi": true}
)
Try it on mongoplayground.net.

How can I find subdocument using Mongoose?

I have defined a model like this.
const ShotcountSchema = new Schema({
shotCountId : {
type : ObjectId,
required : true,
ref : 'member-info'
},
userId : {
type : String,
required : true,
unique : true
},
shot : [{
shotId : {
type : String,
required : true,
unique : true
},
clubType : {
type : String,
required : true
}
createdAt : {
type : Date,
default : Date.now
}
}]
});
If you perform a query to find subdocuments based on clubType as follows, only the results of the entire document are output.
For example, if I write the following code and check the result, I get the full result.
const shotCount = await ShotcountSchema.aggregate([
{
$match : { shotCountId : user[0]._id }
},
{
$match : { 'shot.clubType' : 'driver' }
}
]);
console.log(shotCount[0]); // Full result output
I would like to filter the subdocuments via clubType or createdAt to explore. So, I want these results to be printed.
{
_id: new ObjectId("61d67f0a74ec8620f34c57ed"),
shot: [
{
shotId: 'undefinedMKSf*Tf#!qHxWpz1hPzUBTz%',
clubType: 'driver',
shotCount: 20,
_id: new ObjectId("61d67f0a74ec8620f34c57ef"),
createdAt: 2022-01-06T05:32:58.391Z
}
]
}
How should I write the code?
db.collection.aggregate([
{
"$match": {
_id: ObjectId("61d67f0a74ec8620f34c57ed"),
"shot.clubType": "driver",
shot: {
$elemMatch: {
$and: [
{
"createdAt": {
$gte: ISODate("2022-01-07T05:32:58.391Z")
}
},
{
"createdAt": {
$lte: ISODate("2022-01-09T05:32:58.391Z")
}
}
]
}
}
}
},
{
"$set": {
shot: {
"$filter": {
"input": "$shot",
"as": "s",
"cond": {
$and: [
{
"$eq": [
"$$s.clubType",
"driver"
]
},
{
"$gte": [
"$$s.createdAt",
ISODate("2022-01-07T05:32:58.391Z")
]
},
{
"$lte": [
"$$s.createdAt",
ISODate("2022-01-09T05:32:58.391Z")
]
}
]
}
}
}
}
}
])
mongoplayground

MongoDB joining across array of ids

Before the question, I'm extremely new to mongo DB and NoSQL.
I'm having two collections in my database:
users:
{
"_id" : ObjectId("5f1efeece50f2b25d4be2de2"),
"name" : {
"familyName" : "Doe",
"givenName" : "John"
},
"email" : "johndoe#example.com",
"threads" : [ObjectId("5f1f00f31abb0e3f107fbf93"), ObjectId("5f1f0725850eca800c70ef9e") ] }
}
threads:
{
"_id" : ObjectId("5f1f0725850eca800c70ef9e"),
"thread_participants" : [ ObjectId("5f1efeece50f2b25d4be2de2"), ObjectId("5f1eff1ae50f2b25d4be2de4") ],
"date_created" : ISODate("2020-07-27T16:25:19.702Z") }
}
I want to get all the threads which an user is involved in with the other user's info nested inside.
Something like:
{
"_id" : ObjectId("5f1f0725850eca800c70ef9e"),
"thread_participants" :
[
{
"name" : {
"familyName" : "Doe",
"givenName" : "John"
},
"email" : "johndoe#example.com",
},
{
"name" : {
"familyName" : "Doe",
"givenName" : "Monica"
},
"email" : "monicadoe#example.com",
}
],
"date_created" : ISODate("2020-07-27T16:25:19.702Z") }
},
...,
...,
...
How do I go about this?
You can use $lookup to "join" the data from both collections:
db.threads.aggregate([
{
$lookup: {
from: "$users",
let: { participants: "$thread_participants" },
pipeline: [
{
$match: {
$expr: {
$in: [ "$_id", "$$participants" ]
}
}
},
{
$project: {
_id: 1,
email: 1,
name: 1
}
}
],
as: "thread_participants"
}
}
])
Mongo Playground

How to agregate mongodb rows to columns

is there a way to aggregate rows in to columns in Mongodb
here is the schema
[{
"_id" : "2020-04-17",
"wares" : [{"ware" : "NYC","total" : 5},{"ware" : "SFO","total" : 10}]
},
{
"_id" : "2020-04-18",
"wares" : [{"ware" : "NYC","total" : 6},{"ware" : "SFO","total" : 12},{"ware" : "CHI","total" : 13}]
}]
Final result should be like this
[
{
date: '2020-04-17', NYC: 5, SFO: 10
},
{
date: '2020-04-18', NYC: 6, SFO: 12, CHI:13
}
You can use below aggregation
db.collection.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
{ "$arrayToObject": {
"$map": {
"input": "$wares",
"in": {
"k": "$$this.ware",
"v": "$$this.total"
}
}
}},
{ "_id": "$_id" }
]
}
}}
])
MongoPlayground

MongoDB query subarray in sub array

i want fetch a unitHouse from my document which is an sub array of sub array
Here is the data
{
"_id" : ObjectId("5a17d305c438324308bffb19"),
"floorRow" : [
{
"floorRowNo" : "F1",
"floorRowInfo" : "Best Floor Ever that i have ever seen",
"_id" : ObjectId("5a17d333c438324308bffb1a"),
"unitHouse" : [
]
},
{
"floorRowNo" : "F2",
"floorRowInfo" : "view",
"_id" : ObjectId("5a1bdfbb4d63841c3cb6fc89"),
"unitHouse" : [
{
"unitHouseNo" : "Unit001",
"unitHouseType" : "OFFICE",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1d212bed3a552f0421fd6b"),
},
{
"unitHouseNo" : "Unit002",
"unitHouseType" : "CAT003",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1e3691af12544ff05690e3"),
}
]
}
],
}
Here is what I have queried so far, which i can get floor F2 that i wanted, but it came with both unit. I want only unitHouse with id : 5a1e3691af12544ff05690e3.
propertyDevModel.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("5a17d305c438324308bffb19"),
}
},
{
$project: {
floorRow: {
$filter: {
input: '$floorRow',
as: 'floorRow',
cond: {
$eq: ['$$floorRow._id', mongoose.Types.ObjectId("5a1bdfbb4d63841c3cb6fc89")],
}
}
}
}
},
])
I have answered this q by myself, but i will keep this post for others who have the same problem.
db.aggregate([
{
$match: {
_id: projectId,
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow'},
{
$match: {
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow.unitHouse'},
{
$match: {
'floorRow.unitHouse._id': unitId
}
},
{
$project:{
'floorRow.unitHouse': 1
}
}
])

Resources