mongo remove/pull grandchild item in deeply nested array - mongodb

I have a deeply nested document of family members
[{
"id": 1,
"children" : [
{
"id" : 1,
"grandChildren": [
{
"id" : 1,
"toys": [
{
"id":1
}
]
}
]
},
{
"id" : 2,
"grandChildren": [
{
"id" : 1,
"toys": [
{
"id":1
}
]
}
]
}
]
}]
I'm told the toys are unique to the grandchildren, which are also unique, etc, but there's a chance they're not and I'd like to make absolutely sure when I remove them I'm only removing them for the specific grandchild of the specific child etc,
I have a query that can pull all toys matching the id I was wondering how I can be more specific and burrow down through the specific parents?
db.family.updateOne(
{"id": "1"},
{"$pull" :
{ "children.$[].grandChildren.$[].toys" :
{"id" : "1"}
}
})
All help appreciated

What you want to do is use arrayFilters like so:
db.collection.updateOne({
"id": "1"
},
{
"$pull": {
"children.$[child].grandChildren.$[grandChild].toys": {
"id": "1"
}
}
},
{
arrayFilters: [
{
"child.id": "1"
},
{
"grandChild.id": "1"
}
]
})
Mongo Playground

Related

mongodb update document from first element of array

Consider a collection client with the following documents:
[
{
"id": 1,
"Name": "Susie",
"ownership" : {
"ownershipContextCode" : "C1"
},
"clientIds": [
{
"clientClusterCode": "clientClusterCode_1",
"clientId": "11"
}
]
},
{
"id": 2,
"Name": "John",
"ownership" : {
"ownershipContextCode" : "C2"
},
"clientIds": [
{
"clientClusterCode": "clientClusterCode_2",
"clientId": "22"
}
]
}
]
I am attempting to set a field (ownershipClientCode) as the first element of the clientIds array.
The result should be like that:
[
{
"id": 1,
"Name": "Susie",
"ownership" : {
"ownershipContextCode" : "C1",
"ownershipClientCode" : "clientClusterCode_1"
},
"clientIds": [
{
"clientClusterCode": "clientClusterCode_1",
"clientId": "11"
}
],
},
{
"id": 2,
"Name": "John",
"ownership" : {
"ownershipContextCode" : "C2",
"ownershipClientCode" : "clientClusterCode_2"
},
"clientIds": [
{
"clientClusterCode": "clientClusterCode_2",
"clientId": "22"
}
],
}
]
I'm using this query but I can't get sub object from the first element in the array
db.collection.aggregate([
{
$addFields: {
"Last Semester": {
"$arrayElemAt": [
"$clientIds",
0
]
}
}
}
])
This query add the all object but I want only the field (clientClusterCode).
Some thing like that
db.collection.aggregate([
{
$addFields: {
"Last Semester": {
"$arrayElemAt": [
"$clientIds",
0
].clientClusterCode
}
}
}
])
I'm using mongodb 4.0.0
You're very close: https://mongoplayground.net/p/HY1Pj0P4z12
db.collection.aggregate([
{
$addFields: {
"ownership.ownershipClientCode": {
"$arrayElemAt": [
"$clientIds.clientClusterCode",
0
]
}
}
}
])
You can use the dot notation within the $arrayElemAt as well as when you defining the field name.
To directly set the field, do something like this (use aggregation in the update): https://mongoplayground.net/p/js-usEJSH_A
db.collection.update({},
[
{
$set: {
"ownership.ownershipClientCode": {
"$arrayElemAt": [
"$clientIds.clientClusterCode",
0
]
}
}
}
],
{
multi: true
})
Note: The second method to update needs to be an array, so that it functions as an pipeline.

Querying a multi value array to retrieve specific value in mongodb

I have an array element in my document db with multiple parameters.This is how a single document looks like. I can search based on name which is unique. Is there a way to list all technologies associated with the name.
"name" : "Sam",
"date" : ISODate("2020-02-05T06:34:28.453Z"),
"technology" : [
{
"technologyId" : "1",
"technologyName" : "tech1"
},
{
"technologyId" : "2",
"technologyName" : "tech2"
},
{
"technologyId" : "3",
"technologyName" : "tech3"
},
{
"technologyId" : "4",
"technologyName" : "tech4"
}
],
"sector" : [
{
"sectorId" : "1",
"sectorName" : "sector1"
},
{
"sectorId" : "2",
"sectorName" : "sector2"
},
{
"sectorId" : "3",
"sectorName" : "sector3"
},
{
"sectorId" : "4",
"sectorName" : "sector4"
}
]
This is my simple query
db.getCollection('myCollection').find({'name':'Sam'})
Is there a way to retrieve all technologies for a name in a single query.
My output should have only tech1,tech2,tech3,tech4.
A two stage aggregation using $match, $project and $map.
Query:
db.collection.aggregate([
{
$match: {
name: "Sam"
}
},
{
$project: {
"name": "$name",
"technologies": {
$map: {
input: "$technology",
as: "t",
in: "$$t.technologyName"
}
}
}
}
]);
Result:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Sam",
"technologies": [
"tech1",
"tech2",
"tech3",
"tech4"
]
}
]
In case you don't want the name in the final O/P remove it from project stage.
I'm considering that you don't have duplicate tech under a single name. You can project only the tech names then map:
db.getCollection('myCollection')
.find({ name: 'Sam' }, { 'technology.technologyName': 1 })
.map(function(doc) { return doc['technology.technologyName'] })

How to retrieve just the array values only of a nested field of MongoDB document? [duplicate]

