My data model consist of 3 objects, two of them (the children) are linked to the parent using a pointer.
MyModel is the parent that has 2 properties: colors and goal. Both are pointers to other Objects.
When i delete the parent I want the children to be deleted as well, the caveat is that the pointer might be nil, so I'd need to check if there is something there before attempting deletion.
[I'm new to Javascript so maybe that's also part of the problem]
Parse.Cloud.beforeDelete("MyModel", function(request) {
if request.has(request.object.colors) {
color = request.object.colors;
Parse.Object.destroyAll(color, {
success: function() {},
error: function(error) {
console.error("Error deleting related color " + error.code + ": " + error.message);
}
});
}
if request.has(request.object.goal) {
goal = request.object.goal;
Parse.Object.destroyAll(goal, {
success: function() {},
error: function(error) {
console.error("Error deleting related goal " + error.code + ": " + error.message);
}
});
}
});
Lets break this into smaller functions and correct a couple problems in the OP code along the way. It's very helpful to reduce things to smaller, promise-returning functions keep the code modular and the concurrency manageable.
EDIT
Generally, it's preferred to use pointers to relate objects. Here's a general purpose function to delete an object related via pointer:
function deleteRelatedPointer(myModel, pointerName) {
var pointer = myModel.get(pointerName);
if (pointer) {
return pointer.fetch().then(function(relatedObject) {
return relatedObject.destroy();
});
} else {
return null;
}
}
Some authors relate objects via a string column containing the id of the related object. Here's the equivalent function to delete an object related by id:
function deleteRelatedId(myModel, columnName, relatedClass) {
var relatedId = myModel.get(columnName);
if (relatedId) {
var query = new Parse.Query(relatedClass);
return query.get(relatedId).then(function(relatedObject) {
return relatedObject.destroy();
});
} else {
return null;
}
}
Now, the beforeDelete method is easy to write and understand. (Assuming the relationships via pointers):
Parse.Cloud.beforeDelete("MyModel", function(request, response) {
var myModel = request.object;
deleteRelatedPointer(myModel, "colors").then(function() {
return deleteRelatedPointer(myModel , "goal");
}).then(function() {
response.success();
}, function(error) {
response.error(error);
});
}
The other important thing to notice is that the before hook takes a response object and is required to invoke success / error on that object after the related tasks are complete.
All of this hinges on promises, which are necessary and immensely useful. Read about parse's implementation of them here.
Related
I am using mssql in node.js executing an SP:
var fnGetMapping = function(results) {
new sql.Request(db.sqlConnection)
.input('myParam', sql.VarChar(60), 'myValue')
.execute('usp_MySP', fnProcessUUIDData);
};
Now my fnProcessUUIDData looks like this:
var fnProcessUUIDData = function(err, recordsets) {
if (err) {
console.log("Error calling MS SQL: " + err.message);
} else {
if (recordsets[0].length > 0) {
} else {
};
}
};
What I need now is to pass one of my own parameter into fnProcessUUIDData.
I read some articles that this is possible but I couldn't get it working on my case, would appreciate some comments if what I want to do is possible.
You can use apply to pass some extra arguments without loosing the context.
new sql.Request(db.sqlConnection)
.input('myParam', sql.VarChar(60), 'myValue')
.execute('usp_MySP', function(...args) {
fnProcessUUIDData.apply(this, args.concat(some, extra, parameters));
});
var fnProcessUUIDData = function(err, recordsets, some, extra, parameters) {
console.log(this, some, extra, parameters);
};
Using apply overcomplicates things because fnProcessUUIDData is a plain old function and not a method (it doesn't use this internally). All you need to do is wrap it in another function:
new sql.Request(db.sqlConnection)
.input('myParam', sql.VarChar(60), 'myValue')
.execute('usp_MySP', function(err, recordsets) {
fnProcessUUIDData(err, recordsets, additional_parameter);
});
so i use one js file to load multiple html and js files whenever they are needed. I have a working code for plenty modules. In the example below you can see the first two modules. All of them look exactly the same. Now i want to "outsource" recurring code into a function with parameters so that the code-amount overall gets minimized. Since i have never done something like this before i could need some help (i am learning js at the moment). I would realy appreciate some help.
//first module
if (moduleID === "placeone") {
var isLoaded = 0;
if (isLoaded) {
console.log("file already loaded");
returnValue = new PlaceOneModule(id, moduleInitialData);
}
$("#placeone").load("html/modules/PlaceOneModule.html", function (response, status, xhr) {
console.log("PlaceOneModule.html" + " " + status);
$.getScript("js/modules/PlaceOneModule.js").done(function () {
console.log("PlaceOneModule.js geladen");
isLoaded = 1;
returnValue = new PlaceOneModule(id, moduleInitialData);
}).fail(function () {
console.log("PlaceOneModule.js nicht geladen");
});
});
}
//second module
if (moduleID === "placetwo") {
var isLoaded = 0;
if (isLoaded) {
console.log("file already loaded");
returnValue = new PlaceTwoModule(id, moduleInitialData);
}
$("#placetwo").load("html/modules/PlaceTwoModule.html", function (response, status, xhr) {
console.log("PlaceTwoModule.html" + " " + status);
$.getScript("js/modules/PlaceTwoModule.js").done(function () {
console.log("PlaceTwoModule.js geladen");
isLoaded = 1;
returnValue = new PlaceTwoModule(id, moduleInitialData);
}).fail(function () {
console.log("PlaceTwoModule.js nicht geladen");
});
});
}
The question is rather complex to answer, as there are many things to account for.
var cache = {};
function module(name, target, done) {
if (!(name in cache)) {
return $(target).load('html/modules/' + name + '.html', function(response, status, xhr) {
console.log(name + '.html ' + status);
$.getScript('js/modules/' + name + '.js')
.done(function() {
console.log(name + '.js geladen');
cache[name] = window[name];
done(null, cache[name]);
})
.fail(function() {
var message = name + '.js nicht geladen';
cache[name] = function() {
console.error(message);
};
done(message);
});
});
}
setTimeout(function() {
done(null, cache[name]);
}, 0);
}
I'll try to explain my train of thought behind this:
var cache = {} - you will need something to keep track of each individual module
function module(name, target, done) {
name would be the base name of your module, e.g. PlaceTwoModule, this was already used consistently across the html and js files and the js function name
target would be the selector where the html file should be loaded
as one of the actions you take requires async operation, the entire functionality needs to become async, I introduce a callback (done) argument
if (!(name in cache)) - if the module is not yet cached, it requires some fetching, so the load is triggered first thing
once the load completes, it will fire the $.getScript
if the $.getScript works out, the name will be assumed to be in window and a reference is stored in the cache variable, after that, the done callback is invoked (with the function as second argument).
if the $.getScript didn't work out, we add a function to the cache, which does nothing more than telling you it will not work, after that, the done callback is invoked (with an error as first argument).
if the name did exist in the cache, we will be calling the done callback right after we exit the module function
So, how to use this?
It now boils down to calling the module function
module('#placeone', 'PlaceOneModule', function(error, PlaceModule) {
if (error) throw new Error(error);
var instance = new PlaceModule(id, initial);
// ...
}
I have used the common function(error, value) {..} signature for the callback function, which always has the error as first argument (allowing for other arguments to be added and made optional).
There are some caveats/assumptions worth mentioning:
I have not provided a failsafe for preventing multiple loads of the same module (so it is the same as in your example) if earlier calls to module are still loading
no matter what target you invoke module with, it will only load 'once' (well, see the previous line ;-) )
I assume the loaded modules are in the global (window) scope in order to keep the example simple, keep in mind to not 'pollute the global scope'
This has become a rather elaborate answer, I hope I explained every step involved sufficiently.
Something like this possibly:
var modules = [];
modules.push({
js: 'PlaceOneModule',
id: 'placeone'
});
modules.push({
js: 'PlaceTwoModule',
id: 'placetwo'
});
var module = modules.filter(function(m) {
return m.id === moduleID;
});
if (module) {
var isLoaded = 0;
if (!!window[module.js]) {
console.log("file already loaded");
returnValue = window[module.js];
}
$("#" + module.id).load("html/modules/" + module.js + ".html", function(response, status, xhr) {
$.getScript("js/modules/" + module.js + ".js").done(function() {
returnValue = new window[module.js](id, moduleInitialData);
}).fail(function() {
console.log("PlaceOneModule.js nicht geladen");
});
});
}
This is the first time I've used Parse, I'm loving it so far, reading and writing data is working.
I now want to do something seemingly simple, but in the context of Parse seems like a pain to implement and my google-fu is failing me.
When I find the items like this:
var query = new Parse.Query(CommentClass);
query.limit(2);
query.descending('createdAt');
query.find({
success: function(object) {
callback({
comments: object
});
},
error: function(object, error) {
console.log('there was an error');
}
});
I want to know what indexes of the returned items are in the total list. If my list has 5 items, this will return the last 2 created, but there's no way - without another request knowing the number of items in the list.
There is not an easy way to do it in Parse. One way is to keep a global index in a separate object. In every new comment you need to get this global index, increment it, and put it into the comment. But it can get messy in case of comment deletions. Here is an example assuming no comment deletion:
SequenceForComment.js
// A class called SequenceForComment. Only one row.
// Only one integer property called 'sequence'.
// You can create the row by using the Parse dashboard in the beginning.
var SequenceForComment = Parse.Object.extend("SequenceForComment");
function getSequenceForComment(callback) {
var query = new Parse.Query(SequenceForComment);
return query.first().then(function (object) {
// https://parse.com/docs/js/api/classes/Parse.Object.html#methods_increment
//Increment is atomic.
object.increment('sequence');
return object.save();
}).then(function (object) {
callback(object.get('sequence'));
}, function (error) {
console.log(error);
callback(undefined);
});
}
module.exports = {
getSequenceForComment: getSequenceForComment
};
main.js
var SequenceModule = require("cloud/SequenceForComment.js");
Parse.Cloud.beforeSave("Comment", function(request, response) {
var comment = request.object;
// First time this comment will be saved.
// https://parse.com/docs/js/api/classes/Parse.Object.html#methods_isNew
if (comment.isNew()) {
// Get a sequence/index for the new comment
SequenceModule.getSequenceForComment(function(sequence) {
if (sequence) {
comment.set("sequence", sequence);
response.success();
} else {
response.error('Could not get a sequence.');
}
});
} else { // Not a new save, already has an index
response.success();
}
});
I have a problem with performance on a relatively simple parse.com routine:
We have two classes and we want to make a cross product of them. One class contains a single object of boilerplate for the new objects (description etc.) the other class contains "large" sets (only 1000s of objects) of variable data (name, geopoint etc). Each new object also has some of its own columns not just data from the parents.
To do this, we do a query on the second class and perform an each operation. In each callback, we create and populate our new object and give it pointers to its parents. We call save() on each object (with some then() clauses for retries and error handling) and push the returned promise into an array. Finally we return status inside a when() promise on that array of save promises.
We originally created all the objects and then performed a saveall on them but couldn't get good enough error handling out of it - so we moved to when() with chains of promises with retries.
Trouble is, it's slow. It doesn't feel like the type of thing a nosql database should be slow at so we're blaming our design.
What's the best practice for cloning a bunch of objects from one class to another? or is it possible to get better results from saveAll failures?
My current code looks like this:
var doMakeALoadOfObjects = function(aJob,aCaller) {
Parse.Cloud.useMasterKey();
return aJob.save().then(function(aJob) {
theNumTasksPerLoc = aJob.get("numberOfTasksPerLocation");
if (theNumTasksPerLoc < 1) {
theNumTasksPerLoc = 1;
}
var publicJob = aJob.get("publicJob");
return publicJob.fetch();
}).then(function(publicJob) {
var locationList = aJob.get("locationList");
return locationList.fetch();
}).then(function(locationList) {
publicReadACL = new Parse.ACL();
publicReadACL.setPublicReadAccess(true);
publicReadACL.setRoleReadAccess("Admin",true);
publicReadACL.setRoleWriteAccess("Admin",true);
// Can't create a promise chain inside the loop so use this function.
var taskSaver = function(task) {
return task.save.then(function success(){
numTasksMade++;
},
function errorHandler(theError) {
numTimeOuts++;
numTaskCreateFails++;
logger.log("FAIL: failed to make a task for job " + aJob.get("referenceString") + " Error: " + JSON.stringify(theError));
});
};
var taskSaverWithRetry = function(task) {
return task.save().then(function() {
numTasksMade++;
return Parse.Promise.as();
}, function(error) {
logger.log("makeJobLive: FAIL saving task. Will try again. " + JSON.stringify(error));
numTimeOuts++;
return task.save().then(function() {
numTasksMade++;
return Parse.Promise.as();
}, function(error) {
numTimeOuts++;
numTaskCreateFails++;
logger.log("makeJobLive: FAIL saving task. Give up. " + JSON.stringify(error));
return Parse.Promise.as();
});
})
}
for (var j = 0; j < theNumTasksPerLoc; j++) {
var Task = Parse.Object.extend("Task");
var task = new Task();
task.set("column",stuff);
// Can't create a promise chain in the loop so use the function above.
taskSaverArray.push(taskSaverWithRetry(task));
}
return Parse.Promise.when(taskSaverArray);
}).then(function() {
}).then(function() {
// happy happy
},function(error){
// we never land here.
});
}
..."looks" like because I've deleted a lot of the object creation code and some housekeeping we do at the same time. I may have deleted some variable definitions too so I doubt this would run as is.
I have two classes, Groups and memberships. When a group is made, I'd like its corresponding membership to be made as well. I attempt to do so like this:
Parse.Cloud.beforeSave("Group", function(request, response) {
var name = request.object.get("name");
if (!name) {
response.error("A Group must have a name");
} else {
if (!(/^\w+$/i.test(name))){
response.error("Only letters and numbers, please.");
}
var query = new Parse.Query("Group");
query.equalTo("name", name);
query.first({
success: function(object) {
if (object) {
response.error("A group with this name exists.");
} else {
createMembershipForGroup(request.object, Parse.User);
response.success();
}
},
error: function(error) {
response.error("Could not validate Uniqueness");
}
});
}
});
createMembershipForGroup = function(group, user) {
var MembershipClass = Parse.Object.extend("Membership");
var membership = new MembershipClass();
membership.set("group", group);
membership.set("user", user);
membership.save(null,{
success:function(membership) {
response.success(membership);
},
error:function(error) {
response.error(error);
}
});
}
I receive an error back, saying the following:
E2014-05-06T21:40:10.740Z] v30: before_save triggered for Group for
user xgzDnZ9h0A Input:
{"original":null,"update":{"admin":{"objectId":"xgzDnZ9h0A","className":"_User","__type":"Pointer"},"name":"test","public":false}}
Result: undefined
Not too sure what the error is. How could I go about debugging code like this so I can isolate the issue easily?
Let beforeSave handle only the validation, and move your membership code to afterSave. The problem is probably that you're trying to store a relation to an object that is not yet saved (as you're in beforeSave, the object does not yet have an objectId)
problem is you are trying to call an async function and leaves before wait to complete it.
createMembershipForGroup(request.object, Parse.User);//Calling async func
response.success();//Leaves beforeSave function
You should write your createMembershipForGroup function inside success response of your query and return your response.success() method in here
membership.save(null,{
success:function(membership) {
response.success(membership); // this should be response of beforeSave
},
error:function(error) {
response.error(error);
}
});
I will post working code if this not helps.