how to modify every field of a nested document mongo? - mongodb

so imagine I have the following document:
{
"_id":...,
"data":{"a":[],"b":[],"x":[]}
}
I don't know beforehand which fields the subdocument data may have. I just know that every field in that subdocument will be an array
How do I make an update so that the object results like:
{
"_id":...,
"data":{"a":[1],"b":[1],"x":[1]}
}
Constraint: Using only mongodb operators. One single update. Without knowing the fields inside the 'data' subdocument

db.collection.update({},
[
{
$set: {
data: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$data" },
as: "d",
in: { k: "$$d.k", v: [ 1 ] }
}
}
}
}
}
])
mongoplayground

Related

Find in nested array with compare on last field

I have a collection with documents like this one:
{
f1: {
firstArray: [
{
secondArray: [{status: "foo1"}, {status: "foo2"}, {status: "foo3"}]
}
]
}
}
My expected result includes documents that have at least one item in firstArray, which is last object status on the secondArray is included in an input array of values (eg. ["foo3"]).
I don't must use aggregate.
I tried:
{
"f1.firstArray": {
$elemMatch: {
"secondArray.status": {
$in: ["foo3"],
},
otherField: "bar",
},
},
}
You can use an aggregation pipeline with $match and $filter, to keep only documents that their size of matching last items are greater than zero:
db.collection.aggregate([
{$match: {
$expr: {
$gt: [
{$size: {
$filter: {
input: "$f1.firstArray",
cond: {$in: [{$last: "$$this.secondArray.status"}, ["foo3"]]}
}
}
},
0
]
}
}
}
])
See how it works on the playground example
If you know that the secondArray have always 3 items you can do:
db.collection.find({
"f1.firstArray": {
$elemMatch: {
"secondArray.2.status": {
$in: ["foo3"]
}
}
}
})
But otherwise I don't think you can check only the last item without an aggregaation. The idea is that a regular find allows you to write a query that do not use values that are specific for each document. If the size of the array can be different on each document or even on different items on the same document, you need to use an aggregation pipeline

MongoDB - Update data type for the nested documents

