I have a nested json object in a hierarchical structure as defined below
[
{
"categoryId": 1,
"categoryName": "Category 1",
"childCategory": null,
"active": false
},
{
"categoryId": 2,
"categoryName": "Category 2",
"active": true,
"childCategory": [
{
"categoryId": 4,
"categoryName": "Category 4",
"childCategory": null,
"active": false
},
{
"categoryId": 5,
"categoryName": "Category 5",
"childCategory": null,
"active": true
}
]
},
{
"categoryId": 10,
"categoryName": "Category 10",
"childCategory": null,
"active": true
}
]
From this I want to select all the active categories to a single array structure. My output should be
[
{
"categoryId": 2,
"categoryName": "Category 2",
"active": true
},
{
"categoryId": 5,
"categoryName": "Category 5",
"active": true
},
{
"categoryId": 10,
"categoryName": "Category 10",
"active": true
}
]
Is it possible to directly fetch this data in a single query statement. I am using spring data for mongodb.
You can try below aggregation.
$redact to go through a document level at a time and perform $$DESCEND and $$PRUNE on the matching criteria.
$unwind the $childCategory with preserveNullAndEmptyArrays to keep the array with null values.
db.collection.aggregate({
$redact: {
$cond: [{
$eq: ["$active", true]
}, "$$DESCEND", "$$PRUNE"]
}
}, {
$unwind: {
path: "$childCategory",
preserveNullAndEmptyArrays: true
}
})
Related
I have the following document:
{
"_id": ObjectId("10..."),
"ownerName": "Merchant 10",
...,
"stores": [
{
"id": ObjectId("20..."),
"storeName": "Store 20"
},
{
"id": ObjectId("21..."),
"storeName": "Store 21"
}
]
},
{
"_id": ObjectId("11..."),
"ownerName": "Merchant 11",
...,
"stores": [
{
"id": ObjectId("22..."),
"storeName": "Store 22"
}
]
}
Is it possible with aggregate to return paginated results based on nested array "stores", and end up with the following result:
{
"_id": "20...",
"ownerName": "Merchant 10",
"storeName": "Store 20"
}, {
"_id": "21...",
"ownerName": "Merchant 10",
"storeName": "Store 21"
}, {
"_id": "22...",
"ownerName": "Merchant 11",
"storeName": "Store 22"
},
So basically, I would like to be able to share owner info but returning result based on stores.
I tried another way with a stores document but with my specific requirement, I ended up with a circular dependency.
Thanks
Using unwind on stores you can flatten out the documents. After that, just project the fields you need:
db.mycollection.aggregate( [
{ $unwind: "$stores" },
{ $project: { "ownerName": 1, "storeName": "$stores.storeName" } }
] )
I've been trying to get data back with an additional field inside a nested array using $addFields.
The data is:
{
"show_id": 1,
"ext1_id": 126790,
"ext2_id": 44275,
"show_title": "Some Big title name",
"poster_url": "https://cdn.something.com/media/cover/o65bBCMVI.jpg",
"description": "show description",
"year": 2021,
"episodes": [
{
"episode_title": "episode title 1",
"episode_number": "01",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADKWGOO8WclqGSBG6Kdy0DkRTAbLgGAALMAgACvj7BVnkS-frrNBz8HgQ",
"unique_id": "AgADzAIAAr4-wVY",
"message_id": 41,
"added_by": "bot",
"watched_by": [919205468, 1778357657],
"timestamp": "2021-11-12T15:32:46.172302"
},
{
"episode_title": "Episode title 2",
"episode_number": "02",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADKmGOO9QAAZCDz6lonoW1R5zPUM9sVAACOAQAApgFAVdhA3c9xoF0oB4E",
"unique_id": "AgADOAQAApgFAVc",
"message_id": 42,
"added_by": "bot",
"watched_by": [919205468],
"timestamp": "2021-11-12T15:33:01.053049"
},
{
"episode_title": "episode title 3",
"episode_number": "03",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADK2GOO9zX4pgK7d1ERd5BeQkvvobSAAKaBAAC2tNJVwYVsJ_OGprTHgQ",
"unique_id": "AgADmgQAAtrTSVc",
"message_id": 43,
"added_by": "bot",
"watched_by": [919205468],
"timestamp": "2021-11-12T15:33:08.862887"
}
],
"alt_titles": ["alt title", "ALT TITLE", "Alt-Title"],
"is_airing": true,
"added_by": "bot",
"timestamp": "2021-11-12T15:32:45.298797"
}
So basically, what I was trying was to add a field user_watched inside the episodes list so that it'll just return True / False by checking if the user_id is present in the watched_by list which is again inside the episodes list.
I also didn't want the watched_by to be retured in the result.
I expected something like this:
{
"show_id": 1,
"ext1_id": 126790,
"ext2_id": 44275,
"show_title": "Some Big title name",
"poster_url": "https://cdn.something.com/media/cover/o65bBCMVI.jpg",
"description": "show description",
"year": 2021,
"episodes": [
{
"episode_title": "episode title 1",
"episode_number": "01",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADKWGOO8WclqGSBG6Kdy0DkRTAbLgGAALMAgACvj7BVnkS-frrNBz8HgQ",
"unique_id": "AgADzAIAAr4-wVY",
"message_id": 41,
"added_by": "bot",
"user_watched": true,
"timestamp": "2021-11-12T15:32:46.172302"
},
{
"episode_title": "Episode title 2",
"episode_number": "02",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADKmGOO9QAAZCDz6lonoW1R5zPUM9sVAACOAQAApgFAVdhA3c9xoF0oB4E",
"unique_id": "AgADOAQAApgFAVc",
"message_id": 42,
"added_by": "bot",
"user_watched": true,
"timestamp": "2021-11-12T15:33:01.053049"
},
{
"episode_title": "episode title 3",
"episode_number": "03",
"version": null,
"file_id": "BAACAgUAAx0EYN2eLQADK2GOO9zX4pgK7d1ERd5BeQkvvobSAAKaBAAC2tNJVwYVsJ_OGprTHgQ",
"unique_id": "AgADmgQAAtrTSVc",
"message_id": 43,
"added_by": "bot",
"user_watched": true,
"timestamp": "2021-11-12T15:33:08.862887"
}
],
"alt_titles": ["alt title", "ALT TITLE", "Alt-Title"],
"is_airing": true,
"added_by": "bot",
"timestamp": "2021-11-12T15:32:45.298797"
}
I tried this following code in pymongo:
db.data_col.aggregate([
{'$sort' : { 'timestamp' : -1 } },
{'$facet' : {
"metadata": [ { "$count": "total" } ],
"data": [
{'$addFields': {
'episodes.user_watched':
{'$cond': [
{
'$in': [
919209968,
"$episodes.watched_by"
]
},
True,
False
]
}
}},
{ "$skip": offset }, { "$limit": limit },
{ '$project' : {"episodes.watched_by":0} } ,
]
}}
])
but I got every user_watched fields False even if the user_id is present among the watched_by list.
and the returned data.
Query
map on episodes, check if the user(1778357657) in in the watched_by array and add the field true/false
removed the field watched_by
*your query looks different i guess you do more things, but this produces the expected output
*your code didn't work because "$episodes.watched_by", is an array that contains all the watched_by arrays, with $map each array is checked separately.
PlayMongo
aggregate(
[{"$set":
{"episodes":
{"$map":
{"input": "$episodes",
"in":
{"$mergeObjects":
["$$this",
{"user_watched":
{"$in": [1778357657, "$$this.watched_by"]}}]}}}}},
{"$set": {"episodes.watched_by": "$$REMOVE"}}])
[
{
"Empname": "Doug",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
},
{
"Categoryid": 233,
"Categoryname": "Maths"
}
]
}
]
},
{
"Empname": "stark",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
},
{
"Categoryid": 144,
"Categoryname": "language "
}
]
}
]
}
]
Here I want to insert two fields if it meets the following conditions
{
"Categoryid": 123,
"Categoryname": "science"
}
output should be like
[
{
"Empname": "Doug",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
"CategoryLabel": "Hex",
"CategoryCode": "Hex-D",
},
{
"Categoryid": 233,
"Categoryname": "Maths"
}
]
}
]
},
{
"Empname": "stark",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science",
"CategoryLabel": "Hex",
"CategoryCode": "Hex-D",
},
{
"Categoryid": 144,
"Categoryname": "language "
}
]
}
]
}
]
You need update with arrayFilters to meet the condition for Category array.
And with multi: true to update multiple documents.
db.collection.update({},
{
$set: {
"Group.$[].Category.$[category].CategoryLabel": "Hex",
"Group.$[].Category.$[category].CategoryCode": "Hex-D"
}
},
{
arrayFilters: [
{
"category.Categoryid": 123,
"category.Categoryname": "science"
}
],
multi: true
})
Sample Mongo Playground
Given the following mongdoDB structure, how can i update the field isAvailable to false given that the shopName is "jamrt" and slug is "67626dae-1537-40d8-837d-483e5759ada0". This is my query but it does not work: Shop.find({ shopName: shopName}).update({products: {$elemMatch: {slug: slug}}}, { $set: { isAvailable: req.body.isAvailable} } Thanks!
"shopName": "jmart",
"products": [{
"id": 1,
"name": "Clean and Clear Deep Clean Cleanser 100g",
"slug": "8d1c895c-6911-4fc8-a34c-89c6948233d7",
"price": 4.5,
"discount_price": 0,
"category": "Health and Beauty",
"sale": false,
"subcategory": "personal care",
"color": "black",
"article": "Clean and Clear",
"quantity": 9,
"img": "https://firebasestorage.googleapis.com/v0/b/swifty-products.appspot.com/o/Jmart%2FBeauty%2FClean%20and%20Clear%20Deep%20Clean%20Cleanser%20100g.jpg?alt=media",
"vendor": {
"id": 1,
"name": "Clean and Clear"
},
"ratings": {
"star_ratings": 0,
"votes": 0
},
"isAvailable": true
}, {
"id": 2,
"name": "Colgate Total Pro Breath Health",
"slug": "67626dae-1537-40d8-837d-483e5759ada0",
"price": 4.5,
"discount_price": 0,
"category": "Health and Beauty",
"sale": false,
"subcategory": "personal care",
"color": "black",
"article": "Colgate",
"quantity": 9,
"img": "https://firebasestorage.googleapis.com/v0/b/swifty-products.appspot.com/o/Jmart%2FBeauty%2FColgate%20Total%20Pro%20Breath%20Health.jpg?alt=media",
"vendor": {
"id": 2,
"name": "Colgate"
},
"ratings": {
"star_ratings": 0,
"votes": 0
},
"isAvailable": true
},
]
In your case, you are trying to update only the matching sub documents.
The $elemMatch operator while using in projection updates only the first matching sub document.
The $elemMatch operator while using in find updates all the fields of the matching document.
This solution might help you.
With your case, the solution might be the below in mongodb query:
db.Shop.update({"shopName":"jmart","products.slug":"67626dae-1537-40d8-837d-483e5759ada0"}, {$set: {“products.$[i].isAvailable”: false}}, {arrayFilters: [{“i.slug”: "67626dae-1537-40d8-837d-483e5759ada0"}]})
I have a timesheet schema which results as below and I want to replace single data array object by a new set of values in mongoose. Either i want remove already existing object and push a new set of values or update the old object with new values.Please suggest me a proper way to achieve this. I am was struggled a lot to do this, because i am new to mongoose and mongodb. Thanks in advance
{
"_id": ObjectId("53f1fb3401ea96440d62646b"),
"user_id": "andrew",
"type": "Solutions",
"timesheets": [{
"weekStartDate": ISODate("2014-08-18T00:00:00Z"),
"weekEndDate": ISODate("2014-08-22T00:00:00Z"),
"_id": ObjectId("53f1fb3401ea96440d62646c"),
"data": [{
"date": ISODate("2014-08-18T00:00:00Z"),
"_id": ObjectId("53f1fb3401ea96440d62646d"),
"record": [{
"_id": ObjectId("53f1fb3401ea96440d626470"),
"desc": "",
"cellId": "0:0",
"custName": "Coach",
"custID": "2",
"timeSpend": "04:00",
"categoryName": "Billing",
"categoryID": "1"
}, {
"_id": ObjectId("53f1fb3401ea96440d62646f"),
"desc": "Description",
"cellId": "0:1",
"custName": "",
"custID": "",
"timeSpend": "05:00",
"categoryName": "Solution",
"categoryID": "4"
}, {
"_id": ObjectId("53f1fb3401ea96440d62646e"),
"desc": "Description",
"cellId": "0:2",
"custName": "",
"custID": "",
"timeSpend": "06:00",
"categoryName": "GTM",
"categoryID": "3"
}],
"savedFlag": false,
"submitFlag": false
}, {
"date": ISODate("2014-08-19T00:00:00Z"),
"_id": ObjectId("53f1fb4e01ea96440d626471"),
"record": [{
"_id": ObjectId("53f1fb4e01ea96440d626474"),
"desc": "",
"cellId": "1:0",
"custName": "Morgan",
"custID": "3",
"timeSpend": "04:00",
"categoryName": "RFP",
"categoryID": "2"
}, {
"_id": ObjectId("53f1fb4e01ea96440d626473"),
"desc": "",
"cellId": "1:1",
"custName": "Morgan",
"custID": "3",
"timeSpend": "05:00",
"categoryName": "RFP",
"categoryID": "2"
}, {
"_id": ObjectId("53f1fb4e01ea96440d626472"),
"desc": "",
"cellId": "1:2",
"custName": "Citi",
"custID": "1",
"timeSpend": "04:00",
"categoryName": "Sales",
"categoryID": "0"
}],
"savedFlag": false,
"submitFlag": false
}],
"savedFlag": true,
"submitFlag": false
}],
"__v": 0
} >