findOneAndUpdate Not Updating Discriminator - javascript

I am working on a REST API using Node, Express and Mongoose. Everything works perfectly when I update the base model. But when I try to update the discriminator object sportEvent in this case, it doesn't work.
Event.js - Event data model has a base schema common for all the collections with a discriminator for additional detail for that collection.
// base schema for all the events
// includes basic detail for all the events
const eventSchema = new Schema({
//title for the event
title: {
type: String,
required: true
},
//description for the events
description: {
type: String,
required: true
},
//event type for the event. such as Music, Sports, Expo, Leisure
eventType: {
type: String,
required: true,
}
}, { discriminatorKey: 'eventType' });
//sport event model for extending the basic event model
const sportEvent = Event.discriminator("sports", new Schema({
sportEvent: {
//sport name. for eg: cricket, football, etc
sportName: {
type: String,
required: true
},
//first team name
firstTeam: {
type: String,
required: true
},
//second team name
secondTeam: {
type: String,
required: true
},
}
}));
EventController.js - has a PUT method for updating the collection. Here is a code snippet.
//for updating the event added a PUT method in /event route
router.put('/events/:eventId', function(req, res, next){
//getting the event id form the url
eventId = req.params.eventId;
//checking the provided event id is a valid mongodb _id object or not
if(objectId.isValid(eventId)){
Event.findOneAndUpdate({_id: eventId}, {$set: req.body}, {new: true, runValidators: true}, function(err, event){
if(err) {
next(err);
}
sendResponse(res, "Event Successfully Updated", event);
});
} else {
//sending a bad request error to the user if the event id is not valid
sendError(res, 400, "Invalid Event ID");
}
});

Ensure the discriminator key is present in the update object, or as an argument to the update function, write a switch case based on discriminator key, call update on the specific Schema type
callback = function(err, doc){
if(err) console.log(err)
console.log(doc)
};
var id = ObjectId("5a75d22e6dabf3102c059f56");
var update = {
title : 'title-name',
eventType : 'sports' ,
sportEvent : {
firstTeam : 'first-name',
secondTeam : 'second-name',
sportName : 'sport-name'
}
};
switch(update.eventType){
case 'sports':
SportEventSchema.findByIdAndUpdate(id, {$set : update}, {new : true, upsert : false}, callback)
break;
case 'games':
GameEventSchema.findByIdAndUpdate(id, {$set : update}, {new : true, upsert : false}, callback)
break;
default:
Event.findByIdAndUpdate(id, {$set : update}, {new : true, upsert : false}, callback);
break;
}
output : update for a sports event type
Mongoose: events.findAndModify({ eventType: 'sports', _id: ObjectId("5a75d22e6dabf3102c059f56") }, [], { '$set': { title: 'title-name', eventType: 'sports', sportEvent: { firstTeam: 'first-name', secondTeam: 'second-name', sportName: 'sport-name' } } }, { new: true, upsert: false, remove: false, fields: {} })
{ sportEvent:
{ firstTeam: 'first-name',
secondTeam: 'second-name',
sportName: 'sport-name' },
eventType: 'sports',
_id: 5a75d22e6dabf3102c059f56,
title: 'title-name',
description: 'desc',
__v: 0 }

As of Mongoose 6, you can modify the value of the discriminator key by using the overwriteDiscriminatorKey option:
//for updating the event added a PUT method in /event route
router.put('/events/:eventId', function (req, res, next) {
//getting the event id form the url
eventId = req.params.eventId;
//checking the provided event id is a valid mongodb _id object or not
if (objectId.isValid(eventId)) {
Event.findById(eventId, function (err, event) {
Event.replaceOne(
{ _id: eventId },
{ ...event.toObject(), ...req.body },
{
overwriteDiscriminatorKey: true,
new: true,
runValidators: true,
},
function (err, event) {
if (err) {
next(err);
}
sendResponse(res, 'Event Successfully Updated', event);
}
);
});
} else {
//sending a bad request error to the user if the event id is not valid
sendError(res, 400, 'Invalid Event ID');
}
});

Related

adding data to array of existing object into mongodb from form

