Update nested array in a mongo document - arrays

I have below collection
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
ObjectId("5d55a648e2f7320001e578ac")
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
ObjectId("5d55a648e2f732676676676”),
ObjectId("5d55a648e2612333556888”)
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I am looking for below output . Is there an operator which can directly convert the array of objects into array of strings as shown below. I can do it with a script by looping over the taskMetaDataIds array but I am looking to use a direct mongo operator which suits my purpose.
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
"5d55a648e2f7320001e578ac"
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
"5d55a648e2f732676676676”,
“5d55a648e2612333556888”
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I tried below but it does not seem to be working-
db.getCollection("data").updateOne({"_id":ObjectId("5d7e6c54c23c210001108556")}, [{ $set: { "tasks.taskMetaDataIds": "$tasks.taskMetaDataIds.str" } }])

$set - Set tasks array field.
1.1. $map - Iterate each element in the tasks array and return a new array.
1.1.1. $mergeObjects - Merge the current iterated task object and taskMetaDataIds array (from the result 1.1.1.1).
1.1.1.1. $map - Iterate the current iterated task object's taskMetaDataIds array and returns a new array.
1.1.1.1.1. $toString - Convert each iterate id from ObjectId type to string type.
db.getCollection("data").updateOne({
"_id": ObjectId("5d7e6c54c23c210001108556")
},
[
{
$set: {
"tasks": {
$map: {
input: "$tasks",
as: "task",
in: {
$mergeObjects: [
"$$task",
{
"taskMetaDataIds": {
$map: {
input: "$$task.taskMetaDataIds",
in: {
$toString: "$$this"
}
}
}
}
]
}
}
}
}
}
])
Demo # Mongo Playground

Related

MongoDB aggregate with foreign model inside array

I am trying to aggregate data with a foreign model.
The structure I am trying to supercharge is the following:
{
"_id" : ObjectId("62b489664cbb9bc8c947f19f"),
"user_id" : ObjectId("61a775da4cbb9bc8c947edd9"),
"product_types" : [
{
"type" : NumberLong(1),
"product_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
},
{
"type" : NumberLong(1),
"product_id" : ObjectId("62b4890f4cbb9bc8c947e5ed"),
}
]
}
I am trying to add product data from product_id, and I think I am pretty close to it, but I am adding 2 identical products in an array instead of the correct one:
Query:
db.getCollection('interests').aggregate([
{
$lookup:{
from: "products",
localField: "product_types.product_id",
foreignField: "_id",
as: "productInterestData"
}
},
{
$set: {
"product_types.product": {
$map: {
input: "$product_types",
in: {
$mergeObjects: [
"$this",
{
$arrayElemAt: [
"$productInterestData",
{$indexOfArray: ["$productInterestData.id", "$this.id"]}
]
}
]
}
}
}
}
},
{$unset: "productInterestData"}
])
Result (with an array of 2 identical products, instead of the correct one):
{
"_id" : ObjectId("62b489664cbb9bc8c947f19f"),
"user_id" : ObjectId("61a775da4cbb9bc8c947edd9"),
"product_types" : [
{
"type" : NumberLong(0),
"product_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
"product" : [
{
"_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
"name" : "olive",
},
{
"_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
"name" : "olive",
}
]
},
{
"type" : NumberLong(1),
"product_id" : ObjectId("62b4890f4cbb9bc8c947e5ed"),
"product" : [
{
"_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
"name" : "olive",
},
{
"_id" : ObjectId("62b4890f4cbb9bc8c947e5ef"),
"name" : "olive",
}
]
}
]
}
Any idea on how to fix the query to have only one product instead of an array of identical ones?
Few small adjustments on the $set phase:
product_types, not product_types.product, in order to avoid duplication of the array. In order to nest it anther product add the key product in the $mergeObjects operation.
$productInterestData._id instead of $productInterestData.id
$$this instead of $this (we need two $ here)
$$this.product_id instead of $this.id
db.interests.aggregate([
{
$lookup: {
from: "products",
localField: "product_types.product_id",
foreignField: "_id",
as: "productInterestData"
}
},
{
$set: {
product_types: {
$map: {
input: "$product_types",
in: {
$mergeObjects: [
"$$this",
{product:{
$arrayElemAt: [
"$productInterestData",
{$indexOfArray: ["$productInterestData._id", "$$this.product_id"]}
]
}}
]
}
}
}
}
},
{$unset: "productInterestData"}
])
See how it works on the playground example

Update value of key in Object in nested array of objects in MongoDB

I am trying to update data of "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") and Set Name to John But it's updating in all array2's first element's name to John.
db.getCollection('tests')
.updateOne({ "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") },{ $set: { "array1.$[].array2.$.name" : "John" } })
{
"_id" : ObjectId("627a6fab60dc3c523b396aec"),
"array1" : [
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af1"),
"name" : "test"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af2"),
"name" : "ABC"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aed")
},
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af3"),
"name" : "XYZ"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af4"),
"name" : "Testing"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aee")
}
]
}
Based on this great answer by #R2D2, you can do:
db.collection.update({
"array1.array2._id": ObjectId("627a6fab60dc3c523b396af1")
},
{
$set: {
"array1.$[].array2.$[y].name": "John"
}
},
{
arrayFilters: [
{
"y._id": ObjectId("627a6fab60dc3c523b396af1")
}
]
})
As you can see on this playground example

