1

I have got a Order document with below structure,

"order" : {
    "id": 999, 
    "products" : [ 
        {
            "id": 1,
            "price": 12.344746
        },
        {
            "id": 2,
            "price": 45.688755
        },
        {
            "id": 3,
            "price": 89.5667574
        }
    ],
    "total": 151.7965567
}

I'm writing a script to fix the decimal places of the price field using toFixed(). In order to update all the products I am looping products and calling update() on each product id.

order.products.forEach((product) => {
   var updatedPrice = product.price.toFixed(2);
   db.orders.updateOne({
        id: 999,
        products: {
            $elemMatch: {
                id: product.id
            }
        }
   }, {
        $set: {
            "products.$.price": updatedPrice
        }
    })
})
var updatedTotal = order.total.toFixed(2);
db.orders.updateOne({
    id: 999
},
{
    $set: {
        total: updatedTotal
    }
})

So, in order to update one record I am doing 4 separate update operations.

Is it possible to update everything in a single operation?

P.S: Apart from products and total there are many other fields. I have used only two for simplicity. Also I do not wish to set each field.

Anuj Pancholi
  • 1,153
  • 8
  • 13
Shambhavi_RB
  • 111
  • 1
  • 8
  • are there more orders right? I can't get very well your json. Maybe include a link to a more complete snippet – Minsky Oct 28 '20 at 13:45
  • Here you go. https://stackoverflow.com/questions/4669178/how-to-update-multiple-array-elements-in-mongodb . Notice this reference in Neil Lunn answer : https://docs.mongodb.com/master/reference/operator/update/positional-filtered/ . hope this helps – Mykola Borysyuk Oct 28 '20 at 13:53
  • 1
    So you do just want to fetch one order and update all the `products` and the `total` property for that product, right? – Anuj Pancholi Oct 28 '20 at 14:34
  • I wrote this, is not there yet, ` db.collection.updateOne( { id: 999 }, [ { $set: { "products": { $map: { input: "$products", as: "p", in: { $trunc:["$$p.price", 2] }} }} }] )` – Minsky Oct 28 '20 at 14:37
  • @Minsky doesn't `$map` only work in aggregation? – Anuj Pancholi Oct 28 '20 at 14:44
  • 1
    @AnujPancholi hi :-) `$map` was specifically for aggregation, but now (4.2+) some some aggregation operators are allowed in `update`. Feel free to go over my suggestion, I can't go further – Minsky Oct 28 '20 at 14:50

2 Answers2

1

One possible approach is (with reference to a comment on the question by Mykola Borysyuk) to just update the products array and the total in memory and with just one query, update them both.

const order = await db.collection("orders").findOne({
    _id: 999 //ObjectID of whatever order you want updated
})

order.products.forEach((product) => {
   product.price = parseFloat(product.price.toFixed(2));
})

const updatedTotal = parseFloat(product.total.toFixed(2));

db.collection("orders").updateOne({
    id: 999
},
{
    $set: {
        products: order.products,
        total: updatedTotal
    }
})

NOTE: The .toFixed() function returns a string, not a number, and accroding to the data model you've described the fields you want to update are floating point numbers, so, unless you want to change the type of these fields, you should run them through parseFloat.

Anuj Pancholi
  • 1,153
  • 8
  • 13
-1

You can just use the MongoDB updateMany function which supports a filter.

bluepuma77
  • 143
  • 1
  • 7