I have my route below that takes the form data and tries to input the page and archive into an existing name that is already in the mongodb. My form data is being sent correctly as I can see from my console.log.
Now I need to insert the page name and archive name where it equals name. Whether it already has data or not. They are already setup with array in the schema. You can see my schema below
Any ideas on how to get this setup?
router.post('/add-page', function(req, res, next){
if(req.body.name && req.body.page && req.body.archive){
//create object with form input
var pageData = {
client: req.body.name,
page: req.body.page,
archive: req.body.archive
};
console.log(pageData);
//insert data
Page.then(function(db){
delete req.body._id;
db.collection('pages').insertOne();
});
return res.render('index', { title: 'Home' });
}else{
var err = new Error('All fields required.');
err.status = 400;
return next(err);
}
});
var ClientSchema = new mongoose.Schema({
client: {
type: String,
required: true,
trim: true,
unique: true,
},
page: {
type: [],
trim: true,
},
archive: {
type: [],
trim: true,
}
});

Unable to read json file second time : "Unexpected end of input error"

I am using cron based schedular to hit an external api that gives me json data every 2mins. I write data to the file and then read it, clean the data and add it to a collection in mongodb. It works fine the first time but second time I get an error like this:-
C:\smatserver\smatserver\deals.js:74
throw err;
^
SyntaxError: ./files/localdeals.json: Unexpected end of input
at Object.parse (native)
at C:\smatserver\smatserver\node_modules\jsonfile\index.js:31:18
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:380:3)
Here is the deals.js code where I am hitting external API and saving in file and reading it and trying to push it to mongodb:-
var schedule=require('node-schedule');
var request=require('request');
var jsonfile = require('jsonfile');
var _=require('underscore');
var LocalDeals=require('./models/localdeal.js');
//run job every 2 minutes
var dataSchedular=schedule.scheduleJob('*/2 * * * *', function(){
var local_deals_file='./files/localdeals.json';
var local_deals_url='http://api.8coupons.com/v1/getdeals?key=API_KEY&mileradius=10&limit=100&orderby=radius';
request({url:local_deals_url,
json:true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
jsonfile.writeFile(local_deals_file, body, {spaces: 2}, function(err) {
console.error(err);
});
console.log('File write success for '+local_deals_file);
//problem area
jsonfile.readFile(local_deals_file, function(err, local_deals_obj) {
//save in mongodb collection
if(err){ //this error gets thrown on 2nd read
throw err;
}
local_deals_obj.forEach(function(local_deal_obj){
var local_deal_filtered=_.pick(local_deal_obj, 'name', 'address', 'storeID','chainID','phone','state','city','ZIP','URL','storeURL',
'dealTitle','dealinfo','expirationDate','postDate','showImageStandardBig','showImageStandardSmall','providerName','DealTypeID','categoryID',
'lat','lon','distance','dealOriginalPrice','dealPrice','dealSavings','dealDiscountPercent');
var new_local_deal=new LocalDeals({
name : local_deal_filtered.name,
address : local_deal_filtered.address,
storeID : local_deal_filtered.storeID,
chainID : local_deal_filtered.chainID,
phone : local_deal_filtered.phone,
state : local_deal_filtered.state,
city : local_deal_filtered.city,
ZIP : local_deal_filtered.ZIP,
URL : local_deal_filtered.URL,
storeURL : local_deal_filtered.storeURL,
dealTitle : local_deal_filtered.dealTitle,
dealinfo : local_deal_filtered.dealinfo,
expirationDate:local_deal_filtered.expirationDate,
postDate : local_deal_filtered.postDate,
showImageStandardBig: local_deal_filtered.showImageStandardBig,
showImageStandardSmall: local_deal_filtered.showImageStandardSmall,
providerName: local_deal_filtered.providerName,
DealTypeID: local_deal_filtered.DealTypeID,
categoryID: local_deal_filtered.categoryID,
lat: local_deal_filtered.lat,
lon: local_deal_filtered.lon,
distance: local_deal_filtered.distance,
dealOriginalPrice: local_deal_filtered.dealOriginalPrice,
dealPrice: local_deal_filtered.dealPrice,
dealSavings: local_deal_filtered.dealSavings,
dealDiscountPercent: local_deal_filtered.dealDiscountPercent
});
LocalDeals.saveLocalDeals(new_local_deal,function(err,deal){
if(err){
throw err;
}else{
//console.log("local deals added to mongodb");
}
});
});
});
}
else{
console.log(error);
}
});
});
module.exports=dataSchedular;
And here is my model file called localdeal.js:-
var mongoose=require('mongoose');
mongoose.connect('mongodb://localhost/smat');
var db=mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log('Successfully connected to mongodb');
});
var localdealsSchema=mongoose.Schema({
name: {
type: String
},
address:{
type: String
},
storeID:{
type: String
},
chainID:{
type: String
},
phone:{
type: String
},
state:{
type: String
},
city:{
type: String
},
ZIP:{
type: String
},
URL:{
type: String
},
storeURL: {
type: String
},
dealTitle:{
type: String
},
dealinfo:{
type: String
},
expirationDate:{
type: String
},
postDate:{
type: String
},
showImageStandardBig:{
type: String
},
showImageStandardSmall:{
type: String
},
providerName:{
type: String
},
DealTypeID:{
type: Number
},
categoryID:{
type: Number
},
lat:{
type: Number
},
lon:{
type: Number
},
distance:{
type: Number
},
dealOriginalPrice:{
type: Number
},
dealPrice:{
type: Number
},
dealSavings:{
type: Number
},
dealDiscountPercent:{
type: Number
}
});
var LocalDeals=module.exports=mongoose.model('LocalDeals',localdealsSchema);
module.exports.saveLocalDeals=function(newLocalDeals, callback){
newLocalDeals.save(callback);
}
Never use setTimeout in this case !
writeFile return callback ! ^^
Simply put the readFile in the writeFile callback
jsonfile.writeFile(local_deals_file, body, {spaces: 2}, function(err) {
if(err){
// error case
}
else
{
jsonfile.readFile(local_deals_file, function(err, local_deals_obj) {
if(err){
// error case
}
else
{
// do something
}
});
}
});
But while reading the file ? You can simply parse the response body :
jsonfile.writeFile(local_deals_file, body, {spaces: 2}, function(err) {
if(err){
// error case
}
else
{
var obj = null;
try
{
obj = JSON.parse(body);
}
catch(err){
obj = null;
}
if(obj === null)
{
// error case
}
else
{
// insert in mongo
}
}
});
In the part where I am reading the file, I needed a setTimeout because it takes some time to write to the file. So here is my change:-
setTimeout(function(){
jsonfile.readFile(local_deals_file, function(err, local_deals_obj) {
//the code same as above
})
}, 1000); //waiting a second to read file

