I am working on a parse cloud code function which performs a query and filters the results afterwards. These are my first lines of code written in JavaScript, so I have no clue how to solve the following problem.
The problem is, that my filter predicate needs some elements stored in the someArray variable. All elements of someArray aren't fetched yet. But fetching an an Parse.Object is a asynchronous call, so I have no chance to return trueor false expected by filter().
How can I solve this problem?
Or I am misinterpreting the fact, that when I don't fetch the arrayElement
console.log("arrayElement with name: ".concat(typeof(arrayElement), arrayElement.get("name")));
prints
arrayElement with name:objectundefiend
although I know that Object represented by arrayElment has a name-column which is always defined?
//The cloud code
Parse.Cloud.define("search", function(request, response) {
var query = new Parse.Query("Location");
query.withinKilometers("gps", request.params.searchLocation, request.params.searchRadius / 1000);
query.find({
success: function(results) {
// results is an array of Parse.Object.
var locations = results.filter(function(location) {
console.log("Location with name: ".concat(location.get("name")));
var someArray = location.get("someArray");
if (someArray instanceof Array) {
console.log("The array of this location has ".concat(someArray.length, " elements."));
someArray.forEach(function(arrayElement) {
arrayElement.fetch().then(
function(fetchedArrayElement) {
// the object was fetched successfully.
console.log("arrayElement with name: ".concat(typeof(fetchedArrayElement), fetchedArrayElement.get("name")));
if (menuItem) {};
return true;
},
function(error) {
// the fetch failed.
console.log("fetch failed");
});
});
}
});
response.success(locations);
},
error: function(error) {
// error is an instance of Parse.Error.
response.error(error);
}
});
});
some useful links:
Parse JavaScript SDK API
Parse Cloud Code Guide
If I'm reading this right, you've got an array column of Parse Objects. You should use the include method on your query so that the objects are fetched in the initial query.
query.include('someArray');
I don't think you'll want to use a filter, and should take a look at the Promises documentation here: https://parse.com/docs/js_guide#promises
You're right that since it's asynchronous, you need to wait for everything to be done before calling response.success which doesn't currently happen in the code. Promises and defining your own async functions that use Promises is a great way of chaining functionality and waiting for groups of functions to complete before going forward.
If all you wanted to do what include that column, the whole thing should just be:
Parse.Cloud.define("search", function(request, response) {
var query = new Parse.Query("Location");
query.include('someArray');
query.withinKilometers("gps", request.params.searchLocation, request.params.searchRadius / 1000);
query.find().then(function(results) {
response.success(locations);
}, function(error) {
response.error(error);
});
});
Related
I have those two functions where i call "http" from "Count" the "http" return promise. I want to use the return value of "http" in "Count". What I receive now is Undefined !!!
What I'm missing ?
Count Function :
Parse.Cloud.define('count', function(request, response) {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
query.find({
success: function(results) {
Parse.Cloud.run('http', {params : results}).then(
function(httpResponse) {
console.log('httpResponse is : ' + httpResponse.length);
response.success('Done !');
}, function(error) {
console.error(error);
});
},
error: function(error) {
response.error(error);
}
});
});
http Function :
Parse.Cloud.define('http', function(request, response) {
var query = new Parse.Query(Parse.Installation);
.
.
.
}
Relying on calling your own functions through an external interface is not a very good practice.
Now that you've realized you're going to need the same code for a different purpose, you should take the time to refactor your code such that you don't need to call the 'http' handler through Parse.Cloud.run():
function doHttp(params) {
// original implementation here
}
Parse.Cloud.define('http', function(request, response) {
doHttp(request.params)
.then(response.success)
.fail(response.error);
}
Parse.Cloud.define('count', function(request, response)) {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
query.find()
.then(doHttp) // doHttp will receive the results from `query` as its parameter
.then(function(httpResponses) {
// httpResponses is an array-like object as per the other question:
httpResponses = Array.prototype.slice.call(httpResponses);
httpResponses.forEach(function (response) {
console.log('httpResponse is : ' + response.length);
});
}).fail(response.error);
}
I've taken a look at the other question and as far as the implementation of count goes, I believe you're missing the point that 'http' is returning arguments, which is only an Array-like object.
This should be okay if Parse.Cloud.run runs your function on another virtual machine, but this kind of weird behaviour is another symptom of not refactoring and reusing your code through an external call (an HTTP request inside their infrastructure with JSON passing! It might greatly reduce performance and count against your requests/second quota). If Parse instead does some magic to call your function directly as if it was defined on the same environment, you're going to have problems with it not being an actual Array.
You should modify that function to return a proper array if possible. Parse CloudCode has a version of the Underscore library:
// on http
var _ = require('underscore');
Parse.Promise.when(promises).then(function() {
var results = _.toArray(arguments) // equivalent to Array.prototype.slice above
response.success(results);
}
I think what you're asking is how to use an externally callable cloud function as a step in a bigger cloud procedure. Here's how to do it: (#paolobueno has it essentially correct, with only a couple mistakes in the details).
First, let's convert that 'http' cloud function to a regular JS function. All we need to do is factor out the request and response objects. (#paolobueno has a very good idea to use underscorejs, but I won't here because its another new thing to learn).
// for each object passed in objects, make an http request
// return a promise to complete all of these requests
function makeRequestsWithObjects(objects) {
// underscorejs map() function would make this an almost one-liner
var promises = [];
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
promises.push(makeRequestWithObject(object));
}
return Parse.Promise.when(promises);
};
// return a promise to do just one http request
function makeRequestWithObject(object) {
var url = 'http://185.xxxxxxx'+ object +'&languagePath=en';
return Parse.Cloud.httpRequest({ url:url });
}
It looks like you want the updated cloud function -- rather than use params from the client -- to first make a query and use the results of that query as parameters to the http calling function. Here's how to do that. (Again, using #paolobueno's EXCELLENT practice of factoring into promise-returning functions...)
// return a promise to find MyS instances
function findMyS() {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
return query.find();
}
Now we have everything needed to make a clear, simple public function...
Parse.Cloud.define('count', function(request, response) {
findMyS().then(function(objects) {
return makeRequestsWithObjects(objects);
}).then(function(result) {
response.success(result);
} , function(error) {
response.error(error);
});
});
I am doing a query from Parse.com through a Javascript function as below.
function doFunction () {
var query = new Parse.Query("english");
query.find({
success: function(results) {
alert (results)
},
error: function(error) {
// error is an instance of Parse.Error.
}
});
}
while I can see the length of query response by alerting results.length, I cant get what is inside the results. alert(results) shows only [object Object],[object Object]...
What is the response format, is it a JSON or an array? how can I get the values?
Thanks
Use console.log in your code:
function doFunction () {
var query = new Parse.Query("english");
query.find({
success: function(results) {
console.log(results);
},
error: function(error) {
// error is an instance of Parse.Error.
}
});
}
And then see in Developer Tools(F12) -> Console, what is being returned as response.
in javascript you can check objects with a console.log.
console.log is very flexible. It can take n-parameters and every type.
So you can mix up Strings and Objects seperated with a comma.
var myTestObject = { testString: "Hello World!" };
console.log("This is my test Object:", myTestObject);
//Output: This is my test Object: Object {testString: "Hello World!"}
While I agree with the above answers that console.log() is a good way to print out your objects, there are better ways to do this. Besides, I recommend that you always use an alert() function in your success and error blocks while in development.
This is because it is possible to have bugs in your code where requests are made to Parse.com an infinite number of times. Since Parse.com will charge you money when you make a certain number of requests per second, this could cause you to accidentally be charged by Parse.com against your wishes. If you are using console.log() and accidentally do this, you won't be aware of the bug unless you have the console open. However, if you use alert(), you will be prompted each time a success or failure call is made, and thus you will be able to prevent this issue.
Furthermore, you don't HAVE to use console.log() to see your data. You can simply call properties on the returned object (which is in JSON format), using the following:
query.find({
success: function(results) {
alert(results.get("propertyName"));
},
// error blocks and result of logic here
While object.id gives the object id I needed to use object.get('ephrase') to get other parameters.
function doFunction () {
var query = new Parse.Query("english");
query.find({
success: function(results) {
alert("Successfully retrieved " + results.length + " scores.");
// Do something with the returned Parse.Object values
for (var i = 0; i < results.length; i++) {
var object = results[i];
alert(object.id + ' - ' + object.get('ephrase'));
}
},
error: function(error) {
// error is an instance of Parse.Error.
}
});
}
Here is my code, it loops through forEach and prints out '1' but never returns from object.save() & never prints out 2, 3 or anything else. I have tried a bunch of other ways but none seems to work.
Note: response.succes(or error) is not being called anywhere, the code is definitely waiting for object.save() to be completed.
var promise = new Parse.Promise();
var query = new Parse.Query("SomeClass");
query.find().then(function(results) {
var promises = [];
results.forEach(function(object) {
object.set("SomeColumnName", true);
console.log('1');
promises.push(object.save(null, {
success: function(result) {
alert('2');
return ;
},
error: function(result, error) {
alert('3');
return ;
}
}));
});
Parse.Promise.when(promises).then(function() {
console.log('inside resolve');
promise.resolve();
}, function() {
console.log('inside reject');
promise.reject();
});
});
return promise;
You're on the right track, but you should take advantage of the fact that most of the sdk functions create and return promises for you. With those, you can substantially simplify the code:
// very handy utility library that provides _.each among many other things
// www.underscorejs.org
var _ = require('underscore');
// answer a promise to modify all instances of SomeClass
function changeSomeClass() {
var query = new Parse.Query("SomeClass");
// if there are more than 100 rows, set query.limit up to 1k
return query.find().then(function(results) { // find returns a promise
_.each(results, function(result) {
result.set("SomeColumnName", true);
});
return Parse.Object.saveAll(results); // and saveAll returns a promise
});
}
Wrap it in a cloud function and call success/error like this:
Parse.Cloud.define("changeSomeClass", function(request, response) {
changeSomeClass().then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
You can only have one Parse request happening at a time for each object. If multiple requests are sent, all but the first are ignored. You're probably trying to do this while other threads are making Parse requests for those objects. I know that if you save an object, it also saves it's child objects, so you could be hitting a problem with that. Make sure you do as much as you can in background threads with completion blocks, or use saveEventually / fetchEventually where possible.
I am trying to read results from JavaScript azure mobile service and I want to return items with a specific values. I am using getTable(‘’).where.(‘’).read(‘’) to check if one of the returned json value match a specific pattern as shown in this post:
Here is my client side script:
function getSP() {
var spUser = client.getTable("User").where(function (contains) {
return this.extraname.indexOf(contains) > 0;
}, "Eyad").read().done(function (results) {
alert(JSON.stringify(results));
}, function (err) {
alert("Error: " + err);
});}
And the request URL generated from client:
https://XYZ.azure-mobile.net/tables/User?$filter=(indexof(extraname,'Eyad') gt 0)
But the code above return an empty [] object form the service, while performing the same operation without the where() check clearly returns my intended value:
What am I doing wrong? How can I return the row(s) where the returned "extraname" contains the substring "Eyad"?
NOTE: I also have a custom read script on the service side, and you can see the "extraname" value is hardcoded for testing purposes:
function read(query, user, request) {
request.execute({
success: function(results) {
var now = new Date();
results.forEach(function(item) {
console.log(item.userjsondata);
item.extraname = "Eyad";
});
request.respond(); //Writes the response
}
});
}
If you're generating a new column dynamically, then there's no way to - directly - use a $filter clause to filter the results based on that value. Notice that you can write arbitrary code to calculate that value, so the mobile service runtime has no way to know what value it will end up generating, and cannot perform a filter based on that.
There are a couple of workarounds for your solution: if possible, you can send a where clause with the same expression that you use to generate the value. In some cases the service will accept that expressions in the $filter clause as well. That has the drawback that you'll end up with the same logic in two different places, and there's a big chance that you'll change one and end up forgetting to change the other.
Another alternative is to pass the parameter for which you want to query the generated property not in the $filter parameter (i.e., don't use the where function, but pass the parameters inside the read call. Those parameters will be passed to the read script in the request.parameters object, and you can add your logic to filter based on that value after you're done reading from the database (see below).
Client:
var spUser = client.getTable("User").read({mustContain: "Eyad").done(function (results) {
alert(JSON.stringify(results));
}, function (err) {
alert("Error: " + err);
});}
Service:
function read(query, user, request) {
var mustContain = request.parameters.mustContain;
request.execute({
success: function(results) {
var now = new Date();
var filteredResults = [];
results.forEach(function(item) {
console.log(item.userjsondata);
item.extraname = "Eyad";
if (item.extraname.indexOf(mustContain) >= 0) {
filteredResults.push(item);
}
});
request.respond(200, filteredResults); //Writes the response
}
});
}
change your comparison from this:
return this.extraname.indexOf(contains) > 0;
to this:
return this.extraname.indexOf(contains) >= 0;
A matched string can be (in your case, always) found in the first index. Therefore you have to use greater than OR equal to operator to handle that case.
This fiddle shows more or less what I am trying to do.
I am querying some results from a Parse.com database. The results are returned in the success callback.
Can anyone tell me the best way to work with them in the mainView() function? I would like to keep all queries separate from the logic of how they are displayed. I have tried quite a few different approaches, but haven't been able to get it working.
Simply store a reference to this outside the callback.
var userInterface = {
newQuery: function() {
var that=this; //store a reference to "this"
Query = Parse.Object.extend("Test");
query = new Parse.Query(Query);
query.descending("createdAt");
query.equalTo("column1", "a");
query.find({
success:function(results){
that.mainView(results); //that points to the userInterface object
},
error:function(error){
}
});
},
mainView: function(res){
console.log(res);
},
init: function() {
this.newQuery();
}
};
userInterface.init();
http://jsfiddle.net/4XsLq/8/