MongoDb operator not working for array of object properties - mongodb

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.

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 all properties in an object using MongoShell

I have a collection with many documents containing shipping prices:
{
"_id": {
"$oid": "5f7439c3bc3395dd31ca4f19"
},
"adapterKey": "transport1",
"pricegrid": {
"10000": 23.66,
"20000": 23.75,
"30000": 23.83,
"31000": 43.5,
"40000": 44.16,
"50000": 49.63,
"60000": 50.25,
"70000": 52,
"80000": 56.62,
"90000": 59,
"100000": 62.5,
"119000": 68.85,
"149000": 80,
"159000": 87,
"179000": 94,
"199000": 100.13,
"249000": 118.5,
"299000": 138.62,
"999000": 208.63
},
"zones": [
"25"
],
"franco": null,
"tax": 20,
"doc_created": {
"$date": "2020-09-30T07:54:43.966Z"
},
"idConfig": "0000745",
"doc_modified": {
"$date": "2020-09-30T07:54:43.966Z"
}
}
In pricegrid, all the properties can be different from one grid to another.
I'd like to update all the prices in the field "pricegrid" (price * 1.03 + 1).
I tried this :
db.shipping_settings.updateMany(
{ 'adapterKey': 'transport1' },
{
$mul: { 'pricegrid.$': 1.03 },
$inc: { 'pricegrid.$': 1}
}
)
Resulting in this error :
MongoServerError: Updating the path 'pricegrid.$' would create a conflict at 'grille.$'
So I tried with only $mul (planning on doing $inc in another query) :
db.livraison_config.updateMany(
{ 'adapterKey': 'transport1' },
{
$mul: { 'pricegrid.$': 1.03 }
}
)
But in that case, I get this error :
MongoServerError: The positional operator did not find the match needed from the query.
Could you please direct me on the correct way to write the request ?
You can use an aggregation pipeline in an update. $objectToArray pricegrid to convert it into an array of k-v tuple first. Then, do a $map to perform the computation. Finally, $arrayToObject to convert it back.
db.collection.update({
"adapterKey": "transport1"
},
[
{
$set: {
pricegrid: {
"$objectToArray": "$pricegrid"
}
}
},
{
"$set": {
"pricegrid": {
"$map": {
"input": "$pricegrid",
"as": "p",
"in": {
"k": "$$p.k",
"v": {
"$add": [
{
"$multiply": [
"$$p.v",
1.03
]
},
1
]
}
}
}
}
}
},
{
$set: {
pricegrid: {
"$arrayToObject": "$pricegrid"
}
}
}
])
Here is the Mongo playground for your reference.
You can do it with Aggregation framework:
$objectToArray - to transform pricegrid object to array so you can iterate of its items
$map to iterate over array generated in previous step
$sum and multiply to perform mathematical operations
$arrayToObject to transform updated array back to object
db.collection.update({
"adapterKey": "transport1"
},
[
{
"$set": {
"pricegrid": {
"$arrayToObject": {
"$map": {
"input": {
"$objectToArray": "$pricegrid"
},
"in": {
k: "$$this.k",
v: {
"$sum": [
1,
{
"$multiply": [
"$$this.v",
1.02
]
}
]
}
}
}
}
}
}
}
],
{
"multi": true
})
Working example
I might be wrong, but it looks like there's currently no support for this feature - there's actually an open jira-issue that addresses this topic. Doesn't look like this is going to be implemented though.

MongoDb update array element with multiply on array keys

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

Return array of fields from subdocument

can anyone please help I want to query mongo to unwind the array,and i am using mongdb native driver My collection document is as follow,Also please ignore my objectId its just for sample
{
"_id":ObjectId(123),
"name":"Sam",
"age":20,
"hobbiesDetail":[
{
"description":"FootBall",
"level":"20%"
},
{
"description":"Cricket",
"level":"80%"
}
]
},
{
"_id":ObjectId(124),
"name":"Ted",
"age":26,
"hobbiesDetail":[
{
"description":"FootBall",
"level":"20%"
}
]
}
And my expected output is
[
{
"name":"Sam",
"age":20,
"hobbies":"Football,Cricket"
},
{
"name":"Ted",
"age":26,
"hobbies":"Football"
}
]
I just want to unwind my array and add a comma between hobbies description in one query, Thanx for any help
All you need is a $projection with the $map array operator.
db.collection.aggregate([
{ "$project": {
"name": 1,
"age": 1,
"hobbies": {
"$map": {
"input": "$hobbiesDetail",
"as": "hobby",
"in": "$$hobby.description"
}
}
}}
])