This question already has answers here:
How to return just the nested documents of an array from all documents
(2 answers)
Closed 3 years ago.
I'm trying to deep query and retrieve specific fields from MongoDB, but unfortunately couldn't able to figure out the correct solution.
Document data:
[ {
"_id": 39127198,
"name": "Mike",
"details": {
"age": 25,
"vehicles":[
{"brand":"Chevrolet","model":"Silverado","plate":"AB11"},
{"brand":"Jeep","model":"Cherokee","plate":"CG678"}
]
}
}, {
"_id": 39127198,
"name": "Taylor",
"details": {
"age": 25,
"vehicles": [
{"brand":"GMC","model":"Sierra","plate":"748397"}
]
}
} ]
My requirement: Return "vehicles" array alone for a specific player. Let's say for user "Mike" in this case.
Here is what I tried;
collection.find( {"name":"Mike"} )
.project( {"details.vehicles" : 1, "_id": 0, "name": 0} )
.toArray(function(err, result) { ... } )
collection.aggregate([
{ $match: { "name":"Mike" } },
{ $project: {"details.vehicles" : 1, "_id": 0, "name": 0} }
]).toArray(function(err, result) { ... } )
Here is what I get for the above code:
[
{
"details": {
"vehicles": [
{"brand":"Chevrolet","model":"Silverado","plate":"AB11"},
{"brand":"Jeep","model":"Cherokee","plate":"CG678"}
]
}
}
]
Expected:
[
{"brand":"Chevrolet","model":"Silverado","plate":"AB11"},
{"brand":"Jeep","model":"Cherokee","plate":"CG678"}
]
I am using MongoClient. MongoDB shell version v4.2.1
You can use $unwind and $replaceRoot stages to achieve this :
db.collection.aggregate([
{
$match: {
"name": "Mike"
}
},
{
$unwind: "$details.vehicles"
},
{
$replaceRoot: {
newRoot: "$details.vehicles"
}
}
])
Will output exactly what you need.
Hope it helps
The query:
db.vehi.aggregate( [
{ $match: { "name":"Mike" } },
{ $project: { "vehicles": "$details.vehicles", "_id": 0 } }
] ).next().vehicles
The exact output:
[
{
"brand" : "Chevrolet",
"model" : "Silverado",
"plate" : "AB11"
},
{
"brand" : "Jeep",
"model" : "Cherokee",
"plate" : "CG678"
}
]
- OR -
This also gets the same result:
db.vehi.find(
{ "name" : "Mike" },
{ "details.vehicles" : 1, _id : 0 }
).next().details.vehicles

Join Same Collection in Mongo

Below is the sample collection document record that i want to join the same collection with different child array elements.
Sample Collection Record :
{
"_id": "052dc2aa-043b-4cd7-a3f2-f3fe6540ae52",
"Details": [
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "12345",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}
]
},
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "55555",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
}
]
}
],
"Payments": [
{
"Id": "61ee1a6f-3334-4f33-ab6c-51c646b75c41",
"Number": "12345"
}
]
}
The mongo Pipeline query which i would like to fetch the Percentages Array with matched conditions whose Details.Number and Payment.Number should be same
Result:
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}]
How to bring the result by joining the same collections child elements using aggregate ?
Following query does what you want:
db.collection.aggregate([
{$unwind : "$Details"},
{$unwind : "$Details.Percentages"},
{$unwind : "$Payments"}, // $unwind all your arrays
{
$addFields : { //This include new `isMatch` field, which is gonna be true, only if Details.Number = Payment.Number
"isMatch" : {$cond: { if: { $eq: [ "$Details.Number", "$Payments.Number" ] }, then: true, else: false }}
}
},
{
$match : { // This ignores all others, for which Details.Number != Payment.Number
"isMatch" : true
}
},
{
$group : { // This will return only the Percentage objects
_id : null,
"Percentages" : {$push : "$Details.Percentages"}
}
},
{
$project : { // To ignore "_id" field
_id : 0,
"Percentages" : 1
}
}
])
Result:
{
"Percentages" : [
{
"Code" : "55555",
"Percentage" : "45"
},
{
"Code" : "55333",
"Percentage" : "50"
}
]
}
Hope this helps!

Upsert KV pair in subdocument for specific rules

How to update a document and insert key-value in subdocument for specific rules?
MongoDB version: 3.4
Use this CLI to insert simulation data
db.country.insertMany([{"_id":"us","groups":[{"group":"1"},{"group":"2"} ]},{"_id":"eu","groups":[{"group":"1"},{"group":"2"}]}, {"_id":"jp","groups":[{"group":"2"}]}])
original data
db.country.find()
{
"_id": "us", "groups": [ { "group" : "1" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
How to get this result? ( just add status: happy to group 1 )
{
"_id": "us", "groups": [ { "group" : "1", "status": "happy" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1", "status": "happy" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
I know how to select all groups that match group=1
db.country.aggregate([
{'$unwind': '$groups'},
{'$match': {'groups.group': '1'}} ,
{'$project': {'group': '$groups.group', _id:0 }}
])
{ "group" : "1" }
{ "group" : "1" }
and also know how to use update + $set like this
// { "_id": 1, "people": {"name": "tony" } }
db.test.update({_id: 1}, { $set: {'people.country': 'taiwan'}})
// { "_id": 1, "people": {"name": "tony" , "country": "taiwan" } }
but how to merge update + $set and aggregate function? Please help me.
pymongo is OK for me.
How to get this result? ( just add status: happy to group 1 )
Use $ to refer the position of the matched sub-document in array.
db.coll.update_many({'groups.group':'1'}, {'$set': {'groups.$.status': 'happy'}})
see more here