Issues with dynamic routing using meteor-autoform and iron:router

What I am trying to do is create a form with meteor-autoform that will redirect the user to a newly generated route on submit. My thought process is that I can take the submissions _id and use it for an iron:router parameter. What I have so far looks as followed:
Creation of Form
Submits = new Meteor.Collection('Submits');
Submits.allow({
insert: function(username, doc){
return !!username;
}
});
SubmitSchema = new SimpleSchema({
title: {
type: String,
label: "Title"
},
subject:{
type: String,
label: "Subject"
},
summary:{
type: String,
label: "Summary"
},
author:{
type: String,
label: "Author",
autoValue: function() {
return this.userId
},
autoform: {
type: "hidden"
}
},
createdAt: {
type: Date,
label: "Created At",
autoValue: function(){
return new Date()
},
autoform: {
type: "hidden"
}
}
});
Submits.attachSchema( SubmitSchema );
Routing
Router.route('/submit', {
layoutTemplate: 'submitLayout',
waitOn: function() { return Meteor.subscribe("Submits"); },
loadingTemplate: 'loading'
});
Router.route('/submit/:_id', {
name: 'formDisplay',
data: function() {
return Submits.findOne({this.params._id});
}
});
And then I just have the average publish and find calls. My issues are I'm not sure how to perform the redirect on submit, and I am not sure how to display the form results on the newly generated route.
Any help would be appreciated.
I was able to do it by adding an autoform.hook and changing my routing a bit.
Autoform hook:
AutoForm.addHooks('insertSubmit', {
onSuccess: function(doc) {
Router.go('formDisplay',{_id: this.docId});
}
})
Routing:
Router.route('/submit/:_id', {
name: 'submitLayout',
data: function() { return Products.findOne(this.params._id);}
});
I got this information from this post:
Route to the new data submitted by Meteor autoform using iron router?

Mongoose findOne callback not working

