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.
Related
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
I have several documents as given below. Now I need to do rename the middlename field into mid_name if middlename exists in the document.
{
"id":"abc",
"name":[
{
"first_name":"abc",
"last_name":"def"
},
{
"first_name":"ghi",
"last_name":"mno",
"middilename":"xyz"
}
]
}
This is something that I expect it to be.
{
"id":"abc",
"name":[
{
"first_name":"abc",
"last_name":"def"
},
{
"first_name":"ghi",
"last_name":"mno",
"mid_name":"xyz"
}
]
}
And this is what I have done but it throws the error.
db.md_carrierInformation.updateMany({"name.middlename":{$exists:true}}, {$rename:{"name.$.middlename":"name.mid_name"}})
ERROR
MongoServerError: The source field for $rename may not be dynamic: name.$.middlename
Work on the update with the aggregation pipeline.
$set - Set name array field.
1.1. $map - Iterate each item in name array and return new array.
1.2. $cond - Condition for checking current document's middlename is not existed.
1.2.1. If true, with merge current document with the document with field mid_name via $mergeObjects.
1.2.2. If false, remain the existing document.
$unset - Remove field for name.middlename.
db.md_carrierInformation.updateMany({
"name.middlename": {
$exists: true
}
},
[
{
$set: {
"name": {
$map: {
input: "$name",
in: {
$cond: {
if: {
$ne: [
"$$this.middlename",
undefined
]
},
then: {
$mergeObjects: [
"$$this",
{
mid_name: "$$this.middlename"
}
]
},
else: "$$this"
}
}
}
}
}
},
{
$unset: "name.middlename"
}
])
Sample Mongo Playground
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
i've a problem updating a nested object in MongoDB 4.
I wrote a playground where I'm doing some tests to be able to solve the problem ..
My desiderata is:
I need the "email" item to be overwritten with the lowercase of itself (result obtained).
I need that in all the N objects contained in the "emailsStatus" array the "emailAddress" item is overwritten with itself but in lowercase
I can not carry out the second point, in the playground you will find everything ready and with the test I am carrying out but I am not succeeding .. What am I wrong?
Playground: https://mongoplayground.net/p/tJ25souNlYZ
Try $map to iterate loop of emailsStatus array and convert emailAddress lower case, merge current object with update email field using $mergeObjects,
one more suggestion for query part is you can check single condition for email using $type is string
db.collection.update(
{ email: { $type: "string } },
[{
$set: {
email: { $toLower: "$email" },
emailsStatus: {
$map: {
input: "$emailsStatus",
in: {
$mergeObjects: [
"$$this",
{ emailAddress: { $toLower: "$$this.emailAddress" } }
]
}
}
}
}
}],
{ multi: true }
)
Playground
Check null condition in emailAddress array using $cond and $type,
$map: {
input: "$emailsStatus",
in: {
$mergeObjects: [
"$$this",
{
emailAddress: {
$cond: [
{ $eq: [{ $type: "$$this.emailAddress" }, "string"] },
{ $toLower: "$$this.emailAddress" },
"$$this.emailAddress"
]
}
}
]
}
}
Playground
I have nested object of objects. Each document in collection looks like this:
{
anything: "whatever",
something: {
// find inside of these document
a: { getThis: "wow" },
b: { getThis: "just wow" },
c: { getThis: "another wow" }
}
}
I would like to find in every getThis from each document in something.
For example I would like to find document which has getThis: "wow".
I've tried to use something like wildcard with *:
{"something.*.getThis": "wow" }
I've also tried $elemMatch but it seems it works only with array;
{ something: { $elemMatch: { getThis: "wow" } } }
You can try using $objectToArray,
$addFields to convert something to array in somethingArr
$match condition getThis is wow or not
$project to remove somethingArr
db.collection.aggregate([
{
$addFields: {
somethingArr: { $objectToArray: "$something" }
}
},
{ $match: { "somethingArr.v.getThis": "wow" } },
{ $project: { somethingArr: 0 } }
])
Playground
Second possible way
$filter input something as array, convert using $objectToArray
filter will check condition getThis is equal to wow or not
db.collection.aggregate([
{
$match: {
$expr: {
$ne: [
[],
{
$filter: {
input: { $objectToArray: "$something" },
cond: { $eq: ["$$this.v.getThis", "wow"] }
}
}
]
}
}
}
])
Playground