nodejs mongoose bulk update - node.js

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

Related

How to get all data matched each record from mongodb using mongoose and nodejs

Tried to get value of product_name from my mongodb using mongoose but i do not know how to do it.
My DB Data collection:
{
_id:ObjectId("5ecea02ebb6f3c19e86fe805"),
product_name:"Test1"
},
{
_id:ObjectId("5ecea02ebb6f3c19e86fe806"),
product_name:"Test2"
},
{
_id:ObjectId("5ecea02ebb6f3c19e86fe807"),
product_name:"Test3"
},
{
_id:ObjectId("5ecea02ebb6f3c19e86fe808"),
product_name:"Test4"
},
{
_id:ObjectId("5ecea02ebb6f3c19e86fe809"),
product_name:"Test5"
}
data.controller.js:
module.exports.getData = (req, res, next) => {
var tableCate = mongoose.model("Product");
tableCate.find({ product_name }, function(err, docs) {
if (err) {
console.log( err);
return
} else {
console.log(docs)// output should be Test1,Test2,Test3,Test4,Test5
}
});
db.collection.find returns a Cursor which is A pointer to the result set of a query, to access the result you can use db.collection.find({}).toArray() to return an array of documents or
.forEach(function(item){
// and you can print or do what you want with each item
})

Expressjs does not execute sequencially and function return does not work

I am new to node/express js, and trying to execute the following. The control executes the lines after function call "var nextVersion =getNextContractVersion(cid)", even before the function returns a response. As a result the value for newVersion is not updated to Contract object. Also, function getNextContractVersion(cid) returns undefined, unlike the updated nextVersion.
How do i fix this behavior, please suggest. Also, is the right way of invoking function?
// Package imports
const express = require('express');
var router = express.Router();
const mongoose = require('mongoose');
//Local imports
var { Customer } = require('../models/customer');
var { Contract } = require('../models/contract');
router.put('/:cid/contracts', (req, res) => {
var cid = req.params.cid;
var nextVersion =getNextContractVersion(cid);
var contract;
if (validateCustomerId(cid)) {
req.body.contract.forEach((item) => {
contract = new Contract({
customerID: cid,
startDate: item.startDate,
endDate: item.endDate,
conditions: item.conditions,
price: item.price,
author: item.author,
version: nextVersion
});
});
contract.save((err, docs) => {
if (!err) {
Customer.findOneAndUpdate({ customerID: cid }, { $push: { contract: contract } },
{ safe: true, upsert: true, new: true }).populate({ path: 'contract' }).exec((err1, docs1) => {
if (!err1) {
res.send(docs1).status(200);
} else {
console.log('Error is adding a new contract:' + JSON.stringify(err1, undefined, 2));
}
});
} else {
console.log('Error is updating a new customer:' + JSON.stringify(err, undefined, 2));
}
});
} else {
res.status(400).send('Bad Request - Invalid input!')
}
});
function getNextContractVersion(cid) {
var nextVersion=1;
Contract.findOne({ customerID: cid }).sort({version: 'descending'}).exec((err, doc) => {
if (!err && doc != null) {
var currentVersion = parseInt(doc.version);
nextVersion = currentVersion + 1;
}
});
return nextVersion;
}
You are mixing synchronous and asynchronous code.
Contract.findOne({ customerID: cid }).sort({version: 'descending'}).exec((err, doc) => {
if (!err && doc != null) {
var currentVersion = parseInt(doc.version);
nextVersion = currentVersion + 1;
}
});
The above code effectively says "Go to the database, find one of these objects and whenever in the future that is done, run this code that's in the exec block."
One of the ways to reason about asynchronous code from a synchronous mindset is that of promises.
Here's a semi pseudo implementation:
router.put('/:cid/contracts', (req, res) => {
var cid = req.params.cid;
return getTheMostRecentContract(cid)
.then(function(oldContract){
var nextVersion = oldContract.version +1;
if(!validateCustomerId(cid)){
return res.status(400).send('Bad Request - Invalid input!');
}
var contract;
var savePromises = [];
req.body.contract.forEach((item) => {
contract = new Contract({
customerID: cid,
startDate: item.startDate,
endDate: item.endDate,
conditions: item.conditions,
price: item.price,
author: item.author,
version: nextVersion
});
savePromises.push(contract.save());
});
return Promise.all(savePromises);
})
.then(function(resultOfAllSavePromises){
//rest of code here
}).catch(function(error){
console.log('Error is updating a new customer:' + JSON.stringify(err, undefined, 2));
return res.status(400);
})
});
function getTheMostRecentContract(cid) {
return Contract.findOne({ customerID: cid }).sort({version: 'descending'});
}
As a matter of practice though, have the database control your auto-increment values. This code won't work in a high traffic environment.

Can I perform mongoose update from post save middleware?

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' });

Backbone and Express: concatinating (duplicating) routes on res.redirect

I have an action where I need to update MongoDB entry including _id field, which requires deleting old entry and making a new one, here is server side:
exports.update = function(req, res, next){
var outcome = [];
outcome.previousId = req.params.id;
outcome.newId = req.body.name;
var getPreviousRecord = function(callback) {
req.app.db.models.AccountGroup
.findOne({ _id: req.params.id })
.lean()
.exec(function(err, accountGroups) {
if (err) {
return callback(err, null);
}
outcome.accountGroups = accountGroups;
return callback(null, 'done');
});
};
var makeNewRecord = function(callback) {
var permissions = outcome.accountGroups.permissions;
var fieldsToSet = {
_id: outcome.newId.toLowerCase(),
name: outcome.newId,
permissions: permissions
};
req.app.db.models.AccountGroup
.create(fieldsToSet, function(err, record) {
if (err) {
return callback(err, null);
}
outcome.record = record;
return callback(null, 'done');
});
};
var deletePreviousRecord = function() {
req.app.db.models.AccountGroup
.findByIdAndRemove(outcome.previousId)
.exec(function(err) {
if (err) {
return next(err);
}
res.redirect('admin/account-groups/' + outcome.newId + '/');
});
};
var asyncFinally = function(err) {
if (err) {
return next(err);
}
};
require('async').series([getPreviousRecord, makeNewRecord, deletePreviousRecord], asyncFinally);
};
It works fine, but I can't make this work normally on the front-end, it returns me both old route and a new route, for example:
PUT /admin/account-groups/customers22/admin/account-groups/Customers2233/ 404 213.749 ms - 31
where customers22 is old _id and customers2233 is new _id. If I navigate from another page to new entry it gets route normally.
On client side:
(function() {
'use strict';
app = app || {};
app.Details = Backbone.Model.extend({
idAttribute: '_id',
defaults: {
success: false,
errors: [],
errfor: {},
name: ''
},
url: function() {
return '/admin/account-groups/'+ app.mainView.model.id +'/';
},
parse: function(response) {
if (response.accountGroup) {
app.mainView.model.set(response.accountGroup);
delete response.accountGroup;
}
return response;
}
});
app.DetailsView = Backbone.View.extend({
el: '#details',
events: {
'click .btn-update': 'update'
},
template: Handlebars.compile( $('#tmpl-details').html() ),
initialize: function() {
this.model = new app.Details();
this.syncUp();
this.listenTo(app.mainView.model, 'change', this.syncUp);
this.listenTo(this.model, 'sync', this.render);
this.render();
},
syncUp: function() {
this.model.set({
_id: app.mainView.model.id,
name: app.mainView.model.get('name')
});
},
render: function() {
this.$el.html(this.template( this.model.attributes ));
for (var key in this.model.attributes) {
if (this.model.attributes.hasOwnProperty(key)) {
this.$el.find('[name="'+ key +'"]').val(this.model.attributes[key]);
}
}
},
update: function() {
this.model.save({
name: this.$el.find('[name="name"]').val()
});
}
});
app.MainView = Backbone.View.extend({
el: '.page .container',
initialize: function() {
app.mainView = this;
this.model = new app.AccountGroup( JSON.parse( unescape($('#data-record').html()) ) );
// ...
app.detailsView = new app.DetailsView();
}
});
$(document).ready(function() {
app.mainView = new app.MainView();
});
}());
It probably requires to trigger both model.save and model.destroy or prevent URL being used. Any advice on how to do it is appreciated, thank you.
Edit
Just a typo mistake here that is not related to the question, recklessly checking routes, see as cancelled
I believe the problem is here:
res.redirect('admin/account-groups/' + outcome.newId + '/');
That's a relative path so it'll be appended onto the current URL. I suspect you want something like this:
res.redirect('/admin/account-groups/' + outcome.newId + '/');

Mongoose not updating when using stream()

I want to run an update and publish a message for each updated resource. It works fine when running as a standard mongoose query with a callback, but fails to have an effect using stream, even though the resources are output into the stream.
var discount = {
startDate: process.env.START_DATE,
endDate: process.env.END_DATE,
funding: process.env.FUNDING,
percentage: process.env.PERCENTAGE
}
var connection = mongoose.createConnection(mongoEndpoint, config.mongo);
var Resources = connection.model('Resources', resourcesSchema);
var stream = Resources.update({_id:6375668}, { $push: { discounts: 'blob2' } }, { multi: true });
stream.on('data', function(resource) {
console.log('Updated resource + published message',resource._id, discount)
rabbit.publish('index-resource', resource);
});
stream.on('end', function() {
console.log('Updated all resources in query');
process.exit(0);
})
var ResourceSchema = module.exports = new Schema({
_id: Number,
discounts: Array
}, {versionKey: false});
It looks like your resourcesSchema is undefined here?
var Resources = connection.model('Resources', resourcesSchema);

Resources