mongoose findOneAndUpdate deletes nested vars - javascript

In my express/mongoose code when I update a nested var it deletes any other nested vars in the object I didn't update?
My schema:
var MySchema = new Schema({
active: { type: Boolean, required: true, default: true },
myvar: {
useDefault: { type: Boolean, required: true },
custom: { type: String },
default: { type: String }
}
});
My express middleware update function:
var updateData = req.body;
MySchema.findOneAndUpdate({ active: true }, updateData, function(err, myschema) {
if (err) throw err;
if (!myschema) { response(403, { success:false, message: "myschema not updated." }, res); }
// success
response(200, { success:true, message: "myschema updated." }, res);
});
The record initially look like this:
{
"myvar": {
"useDefault": true,
"custom": "some custom var",
"default": "some value"
}
}
When I submit an update to say the myvar.useDefault value the record ends up like this:
{
"myvar": {
"useDefault": false
}
}
Can anyone advise how to update only the targeted var and leave any other vars as they were?

The answer was to convert the response.body object to dot notation and pass that modified object to mongoose findOneAndUpdate. Thanks to #btkostner on Gitter for the answer and his toDot function. Here is the relevant bits of code:
function(req, res) {
// convert response to dot notation so objects retain other values
var dotData = toDot(req.body, '.');
MySchema.findOneAndUpdate({ active: true }, dotData, function(err, myschema) {
...
}
// author: #btcostner
function toDot (obj, div, pre) {
if (typeof obj !== 'object') {
throw new Error('toDot requires a valid object');
}
if (pre != null) {
pre = pre + div;
} else {
pre = '';
}
var iteration = {};
Object.keys(obj).forEach(function(key) {
if (_.isPlainObject(obj[key])) {
Object.assign(iteration, _this.toDot(obj[key], div, pre + key));
} else {
iteration[pre + key] = obj[key];
}
});
return iteration;
};
NOTE: I had to convert the toDot() function to ES5 for my project, here is the original ES6 version: github.com/elementary/houston/blob/master/src/lib/helpers/dotNotation.js

Related

How to access array elements that are defined in another array of Mongoose scheme object Array?

This is the User schema in mongoose:
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
},
name: {
type: String,
required: true,
},
Addtasks: [
{
topic: String,
words: Number,
keywords: String,
website: String,
otherdetails: String,
exampleRadios: String,
deadline: Date,
Date: String,
fileName: String,
Bigpaths: [],
},
],
});
module.exports = mongoose.model('User', userSchema);
I want to use/access the Bigpaths array, which is defined inside the Addtasks array, which is defined in User. Data is already are there in mongoDB, which I have inserted via UI page. I am trying the following code but I am getting this error in console:
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(
(element) => {
// ...
}
)
as
TypeError: Cannot read property 'Bigpaths' of undefined
at \Desktop\grumpytext\routes\index.js:99:71
Code:
const { files } = req;
User.findOne({ email: req.user.email }, function (error, data) {
if (error) {
console.log('Three');
} else if (data) {
if (Object.keys(data.Addtasks).length > 1) {
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(
(element) => {
files.forEach((currentElement) => {
if (element.name == currentElement.filename) {
files.pull(currentElement.filename);
}
});
}
);
}
}
});
How to resolve this error or how to access all the elements of Bigpaths array so that I can iterate it with forEach loop?
I'm not sure here, but I think you need to populate Addtasks prior to manipulating it:
const files = req.files;
User.findOne({email:req.user.email}).populate('Addtasks').exec((error, data) => {
if (error) {
console.log("Three");
}
else
{
if(data)
{
if(Object.keys(data.Addtasks).length > 1)
{
console.log("Addtasks count: " + Object.keys(data.Addtasks).length);
data.Addtasks[Object.keys(data.Addtasks).length - 2].Bigpaths.forEach(element => {
files.forEach(currentElement => {
if(element.name == currentElement.filename)
{
files.pull(currentElement.filename);
}
})
});
}
}
}
});
Please notice the log console.log("Addtasks count: " + Object.keys(data.Addtasks).length); - in case the solution does not work, I advise to add some prints, especially to check if the count of elements is as expected or properties within an object are fine.

Updating a shadow field, via updateOne pre hook, in mongoose?

