MongoDb update array element with multiply on array keys - mongodb

I have a mongo document as follows
{
"id": "mongoId",
"results": [
"score" : 80,
"details": {
"credit": 2,
"creditPoints": 40,
"creditScore": 0
}
]
}
I want to update all documents the creditScore as credit * creditPoints.
What i have tried till now is
db.match.updateMany({}, { "$set" : { "results.$[].details.creditScore": {"$mul" : { "$results.details.credit": "$results.details.creditPoints" } } }} );

Direct update is not allow to use internal fields operations, you need to use update with aggregation pipeline starting from MongoDB 4.2,
$map to iterate loop of results array
$multiply to multiply credit and creditPoints
$mergeObjects to merge details object with updated creditScore field
$mergeObjects to merge current object with updated details object
db.match.updateMany({},
[
{
$set: {
results: {
$map: {
input: "$results",
in: {
$mergeObjects: [
"$$this",
{
details: {
$mergeObjects: [
"$$this.details",
{
creditScore: {
$multiply: [
"$$this.details.credit",
"$$this.details.creditPoints"
]
}
}
]
}
}
]
}
}
}
}
}
])
Playground

Related

A MongoDB update query to update array values to their lowercase value

I have a Mongo collection of states, where each state contains an array of cities:
{
"_id":"636d1137cf1e57408486f795",
"state":"new york",
"cities":[
{
"cityid":"62bd8fa5396ba8aef4ad1041",
"name":"Yonkers"
},
{
"cityid":"62bd8fa5396ba8aef4ad1043",
"name":"Syracuse"
}
]
}
I need an update query that will lowercase every cities.name in the collection.
I can do an update with a literal value e.g.
db.states.updateMany(
{},
{ $set: { "cities.$[].name" : "some_value" } }
)
... , but I need the value to be based on the existing value. The closest I can get is something like this (but that doesn't work -- FieldPath field names may not start with '$')
db.states.updateMany(
{},
{ $set: { "cities.$[].name" : { $toLower: "cities.$[].name"} } }
)
You can chain up $map and $mergeObjects to perform the update. Put it in an aggregation pipeline in update.
db.collection.update({},
[
{
$set: {
cities: {
"$map": {
"input": "$cities",
"as": "c",
"in": {
"$mergeObjects": [
"$$c",
{
"name": {
"$toLower": "$$c.name"
}
}
]
}
}
}
}
}
])
Mongo Playground

How to get field and values from the nested document and ignore arrays and objects

I have the below 2 documents from a post collection. How to get only key value pairs from "post" object. The match condition will be using "post_id".
{
"_id":"1001",
"post":{
"country_name":"India",
"state_name":"Maharashtra",
"city_name":"Mumbai",
"duration":"10",
"country":[
{
"name":"india"
}
],
"site":[
{
"site_no":"101",
"code":"Taj",
"name":"santacruz"
}
]
},
"post_id":"abcd123"
}
{
"_id":"1002",
"post":{
"country_name":"India",
"state_name":"Karnataka",
"city_name":"Bangalore",
"duration":"20",
"country":[
{
"name":"india"
}
],
"site":[
{
"site_no":"201",
"code":"COLES",
"name":"Coles Park"
}
]
},
"post_id":"abcd234"
}
The expected result is:
"abcd123":{
"country_name":"India",
"state_name":"Maharashtra",
"city_name":"Mumbai",
"duration":"10"
}
"abcd234" : {
"country_name":"India",
"state_name":"Karnataka",
"city_name":"Bangalore",
"duration":"20"
}
I'm able to filter for one object, but for the bulk and with good performance, can you help me to solve this.
You can try this aggregate query:
The trick here is to create an object with keys k and v to define the key and value for the next stage.
The key will be post_id and the value the object with desired values.
In this case values has to be dinamically created so you can:
Parse $post object to an array, which allows you to filter values inside.
$filter that values to not get arrays or objects using $type.
Then convert again the array to object using $arrayToObject.
And the last step is to use $replaceRoot so you can get your desired output.
db.collection.aggregate([
{
"$project": {
"k": "$post_id",
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$post" },
"cond": {
"$not": [
{
"$in": [
{ "$type": "$$this.v" },
[ "object", "array" ]
]
}
]
}
}
}
}
}
},
{
"$replaceRoot": {
"newRoot": { "$arrayToObject": [ [ { k: "$k", v: "$v" } ] ] }
}
}
])
Example here

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

MongoDb operator not working for array of object properties

I am facing a problem with MongoDB query. Our collection named is products and the data is placed something like this.
{
"_id":"61b823681975ba537915cb0c",
"salesInfo":{
"_id":"61b823681975ba537915c23c",
"salesDate":[
{
"_id":"61b3aa4a7b04b30cd0a76b06",
"salesQuantity":100,
"soldPieces":36,
"holdPieces":0
},
{
"_id":"61b3aa4a7b04b30cd0a75506",
"salesQuantity":100,
"soldPieces":36,
"holdPieces":0
}
]
}
}
I want to add a new field named percentageSold inside an array of objects, and the value should be the calculation of the following formula ((soldPieces + holdPieces) / salesQuantity * 100).
My query is this but it is returning null for the percentageSold property.
db.products.aggregate( [
{
$addFields: {
"salesInfo.salesDate.percentageSold": {$divide: [{$add: ["$salesDate.soldPieces", "$salesDate.holdPieces"]}, {$multiply: ["$salesDate.salesQuantity", 100]}]}
}
}
])
As salesInfo.salesDate is an array field, you need to to use array operator like $map to perform element-wise operation.
db.products.aggregate([
{
$addFields: {
"salesInfo.salesDate": {
"$map": {
"input": "$salesInfo.salesDate",
"as": "s",
"in": {
"_id": "$$s._id",
"salesQuantity": "$$s.salesQuantity",
"soldPieces": "$$s.soldPieces",
"holdPieces": "$$s.holdPieces",
"percentageSold": {
$divide: [
{
$add: [
"$$s.soldPieces",
"$$s.holdPieces"
]
},
{
$multiply: [
"$$s.salesQuantity",
100
]
}
]
}
}
}
}
}
}
])
Here is the Mongo playground for your reference.

find and update with pull and get the pulled document in return

Here is my collection
[
{_id:1,
persons:[{name:"Jack",age:12},{name:"Ma",age:13}]
}
]
I want to remove {name:"Jack",age:12} in persons by pull but I also want after pulling is completed I will be returned the pulled {name:"Jack",age:12}. How can I do this?
I want to do it like this
db.getCollection('test').findOneAndUpdate({_id:1},{$pull:{"persons":{name:"Jack"}}},
{projection:{"person":{
$filter : {
input: "$persons",
as : "ele",
cond : {$eq : ["$$ele.name","Jack"]}
}
}}})
You can use $reduce, because $filter will return array, also aggregation array operators will support from MongoDB 4.4,
db.getCollection('test').findOneAndUpdate({_id:1},
{ $pull: { "persons": { name: "Jack" } } },
{
projection: {
"person":{
$reduce: {
input: "$persons",
initialValue: {},
in: { $cond: [{$eq: ["$$this.name","Jack"]}, "$$this", "$$value"] }
}
}
}
}
)
Playground