Xameer
Xameer

Reputation: 31237

Update MongoDB collection based on values from an object

I have a MongoDB collection which looks like this:

[
  {
    "stock": "GOOGLE",
    "price": 0
  },
  {
    "stock": "FACEBOOK",
    "price": 0
  }
]

I have a Stock_Prices object like this:

{
  "GOOGLE": {
    "price": 31.35
  },
  "FACEBOOK": {
    "price": 10.75
  }
}

I need to update each stock in the collection from the Stock_Prices object using Node.js.

I have thought of the following approach:

This is an unacceptable approach because I have thousands and thousands of records and update is needed on an immediate basis.

Update: not necessary, it should be an update operation - if it's a lookup then also will do the work I believe

How do I do it?

Upvotes: 0

Views: 583

Answers (2)

thammada.ts
thammada.ts

Reputation: 5245

You'll have to take your object and convert it to an array and pass it to the aggregation pipeline.

const stocksMap = {
  "GOOGLE": {
    "price": 31.35
  },
  "FACEBOOK": {
    "price": 10.75
  }
}

const stocks = Object.entries(stocksMap).map(([stock, price]) => ({ stock, price : price.price }))

/* You will get something like this
[{
  "stock": "GOOGLE",
  "price": 31.35
}, {
  "stock": "FACEBOOK",
  "price": 10.75
}]
*/

/* If you want to just read the data, without updating */

db.stocks.aggregate([
  {
    $set: {
      price: {
        $reduce: {
          input: stocks,
          initialValue: 0,
          in: {
            $cond: [
              { $eq: ["$$this.stock", "$stock"] },
              "$$this.price",
              "$$value"
            ]
          }
        }
      }
    }
  }
])

/* If you want to update the existing data, you can use the same pipeline in an aggregation (available from v4.2) */

db.stocks.update({}, [
  {
    $set: {
      price: {
        $reduce: {
          input: stocks,
          initialValue: 0,
          in: {
            $cond: [
              { $eq: ["$$this.stock", "$stock"] },
              "$$this.price",
              "$$value"
            ]
          }
        }
      }
    }
  }
])

If you don't want to get the documents updated, you can put stocks in another collection and use $lookup instead. That should be more performant.

Upvotes: 1

prasad_
prasad_

Reputation: 14287

I will take one stock object from the post and show how to update:

var stock_price =  { "GOOGLE": { "price": 31.35 } }  // this is the input

var stock_price_doc = Object.keys(stock_price).map (k => ( {field: k, value: stock_price[k] } ) )[0]

stock_price_doc has this value now: { "field" : "GOOGLE", "value" : { "price" : 31.35 } }

The collection's document (from the post):

{
    "stock": "GOOGLE",
    "price": 0
}

The update operation:

db.stocks.update(
  { stock: stock_price_doc.field },
  { $set: { price: stock_price_doc.value.price } }
)

The updated document (result):

{ "stock" : "GOOGLE", "price" : 31.35 }

In case the update is to increment the stock price, instead of a setting the value use this:

{ $inc: { price: stock_price_doc.value.price } }

Upvotes: 0

Related Questions