Can anyone explain how to use the updateOne pre-hook, in mongoose (5.9.5)?
I am need to create a normalised 'shadow field' (not sure the right term) to help with certain searches. While I am able to update the shadow field during a save, I am having trouble during update.
The save pre-hook:
personSchema.pre('save', function (next) {
if (this.isModified('name')) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.shadowName = shadowName.toLowerCase();
} else {;
this.shadowName = name;
}
}
// do stuff
next();
});
Doing the equivalent for updateOne does not seem to work (shadowName stays with the value it was given during the initial save):
personSchema.pre('updateOne', function (next) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.update({}, { shadowName: shadowName.toLowerCase() });
} else {
this.shadowName = name;
}
// do stuff
next();
});
The schema:
const personSchema = new mongoose.Schema({
resourceId: {
type: String,
required: true,
unique: true,
index: true,
uppercase: true
},
name:{
type: String,
required:true,
index: true
},
// can be used for searches, but don't update directly
shadowName: {
type: String,
index: true
},
});
BTW I can confirm the hook is called, but the field is not updated.
Turns out you can't access the field values directly and instead need to leverage the get() and set() methods on the query.
Changing the pre-updateOne hook to be the following works:
personSchema.pre('updateOne', function (next) {
const name = this.get('name');
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.set('shadowName', shadowName.toLowerCase());
} else {
this.set('shadowName', name);
}
// do stuff
next();
});

Custom validator, cb is not a function

Problem with a custom validator in node.js, using mongoose. I'm trying to check if a query exists in headerLog prior to inserting it.
My code is below:
var mongoose = require('mongoose'); //layer above mongodb
var Schema = mongoose.Schema;
var headerLogSchema = new Schema({
query: { type: String, required: true, unique: true, validate: {
validator: function(v, cb) {
HeaderLog.find({query: v}, function(err, documents){
cb(documents.length == 0);
});
},
message: 'Header already exists in log, didnt save this one.'
}
}
})
var HeaderLog = mongoose.model('headerLog', headerLogSchema);
module.exports = HeaderLog;
The error: TypeError: cb is not a function.
I'm calling this function like so:
function logHeader(query) {
var newHeaderLog = new HeaderLog({
query: query
})
newHeaderLog.save(function(err) {
if (err) {
console.log(err);
}
else {
console.log('New header logged');
}
});
}
What am I doing wrong?
As the reference states, asynchronous validators should either have isAsync flag:
validate: {
isAsync: true,
validator: function(v, cb) { ... }
}
Or return a promise. Since the validator already uses another model, and Mongoose models are promise-based, it makes sense to use existing promise:
validator: function(v) {
return HeaderLog.find({query: v}).then(documents => !documents.length);
}
countDocuments is a better alternative to find for cases when only documents count is needed.
If you look at the async validator example here in the doc, it looks like you have to pass the option isAsync: true in order to tell mongoose that you are using an async validator and thus it should pass a callback to it.
var headerLogSchema = new Schema({
query: {
type: String,
required: true,
unique: true,
validate: {
isAsync: true, // <======= add this
validator: function(v, cb) {
HeaderLog.find({query: v}, function(err, documents){
cb(documents.length == 0);
});
},
message: 'Header already exists in log, didnt save this one.'
}
}
})

Undefined returned while accessing data of another model of loopback from remote method?

