Mongoose : update a document in a pre save hook - node.js

i'm quite new to nodeJS and have some problem i cannot solve...
i would like to update a related document in a pre save hook in mongoose :
No errors, but as a result i always get:
updated:{"n":1,"nModified":0,"ok":1}
so document is never updated.... how can i solve this ? i know i can make all controls before, but pre-hook seems the good place for that.
thanks for your help...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var suivilotSchema = new Schema({
code_lot: { type: String,Required: 'Code Lot cannot be left blank.' },
site_IMS: { type: String,Required: 'Site IMS cannot be left blank'},
date_plantws: { type: String,Required: 'tws plan prod cannot be left blank'},
nb_server: { type: Number,Required: 'nb Server cannot be left blank.',default: 1},
nb_server_done: { type: Number,Required: 'nb Server done cannot be left blank.',default: 0},
date_created: { type: Date,default: Date.now },
date_updated: { type: Date },
retry: { type: Number },
status: { type: Number}
});
suivilotSchema.pre('save', function (next) {
console.log('***** PRE HOOK suivilot *******');
var self = this;
this.constructor.findOne({'code_lot' : self.code_lot,'date_plantws' : self.date_plantws, 'retry' :
self.retry },function (err,existinglot) {
if (!existinglot){
console.log('SUIVILOTS : pas de resultat');
next();
}
else{
console.log('SUIVILOTS : lot exists: ',existinglot._id);
existinglot.updateOne({ '_id' : existinglot._id }, { $inc:{'nb_server' : 1}
},function(err,updated){
if(err)
console.log("Error during increment server :"+err);
console.log('updated:'+JSON.stringify(updated));
console.log('updated:'+JSON.stringify(existinglot));
});
}
});
});

Related

MongoDB aggregate, geNear and iterate over callback

I have a problem and I can´t find a solution. I have some MongoSchemas where I store Geolocation from users. Mobile Phone is sending me longitude and latitude every 5 minutes. This API is working perfectly.
Mongo-Schema looks like:
// Importing Node packages required for schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//= ===============================
// User Schema
//= ===============================
const GeolocationSchema = new Schema({
loc: {
type: { type: String },
coordinates: { type: [Number], index: '2dsphere' }
},
user: { type: Schema.Types.ObjectId, ref: 'User' }
},
{
timestamps: true
});
module.exports = mongoose.model('Geolocation', GeolocationSchema);
Now, I want to calculate users-nearby which have an "updateAt"-timestamp not even longer than 5 minutes in the past. That means that one or more users can be in a distance of e.g. 500m until 5 minutes in the past. This should be a match. For this I use Mongo aggregate, and I want to iterate the callback-result and extract the user._id out of the result to build a match.
This is what I tried:
const Geolocation = require('../models/geolocation')
User = require('../models/user'),
config = require('../config/main');
exports.setGeolocation = function (req, res, next) {
// Only return one message from each conversation to display as snippet
console.log(req.user._id);
var geoUpdate = Geolocation.findOneAndUpdate( { user: req.user._id },
{ loc: {
type: 'Point',
coordinates: req.body.coordinates.split(',').map(Number)
},
user: req.user._id
},
{upsert: true, new: true, runValidators: true}, // options
function (err, doc) { // callback
if (err) {
console.log(err);
}
});
// create dates for aggregate query
var toDate = new Date( (new Date()).getTime());
var fromDate = new Date( (new Date()).getTime() - 5000 * 60 );
var match = Geolocation.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: req.body.coordinates.split(',').map(Number)
},
distanceField: "dist.calculated",
maxDistance: 500,
includeLocs: "dist.location",
uniqueDocs: true,
query: { user: {$ne: req.user._id } , updatedAt: { $gte: fromDate,$lte: toDate }},
num: 5,
spherical: true
}
}], function (err, doc){
//here I´m going in trouble correctly parsing doc
var str = JSON.stringify(doc);
var newString = str.substring(1, str.length-1);
var response = JSON.parse(newString);
console.log(response.user);
});
res.sendStatus(200);
};
As you can see I´m going in trouble in parsing the "doc"-callback to iterate over the documents. If I want to parse it as jSON I´m getting an token-error on position 1. If I have more than 2 results, I´m getting an error on position 288.
That´s why I tried to parse and stringify the "doc". But this is not working correctly.
Maybe, someone could help me with a solution. I´m not familiar with mongo-functions because I´m starting with it, maybe there is a better solution but I can´t find something else to calculate geoNear and iterate afterwards over the results.
Thx at all who can help...