I have this collection: (see the full collection here https://mongoplayground.net/p/_gH4Xq1Sk4g)
{
"_id": "P-00",
"nombre": "Woody",
"costo": [
{
"tipo": "Cap",
"detalle": "RAC",
"monto_un": "7900 ",
"unidades": "1",
"total": "7900 "
}
]
}
I tried a lot of ways to transform monto_un, unidades and total into int, but I always get an error.
Neither of these works.
db.proyectos.updateMany({}, {'$set': {"costo.monto_un": {'$toInt': 'costo.$.monto_un'}}})
db.collection.update({},
[
{
$set: {
costo: {
monto_un: {
$toInt: {
costo: "$monto_un"
}
}
}
}
}
],
{
multi: true
})
MongoDB 5.0.9
Any suggestions?
$set - Update costo array.
1.1. $map - Iterate each element in the costo array and return a new array.
1.2. $mergeObjects - Merge current document with the document from 1.3.
1.3. A document with the monto_un field. You need to trim space for the monto_un field in the current iterate document via $trim and next convert it to an integer via $toInt.
In case you are also required to convert the unidades and total as int, add those fields with the same operator/function logic as monto_un in 1.3. Those fields in the document (1.3) will override the existing value due to $mergeObjects behavior.
db.collection.update({},
[
{
$set: {
costo: {
$map: {
input: "$costo",
in: {
$mergeObjects: [
"$$this",
{
monto_un: {
$toInt: {
$trim: {
input: "$$this.monto_un"
}
}
}
}
]
}
}
}
}
}
],
{
multi: true
})
Sample Mongo Playground

How to replace dynamic keys in mongodb?

I have multiple documents of below structure.
"GENUS_DATA": {
"File_6489248.ZLM": {
"MeterID_6489248": {}
}
}
"GENUS_DATA": {
"File_12345.ZLM": {
"MeterID_12345": {}
}
}
I want to replace File_6489248.ZLM and File_12345.ZLM with name "File-Name" AS KEY
dynamically in mongo-Db remember there will be n number of records so defining only the above 2 values will not work.
One option is to use $objectToArray with update pipeline:
db.collection.update({},
[
{$set: {GENUS_DATA: {$objectToArray: "$GENUS_DATA"}}},
{$project: {GENUS_DATA: {"File-Name": {$first: "$GENUS_DATA.k"},
"data": {$first: "$GENUS_DATA.v"}}}
}
],
{multi: true})
See how it works on the playground example
EDIT:
If you consider each dictionary here can have several keys and you want all keys to be generic (as they should), you can use $map as each key can contain array of items:
db.collection.aggregate([
{$project: {GENUS_DATA: {$objectToArray: "$GENUS_DATA"}}},
{
$project: {
GENUS_DATA: {
$map: {
input: "$GENUS_DATA",
as: "item",
in: {"File-Name": "$$item.k", met: {$objectToArray: "$$item.v"}}
}
}
}
},
{
$project: {
GENUS_DATA: {
$map: {
input: "$GENUS_DATA",
as: "file",
in: {"File-Name": "$$file.File-Name",
met: {
$map: {
input: "$$file.met",
as: "met",
in: {"METNO": "$$met.k", data: "$$met.v"}
}
}
}
}
}
}
}
])
See how it works on the playground example - generic multi keys

update array of objects with an array in a mongodb aggregation

I have the following situation in my aggregation: At some point my documents are holding an array of objects and an array of the same length at the root level. An example would be:
{_id:0,
cars:[{name:"vw polo",door:3},{name:"vw golf",door:5}],
possible_colors:[["black","blue"],["white"]]}
What I'm trying now is to update project the possible colors into each object of the cars array. The expected result should look as follows.
{_id:0,
cars:[{name:"vw polo",door:3,possible_colors:["black","blue"]},
{name:"vw golf",door:5,possible_colors:["white"]}],
}
I already tried {$addfields:{cars:{$zip:[$cars,$possible_colors]}}}, but this creates a list of arrays, where each array contains the object and the correct subarray, but i was not able to merge them.
The $zip operator is a good approach however you need additional $map along with $mergeObjects in order to get the desired structure:
db.collection.aggregate([
{
$addFields: {
cars: {
$map: {
input: { $zip: { inputs: [ "$cars", "$possible_colors" ] } },
in: {
$mergeObjects: [
{ $arrayElemAt: [ "$$this", 0 ] },
{ possible_colors: { $arrayElemAt: [ "$$this", 1 ] } }
]
}
}
}
}
}
])
Mongo Playground

Renaming Field Within an Array in MongoDB

I need to update the name of field in a collection. The problem is that the field in question is within an array. So I'm trying to determine the correct way do this. I tried this to accomplish renaming the field that exists within the "plans" array: :
db.customers.updateMany( {}, { $rename: { "plans" : { "subscriptionType": "membershipType" } } } );
But this won't work. What's the correct way to handle this kind of transformation of a field within an array?
The data looks like this:
{
_id: 123,
prop1: value,
prop2: value,
prop3: value,
plans: [
subscriptionType: value,
otherProp: value,
otherProp: value
]
}
You can use Aggregation Framework's $addFields to override plans field and $map operator to rename field inside an array. Then you can use $out to override existing collection:
db.customers.aggregate([
{
$addFields: {
plans: {
$map:{
input: "$plans",
as: "plan",
in: {
membershipType: "$$plan.subscriptionType",
otherField: "$$plan.otherField",
otherField2: "$$plan.otherField2"
}
}
}
}
},
{
$out: "customers"
}
])
Alternatively you can do that dynamically. In this solution you don't have to explicitly specify other field names:
db.customers.aggregate([
{
$addFields: {
plans: {
$map:{
input: "$plans",
as: "plan",
in: {
$mergeObjects: [
{ membershipType: "$$plan.subscriptionType" },
{
$arrayToObject: {
$filter: {
input: { $objectToArray: "$$plan" },
as: "plan",
cond: { $ne: [ "$$plan.k", "subscriptionType" ] }
}
}
}
]
}
}
}
}
},
{
$out: "customers"
}
])
Using $objectToArray to $filter out old key-value pair and the using $mergeObjects to combine that filtered object with new renamed field.