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 => {
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 => {
console.log('Saved the symbol', doc)
return res.send(highLow)
}).catch(e => {
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)
Best Answer
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
// Example of a simple findOneAndUpdate operation
// Example of a simple findOneAndUpdate operation using a Promise.
// Example of a simple findOneAndUpdate operation using a Generator and the co module.
As Mongoosejs documented here
Query.prototype.findOneAndUpdate() Parameters
[options] «Object»
This function triggers the following middleware.
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.
Examples