How to update an array and pull a nested element from same array

With the following document:
{
"_id" : "123",
"firstArray" : [
{
"_id" : "456",
"status" : "open",
"nestedArray" : [
{
"_id" : "100",
"quantity" : 10
},
{
"_id" : "101",
"quantity" : 10
},
{
"_id" : "102",
"quantity" : 10
}
},
{
"_id" : "789",
"status" : "open",
"nestedArray" : [
{
"_id" : "200",
"quantity" : 10
},
{
"_id" : "201",
"quantity" : 10
},
{
"_id" : "202",
"quantity" : 10
}
}
]
}
How can I update the quantity by 20 of the nested ID 101 element and pull the one with the ID 201 from the same MongoDB query ?
I am trying to do that in Java with $set and $pull operator and I'm stuck with the following error:
[BulkWriteError{index=0, code=40, message='Update created a conflict
at 'firstArray.0.nestedArray'', details={}}]
MongoDB doesn’t allow multiple operations on the same property in the same update call. This means that the two operations must happen in two individual queries.
The first solution is you can write 2 seperate queries for both the operations.
The second solution is you can try update with aggregation pipeline, starting from MongoDB 4.2,
$map to iterate loop of firstArray
$filter to iterate loop of nestedArray and remove _id: "201" record
$map to iterate loop of above filtered nestedArray
$cond check condition if _id: "101" then return new quantity otherwise return current
$mergeObjects to merge current object with updated properties
db.collection.update(
{ "firstArray.nestedArray._id": "101" },
[{
$set: {
firstArray: {
$map: {
input: "$firstArray",
in: {
$mergeObjects: [
"$$this",
{
nestedArray: {
$map: {
input: {
$filter: {
input: "$$this.nestedArray",
cond: { $ne: ["$$this._id", "201"] }
}
},
in: {
_id: "$$this._id",
quantity: {
$cond: [
{ $eq: ["$$this._id", "101"] },
20,
"$$this.quantity"
]
}
}
}
}
}
]
}
}
}
}
}
])
Playground

update or replace [] on array field in mongodb

So by mistake, I migrated 68k documents from a table to another, but the problem is ( i'm a SQL Server DBA learing mongodb):
I need a field ( array ) that has the value:
something : [ 000 ]
but during the project, I used another [ and ]:
$project:
{
field_name:[$field_value]
}
but the field is already array so the result I got was:
Vieweing with JASON:
"targetMarketCountryCode" : [
[
"076"
]
],
is there a way to update;replace this value to remove this quotes [[]] and use only one normal array quote []??
I would like to update ALL documents.
I'm trying to replace but with no success:
db.rep.replaceOne( {"field":[[079]]}), {"Idate":[079]} )
this is just a test, not the oficial table.
Tests:
db.teste.insert({name:"butter",value:[["077"]]})
db.teste.insert({name:"butter",value:[["077"]]})
db.teste.insert({name:"butter",value:[["077"]]})
db.teste.find()
/* 1 createdAt:14/08/2019 13:08:15*/
{
"_id" : ObjectId("5d5431ef44f85649486e4100"),
"name" : "butter",
"value" : [
[
"077"
]
]
},
/* 2 createdAt:14/08/2019 13:08:21*/
{
"_id" : ObjectId("5d5431f544f85649486e4101"),
"name" : "butter",
"value" : [
[
"077"
]
]
},
/* 3 createdAt:14/08/2019 13:08:22*/
{
"_id" : ObjectId("5d5431f644f85649486e4102"),
"name" : "butter",
"value" : [
[
"077"
]
]
}
///////////////////////////////////////////////////////
db.teste.aggregate({$unwind: "$value"})
/* 1 createdAt:14/08/2019 13:08:15*/
{
"_id" : ObjectId("5d5431ef44f85649486e4100"),
"name" : "butter",
"value" : [
"077"
]
},
/* 2 createdAt:14/08/2019 13:08:21*/
{
"_id" : ObjectId("5d5431f544f85649486e4101"),
"name" : "butter",
"value" : [
"077"
]
},
/* 3 createdAt:14/08/2019 13:08:22*/
{
"_id" : ObjectId("5d5431f644f85649486e4102"),
"name" : "butter",
"value" : [
"077"
]
}
////////////////////////////////////////////////////
db.teste.aggregate([{$unwind:"$value"}, {$out:"teste" }])
db.teste.find()
/* 1 createdAt:14/08/2019 13:08:15*/
{
"_id" : ObjectId("5d5431ef44f85649486e4100"),
"name" : "butter",
"value" : "077"
},
/* 2 createdAt:14/08/2019 13:08:21*/
{
"_id" : ObjectId("5d5431f544f85649486e4101"),
"name" : "butter",
"value" : "077"
},
/* 3 createdAt:14/08/2019 13:08:22*/
{
"_id" : ObjectId("5d5431f644f85649486e4102"),
"name" : "butter",
"value" : "077"
}
What I can see is:
1) I have an array with [["value"]]
2) Using $unwind I can replace [["value"]] to ["value"].
3) Uing it to "update" the document, it removes both arrays and replace the value with a simple string.
For the MongoDB version: 4.2+
db.rep.updateMany(
{},
[
{
$set: {
"targetMarketCountryCode": {
$arrayElemAt: ["$targetMarketCountryCode",0]
}
}
}
]
)
It would replace the 'targetMarketCountryCode' array with its first element in all the documents.
For lower versions: 2.6+
db.rep.aggregate([
{
$unwind:"$targetMarketCountryCode"
},
{
$out:"rep"
}
])
It performs aggregation on the 'rep' collection. The 'targetMarketCountryCode' is unwinded and old data of the collection is replaced with the output of the aggregation query.
Sample data:
{
"_id" : ObjectId("5d5430aa7c780d119a01a6ce"),
"targetMarketCountryCode" : [
[
"076"
]
]
}
Output:
{
"_id" : ObjectId("5d5430aa7c780d119a01a6ce"),
"targetMarketCountryCode" : [
"076"
]
}
Thank you guys for the answers. As i'm using mongo 4.0 I could make this:
db.collection.update({ field: [["value"]] }, {
$set: {
"field": [ "value" ]},
{ multi: true, upsert: false}})

MongoDB: Find all matched array element from single document

I have a mongodb document like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"eId": 101,
"about": "test",
"tags" : [
{"name": "jana"},
{"name":"bala"},
{"name":"jk"},
{"name":"charles"}
]
}
I need to find all matched array elements, where the name matched with given array.
db.coll.find({"tags": {"$elemMatch": {"name": {"$in": [/^jana/i, /^charles/i] }}}})
for this query i got the following result
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
}
]
}
$elemMatch query only return the first matched element, but i want all the matched array element like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
Is it possible to get the result like this?
note: i don't want any others fields, I want only the matched array elements along with _id
You can use MongoDB Aggregation Pipeline:
db.coll.aggregate([
{'$unwind': '$tags'},
{'$match':
{"tags.name":
{"$in": [/^jana/, /^charles/i] }
}
},
{'$group':
{
'_id': '$_id',
'tags':
{'$push': '$tags'}
}
}
])
Result : -
{
"result" : [
{
"_id" : ObjectId("5538b214706a90c718f75a41"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
],
"ok" : 1
}

Resources