Decimal support Mongodb

Trying to use decimal128 datatype in my nodejs application.
mongo version "3.4.2",
os: { type: "Darwin", name: "Mac OS X", architecture: "x86_64", version: "16.7.0"
nodejs version 6.11.2
mongoose version 4.11.13
mongoose using native mongodb driver version 2.2.31
Mongodb config:
storage:
engine: wiredTiger
dbPath: "/Users/backend/Desktop/mongo/data"
What am i doing?
I've got mongoose subdocument schema
const Premises = mongoose.Schema({
floor : { type: mongoose.Schema.Types.Decimal128, required: true },
deleted : { type: Boolean, default: false },
created_at : { type: Date, default: Date.now },
updated_at : { type: Date, default: Date.now }
});
This schema is a subdocument of document with schema below:
...
premises : [ Premises ],
...
To add new subdocuments im using update method:
var queryFilter = {
'deleted' : false,
'buildings._id' : params.building_id
};
var premise = {
'_id': mongoose.Types.ObjectId(),
'floor': params['floor']
};
Block.update(queryFilter, { '$addToSet': { 'buildings.$.premises': premise } }, { safe: true }, function (error, result) {
result['_id'] = premise['_id'];
callback(error, result || null);
return;
});
Also i used code below:
var queryFilter = {
'deleted' : false,
'buildings._id' : params.building_id
};
var premise = {
'_id': mongoose.Types.ObjectId(),
'floor': mongoose.Types.Decimal128.fromString(params['floor'])
};
Block.update(queryFilter, { '$addToSet': { 'buildings.$.premises': premise } }, { safe: true }, function (error, result) {
result['_id'] = premise['_id'];
callback(error, result || null);
return;
});
But get the same error in both situations:
{"errors":{"name":"MongoError","message":"$numberDecimal is not valid
for
storage.","driver":true,"index":0,"code":52,"errmsg":"$numberDecimal
is not valid for
storage."},"data":{"ok":0,"n":0,"nModified":0,"_id":"59ce4e8cecba947a9a342f37"}}
I dont wanna use some workaround like
mongoose-double
to support negative numbers in my collections.
Much thanx for ur answers and solutions.
I faced with the same issue "$numberDecimal is not valid for storage" when I tried to add subdocument into array.
I tend to think this happens because of
Decimal128.prototype.toJSON = function() {
return { "$numberDecimal": this.toString() };
}
from http://mongodb.github.io/node-mongodb-native/2.2/api/node_modules_bson_lib_bson_decimal128.js.html
Maybe that is not the best solution but workaround below helped me:
mongoose.Types.Decimal128.prototype.toJSON = mongoose.Types.Decimal128.prototype.toString;
For adding item to an existing array I used:
entity.subentities.addToSet(subentity);
const updatedEntity = await entity.save();

Mongodb: Cannot see data of the Embedded Document via command

For mongodb's embedded document, I don't know why the data is not saved in the database or something else might be wrong? I tried to print out everything to make sure it works till the last step. But still got nothing when querying the embedded document, as you can see from below.
My schema:
// create competitorAnalysisSchema
var CompetitorAnalysis = new Schema({
firstObservation: { type: String },
secondObservation: { type: String },
thirdObservation: { type: String },
brandName: { type: String },
productCategory: { type: String },
photo1: { data: Buffer, contentType: String },
photo2: { data: Buffer, contentType: String },
photo3: { data: Buffer, contentType: String },
photo4: { data: Buffer, contentType: String }
});
// create UserSchema
var UserSchema = new Schema({
userName: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
currentDemo: { type: String },
nextDemo: { type: String },
startTime: { type: String },
startLocation: { type: String },
arriveTime: { type: String },
arriveLocation: { type: String },
leaveTime: { type: String },
leaveLocation: { type: String },
competitorAnalysis: [CompetitorAnalysis],
created_at: Date,
updated_at: Date
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
In my index.js, all debug messages can be successfully printed out.:
// on routes that end in /users/competitorAnalysisTextData
// ----------------------------------------------------
router.route('/users/competitorAnalysisTextData/:userName')
// update the user info (accessed at PUT http://localhost:8080/api/users/competitorAnalysisTextData)
.put(function(req, res) {
// use our user model to find the user we want
User.findOne({ userName: req.params.userName}, function(err, user) {
if (err)
res.send(err);
console.log('Got the user!');
// update the text data
user.competitorAnalysis.firstObservation = req.body.firstObservation;
user.competitorAnalysis.secondObservation = req.body.secondObservation;
user.competitorAnalysis.thirdObservation = req.body.thirdObservation;
user.competitorAnalysis.brandName = req.body.brandName;
user.competitorAnalysis.productCategory = req.body.productCategory;
console.log('req.body.firstObservation: %s', req.body.firstObservation);
console.log('user.competitorAnalysis.firstObservation: %s', user.competitorAnalysis.firstObservation);
console.log('Save the text data for competitorAnalysisTextData!');
// save the user
user.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'User updated!' });
console.log('user.competitorAnalysis.firstObservation: %s', user.competitorAnalysis.firstObservation);
console.log('Finally save the User!');
});
});
})
As in console:
Got the user in "Put"!
req.body.firstObservation: 3
user.competitorAnalysis.firstObservation: 3
Save the text data for competitorAnalysisTextData!
user.competitorAnalysis.firstObservation: 3
Finally save the User!
Problem
However, when I search in my mongodb database, there is no data saved for the embedded document:
...
"leaveTime" : "Your Current Time:\n 2016-08-23 10:27:45 AM",
"leaveLocation" : "Your Current Address:\n 1\nInfinite Loop\nCupertino\n95014",
"competitorAnalysis" : [ ]
}
> db.users.find({"competitorAnalysis.firstObservation" : "3"}).pretty()
>
Empty here!
I'm new to mongodb. It'll be great if I can get some hints on where else I can check or what the problem might be.
Update
Output of collection:
> db.users.find().pretty()
{
"_id" : ObjectId("57ba5f41ad8858305a5d3e58"),
"created_at" : ISODate("2016-08-22T02:11:13.968Z"),
"updated_at" : ISODate("2016-08-24T19:42:56.311Z"),
"nextDemo" : "12:00pm - 3:00pm, Whole Foods Market, 5880 Centre Ave, Pittsburgh PA 15206",
"currentDemo" : "9:00am - 1:00pm, Whole Foods Market, 5880 Centre Ave, Pittsburgh PA 15206",
"password" : "<3da4dafc c96e05cd 855da8b3 ff0bf074 8156ec4b b9f1a002 ba907bcc d5e4aa5b fcd2fef9 dec240cd 86489978 7d85cec8 f11eae1c 7b60b2cc 6693da1a 4eae3a73>",
"email" : "chenya#gmail.com",
"userName" : "Chenya",
"__v" : 1,
"startLocation" : "Your Current Address:\n 10141\nBilich Pl\nCupertino\n95014",
"startTime" : "Your Current Time:\n 2016-08-24 03:42:42 PM",
"arriveTime" : "Your Arriving Time:\n 2016-08-24 03:42:44 PM",
"arriveLocation" : "Your Arriving Address:\n 10131\nBilich Pl\nCupertino\n95014",
"leaveTime" : "Your Current Time:\n 2016-08-23 10:27:45 AM",
"leaveLocation" : "Your Current Address:\n 1\nInfinite Loop\nCupertino\n95014",
"competitorAnalysis" : [ ]
}
>
These statements are the problem:
user.competitorAnalysis.firstObservation = req.body.firstObservation;
user.competitorAnalysis.secondObservation = req.body.secondObservation;
user.competitorAnalysis.thirdObservation = req.body.thirdObservation;
user.competitorAnalysis.brandName = req.body.brandName;
user.competitorAnalysis.productCategory = req.body.productCategory;
You're treating your competitorAnalysis array as if it were an object.
I don't work with Mongoose, so don't know the syntax, but you want to do something like this instead:
user.competitorAnalysis.push({
firstObservation: req.body.firstObservation,
secondObservation: req.body.secondObservation,
thirdObservation: req.body.thirdObservation,
brandName: req.body.brandName
productCategory: req.body.productCategory
});

