如何将数千个对象保存到Parse.com?

在我使用Parse的iOS应用程序中,一些用户需要在一个动作中保存数千个对象。 我已经试过遍历数据数组并逐个创build/保存对象,但是这导致对象保存到我的数据浏览器很慢。 每个对象只需要包含几个string,所以我不明白为什么要花费这么长时间来保存这些对象。

有更快的方式来保存成千上万的对象parsing?

编辑:

我一直使用[PFObject saveAllInBackground:array block:^(BOOL succeeded,NSError * error){}]; 但是….我刚刚尝试半成功的另一种方法是上传一个JSONstring作为PFFile(没有128k的限制),然后使用云代码来parsing它并创build必要的PFObjects。 我能够得到这个小批量的工作,但不幸的是云代码超时大量使用。 我反而select使用后台作业来执行parsing。 这在数据完全可用之前需要相当长的时间,但是可以处理大量的数据。 上传时间本身要快得多。 当使用3个string的1000个对象,每个上传大约.8秒,vs 23秒做一个全部保存在后台,5000个对象3个string,每个上传时间只有2.5秒。 除了更快的时间,你也获得进度更新。 根据使用情况,如果即时和快速上传非常重要,那么利用这种替代方法可能效果最佳,而使数据立即可用。

IOS代码:

