MongoDB - Mongoose query findOneAndUpdate() doesn't update/duplicates the DB I am trying to save and update (upsert: true - creates the object if it doesn't exist) the result of the Web Api which contains multiple arrays of data to populate the stock chart. Each time I enter a symbol and click the "Get Quote" button it should fetch data from the web api, save/update it under the Child Schema in the database. For some reason it adds duplicate values. Is there a way to fix avoid duplicate values? Here is the code that I tried....

let curValueSchema = new parentSchemaSymbol()

      curValueSchema.symbol = curValue
      highLow.map(item =&gt; {
        curValueSchema.data.push(item)
      })
      const query = { symbol: `${curValue.toUpperCase()}` }
      const update = curValueSchema
      const options = { upsert: true, new: true }
      parentSchemaSymbol.findOneAndUpdate(query, update, options).then(doc =&gt; {
        console.log('Saved the symbol', doc)
        return res.send(highLow)
      }).catch(e =&gt; {
        console.log(e)
      })


SOLUTION

Because Mongoose by default creates a new MongoDB ObjectId ( this hidden _id field) every time you pass it a Javascript Object to update the field of a document.

To go around you can tell Mongoose to not create a new ObjectId, by making sure your mongoose schema is as followed:

Folder - Models - Stock.js


  "_id": false - Has to be added to the Schema


const mongoose = require('mongoose')
mongoose.Promise = global.Promise
mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true })
const slug = require('slug')


const childSchemaData = new mongoose.Schema({
  "_id": false,
  date: mongoose.Decimal128,
  open: mongoose.Decimal128,
  high: mongoose.Decimal128,
  low: mongoose.Decimal128,
  close: mongoose.Decimal128,
  volume: mongoose.Decimal128
})

const parentSchemaSymbol = new mongoose.Schema({
  "_id": false,
  symbol: {
    type: String,
    trim: true,
    minlength: 2,
    maxlength: 4,
    required: 'Plese enter a valid symbol, min 2 characters and max 4'
  },
  // Array of subdocuments
  data: [childSchemaData],
  slug: String

});

//we have to PRE-save slug before save the parentSchemaSymbol into DB
parentSchemaSymbol.pre('save', function (next) {
  if (!this.isModified('symbol')) {
    next()//skip it
    return//stop this function from running
  }
  this.slug = slug(this.symbol)
  next()
  //TODO make more resiliant soslug are unique
})

module.exports = mongoose.model('Stock', parentSchemaSymbol)
As MongoDB Github collection documented with respect to Node.js MongoDB Driver API here Find a document and update it in one atomic operation, requires a write lock for the duration of the operation.

Syntax

findOneAndUpdate(filter, update, options, callback)


// Example of a simple findOneAndUpdate operation

var MongoClient = require('mongodb').MongoClient,
  test = require('assert');
MongoClient.connect('mongodb://localhost:27017/test', function(err, db) {
  // Get the collection
  var col = db.collection('find_one_and_update');
  col.insertMany([{a:1, b:1}], {w:1}, function(err, r) {
    test.equal(null, err);
    test.equal(1, r.result.n);

    col.findOneAndUpdate({a:1}
      , {$set: {d:1}}
      , {
            projection: {b:1, d:1}
          , sort: {a:1}
          , returnOriginal: false
          , upsert: true
        }
      , function(err, r) {
        test.equal(null, err);
        test.equal(1, r.lastErrorObject.n);
        test.equal(1, r.value.b);
        test.equal(1, r.value.d);

        db.close();
    });
  });
});


// Example of a simple findOneAndUpdate operation using a Promise.

var MongoClient = require('mongodb').MongoClient,
  test = require('assert');
MongoClient.connect('mongodb://localhost:27017/test', function(err, db) {
  // Get the collection
  var col = db.collection('find_one_and_update_with_promise');
  col.insertMany([{a:1, b:1}], {w:1}).then(function(r) {
    test.equal(1, r.result.n);

    col.findOneAndUpdate({a:1}
      , {$set: {d:1}}
      , {
            projection: {b:1, d:1}
          , sort: {a:1}
          , returnOriginal: false
          , upsert: true
        }
      ).then(function(r) {
        test.equal(1, r.lastErrorObject.n);
        test.equal(1, r.value.b);
        test.equal(1, r.value.d);

        db.close();
    });
  });
});


// Example of a simple findOneAndUpdate operation using a Generator and the co module.

var MongoClient = require('mongodb').MongoClient,
  co = require('co');
  test = require('assert');

co(function*() {
  var db = yield MongoClient.connect('mongodb://localhost:27017/test');
  // Get the collection
  var col = db.collection('find_one_and_update_with_generators');
  var r = yield col.insertMany([{a:1, b:1}], {w:1});
  test.equal(1, r.result.n);

  var r = yield col.findOneAndUpdate({a:1}
    , {$set: {d:1}}
    , {
          projection: {b:1, d:1}
        , sort: {a:1}
        , returnOriginal: false
        , upsert: true
      }
    );
  test.equal(1, r.lastErrorObject.n);
  test.equal(1, r.value.b);
  test.equal(1, r.value.d);

  db.close();
});


As Mongoosejs documented here

Query.prototype.findOneAndUpdate()
Parameters


[query] «Object|Query»
[doc] «Object»


[options] «Object»


[options.rawResult] «Boolean» if true, returns the raw result from
the MongoDB driver
[options.strict] «Boolean|String» overwrites the schema's strict mode
option
[options.multipleCastError] «Boolean» by default, mongoose only
returns the first error that occurred in casting the query. Turn on
this option to aggregate all the cast errors.
[options.lean] «Object» if truthy, mongoose will return the document
as a plain JavaScript object rather than a mongoose document. See
Query.lean().
[callback] «Function» optional params are (error, doc), unless
rawResult is used, in which case params are (error, writeOpResult)


This function triggers the following middleware.

findOneAndUpdate()


Available options


new: bool - if true, return the modified document rather than the
original. defaults to false (changed in 4.0)
upsert: bool - creates the object if it doesn't exist. defaults to
false.
fields: {Object|String} - Field selection. Equivalent to
.select(fields).findOneAndUpdate()
sort: if multiple docs are found by the conditions, sets the sort
order to choose which doc to update
maxTimeMS: puts a time limit on the query - requires mongodb >= 2.6.0
runValidators: if true, runs update validators on this command.
Update validators validate the update operation against the model's
schema.
setDefaultsOnInsert: if this and upsert are true, mongoose will apply
the defaults specified in the model's schema if a new document is
created. This option only works on MongoDB >= 2.4 because it relies
on MongoDB's $setOnInsert operator.
rawResult: if true, returns the raw result from the MongoDB driver
context (string) if set to 'query' and runValidators is on, this will
refer to the query in custom validator functions that update
validation runs. Does nothing if runValidators is false.



  Callback Signature function(error, doc) {   // error: any errors that
  occurred   // doc: the document before updates are applied if new:
  false, or after updates if new = true }


Examples

query.findOneAndUpdate(conditions, update, options, callback) // executes
query.findOneAndUpdate(conditions, update, options)  // returns Query
query.findOneAndUpdate(conditions, update, callback) // executes
query.findOneAndUpdate(conditions, update)           // returns Query
query.findOneAndUpdate(update, callback)             // returns Query
query.findOneAndUpdate(update)                       // returns Query
query.findOneAndUpdate(callback)                     // executes
query.findOneAndUpdate()                             // returns Query