MongoDB update Nested field - mongodb

In MongoDB how do you use $set to update a nested value?
For example, consider a collection people with the following document:
{
_id: ObjectId("5a7e395e20a31e44e0e7e284"),
name: "a",
address: [{ street: "123", town: "bar" }]
}
How do I update the street field embedded in the address document from "123" to "Main Street"?

Thanks, but I found the solution :
db.collection.updateMany(
{ "address.street": "123" },
{ "$set": { "address.$[].street": "Main Street" } }
)

Use $set along with $ postion operator like this :
db.collection.update(
{ "address.street": "123" },
{ "$set": { "address.$.street": "Main Street" } }
)

You have to use $[<identifier>] (positionnal update operator) to update the address that match (here street="123" and town="bar")
Using this slightly different model (just added addresses to better understand) :
{
"_id" : ObjectId("5a7e395e20a31e44e0e7e284"),
"name" : "a",
"address" : [
{
"street" : "123",
"town" : "bar"
},
{
"street" : "Lower Street",
"town" : "bar"
},
{
"street" : "123",
"town" : "foo"
}
]
}
The query to apply :
db['01'].update(
{"_id" : ObjectId("5a7e395e20a31e44e0e7e284")},
{$set:{"address.$[current].street":"Main Street"}},
{ arrayFilters: [{current:{street:"123","town":"bar"}} ]}
)
Will result in :
{
"_id" : ObjectId("5a7e395e20a31e44e0e7e284"),
"name" : "a",
"address" : [
{
"street" : "Main Street",
"town" : "bar"
},
{
"street" : "Lower Street",
"town" : "bar"
},
{
"street" : "123",
"town" : "foo"
}
]
}

Related

Combining 2 collections after filtering 2nd one based on ids from first one

I have 2 collections like this:
Batches:
{
"userId": "",
"productId: "",
}
Products:
{
"_id": "",
"name": "",
}
What i want to do is filter the batches by userId first. And then get all products which i get ids in filtered elements of batches. I have seen a lot of examples but mostly goes the opposite way.
My final result i would like to look like this:
[{
name: "product 1",
batches: [...]
}]
You can try lookup with aggregation pipeline,
$lookup in branches collection, pass productId in let, and match expression condition for for productId and filter userId
db.products.aggregate([
{
$lookup: {
from: "branches",
let: { productId: "$_id" },
pipeline: [
{
$match: {
userId: "1", // filter user id here
$expr: { $eq: ["$$productId", "$productId"] }
}
}
],
as: "brnaches"
}
}
])
Playground
Maybe something like this:
mongos> db.Batches.find()
{ "_id" : ObjectId("5ff9a66fda146da4e1359dc9"), "userId" : "User1", "productId" : "product1" }
{ "_id" : ObjectId("5ff9a675da146da4e1359dca"), "userId" : "User2", "productId" : "product1" }
{ "_id" : ObjectId("5ff9a682da146da4e1359dcb"), "userId" : "User3", "productId" : "product2" }
{ "_id" : ObjectId("5ff9a6deda146da4e1359dcc"), "userId" : "User3", "productId" : "product1" }
mongos> db.Products.find()
{ "_id" : "product1", "name" : "product 1" }
{ "_id" : "product2", "name" : "product 2" }
{ "_id" : "product3", "name" : "product 3" }
{ "_id" : "product4", "name" : "product 4" }
mongos> db.Batches.aggregate([ {$match:{"userId":{$in:["User3","User1"]}}}, { $lookup:{ from:"Products" , localField:"productId" , foreignField:"_id" , as:"match" } } , {$unwind:"$match"} , {$project:{ name:"$match.name" , batches :{userId:"$userId" , productId:"$productId"} }} , {$group:{_id:"$name" , batches:{$addToSet:"$batches"}}} , {$project:{_id:0, name:"$_id" , batches:1}} ])
{ "batches" : [ { "userId" : "User1", "productId" : "product1" }, { "userId" : "User3", "productId" : "product1" } ], "name" : "product 1" }
{ "batches" : [ { "userId" : "User3", "productId" : "product2" } ], "name" : "product 2" }
mongos>

Getting an array out of the input using aggregate

My input file looks like this:
[
{
"type" : "asdf",
"properties" : {
"Name" : "First center",
"Code" : "ABCD",
"Address" : "Emmastr 14",
"City" : "Rotterdam",
"Postcode" : 55968,
}
},
{
"type" : "qwer",
"properties" : {
"Name" : "Second center",
"Code" : "OTHER",
"Address" : "Havenstraat 15",
"City" : "Rotterdam",
"Postcode" : 88767,
}
},
{
"type" : "zxcv",
"properties" : {
"Name" : "Third center",
"Code" : "ABCD",
"Address" : "Kerkstraat 16",
"City" : "Amsterdam",
"Postcode" : 33948,
}
},
{
"type" : "tyiu",
"properties" : {
"Name" : "Fourth center",
"Code" : "ABCD",
"Address" : "Zeestraat 17",
"City" : "Amsterdam",
"Postcode" : 56475,
}
}
]
I've been tasked to present this information grouped per city (a document for each city).
Only the items that have Code="ABCD" should appear in the output.
Output should be ordered by city name (_id).
Output should be written to a new collection.
So the output I'm looking for is something like this:
_id: "Amsterdam",
center: [
{"Name": "Third center" , "Postcode": 33948, "Address": "Kerkstraat 16"},
{"Name": "Fourth center" , "Postcode": 56475, "Address": "Zeestraat 17"}
]
_id: "Rotterdam",
center: [
{"Name": "First center" , "Postcode": 55968, "Address": "Emmastr 14"}
]
This little snippet filter by "ABCD", groups by city and writes the output to a new collection.
db.centers.aggregate ([
{$match: {"properties.Code": "ABCD"}}
,{ $group: {_id: "$properties.City"}}
,{ $out: "newColl"}
])
But I'm not getting much further because of lack of hands on experience.
I struggle getting an array out of something that's not an array in the input. Is there anyone that could help?
$push to make array of required fields
$sort by _id in ascending order
db.centers.aggregate([
{ $match: { "properties.Code": "ABCD" } },
{
$group: {
_id: "$properties.City",
center: {
$push: {
Name: "$properties.Name",
Postcode: "$properties.Postcode",
Address: "$properties.Address"
}
}
}
},
{ $sort: { _id: 1 } },
{ $out: "newColl" }
])
Playground

MongoDB - double $group make 2nd ObjectID a nested document

I have a collection that after unwind has this structure (I've deleted information which I think is not relevant to the question)
{
"_id" : ObjectId("1"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("2"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("3"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("4"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("5"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("6"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
I have used double Object Id to get a result like this:
{ "_id" : { "group" : "A", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "A", "city" : "Seattle" }, "totalMembers" : 2 }
{ "_id" : { "group" : "B", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "B", "city" : "Los Angeles" }, "totalMembers" : 2 }
I want to be able to obtain a document with the following structure:
{
"_id" : "A",
"Cities" : {
"New York" : 1,
"Seattle" : 2
}
}
{
"_id" : "B",
"Cities" : {
"New York" : 1,
"Los Angeles" : 2
}
}
This is my code so far, I haven't been able to group by 'group' and then by 'city'
db.Users_Group.aggregate([
{"$unwind":"$Members"},
{"$group":{"_id":{"group":"$Group_Name","City":"$Members.City"},"totalUsers":{"$sum":1}}},
{"$group":{"_id":"$_id.grupo","total":{"$sum":1}}}
] )
With this I get the sum of all members in that group not separated by cities. How can I nest a document of the cities with the total of users of that city within each group? Appreciate any help on this. Thanks in advance.
You need to run one more $group and prepare the data for $arrayToObject which takes an array of k-v pairs:
db.collection.aggregate([
// your current aggregation stages
{
$group: {
_id: "$_id.group",
Cities: { $push: { k: "$_id.city", v: "$totalMembers" } }
}
},
{
$project: {
_id: 1,
Cities: { $arrayToObject: "$Cities" }
}
}
])
Mongo Playground

Update double nested array mongodb

I have the below document which contains double nested array format. I have to update the "level" field to "Senior Engineer" when the "someKey":"somevalue" and "Company":"Company1" and "Name":"Nandhi".
Document
{
"_id" : "777",
"someKey" : "someValue",
"someArray" : [
{
"Company" : "Company1",
"someNestedArray" : [
{
"name" : "Nandhi",
"level" : "Junior Engineer"
},
{
"name" : "Rajan",
"level" : "Senio Engineer"
}
]
}],
{
"Company" : "Company2",
"someNestedArray" : [
{
"name" : "Nandhi",
"level" : "Junior Engineer"
},
{
"name" : "Rajan",
"level" : "Senio Engineer"
}
]
}
]
}
Update Query I tried
db.Test123.updateOne(
{"someKey" : "someValue","someArray.Company":"Company1"},
{$set:{"someArray.$[someNestedArray].level":"Senior Developer"}},
{arrayFilters:[{"someNestedArray.name":"Nandhi"}]}
);
Output Screenshot
As you can seen that, the modifiedCount returns 0. Please advice on this!
You need to define arrayFilter for every level of nesting, try:
db.Test123.update(
{ "someKey" : "someValue" },
{ "$set": { "someArray.$[someArrayDoc].someNestedArray.$[someNestedArrayDoc].level": "Senior Developer" } },
{ arrayFilters: [ {"someArrayDoc.Company": "Company1"}, { "someNestedArrayDoc.name": "Nandhi" } ] }
)

A query to do something like an outer join between two arrays in the same document

Given the following collection structure:
db.two_lists.insert(
{
"addresses":[
{
"studentId":1111,
"addr":"1234 N Oak St.",
"city":"Chicago",
"state":"IL",
"zipcode":60601
},
{
"studentId":3333,
"addr":"1234 N Oak St.",
"city":"Chicago",
"state":"IL",
"zipcode":60601
}
],
"students":[
{
"id":1111,
"name":'Frank Smith'
},
{
"id":2222,
"name":'Joe Smith'
}
]
}
);
I'm trying to return a list of students and their corresponding addresses. The problem is those two are stored in separate arrays. I need to match them up by studentId. I found a way to do that:
db.two_lists.aggregate([
{$match:{}}
,{$unwind:"$students"}
,{$unwind:"$addresses"}
,{$project:{ addresses:1, students:1,
sameId : {$cond: { if: { $eq: [ "$addresses.studentId", '$students.id' ] }, then: true, else: false }}}
}
,{$match:{sameId:true}}
]);
Unfortunately my query eliminates students w/o addresses (studentId:2222). How to best deal with a problem like that. Changing collection structure is not an option.
Expected output
{
"result" : [
{
"_id" : ObjectId("567b6f3aba874c0b52280d49"),
"addresses" : {
"studentId" : 1111,
"addr" : "1234 N Oak St.",
"city" : "Chicago",
"state" : "IL",
"zipcode" : 60601
},
"students" : {
"id" : 1111,
"name" : "Frank Smith"
}
},
{
"students" : {
"id" : 2222,
"name" : "Joe Smith"
}
}
],
"ok" : 1
}
I found the solution:
db.two_lists.aggregate([
{$match:{}}
,{$unwind:"$students"}
,{$unwind:"$addresses"}
,{$project:{ students:1,
addresses : {$cond: { if: { $eq: [ "$addresses.studentId", "$students.id" ] }, then: "$addresses", else: null }}}
}
,{$group:{_id:{students:"$students"}, addresses:{$addToSet:"$addresses"}}}
]);
Gives me pretty much the result I was looking for:
{
"result" : [
{
"_id" : {
"students" : {
"id" : 2222,
"name" : "Joe Smith"
}
},
"addresses" : [
null
]
},
{
"_id" : {
"students" : {
"id" : 1111,
"name" : "Frank Smith"
}
},
"addresses" : [
null,
{
"studentId" : 1111,
"addr" : "1234 N Oak St.",
"city" : "Chicago",
"state" : "IL",
"zipcode" : 60601
}
]
}
],
"ok" : 1
}