var loopback = require('loopback');
var serviceOffering = loopback.getModelByType('ServiceOffering');
var app = require('../../server/server.js');
module.exports = function (serviceOffering) {
serviceOffering.GetAllOfferings = function (options,category,callback) {
var filter = JSON.parse(JSON.stringify(category));
var arry=[];
var getOrgFil={where:{or:[]}};
var uniqOrList=[];
serviceOffering.find(options,filter, function (err, result) {
for(var i = 0; i < result.length; i++) {
var obj = result[i];
if (arry.indexOf(result[i]._createdBy) === -1) {
arry.push(obj._createdBy);
getOrgFil.where.or.push({UserName:obj._createdBy});
}
}
console.log(getOrgFil);
serviceOffering.app.models.Realm.find(options,getOrgFil, function (err, resl) {
console.log(resl);
return callback(null, resl);
});
});
};
serviceOffering.remoteMethod(
'GetAllOfferings', {
http: { path: '/GetAllOfferings', verb: 'get' },
accepts: { arg: 'category', type: 'object',http: { source: 'query' } },
returns: { type: 'array', root: true }
});
}
I am making a call to 'Realm' model from my 'ServiceOffering' model's remote method to filter and get some data. But the response i am getting for resp is "undefined". I am new to loopback and have very limited idea on how to debug. I was able to print "result", "getOrgFil", but "resp" comes as 'undefined'. Can someone please help me understand why am i getting 'undefined' when i am trying to get the value of resp via the remote method?
I get output as mentioned below
result = [ { SeriviceCategory: 'SCCER',
AssetType: List [ 'ATCOF', 'ATHUS', 'ATMLK' ],
ServiceType: List [ 'STQCR' ],
Identity: null,
_type: 'ServiceOffering',
_createdBy: 'admin',
_modifiedBy: 'admin',
_createdOn: 2018-01-01T09:21:48.638Z,
_modifiedOn: 2018-01-01T09:21:48.638Z,
_scope: List [ 'tenantId:default' ],
_autoScope: { tenantId: 'default' },
_isDeleted: false,
_version: '536c3db2-2323-4004-afb6-2a9e7995aeca',
id: 5a49fdac28a2bd802d8206ba } ]
getOrgFil = { where: { or: [ [Object] ] } }
stringified getOrgFil = {"where":{"or":[{"UserName":"admin"}]}}
resl = undefined
There is no relationship between Realm & ServiceOffering Models.
It will be this
var app = require('../../server/server');
app.models.Realm.find(options,getOrgFil, function (err, resl) { // this instead of serviceOffering.app.models
return callback(null, resl);
});

Issue in document insert using mongoose

I have schema created now i want to insert into mongodb collection, but its throwing error diagramModel.insert is not defined any idea what is implemented wrong ?
app.js
mongoose.connect('mongodb://localhost:27017/develop-modeler');
require('./server/api/diagram/diagram.model.js');
var diagramModel = mongoose.model('Diagram');
var newDiagram = {
"owner" : "sh529u",
"text" : "sco_poc.bpmn",
"users":["wp6307","kz323j","ew6980"],
"groups":[],
"string" : "test"
}
mongoose.connection.on('connected', function () {
diagramModel.insert(newDiagram,function(err,res){
if (err) { console.log(err);}
else {
diagramModel.find({}, function(err, data) { console.log(data);});
}
});
});
diagram.model.js
var DiagramSchema = new mongoose.Schema({
text: String,
owner: {type: String, ref:'User'},
groups: [{type: String, ref: 'Group'}],
users: [{type: String, ref: 'User'}],
string: String
});
mongoose.model('Diagram', DiagramSchema);
I think it's save not insert while saving records. That's why you are getting this error
mongoose.connect('mongodb://localhost:27017/develop-modeler');
var diagramModel = require('./server/api/diagram/diagram.model.js');
var newDiagram = {
"owner": "sh529u",
"text": "sco_poc.bpmn",
"users": ["wp6307", "kz323j", "ew6980"],
"groups": [],
"string": "test"
}
mongoose.connection.on('connected', function() {
diagramModel.save(newDiagram, function(err, res) {
if (err) {
console.log(err);
} else {
diagramModel.find({}, function(err, data) {
console.log(data);
});
}
});
});
var DiagramSchema = mongoose.Schema({
text: String,
owner: {type: String, ref:'User'},
groups: [{type: String, ref: 'Group'}],
users: [{type: String, ref: 'User'}],
string: String
});
module.exports=mongoose.model('Diagram', DiagramSchema);
Create a new instance of your model and this instance contains a save method:
mongoose.connect('mongodb://localhost:27017/develop-modeler');
require('./server/api/diagram/diagram.model.js');
var diagramModel = mongoose.model('Diagram');
var newDiagram = new diagramModel({
"owner" : "sh529u",
"text" : "sco_poc.bpmn",
"users":["wp6307","kz323j","ew6980"],
"groups":[],
"string" : "test"
});
mongoose.connection.on('connected', function () {
newDiagram.save(function(err,res){
if (err) { console.log(err);}
else {
diagramModel.find({}, function(err, data) { console.log(data);});
}
});
});
Try this its working perfect
var diagramModel = require('./server/api/diagram/diagram.model.js');
var finalDiagram = diagramModel({
"owner" : "sh529u",
"text" : "sco_poc.bpmn",
"users":["wp6307","kz323j","ew6980"],
"groups":[],
"string" : "test"
});
mongoose.connection.on('connected', function () {
finalDiagram.save(function (err, dataObj) {
if (err) { console.log(err);}
} else {
console.log("DATA",dataObj);
diagramModel.find({}, function(err, data) { console.log(data);});
}
});
});

Categories

Resources