在我使用Parse的iOS应用程序中,一些用户需要在一个操作中保存数千个对象。我已经尝试迭代数据数组并逐个创建/保存对象,但这会导致对象保存到我的数据浏览器的速度非常慢。每个对象只需要包含几个字符串,所以我不明白为什么它需要这么长时间来保存这些对象。
是否有更快的方法将数千个对象保存到Parse?
答案 0 :(得分:8)
编辑:
我一直使用[PFObject saveAllInBackground:数组块:^(BOOL成功,NSError *错误){}];但....我刚刚尝试半成功的另一种方法是上传一个Json字符串作为PFFile(没有128k限制),然后使用云代码解析它并创建必要的PFObjects。我能够以小批量工作,但不幸的是,当使用大量数据时,云代码超时。我改为选择使用后台作业来执行解析。这在数据完全可用之前需要相当长的时间,但可以处理大量数据。上传时间本身要快得多。当使用具有3个字符串的1000个对象时,每个上载大约为0.8秒,而在后台执行全部保存则为23秒,5000个对象3个字符串,每个上传时间仅为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 Cloud Code中,这可能导致超时问题。它抓取&#34; fileTestSave&#34;中的所有行。 table,解析这些行中的JSON字符串,并将它们添加到&#34; Person&#34;表。一旦完成,它将从表中行。所有异步!
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");
}
);
}
作为参考超时的旧Cloud Code:
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");
}
});
});
答案 1 :(得分:2)
另一种在后台上传数千个对象的方法,这需要一些时间,但可以调整大小以避免超时,因为数组以递归方式保存在块中。我保存10k +物品没问题。作为一个类别实现,只需要一次输入您想要保存的对象数量,它会将它们串行保存在后台,并递归保存,直到保存所有对象,它还通过单独的块进行更新。
// 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
答案 2 :(得分:1)
我认为你应该能够将数量为5的保存过程发送到后台,所以说吧,#34;线程&#34;它,正如苹果所指的那样。
这是指向apple ios线程指南的链接。 我还没有使用它,但我很快就会需要它,因为我正在开发一个庞大的数据库应用程序。
这里是链接
答案 3 :(得分:1)
如果您有一组对象,则可以使用saveAllInBackgroundWithBlock
。此方法将PFObjects数组作为其参数:
https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/saveAllInBackground:block:
答案 4 :(得分:0)
为了加快处理速度,你可以使用解析云代码,这只是一个javascript。您可以创建一个函数,该函数将数据数组作为参数,然后在函数中,您可以保存对象。 解析云代码具有比原生代码更好的处理速度。
如需使用,您可以参考:
https://parse.com/docs/cloud_code_guide https://parse.com/docs/js_guide