NSMutableArray *array = [NSMutableArray array]; for (int i = 0; i<5; i++) { //subclass of PFObject Employee *employee = [Employee object]; employee.firstName = @"FName"; employee.lastName = @"LName"; employee.employeeID = @"fid54"; [array addObject:[employee dictionaryWithValuesForKeys:employee.allKeys]]; } //Seperate class only to store the PFFiles PFObject *testObject = [PFObject objectWithClassName:@"fileTestSave"]; testObject[@"testFile"] = [PFFile fileWithData:[NSJSONSerialization dataWithJSONObject:array options:0 error:nil]]; NSLog(@"started"); //**notice I am only saving the test object with the NSData from the JSONString** [testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (!error && succeeded) NSLog(@"succeeded"); else NSLog(@"error"); }]; 

编辑:而不是保存在可能导致超时问题的beforeSave或afterSave云代码,下面的后台作业可以随时运行。 它抓住“fileTestSave”表中的所有行,parsing这些行中的JSONstring,并将它们添加到“Person”表中。 一旦完成,它将从表中的行。 全部asynchronous!

 var _ = require('underscore.js'); Parse.Cloud.job("userMigration", function(request, status) { // Set up to modify user data Parse.Cloud.useMasterKey(); //Table called fileTestSave stores a PFFile called "testFile" which we will use an HTTPRequest to get the data. Is there a better way to get the data? //This PFFile stores a json string which contains relavent data to add to the "Person" table var testFileSave = Parse.Object.extend("fileTestSave"); var query = new Parse.Query(testFileSave); query.find().then(function(results) { //Generate an array of promises var promises = []; _.each(results, function(testFileSaveInstance){ //add promise to array promises.push(saveJsonPerson(testFileSaveInstance)); }); //only continue when all promises are complete return Parse.Promise.when(promises); }).then(function() { // Set the job's success status console.log("Migration Completed NOW"); status.success("Migration completed"); }, function(error) { // Set the job's error status status.error("Uh oh, something went wrong."); }); }); function saveJsonPerson(fileTestSave) { //Get the pffile testfile var testFile = fileTestSave.get("testFile"); //get the fileURL from the PFFile to generate the http request var fileURL = testFile["url"](); //return the promise from the httpRequest return Parse.Cloud.httpRequest({ method:"GET", url: fileURL }).then(function(httpResponse){ //return the promise from the parsing return parsehttpResponse(httpResponse,fileTestSave); }, function(error){ console.log("http response error"); } ); } function parsehttpResponse(httpResponse,fileTestSave) { var jsonArray = eval( '(' + httpResponse.text + ')' ); var saveArray =[]; //parse each person in the json string, and add them to the saveArray for bulk saving later. for (i in jsonArray) { var personExtend = Parse.Object.extend("Person"); var person = new personExtend(); person.set("classDiscriminator",jsonArray[i]["classDiscriminator"]); person.set("lastName",jsonArray[i]["lastName"]); person.set("firstName",jsonArray[i]["firstName"]); person.set("employeeID",jsonArray[i]["employeeID"]); saveArray.push(person); }; //return the promise from the saveAll(bulk save) return Parse.Object.saveAll( saveArray ).then(function(){ //return the promise from the destory return fileTestSave.destroy( ).then(function(){ },function(error){ console.log("error destroying"); } ); },function(error){ console.log("Error Saving"); } ); } 

作为参考超时的旧云代码:

 Parse.Cloud.afterSave("fileTestSave", function(request) { //When accessing PFFiles you don't get the actual data, there may be an easier way, but I just utitlized an HTTPRequest to get the data, and then continued parsing. var file = request.object.get("testFile"); var fileURL = file["url"](); console.log("URL:"+fileURL); Parse.Cloud.httpRequest({ method:"GET", url: fileURL, success: function(httpResponse) { var jsonArray = eval( '(' + httpResponse.text + ')' ); var saveArray =[]; for (i in jsonArray) { var personExtend = Parse.Object.extend("Person"); var person = new personExtend(); //May be a better way to parse JSON by using each key automatically, but I'm still new to JS, and Parse so I set each individually. person.set("classDiscriminator",array[i]["classDiscriminator"]); person.set("lastName",array[i]["lastName"]); person.set("firstName",array[i]["firstName"]); person.set("employeeID",array[i]["employeeID"]); saveArray.push(person); }; Parse.Object.saveAll(saveArray, { success: function(list) { // All the objects were saved. }, error: function(error) { // An error occurred while saving one of the objects. }, }); }, error: function(httpResponse) { console.log("http response error"); } }); }); 

另一种在后台上传数千个对象的方法,这也需要一些时间,但是可以resize以避免超时,因为数组以recursion方式保存在区块中。 我没有任何问题,节省10K +项目。 作为一个类别实现,只要一次input想要保存的对象数量,将它们连续保存到后台,并recursion保存,直到保存所有对象,还可以通过单独的块更新进度。

 // PFObject+addOns.h #import <Parse/Parse.h> @interface PFObject (addOns) +(void)saveAllInBackground:(NSArray *)array chunkSize:(int)chunkSize block:(PFBooleanResultBlock)block progressBlock:(PFProgressBlock)progressBlock; @end #import "PFObject+addOns.h" @interface PFObject (addOns_internal) +(void)saveAllInBackground:(NSArray *)array chunkSize:(int)chunkSize block:(PFBooleanResultBlock)block trigger:(void(^)())trigger; @end @implementation PFObject (addOns) +(void)saveAllInBackground:(NSArray *)array chunkSize:(int)chunkSize block:(PFBooleanResultBlock)block progressBlock:(PFProgressBlock)progressBlock { unsigned long numberOfCyclesRequired = array.count/chunkSize; __block unsigned long count = 0; [PFObject saveAllInBackground:array chunkSize:chunkSize block:block trigger:^() { count++; progressBlock((int)(100.0*count/numberOfCyclesRequired)); }]; } +(void)saveAllInBackground:(NSArray *)array chunkSize:(int)chunkSize block:(PFBooleanResultBlock)block trigger:(void(^)())trigger { NSRange range = NSMakeRange(0, array.count <= chunkSize ? array.count:chunkSize); NSArray *saveArray = [array subarrayWithRange:range]; NSArray *nextArray = nil; if (range.length<array.count) nextArray = [array subarrayWithRange:NSMakeRange(range.length, array.count-range.length)]; [PFObject saveAllInBackground:saveArray block:^(BOOL succeeded, NSError *error) { if(!error && succeeded && nextArray){ trigger(true); [PFObject saveAllInBackground:nextArray chunkSize:chunkSize block:block trigger:trigger]; } else { trigger(true); block(succeeded,error); } }]; } @end 

我想你应该可以做到这一点,将五个数量的保存过程发送到后台,所以可以把它分开,“线程”,就像苹果指的那样。

这里是苹果ios线程指南的链接。 我还没有使用它,但我很快就会需要它,因为我正在处理大量的数据库应用程序。

这是链接

https://developer.apple.com/library/mac/Documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html

如果你有一个对象数组,你可以使用saveAllInBackgroundWithBlock 。 这个方法接受一个PFObjects数组作为参数:

https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/saveAllInBackground:block

为了更快的处理,你可以使用parsing云代码,这只是一个JavaScript。 您可以创build一个函数,该函数将数据的数组作为参数,然后在函数中可以保存对象。 parsing云代码比本地代码具有更好的处理速度。

对于它的使用,你可以参考:

https://parse.com/docs/cloud_code_guide https://parse.com/docs/js_guide