Mongoose & float values

My lat & lng numbers are being converted to strings. My section integers are still the correct data type of Number. How do I set up model so that I can get my lat & lng back out as Float rather than String?
I'm storing latLng data in my db. Right now I have my data type set to Number for lat & lng. When I check out my db I see this:
{
"_id" : ObjectId("563bd98a105249f325bb8a7e"),
"lat" : 41.8126189999999980,
"lng" : -87.8187850000000054,
"created" : ISODate("2015-11-05T22:34:50.511Z"),
"__v" : 0,
"section" : 0,
}
But when I get my data back out using express I get this:
{
"_id": "563bd98a105249f325bb8a7e",
"lat" : "41.8126189999999980",
"lng" : "-87.8187850000000054",
"__v": 0,
"section" : 0,
"created" : "2015-11-05T22:34:50.511Z",
}
My model:
var WaypointSchema = new Schema({
lat: {
type: Number
},
lng: {
type: Number
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Express controller:
exports.list = function(req, res) {
Waypoint.find().sort('-created').populate('user', 'displayName').exec(function(err, waypoints) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(waypoints);
}
});
};
While the mongoDB fully supports float type, the mongoose supports only type of Number which is integer. If you try to save to mongoDB float number using mongooses type of Number it will be converted to string.
To sort this out, you will need to load some plugin for mongoose which will extend its value types. There are some plugins which work best with currencies or dates, but in your case I would use https://www.npmjs.com/package/mongoose-double.
Your model after changes would look something like this:
var mongoose = require('mongoose')
require('mongoose-double')(mongoose);
var SchemaTypes = mongoose.Schema.Types;
var WaypointSchema = new Schema({
lat: {
type: SchemaTypes.Double
},
lng: {
type: SchemaTypes.Double
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Hope it helps.
As of the current version of mongoose (v5.12.6), It supports Decimal128 which can be used for this.
var mongoose = require('mongoose');<br>
var Schema = mongoose.Schema;<br>
var Waypoint = new Schema({<br>
lat: {<br>
type: SchemaTypes.Double<br>
},<br>
lng: {<br>
type: SchemaTypes.Double<br>
},<br>
section: {<br>
type: Number<br>
}<br>
point: {<br>
type: [Number],<br>
index: '2d'<br>
},<br>
}, {<br>
timestamps: true<br>
})<br>
event.index({<br>
Point: '2dsphere'<br>
});<br>
module.exports = mongoose.model('Waypoint', Waypoint);<br>
waypoint.save(point: [parseFloat(values.latitude), parseFloat(values.longitude)],)

How to update mixed type field in Mongoose without overwriting the current data?

I have the following schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ShopSchema = new Schema({
name: Schema.Types.Mixed,
country: {
type: String,
default: ''
},
createdAt: {
type: Date,
default: Date.now
},
defaultLanguage: {
type: String
},
account: {type : Schema.ObjectId, ref : 'Account'},
});
mongoose.model('Shop', ShopSchema);
"name" field is multilingual. I mean, I will keep the multilingual data like
name: {
"en": "My Shop",
"es": "Mi Tienda"
}
My problem is, in a controller, I am using this code to update the shop:
var mongoose = require('mongoose')
var Shop = mongoose.model('Shop')
exports.update = function(req, res) {
Shop.findByIdAndUpdate(req.params.shopid, {
$set: {
name: req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
};
and it is obvious that new data overrides the old data. What I need is to extend the old data with the new one.
Is there any method to do that?
You should to use the method .markModified(). See the doc http://mongoosejs.com/docs/schematypes.html#mixed
Since it is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect and save those changes. To "tell" Mongoose that the value of a Mixed type has changed, call the .markModified(path) method of the document passing the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Use "dot notation" for the specific element:
Shop.findByIdAndUpdate(req.params.shopid, {
"$set": {
"name.en": req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
});
That wil either only overwrite the "en" element if that is what you want to do or "create" a new element with the data you set it to. So if you used "de" and that did not exist there will be the other elements and a new "de" one with the value.

Resources