Mongoose findOne function call does nothing and I am in trouble again. Callback is not never returned...
schema.js file:
var schemaSizeGroup = new Schema({
sizeGroupId : {type: Number, required: true, index: true, index: { unique: true }}
,sizeGroupName : {type: String, required: true, trim: true, index: { unique: true }}
,sizeGroupValues : {type: String, required: true, trim: true }
,active : {type: Boolean, default: true }
}, { collection: 'sizegroup' }).index({sizeGroupId : 1});
module.exports ={
SizeGroup : mongoose.connection.model('SizeGroup', schemaSizeGroup),
}
index.js file:
findDocumentById : function(sGroupId, callback){
winston.info(" Trying to select!");
model.SizeGroup.findOne( {sizeGroupId : sGroupId} ,function(err, sGroup) {
winston.info(" Select done:");
winston.info(JSON.stringify(sGroup,null,2));
if(!err) {
if(!sGroup) {
callback(new Error(" No SizeObject Found for Id:" + sizeGroupId));
} else { callback(null, sGroup); }
}
else {
callback(err);
}
});
}
}
selectin data using mongo client returns correct data nicely:
db.sizegroup.find({sizeGroupId : 6});
When using mongoose.set('debug', true) output looks like:
Mongoose: sizegroup.findOne({ sizeGroupId: 6 }) { fields: undefined }
I have active mongoose connection, because all the previous insert statements have been successful.
Am I doing something wrong?
It was a callback problem in program flow. Clear case of pure stupidity....

getting an error - Caught error in Fine Uploader jQuery event handler: Object [object Object] has no method 'getUploads'

UPpated this question.
the issue I am having is trying to get the number of jobs submitted using the getUploads API.
when I call the function I am always getting 0 (zero).
Not sure what I did incorrect.
Matt
<script>
// Wait until the DOM is 'ready'
$(document).ready(function () {
var myUploader = $("#fine-uploader").fineUploader({
session: {
endpoint: 'imageStatus.cfm',
params : {transaction_id : <cfoutput>#client.transaction_id#</cfoutput>}
},
debug: true,
request: {
endpoint: 'upload.cfm',
params : {details : "<cfoutput>#client.wallfolder#|#client.DonatorID#|#client.wallid#|#client.transaction_id#|#client.clientid#</cfoutput>"}
},
validation: {
itemLimit: <cfoutput>#evaluate(client.numberofbricks*3)#</cfoutput>,
allowedExtensions: ["jpeg", "jpg", "gif" , "png"],
sizeLimit: 5000000 // 5 MiB
},
messages: {
tooManyItemsError: 'You can only add <cfoutput>#client.numberofbricks#</cfoutput> images'
},
deleteFile: {
enabled: true, // defaults to false
endpoint: 'upload_delete.cfm',
method: 'post',
params : {wallid : "<cfoutput>#client.wallid#</cfoutput>"}
},
retry: {
enableAuto: false
},
scaling: {
sendOriginal: true,
hideScaled: true,
sizes: [
{name: "THUMB_XX", maxSize: 113},
{name: "FULLIMAGE", maxSize: 450}
]
}
})
.on('allComplete', function(responseJSON) {
if (qq.status.UPLOAD_SUCCESSFUL == 'upload successful') {
//get uuid for message
if ($(this).fineUploader("getNetUploads") > 0){
$("#ContinueButton").show();
}else{
$("#ContinueButton").hide();
};
}
})//on
.on('sessionRequestComplete', function(event, id, fileName, responseJSON) {
if ($(this).fineUploader("getNetUploads") > 0){
$("#ContinueButton").show();
};
})//on
.on('deleteComplete', function(event, id, fileName, responseJSON) {
if ($(this).fineUploader("getNetUploads") == 0){
$("#ContinueButton").hide();
};
var submittedFileCount = myUploader.fineUploader("getUploads", {status: qq.status.SUBMITTED});
alert(submittedFileCount);
});//on
$('#ContinueButton').click(function() {
var submittedFileCount = $('#myUploader').fineUploader('getUploads').length
alert(submittedFileCount);
});
}); //close of top script - required
I'm guessing myUploader is the value returned by $("#uploader-container").fineUploader({...});, which is a jQuery object. In that case, you are trying to invoke getUploads on a jQuery object, which of course will not work.
If you want to make this call on the plug-in when using the jQuery wrapper, you'll need to do so like so:
myUploader.fineUploader("getUploads", {status: qq.status.SUBMITTED});
Here is an example of using getNetUploads within the onAllComplete handler with the jQuery uploader:
.on("allComplete", function(responseJSON) {
console.log("allComplete");
if ($(this).fineUploader("getNetUploads") > 0) {
console.log("#ContinueButton.show()");
//$("#ContinueButton").show();
}
else {
console.log("#ContinueButton.hide()");
//$("#ContinueButton").hide();
};
var submittedFileCount = $(this).fineUploader(
"getUploads", {
status: qq.status.SUBMITTED
});
alert(submittedFileCount);
})
The same syntax should apply to any callback handlers your code.
I figured it out. the issue was the filtering of the call. I had it as SUBMITTED and it should have been qq.status.UPLOAD_SUCCESSFUL

Categories

Resources