Can I perform mongoose update from post save middleware? - node.js

Is it possible to update a document from a post save mongoose middleware? Because it is not working for me.
I have tried in different ways.
Way 1:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
// update the record with quoteString
this.update({ _id: this.id }, this, { new: true }, function(err, result) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 2: because this contains the saved object id so I tried directly.
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
enter code here
// update the record with quoteString
this.update(function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 3:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
var _quoteString = doc.quoteNumber+"";
this.update({ _id: doc._id }, { $set: { "quoteString": _quoteString } }, function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
None of these ways works for me.
All I have to do is to update QuoteNumber field after the save. QuoteNumber is being generated from mongoose autoincrement which requires a number field. and I'm also saving a string version of quoteNumber in quoteString field so that in the UI, I can perform regex search in an autocomplete. As regular expression does not work with number type.
any suggestions will be helpful. Thanks.

Just make the autoincrementing field virtual and you don't have to worry about post save hook...
const QuoteSchema = new Schema(
{
quoteNumber: { type: Number },
quoteString: { type: String },
},
);
QuoteSchema.virtual('quote').set(function(value) {
this.quoteNumber = Number(value);
this.quoteString = String(value);
});
QuoteSchema.virtual('quote').get(function() {
return this.quoteNumber;
});
Setup:
QuoteSchema.plugin(autoIncrement.plugin, { model: 'Quote', field: 'quote' });

Related

Referencing filtered Schema object into another using mongoose

I want to filter the data and reference it to another schema. I am not sure what is the right way, but I am trying this and its inserting nothing.
let hyp = Hypothesis.findOne({ name: "test-hyp" }).then(function(doc) {
return doc._id;
});
var comp = {
hypothesis: hyp,
compName: "test",
created_at: new Date()
};
var h2h = new Comparison(comp);
h2h.save(function(error) {
if (!error) {
Comparison.find({ _id: h2h._id })
.populate("hypothesis")
.exec(function(error, comps) {
console.log("insertion done ");
});
}
});
I was using the wrong approach for referencing schema
Hypothesis.findOne({name:"test-hyp"}, function(err,doc) {
var comp1 = new Comparison({
hypothesis_1: doc,
created_at: new Date()
});
comp1.save(function (err, ret) {
console.log("err: ", err);
});
});

POST data with user details in mongo db using node.js

I am developing a web application using the MEAN stack with Angular 6. I have a form to submit data into MongoDB. Following is the save function and it works.
It saves the extruded value in the DB.
saveExtrudedHeightValue(extrudedHeight: NgForm) {
if (extrudedHeight.value != "" && extrudedHeight.value != null) {
this.extrudedHeightService.saveExtrudedHeight(extrudedHeight.value).subscribe(res => {
console.log(res);
}, (err) => {
console.log(err);
});
}
}
Here is the model
// Schema for extruded height panel
var extrudedHeightSchema = new mongoose.Schema({
userName: {
type: String
},
extrudedHeight: {
type: Number
},
});
module.exports = mongoose.model('extrudedHeightValue', extrudedHeightSchema);
Here is my post route
//post extrudedHeight values
router.post("/save", function(req, res) {
var mod = new extrudedHeight(req.body);
extrudedHeight.findOneAndUpdate({
userName: req.body.email,
extrudedHeight: req.body.extrudedHeight,
},
req.body, {
upsert: true,
new: true
},
function(err, data) {
if (err) {
console.log(err);
res.send(err);
} else {
res.send(mod);
}
}
);
});
Here is the service.
// service for save extruded height
saveExtrudedHeight(extrudedHeight): Observable < any > {
return this.http.post('/extrudedHeight/save', extrudedHeight, httpOptions)
.pipe(
catchError(this.handleError)
);
}
Now I want to save data in DB with the current user's userName. I can retrieve the current user's userName by this.
this.payload.user['email']
My problem is that I do not have an idea how to pass this userName to post route to save in db.
Here is where I get token.
this.authService.onTokenChange().subscribe(
(token: NbAuthJWTToken) => {
if (token.isValid()) {
this.user = token.getPayload().user;
this.payload = token.getPayload();
console.log(this.payload.user['email']);
}
}
)
You can first call this.authService.onTokenChange inside the saveExtrudedHeight method, and then use the flatMap operator to unwrap the internal Observable that would be returned by the http.post.
That would translate to code like this:
import { flatMap } from 'rxjs/operators';
import { throwError } from 'rxjs';
...
saveExtrudedHeight(extrudedHeight): Observable<any> {
const requestPayload = {
extrudedHeight
};
return this.authService.onTokenChange()
.pipe(flatMap(
(token: NbAuthJWTToken) => {
if (token.isValid()) {
this.user = token.getPayload().user;
this.payload = token.getPayload();
const email = this.payload.user['email'];
requestPayload.email = email;
// Make the changes here to send the email as the Request Payload.
return this.http.post('/extrudedHeight/save', requestPayload, httpOptions)
.pipe(
catchError(this.handleError)
);
} else {
throwError('Something went wrong!');
}
}
));
}
PS: I'm not really sure if this would work though as I haven't tested it out and I can't without a minimal working StackBlitz.

How to check if that data already exist in the database before save

Hey guys I have a question, how to do validations before saving the edited or posted (post or put action ) data in mongoose !?
for Example, if action already exist in the database, the user will receive a some sort of error. I try this but not working :
1-NOT WORK
var mongoose = require("mongoose"),
Schema = mongoose.Schema;
var actionSchema = new Schema({
action: {
type: String,
required: true,
},
});
var data = mongoose.model('Action', actionSchema);
actionSchema.pre('save', function (next) { // Middlware to verify if action already existe
var self = this;
data.find({
action: self.action
}, function (err, actions) {
if (!actions.length) {
next();
} else {
console.log('action exists: ', self.name);
next(new Error("Action exists!"));
}
});
});
module.exports = mongoose.model('Action', actionSchema);
2 --- NOT WORK SECOND METHODE : ------------------------------------
var data = mongoose.model('Action', actionSchema);
actionSchema.pre('save', function (next) {
data.count({
action: this.action
}, function (err, count) {
if (count == 1) {
console.log('action exists: ', this.action);
next(new Error("Action exists!"));
//already exists
} else {
next();
//do the action
}
});
});
3- WORKING ALTERNATIVE -- NODE JS CONTROLLER ----I found this trick (work good) that is to do a check before the update (check)
But I would like to know if there is possibility to do it before my save in my model MONGOOSE !?
// router.put('/actions/:action_id');
Action.findById(req.params.action_id, function (err, upaction) {
if (err) { //no action id in database match with params.action_id
res.send(err);
} else { // find == true
// chek if action name existe
Action.findOne({
'action': req.body.action
})
.exec(function (err, found_action) {
if (err) { // ereur bizare sest produite
next(err);
}
if (found_action) { // name action exist
res.send('name action existe');
}
else { // name action no exist
upaction.action = req.body.action;
upaction.save(function (err, acti) {
if (err) {
res.send('error on save');
}
res.send(upaction); // send a document
});
}
});
}
});
Check whether the data already avaliable and then perform the action you want
var data = mongoose.model('data', actionSchema);
data.count({action: this.action}, function(err, count) {
if(count == 1){
//already exists
}
else{
actionSchema.pre('save', function (next) {
});
}
});
I don't understand why are you doing too many operation to do a single operation. Mongodb provides update function which has ability to check and insert. If you want a document to be inserted only when some condition comes true or false. update can do that in a singly query. Here you go.
Action.update({ action:{ $eq:req.body.action }},{ $setOnInsert: new_action }, { upsert: true }, function(err, res){
if(!err && !!res.upserted){
// no document was found hence inserted
}else if(!err && !res.upserted){
// already existing
}else{
// something wicked happend
}
})
Here you need to pay attention that new_action must not be a instance of your mongoose model rather it should be simple object/document which you want to insert.

How can i have auto-increment field in nedb?

I want to have exactly auto-increment field like relational or objective databases, so i need an integer _id field with automatically set field value, value should be one more last record _id value like this:
data:
{_id:1,name"foo"}
{_id:2,name"bar"}
remove last record:
{_id:1,name"foo"}
add new record:
{_id:1,name"foo"}
{_id:3,name"newbar"}
I added a function to my datastore and calculate maximum of _id and plus 1 max(_id)+1 and set as field value, but there is problem here:
When we use auto-increment field in relational databases, it works like i said and after you remove last record it reserved a deleted record number and new inserted records continue increment but in my way its says the _id of removed record for new record.
My code is:
var Datastore = require('nedb'),
localDb = new Datastore({
filename: __dirname + '/dbFilePath.db',
autoload: true
});
localDb.getMax = function(fieldName, onFind){
db.find({}).sort({_id:-1}).limit(1).exec(function (err, docs) {onFind && onFind(err, docs['_id']);});
return localDb;
}
localDb.insertAutoId = function(data, onAdd){
var newIndex = 0;
localDb.getMax(function (err, maxValue) {
newIndex = maxValue+1;
if(!data["_id"])
data["_id"] = newIndex;
localDb.insert(data, function (err, newDoc) {
onAdd && onAdd(err, newDoc);
});
});
return localDb;
}
An improved answer for nedb would be:
db.getAutoincrementId = function (cb) {
this.update(
{ _id: '__autoid__' },
{ $inc: { seq: 1 } },
{ upsert: true, returnUpdatedDocs: true },
function (err, affected, autoid) {
cb && cb(err, autoid.seq);
}
);
return this;
};
Which is equivalent to the mongodb way:
db.getAutoincrementId = function (cb) {
this.findAndModify({
query: { _id: '__autoid__' },
update: { $inc: { seq: 1 } },
new: true
}
function (err, autoid) {
cb && cb(err, autoid.seq);
}
);
return this;
};
You can store the last value of the index in the database. Something like this:
var Datastore = require('nedb');
var db = new Datastore({
filename: __dirname + '/dbFilePath.db',
autoload: true
});
// Initialize the initial index value
// (if it already exists in the database, it is not overwritten)
db.insert({_id: '__autoid__', value: -1});
db.getAutoId = function(onFind) {
db.findOne( { _id: '__autoid__' }, function(err, doc) {
if (err) {
onFind && onFind(err)
} else {
// Update and returns the index value
db.update({ _id: '__autoid__'}, { $set: {value: ++doc.value} }, {},
function(err, count) {
onFind && onFind(err, doc.value);
});
}
});
return db;
}
I do not know if it will be useful for you anymore I use a database to store the next ids, inspired in the mysql system. Who always reserves the next id.
So I created a function that verifies if there is an id to the db, if it does not, it add with the value "1", and when it updates it looks for and if it exists and it performs the sequence.
This gave me full control over my ids.
The schema would be:
{
name: nameDb,
nextId: itemID
}
If you want you can create functions for updating documents, versioning, etc.
example:
db.autoincrement = new Datastore({filename: 'data/autoincrement.db', autoload: true});
function getUniqueId(nameDb, cb) {
db.autoincrement.findOne({name: nameDb}, function (err, doc) {
if (err) {
throw err;
} else {
if (doc) {
const itemID = doc.nextId + 1;
db.autoincrement.update({name: nameDb}, {
name: nameDb,
nextId: itemID
}, {}, function (err, numReplaced) {
db.autoincrement.persistence.compactDatafile();
if (err) {
throw err;
} else {
// console.log(numReplaced);
}
cb(doc.nextId);
});
} else {
const data = {
name: nameDb,
nextId: 2
};
db.autoincrement.insert(data, function (err, newDoc) {
if (err) {
throw err;
} else {
// console.log(newDoc);
}
cb(1);
});
}
}
});
}
insert new document example:
function insert(req, cb) {
getUniqueId("testdb", function (uniqueId) {
data.itemId = uniqueId;
db.testdb.insert(data, function (err, newDoc) {
if (err) {
cb({error: '1', message: 'error#2'});
throw err;
}
cb({error: '0', message: 'Item add'});
});
});
}

nodejs mongoose bulk update

I have a collection of documents and I need to add a new field for ever document. If I run a query to get all documents and then update every single one node.js is stopped, may be for memory leak
This is my code
var express = require('express');
var geocoderProvider = 'google';
var httpAdapter = 'http';
var People = require("./models/people").collection.initializeOrderedBulkOp();
var app = express();
var geocoder = require('node-geocoder').getGeocoder(geocoderProvider, httpAdapter, {});
app.get('/', function (req, res) {
People.find({}, function (err, docs) {
if (err) {
res.send(err);
}else{
docs.forEach( function (doc){
geocoder.geocode({address: doc.address, country: 'Italy', zipcode: doc.cap}, function(error, value) {
doc.loc.coordinates[0]=value[0].latitude;
doc.loc.coordinates[1]=value[0].longitude;
People.update({ _id: doc._id }, { $set: { loc: doc.loc }}, { multi: true }, function (error){
if(error){
console.error('ERROR!');
}
});
});
});
}
});
});
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
});
There is any way to bulk update with mongoose?
Thanks in advance
More detailed info about the query and update query.
var bulk = People.collection.initializeOrderedBulkOp();
bulk.find(query).update(update);
bulk.execute(function (error) {
callback();
});
Query is searching with array.
Update needs a $set
var bulk = People.collection.initializeOrderedBulkOp();
bulk.find({'_id': {$in: []}}).update({$set: {status: 'active'}});
bulk.execute(function (error) {
callback();
});
Query is a searching the id
var bulk = People.collection.initializeOrderedBulkOp();
bulk.find({'_id': id}).update({$set: {status: 'inactive'}});
bulk.execute(function (error) {
callback();
});
You can drop down to the collection level and do a bulk update. This action will not be atomic - some of the writes can fail and others might succeed - but it will allow you to make these writes in a single round trip to your database.
It looks like this:
var bulk = People.collection.initializeUnorderedBulkOp();
bulk.find({<query>}).update({<update>});
bulk.find({<query2>}).update({<update2>});
...
bulk.execute(function(err) {
...
});
Check out the docs here: http://docs.mongodb.org/manual/core/bulk-write-operations/
This example should include all the cases that we can mix together using directly with Mongoose bulkWrite() function:
Character.bulkWrite([
{
insertOne: {
document: {
name: 'Eddard Stark',
title: 'Warden of the North'
}
}
},
{
updateOne: {
filter: { name: 'Eddard Stark' },
// If you were using the MongoDB driver directly, you'd need to do
// `update: { $set: { title: ... } }` but mongoose adds $set for
// you.
update: { title: 'Hand of the King' }
}
},
{
deleteOne: {
{
filter: { name: 'Eddard Stark' }
}
}
}
]).then(res => {
// Prints "1 1 1"
console.log(res.insertedCount, res.modifiedCount, res.deletedCount);
});
Official Documentation: https://mongoosejs.com/docs/api.html#model_Model.bulkWrite

Resources