简体   繁体   中英

Mongoose: findOneAndUpdate does not update an existing field

I am trying to fetch every minute a web API data to MongoDB, save it and update when there is any change. The problem is the volume (every field) field is updated only (happens after a new trading day starts) when the new date is added to ChildSchemaData - subdocumet. All this data is used to build a daily (NOT hourly/minute ) stock chart on front-end. And I am trying to stream it from database but since the volume (and all other fields in subdocument) is not updated in database nothing is changing on front-end. I am trying to implemet change Streams MongoDB (data stream from database) so I insist on getting data from database. Any help is appreciated. Thank you!

Edit1 : The $addToSet operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array. It looks like that is the problem. I am wondering if there is a different operator that would update the value that is already present.

Edit2 : I was playing around with the following syntax but not sure if it is correct

const update = {
      $addToSet: { data: webApiData },
      $set: { 'data[0].$.volume': webApiData[0].volume },
    };  

because it is getting into the same path it is not going to work

Controller

const creatStock = async (symbol, webApiData) => {
  try {
    const query = { symbol };
    const update = { $addToSet: { data: webApiData } };
    const options = { upsert: true, new: true };

    await Stock.findOneAndUpdate(query, update, options);
  } catch (ex) {
    console.log(`creatStock error: ${ex}`.red);
  }
};

Schema

const ChildSchemaData = new mongoose.Schema({
  _id: false,
  date: { type: mongoose.Types.Decimal128 },
  open: { type: mongoose.Types.Decimal128 },
  high: { type: mongoose.Types.Decimal128 },
  low: { type: mongoose.Types.Decimal128 },
  close: { type: mongoose.Types.Decimal128 },
  volume: { type: mongoose.Types.Decimal128 },
});

const ParentSchemaSymbol = new mongoose.Schema({
  symbol: {
    type: String,
  },
  // Array of subdocuments
  data: [ChildSchemaData],
});

This solution was provided by srinivasy at the following link link

const creatStock = async (symbol, webApiData) => {
  try {
    // reversed array
    const webApiDataReversed = webApiData.reverse();
    const query = { symbol };

    const position = await Stock.findOne(query);

    if (!position) {
      console.log('New stock'.green);
      return Stock.create({
        symbol,
        data: webApiDataReversed,
      });
    }

    await Stock.bulkWrite([
      {
        updateOne: {
          filter: query,
          update: { $pop: { data: 1 } },
        },
      },
      {
        updateOne: {
          filter: query,
          update: {
            $addToSet: {
              data: webApiDataReversed,
            },
          },
        },
      },
    ]);
  } catch (ex) {
    console.log(`creatStock error: ${ex}`.red);
  }
};

If you want to update an element in an array, you can consider the positional operator: $ and add the field to your update query.

This identifies an element in an array to update based on a condition.

Example to update the embedded array with a volume field equal to 100 and set to 200:

db.getCollection("collection").findOneAndUpdate({
    id: 1,
    "data.volume": 100
}, 
    { $set: { "data.$.volume": 200 } 
});

not an elagant but a better solution than the first one

const creatStock = async (symbol, webApiData) => {
  try {
    const query = { symbol };
    const update = { $addToSet: { data: webApiData } };
    const options = { upsert: true, new: true };

    const stockResult = await Stock.findOneAndUpdate(query, update, options);

    const newOpen = webApiData[0].open.toString();
    const newHigh = webApiData[0].high.toString();
    const newLow = webApiData[0].low.toString();
    const newClose = webApiData[0].close.toString();
    const newVolume = webApiData[0].volume.toString();

    stockResult.data[0].open = newOpen;
    stockResult.data[0].high = newHigh;
    stockResult.data[0].low = newLow;
    stockResult.data[0].close = newClose;
    stockResult.data[0].volume = newVolume;

    await stockResult.save();
  } catch (ex) {
    console.log(`creatStock error: ${ex}`.red);
  }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM