Bring nested value to top level in MongoDB - mongodb

I want to reshape a MongoDB document as followed:
This is how it looks at the moment:
{
"_id" : ObjectId("5a96d4a0af792cca1ec7d8cb"),
"Test" : "ABC",
"DATE" : "2018-02-28T16:04:55.00+01:00",
"Header" : "212735699",
"TraceIds" : [
{
"Id" : 1,
"Names" : [
{
"LangCode" : "de",
"CountryCode" : "DE",
"Text" : "Komponente"
},
{
"LangCode" : "en",
"CountryCode" : "US",
"Text" : "Component"
},
],
"Values" : [
{
"AId" : 1,
"Names" : [
{
"LangCode" : "de",
"CountryCode" : "DE",
"Text" : "Teil"
},
{
"LangCode" : "en",
"CountryCode" : "US",
"Text" : "Part"
},
],
"Value" : "1263000118"
},
]
}
],
Now I want to bring up the Value 1263000118 and give it the german name "Teil". All the other embedded values should vanish. So it should look like this:
{"_id" : ObjectId("5a96d4a0af792cca1ec7d8cb"),
"Test" : "ABC",
"DATE" : "2018-02-28T16:04:55.00+01:00",
"Header" : "212735699",
"Teil" : "1263000118",}
Would be great if someone could help me out here. Thanks

Use the aggregation framework :
db.collection.aggregate([
{$match: {
_id: your_id,
// Treatment to select which nested value you want
}},
{$project: {
_id: "$_id",
Test: "$Test",
DATE: "$DATE",
Header: "$Header",
Teil: "$Values.$.Value
}}
]);
Something like this should work

Related

MongoDB query for documents with nested objects and array

I am having trouble with mongodb querying my object document.
I have the following document:
{
"_id" : ObjectId("1"),
"name" : "Bob",
"fields" : {
"DATE" : [
{
"fromDate" : null,
"values" : [
"2022-08-01"
]
}
]
},
{
"_id" : ObjectId("2"),
"name" : "John",
"fields" : {
"DATE" : [
{
"fromDate" : null,
"values" : [
"1901-08-01"
]
}
]
}
I am trying to get the document where fields.date[0].values is equal to 2022-08-01.
How can I achieve that? Thank you.
You are looking for a nested element value which having some specific value/date.
Since your document is incomplete so, I am creating one for illustrating the solution.
db.employees.insertMany([{
"name" : "Bob",
"fields" : {
"DATE" : [
{
"fromDate" : null,
"values" : [
"2022-08-01"
]
}
]
}},
{
"name" : "John",
"fields" : {
"DATE" : [
{
"fromDate" : null,
"values" : [
"1901-08-01"
]
}
]
}
},
{
"name" : "Eve",
"fields" : {
"DATE" : [
{
"fromDate" : null,
"values" : [
"2022-08-01"
]
}
]
}},
]);
Now we can find the specific value (2022-08-01 as per your requirement) from the nested array/element inside the document. Since the value resides inside "DATE" which is inside the "fields". So we can get that value easily by calling "fields.DATE".
For example you can write some code like this :
db.employees.find({"fields.DATE":{"$elemMatch": {"values": "2022-08-01"}}})
From the above code you will get a result like this.
{ "_id" : ObjectId("62e94392389644cb3fc9c81f"), "name" : "Bob", "fields" : { "DATE" : [ { "fromDate" : null, "values" : [ "2022-08-01" ] } ] } }
{ "_id" : ObjectId("62e94392389644cb3fc9c821"), "name" : "Eve", "fields" : { "DATE" : [ { "fromDate" : null, "values" : [ "2022-08-01" ] } ] } }
Hope this will help you to solve this issue. Happy coding:)

Return only matching sub document from MongoDB document

We have following structure in our Mongo collection
{
"_id" : ObjectId("5f98aeadbaf1ea001affe4c0"),
"name" : "Temp",
"campaigntype" : 3,
"startdate" : ISODate("2021-01-01T00:00:00.000Z"),
"enddate" : ISODate("2021-01-01T00:00:00.000Z"),
"affiliatetag" : "",
"promotedtitles" : [
{
"primaryisbn" : "9781453238431",
"promoprice" : "1.99",
"countries" : [
"US",
"CA",
"AU",
"GB"
],
"retailers" : [
"ALL"
],
"dlp" : "17.99",
"notes1" : "History",
"notes2" : "",
"sequence" : 1
},
{
"primaryisbn" : "9781504063562",
"promoprice" : "1.99",
"countries" : [
"US",
"CA"
],
"retailers" : [
"ALL"
],
"dlp" : "11.99",
"notes1" : "Thrillers",
"notes2" : "",
"sequence" : 2
},
{
"primaryisbn" : "9781497673984",
"promoprice" : "0.99",
"countries" : [
"US",
"CA"
],
"retailers" : [
"ALL"
],
"dlp" : "6.99",
"notes1" : "Romantic Suspense",
"notes2" : "",
"sequence" : 3
},
{
"primaryisbn" : "9780547526959",
"promoprice" : "1.99",
"countries" : [
"CA"
],
"retailers" : [
"All"
],
"dlp" : "17.99",
"notes1" : "History",
"notes2" : "",
"sequence" : 4
},
{
"primaryisbn" : "9781453274248",
"promoprice" : "1.99",
"countries" : [
"US"
],
"retailers" : [
"All"
],
"dlp" : "9.99",
"notes1" : "Historical Fiction",
"notes2" : "",
"sequence" : 5
}
],
"active" : true,
"createdby" : ObjectId("5d2e2755d3851f0012108a05")
}
We need to write a query by passing single country like ['US'] or multiple country like ['US', 'CA']. Query should only return us matching sub document from promoted titles.
For example, If we pass country as 'US', we should get primary isbn 9781453238431, 9781504063562, 9781497673984 and 9781453274248. If we pass country as ['GB', 'CA'], we need to get 9781453238431, 9781504063562, 9781497673984 and 9780547526959. If we pass ['GB'], we should only get 9781453238431 sub document
We were trying with promotedtitles.countries : {$in : ['US', 'CA']} but that don't work.
Thanks in advance
You can $unwind and $match like this:
$unwind is to deconstruct the array and get every object as a different value instead all together in an array.
Then you can filter each object using $match and $in.
db.collection.aggregate([
{
"$unwind": "$promotedtitles"
},
{
"$match": {
"promotedtitles.countries": {
"$in": [
"US"
]
}
}
},
{
"$project": {
"_id": 0,
"result": "$promotedtitles.primaryisbn"
}
}
])
Example here using US and here with GB, CA.
Also, if you want to get all ISBNs in an array you can add a $group like this example

Find a nested object field inside an array in mongodb aggregate

I have this object as below.
{
"_id" : ObjectId("5ec80a981e89a84b19934039"),
"status" : "active",
"organizationId" : "1",
"productId" : "1947",
"name" : "BOOKEND & PAPER WEIGHT SET – ZODIAC PIG – RED COPPER + PLATINUM",
"description" : "This global exclusive Zodiac bookend and paperweight set from Zuny will stand auspiciously on your bookcase and table, spreading good luck and fortune throughout your home just in time for the Year of the Pig.",
"brand" : "ZUNY",
"created" : "2018-09-28 00:00:00",
"updated" : "2020-05-22 09:19:07",
"mainImage" : "https://",
"availableOnline" : true,
"colors" : [
{
"images" : [
{
"type" : "studio",
"url" : "https://"
},
{
"type" : "studio",
"url" : "https://"
},
{
"type" : "studio",
"url" : "https://"
}
],
"extraInfo" : [
{
"type" : "text-tag",
"title" : "CATEGORY",
"tags" : [
"HOME FURNISHING & DÉCOR",
"LIFESTYLE"
]
},
{
"type" : "text-tag",
"title" : "BRAND",
"tags" : [
"ZUNY"
]
},
{
"type" : "text-tag",
"title" : "COLOUR",
"tags" : [
"GOLD",
"ROSE GOLD"
]
},
{
"type" : "text-tag",
"title" : "SEASON",
"tags" : [
"AW(2018)"
]
},
{
"type" : "text-tag",
"title" : "HASHTAG",
"tags" : [
"BOOKCASES",
"BOOKEND",
"COLOUR",
"EXCLUSIVE",
"GLOBAL EXCLUSIVE",
"HOME",
"LEATHER",
"MOTIF",
"OBJECTS",
"PAPER",
"PAPERWEIGHT",
"PLATINUM",
"SET",
"SYNTHETIC",
"ZODIAC",
"HANDMADE",
"time"
]
}
],
"_id" : ObjectId("5ec80a981e89a84b1993403a"),
"colorId" : "1",
"color" : "ROSE GOLD",
"status" : "active",
"sizes" : [
{
"extraInfo" : [
{
"type" : "text-block",
"title" : "Size And Fit",
"text" : ""
},
{
"type" : "text-block",
"title" : "Information",
"text" : "Global exclusive. Colour: Copper/Platinum. Set includes: Zodiac Pig bookend (x 1), Zodiac Pig paperweight (x 1). Metallic copper- and platinum-tone synthetic leather. Pig motif. Iron pellet filling. Handmade"
}
],
"_id" : ObjectId("5ec80a981e89a84b1993403b"),
"sizeId" : "1",
"neo" : "0210111790664",
"size" : "*",
"originalPrice" : "1060.00",
"sellingPrice" : "1060.00",
"discountPercent" : "0.00",
"url" : "https://",
"status" : "active",
"currency" : "HK$",
"stores" : [
{
"storeId" : "1",
"quantity" : 70,
"_id" : ObjectId("5ec80a981e89a84b1993403c"),
"available" : 70,
"reserved" : 0,
"name" : "Park Street",
"status" : "active"
},
{
"storeId" : "2",
"quantity" : 95,
"_id" : ObjectId("5ec80a981e89a84b1993403d"),
"name" : "Rashbehari",
"status" : "active"
}
]
}
]
}
],
"__v" : 0
}
I want the output as follows
{
"name": "Mock Collection",
"collectionId": "92",
"products": [
{
"title": "GLOBAL EXCLUSIVE OFF-SHOULDER SHIRT DRESS",
"imageUrl": "https://",
"productId": "21174",
"currency": "" // This should be this.colors[0].sizes[0].currency
},
]
}
How to get the nested field. I tried using arrayElemAt by which I was able to get to colors[0]. But I am confused how to get inside the nested object of sizes from there. Also the currency node should have the exact value. It comes like currency:{currency: value} which I don't want.
Please help!
Not sure how you've got that output but to extract currency from first object of sizes then you need to try this :
db.collection.aggregate([
{
$project: {
currency: {
$arrayElemAt: [
{
$arrayElemAt: [ "$colors.sizes.currency", 0 ] // gives an array of currency values, in your case since you've only one object just an array of one value
},
0
]
}
}
}
])
Test : mongoplayground

Embed root field in a subdocument within an aggregation pipeline

Maybe someone can help me with Mongo's Aggregation Pipeline. I am trying to put an object in another object but I'm new to Mongo and ist very difficult:
{
"_id" : ObjectId("5888a74f137ed66828367585"),
"name" : "Unis",
"tags" : [...],
"editable" : true,
"token" : "YfFzaoNvWPbvyUmSulXfMPq4a9QgGxN1ElIzAUmSJRX4cN7zCl",
"columns" : [...],
"description" : "...",
"sites" : {
"_id" : ObjectId("5888ae2f137ed668fb95a03d"),
"url" : "www.....de",
"column_values" : [
"University XXX",
"XXX",
"false"
],
"list_id" : ObjectId("5888a74f137ed66828367585")
},
"scan" : [
{
"_id" : ObjectId("5888b1074e2123c22ae7f4d3"),
"site_id" : ObjectId("5888ae2f137ed668fb95a03d"),
"scan_group_id" : ObjectId("5888a970a7f75fbd49052ed6"),
"date" : ISODate("2017-01-18T16:00:00Z"),
"score" : "B",
"https" : false,
"cookies" : 12
}
]
}
I want to put every object in the "scan"-array into "sites". So that it looks like this:
{
"_id" : ObjectId("5888a74f137ed66828367585"),
"name" : "Unis",
"tags" : [...],
"editable" : true,
"token" : "YfFzaoNvWPbvyUmSulXfMPq4a9QgGxN1ElIzAUmSJRX4cN7zCl",
"columns" : [...],
"description" : "...",
"sites" : {
"_id" : ObjectId("5888ae2f137ed668fb95a03d"),
"url" : "www.....de",
"column_values" : [
"University XXX",
"XXX",
"false"
],
"list_id" : ObjectId("5888a74f137ed66828367585"),
"scan" : [
{
"_id" : ObjectId("5888b1074e2123c22ae7f4d3"),
"site_id" : ObjectId("5888ae2f137ed668fb95a03d"),
"scan_group_id" : ObjectId("5888a970a7f75fbd49052ed6"),
"date" : ISODate("2017-01-18T16:00:00Z"),
"score" : "B",
"https" : false,
"cookies" : 12
}
]
}
}
Is there a step in the aggregation pipeline to perform this task?
With a single pipeline I don't see any other way but specifying each field individually as:
db.collection.aggregate([
{
"$project": {
"name": 1, "tags": 1,
"editable": 1,
"token": 1, "columns": 1,
"description": 1,
"sites._id": "$sites._id",
"sites.url": "$sites.url" ,
"sites.column_values": "$sites.column_values" ,
"sites.list_id": "$sites.list_id",
"sites.scan": "$scan"
}
}
])
With MongoDB 3.4 and newer, you can use the $addFields pipeline step instead of specifying all fields using $project. The advantage is that it adds new fields to documents and outputs documents that contain all existing fields from the input documents and the newly added fields:
db.collection.aggregate([
{
"$addFields": {
"sites._id": "$sites._id",
"sites.url": "$sites.url" ,
"sites.column_values": "$sites.column_values" ,
"sites.list_id": "$sites.list_id",
"sites.scan": "$scan"
}
}, { "$project": { "scan": 0 } }
])

Mongodb aggregate for nested subdocument field

I'm trying to get the sum of "Duration" from mongo subdocument's using aggregate,
Here I have event_id = "5656b3655b9e5c8812000007", I need to get the sum of "Duration" field by matching the event_id = "5656b3655b9e5c8812000007" in above example.
Example:
"history" : [
{
"timestamp" : "2015-10-26 14:39:15",
"event" : "Creation",
"createdby" : "Ws",
"data" : [
{
"crm_base_contact_id" : "1847",
"crm_imported_files_id" : ""
}
]
},
{
"timestamp" : "2015-10-26 15:12:28",
"event" : "Task",
"createdby" : "Auto-Filter",
"data" : [
{
"Campaign ID" : "219",
"Campaign Name" : "ASA",
"Task ID" : "2541639",
"Filter Name" : "ASA",
"Filter ID" : "826"
}
]
},
{
"event_id" : ObjectId("5656b3655b9e5c8812000007"),
"timestamp" : "2015-11-26 08:23:17",
"event" : "Session",
"createdby" : "ABC",
"data" : [
{
"Campaign ID" : "219",
"Task ID" : "2541639",
"viopCalls" : [
{
"timestamp" : "2015-11-26 08:25:42",
"CallID" : "46",
"Duration" : NumberInt(5),
"TelNumber" : "0685356189"
},
{
"timestamp" : "2015-11-26 08:26:13",
"CallID" : "45",
"Duration" : NumberInt(3),
"TelNumber" : "0685356189"
}
]
}
]
}
],
Expected output : Duration : 8
Please can anyone help me to get the sum of duration column !!!
Use can use the following aggregate query:
db.collection.aggregate(
{$unwind: '$history'},
{$match: {'history.event_id': ObjectId("5656b3655b9e5c8812000007")}},
{$unwind: '$history.data'},
{$unwind: '$history.data.viopCalls'},
{$group: {_id: null, duration: {$sum: '$history.data.viopCalls.Duration'}}}
)
Result:
{ "_id" : null, "duration" : 8 }