I am trying to import my class (2500 records) from Parse.com into an Algolia index. There is a limit of 100 records by default which obviously is not working for me. Even if I use query.limit = 1000;
How can the below code be used to import my whole class?
Parse.Cloud.define("createIndex", function(request, response) {
var algoliasearch = require('cloud/algoliasearch.parse.js');
var client = algoliasearch('9PsdfsdWVU7', '3b24e897bfb4esdfsdfsdf209e25c28');
var index = client.initIndex('exercises');
console.log("running");
var objectsToIndex = [];
//Create a new query for Contacts
var query = new Parse.Query('Exercises');
query.limit = 1000;
// Find all items
query.find({
success: function(exercises) {
// prepare objects to index from contacts
objectsToIndex = exercises.map(function(exercise) {
// convert to regular key/value JavaScript object
exercise = exercise.toJSON();
// Specify Algolia's objectID with the Parse.Object unique ID
exercise.objectID = exercise.objectId;
return exercise;
});
// Add or update new objects
index.saveObjects(objectsToIndex, function(err, content) {
if (err) {
throw err;
}
console.log('Parse<>Algolia import done');
});
response.success("worked");
},
error: function(err) {
response.error("failed");
throw err;
}
});
});
Parse.com has a find limit, and all other limitations when you want to "get all the objects", you can find more information here: https://parse.com/docs/js/guide#performance-limits-and-other-considerations
For your current issue you could do this:
Parse.Cloud.define("createIndex", function(request, response) {
var algoliasearch = require('cloud/algoliasearch.parse.js');
var client = algoliasearch('9PsdfsdWVU7', '3b24e897bfb4esdfsdfsdf209e25c28');
var index = client.initIndex('exercises');
console.log("running");
//Create a new query for Contacts
var Exercises = Parse.Object.extend('Exercises');
var query = new Parse.Query(Exercises);
var skip = -1000;
var limit = 1000;
saveItems();
function saveItems() {
skip += limit;
query.skip(skip + limit);
query.limit(limit);
query.find({success: sucess, error: error});
}
function sucess(exercices) {
if (exercices.length === 0) {
response.success("finished");
return;
}
exercises = exercises.map(function(exercise) {
// convert to regular key/value JavaScript object
exercise = exercise.toJSON();
// Specify Algolia's objectID with the Parse.Object unique ID
exercise.objectID = exercise.objectId;
return exercise;
});
index.saveObjects(exercises, function(err) {
if (err) {
throw err;
}
console.log('Parse<>Algolia import done');
response.success("worked");
saveItems();
});
}
function error() {
response.error("failed");
throw err;
}
});
Related
I am trying to use parse.com promises to retrieve job data as well as user relation data associated with the job. I have a function that returns promises but not job data. How do I get the job & employee information from the returned promises?
Logically I want to:
1) Query Parse to get array of jobs
2) For each job, query Parse again to get the employee relation information
3) Create local object that contains job & employee details
4) Add each job object to local array
5) Load table with array of objects once all information has been retrieved from Parse
I can do steps 1-4 but I can't figure out how to wait until all information has been retrieved from Parse to refresh the local table.
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
// Create a trivial resolved promise as a base case.
var promise = Parse.Promise.as();
_.each(results, function(result) {
// For each item, extend the promise with a function to add it to the job array
promise = promise.then(function() {
// Return a promise that will be resolved when the job details have been added to the array
var object = result;
promises.push(getEmployeeName(object));
allJobDataArray = promises;
});
});
return Parse.Promise.when(promises);
}).then(function() {
// Every job has been retrieved
console.log("All items have been returned. Refresh table...");
console.log(allJobDataArray);
});
}
The function that does the relational query to get the users associated with the job
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
console.log(employeeNameArray);
}
},
error: function(error){
response.error(error);
}
});
}
Update
It is now working thanks to #eduardo
I have a public array to hold the job objects.
var jobObjectsArray = [];
In the getEmployeeName function I am creating the job objects and adding them to that array
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
_.each(results, function(result) {
promises.push(getEmployeeName(result));
});
return Parse.Promise.when(promises);
}).then(function(allJobDataArray) {
// allJobDataArray should be actually an Array of Array
console.log(jobObjectsArray);
refreshTable();
});
}
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return new Promise(
function(resolve, reject) {
rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
var objAllJobs = new Object();
objAllJobs["jobId"] = jobObject.id;
objAllJobs["location"] = jobObject.get("location");
objAllJobs["startTime"] = jobObject.get("startTime");
objAllJobs["employee"] = employeeNameArray;
jobObjectsArray.push(objAllJobs);
}
console.log(employeeNameArray);
resolve(employeeNameArray);
},
error: function(error){
reject(error);
}
});
}
);
There was a few incorrect uses of the promise concept. I will go through them, but first here is the final code:
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
_.each(results, function(result) {
promises.push(getEmployeeName(result));
});
return Parse.Promise.when(promises);
}).then(function(allJobDataArray) {
// allJobDataArray should be actually an Array of Array
console.dir(allJobDataArray);
console.log(allJobDataArray[0]);
});
}
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
}
console.log(employeeNameArray);
return employeeNameArray;
},
error: function(error){
response.error(error);
}
});
}
"Parse.Promise.as()" should be used only if you have a value that you want to return as a promise. Something like:
Parse.Promise.as("my value").then(function(foo) {
console.log(foo) // "my value"
});
So if your "getEmployeeName" function is returning a promise, which means that this "rQuery.query().find" returns a promise, you don't have to create a new promise or use the "Parse.Promise.as()", it is already a promise and you can push it to the promises array.
Another problem was that you were not return anything in the "getEmployeeName" method callback. Take a look into my version, I'm returning "employeeNameArray".
My version will only work if this "rQuery.query().find" method returns a promise. If that is not the case, you can create a new promise using its callbacks like this:
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return new Promise(
function(resolve, reject) {
rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
}
console.log(employeeNameArray);
resolve(employeeNameArray);
},
error: function(error){
reject(error);
}
});
}
);
}
Please notice this "new Promise()" depends on the browser support of Promise, I don't know if Parse has an equivalent. Anyways you can use it with a polyfill that implements the necessary code if the browser has no support.
More about standard promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Polyfill: https://github.com/jakearchibald/es6-promise/
Hope this helps.
I'm in need to fetch over 1000 elements from a Class.
So I tried following advices on this topic, but something is not working correctly, and already spent the whole day to find a solution.
Parse.Cloud.define("getFollow", function(request, response) {
var following = [];
var user = request.params.user;
user.fetch().then(function(result) {
if (!result.get('following')) {
following = getFollowing({
'user': user
});
}
}).then(function() {
response.success(following);
}, function(error) {
response.error(error);
});
});
function getFollowing(request) {
var count = 0;
var skip = request.skip || 0;
var limit = 1000;
var following = request.following || [];
var Follow = Parse.Object.extend('follow');
var query = new Parse.Query(Follow);
query.limit(limit);
query.ascending('objectId');
query.skip(skip * limit);
query.equalTo('followers', request.user);
query.find().then(function(results) {
skip+= 1;
count = results.length;
/* I can't see any DEBUG, seems nothing is queried */
console.log('[DEBUG] Check <count>: ' + count);
return Parse.Promise.when(results.map(function(result) {
following.push(result.get('followed'));
}));
}).then(function() {
if(count >= limit) {
following = getFollowingUsers({
'user': request.user,
'skip': skip,
'following': following
});
}
}).then(function() {
return following;
}, function(error) {
return error;
});
}
I tried many variant of this code, trying to return the very first result of the query, rather than a collection. I also tried to remove all contraints, but even so, my query seems not to be run.
I also tried to use a Cloud.code function to make this recursively using only Parse.Cloud, but if I do that, I'm having a message Too many recursive calls into Cloud Code
What did I do wrong with this logic ?
I want to get a batch of User objects using Cloud Code. And before collection of objects will send to client they have to take a unique number.
Now it's looking like this
Parse.Cloud.define("getUsers", function(request, response)
{
var query = new Parse.Query(Parse.User);
var mode = parseInt(request.params.mode);
var username = request.params.username;
var skip = parseInt(request.params.skip);
var limit = parseInt(request.params.limit);
if(mode==1)
{
query.notEqualTo("fbLogged",true)
.descending("score")
.notEqualTo("username",username)
.skip(skip)
.limit(limit);
query.find({
success: function(objects)
{
var i = 0;
objects.forEach(function(item)
{
item["rank"]=skip+i; //setting a unique number (position based on score)
});
response.success(objects);
},
error: function(error)
{
response.error(error);
}
});
}
});
And how I use it on client side...
void Start () {
IDictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("username", "477698883");
dict.Add("skip", "300");
dict.Add("limit", "50");
dict.Add("mode", "1");
ParseCloud.CallFunctionAsync<IEnumerable<object>>("getUsers", dict).ContinueWith(t =>
{
if(t.IsCanceled || t.IsFaulted)
{
foreach (var e in t.Exception.InnerExceptions)
Debug.LogError(e.Message);
}
else
{
var r = t.Result;
List<ParseUser> users = new List<ParseUser>();
foreach(var o in r)
{
try {
ParseObject pu = (ParseObject)o;
foreach (var key in pu.Keys)
Debug.Log(key + " = " + pu[key]);
}
catch(Exception e)
{
Debug.LogError(e.Message);
}
break;
}
}
});
}
As you see I just display first of received objects.
And it gives me this data.
But where is the "rank" field?
I just found solution. Each ParseObject which will send to Client by response.success() have to be saved on Parse before sent.
Now my code looks like this and it works
Parse.Cloud.define("getUsers", function(request, response)
{
var query = new Parse.Query(Parse.User);
var mode = parseInt(request.params.mode);
var username = request.params.username;
var skip = parseInt(request.params.skip);
var limit = parseInt(request.params.limit);
if(mode==1)
{
query.notEqualTo("fbLogged",true)
.descending("score")
.notEqualTo("username",username)
.skip(skip)
.limit(limit);
query.find({
success: function(objects)
{
for(var i = 0; i<objects.length; i++)
{
objects[i].set("rank", skip+i);
objects[i].save();
}
response.success(objects);
},
error: function(error)
{
response.error(error);
}
});
}
});
I'm saving some objects into tables on my Parse Data. But I need to add a constraint or make sure that the data i'm trying to insert is unique. I'm using something like the following code. But i want to guarantee that the eventId (that I'm getting from facebook API) is unique in my tables, so i don't have any redundant information. What is the best way to make it work?
var Event = Parse.Object.extend("Event");
var event = new Event();
event.set("eventId", id);
event.set("eventName", name);
event.save(null, {
success: function(event) {
console.log('New object created with objectId: ' + event.eventId);
},
error: function(event, error) {
console.log('Failed to create new object, with error code: ' + error.message);
}
});
Update:
I'm calling it inside a httpRequest. The following is pretty much what I have and I cant figure out just how to call a beforeSave inside it.
Parse.Cloud.define("hello", function(request, response) {
var query = new Parse.Query("Location");
query.find({
success: function(results) {
console.log(results);
var totalResults = results.length;
var completedResults = 0;
var completion = function() {
response.success("Finished");
};
for (var i = 0; i < totalResults; ++i){
locationId = results[i].get("locationFbId");
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.2/'+locationId+'/events?access_token='+accessToken,
success: function(httpResponse) {
console.log(httpResponse.data);
console.log("dsa"+locationId);
for (var key in httpResponse.data) {
var obj = httpResponse.data[key];
for (var prop in obj) {
var eventObj = obj[prop];
if (typeof(eventObj) === 'object' && eventObj.hasOwnProperty("id")) {
var FbEvent = Parse.Object.extend("FbEvent");
var fbEvent = new FbEvent();
fbEvent.set("startDate",eventObj["start_time"]);
fbEvent.set("locationFbId", locationId);
fbEvent.set("fbEventId", eventObj["id"]);
fbEvent.set("fbEventName", eventObj["name"]);
Parse.Cloud.beforeSave("FbEvent", function(request, response) {
var query = new Parse.Query("FbEvent");
query.equalTo("fbEventId", request.params.fbEventId);
query.count({
success: function(number) {
if(number>0){
response.error("Event not unique");
} else {
response.success();
}
},
error: function(error) {
response.error(error);
}
});
});
}
}
}
completedResults++;
if (completedResults == totalResults) {
completion();
}
},
error:function(httpResponse){
completedResults++;
if (completedResults == totalResults)
response.error("Failed to login");
}
});
}
},
error: function() {
response.error("Failed on getting locationId");
}
});
});
So this is occurring in Cloud Code correct? (Im assuming since this is Javascript)
What you could do is create a function that occurs before each "Event" object is saved and run a query to make sure that the event is unique (query based off of "eventId" key, not objectId since the id comes from Facebook). If the event is unique, return response.success(), otherwise return response.error("Event not unique")
EX:
Parse.Cloud.beforeSave("Event", function(request, response) {
if(request.object.dirty("eventId")){
var query = var new Parse.Query("Event");
query.equalTo("eventId", request.object.eventId);
query.count({
success: function(number) {
if(number>0){
response.error("Event not unique");
} else {
response.success();
}
},
error: function(error) {
response.error(error);
}
});
} else {
response.success();
}
});
Parse.Cloud.define("hello", function(request, response) {
var query = new Parse.Query("Location");
query.find({
success: function(results) {
console.log(results);
var totalResults = results.length;
var completedResults = 0;
var completion = function() {
response.success("Finished");
};
for (var i = 0; i < totalResults; ++i){
locationId = results[i].get("locationFbId");
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.2/'+locationId+'/events?access_token='+accessToken,
success: function(httpResponse) {
console.log(httpResponse.data);
console.log("dsa"+locationId);
for (var key in httpResponse.data) {
var obj = httpResponse.data[key];
for (var prop in obj) {
var eventObj = obj[prop];
if (typeof(eventObj) === 'object' && eventObj.hasOwnProperty("id")) {
var FbEvent = Parse.Object.extend("FbEvent");
var fbEvent = new FbEvent();
fbEvent.set("startDate",eventObj["start_time"]);
fbEvent.set("locationFbId", locationId);
fbEvent.set("fbEventId", eventObj["id"]);
fbEvent.set("fbEventName", eventObj["name"]);
// Our beforeSave function is automatically called here when we save it (this will happen every time we save, so we could even upgrade our method as shown in its definition above)
fbEvent.save(null, {
success: function(event) {
console.log('New object created with objectId: ' + event.eventId);
},
error: function(event, error) {
console.log('Failed to create new object, with error code: ' + error.message);
}
});
}
}
}
completedResults++;
if (completedResults == totalResults) {
completion();
}
},
error:function(httpResponse){
completedResults++;
if (completedResults == totalResults)
response.error("Failed to login");
}
});
}
},
error: function() {
response.error("Failed on getting locationId");
}
});
});
This can also be accomplished before ever calling the save by querying and only saving if the query returns with a number == 0.
Summary: For those joining later, what we are doing here is checking to see if an object is unique (this time based on key eventId, but we could use any key) by overriding Parse's beforeSave function. This does mean that when we save our objects (for the first time) we need to be extra sure we have logic to handle the error that the object is not unique. Otherwise this could break the user experience (you should have error handling that doesn't break the user experience anyway though).
I have 2 tables in parse
Memories and Locations with a relation One-To-One, but the pointer is in Location table ( Pointer )
What i need is to read all memories ten by ten with skip and limit and for each memory to atach a property with location object
By now i have this:
var _ = require('underscore.js');
Parse.Cloud.define("feed", function(request, response) {
var memories = Parse.Object.extend("Memories");
var memoriesQuery = new Parse.Query(memories);
memoriesQuery.skip(0);//request.params.skip);
memoriesQuery.limit(10);//request.params.limit);
memoriesQuery.descending("createdAt");
memoriesQuery.include("group");
var parsedResults = [];
memoriesQuery.find().then(function(memories) {
var promise = Parse.Promise.as();
_.each(memories, function(memory) {
promise = promise.then(function() {
var locations = Parse.Object.extend("Locations");
var locationsQuery = new Parse.Query(locations);
locationsQuery.equalTo("memory", memory);
var subPromise = Parse.Promise();
locationsQuery.first().then(function(location) {
memory["location"] = location;
console.log(JSON.stringify(memory) + " ........ " + memory["location"]);
console.log("=============");
parsedResults.push(memory);
subPromise.resolve(memory);
});
return subPromise ;
});
console.log("-----------");
console.log("Promise:" +promise.toString());
});
return promise;
}).then(function(){
response.success(parsedResults);
});
});
I have no clue what to do.. More than 10 hours of tries.
I appreciate any help!
Finally solved!
Maybe it will help somebody.
Solution:
var _ = require('underscore.js');
var memoriesResult = [];
Parse.Cloud.define("feed", function(request, response) {
var promises = [];
var promise = findMemories();
promise.then(function(memories){
console.log("success promise!!");
_.each(memories, function (memory) {
console.log("each");
promises.push(findLocation(memory));
});
return Parse.Promise.when(promises);
}).then(function(){
console.log("Finish");
response.success(memoriesResult);
}, function(error){
console.error("Promise Error: " + error.message);
response.error(error);
});
});
function findMemories(){
console.log("Enter findMemories");
var memories = Parse.Object.extend("Memories");
var memoriesQuery = new Parse.Query(memories);
memoriesQuery.skip(0);//request.params.skip);
memoriesQuery.limit(10);//request.params.limit);
memoriesQuery.descending("createdAt");
memoriesQuery.include("group");
var promise = new Parse.Promise();
memoriesQuery.find().then(function(memories) {
console.log("Memories found!");
promise.resolve(memories);
}, function(error) {
promise.reject(error);
});
return promise;
}
function findLocation(memory) {
console.log("Enter findLocation");
var locations = Parse.Object.extend("Locations");
var locationsQuery = new Parse.Query(locations);
locationsQuery.equalTo("memory", memory);
var promise = new Parse.Promise();
locationsQuery.first().then(function(location) {
console.log("Location found");
memoriesResult.push({"memory": memory, "location" : location});
promise.resolve(memory);
}, function(error) {
promise.reject(error);
});
return promise;
}
After doing some experiments, I've come up with the following requirements and solution:
You want to page through Memories items a page at a time, sorted by their createdAt
You want the Locations related to each memory
You are limited by the fact that the relationship is Locations->Memories
Your first step is to define the query for memories:
var memoryQuery = new Parse.Query('Memories');
memoryQuery.skip(request.params.skip);
memoryQuery.skip(request.params.limit);
memoryQuery.descending('createdAt');
You can now use this query to limit the Locations objects returned. If your links are all setup correctly you will get back 10 records:
var locationQuery = new Parse.Query('Locations');
// limit to only the page of Memory items requested
locationQuery.matchesQuery('memory', memoryQuery);
// include the Memory pointer's data
locationQuery.include('memory');
var memoriesResult = [];
locationQuery.find().then(function(locations) {
var result = _.map(locations, function(location) {
memory: location.get('memory'),
location: location
});
response.success(result);
}, function(error) {
response.error(error);
});
The only issue with the above is that I'm not sure on the order of the returned records, so you might want to re-sort them before returning them. That is extremely simple though with the underscore library.
This will result in 2 queries no matter the page size.