问题 来自私有区域的CKQuery仅返回CloudKit中的前100个CKRecords


查询结果是否有任何限制 Cloudkit 私人默认区域?我不知道为什么我只使用以下查询收到前100条记录:

let p = NSPredicate(format: "(type == 'entered') AND (timestamp >= %@) AND (timestamp <= %@)", from, to)
let q = CKQuery(recordType: self.beaconRecordType, predicate: p)
q.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
self.privateDatabase?.performQuery(q, inZoneWithID: nil, completionHandler: { results, error in

    //count = 100
    println(results.count)

}

好的。正如Edwin在答案中提到的,解决方案是使用CKQueryOperation获取初始数据块,然后使用completionBlock中的“cursor”来触发另一个操作。这是一个例子:

UPDATE

func fetchBeacons(from:NSDate, to:NSDate) {

    let p = NSPredicate(value: true)
    let q = CKQuery(recordType: self.beaconRecordType, predicate: p)

    let queryOperation = CKQueryOperation(query: q)

    queryOperation.recordFetchedBlock = fetchedARecord

    queryOperation.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor!, error : NSError!) in

        if cursor != nil {
            println("there is more data to fetch")
            let newOperation = CKQueryOperation(cursor: cursor)
            newOperation.recordFetchedBlock = self!.fetchedARecord
            newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
            self!.privateDatabase?.addOperation(newOperation)
        }

    }

    privateDatabase?.addOperation(queryOperation)
}

var i = 0
func fetchedARecord (record: CKRecord!) {
    println("\(NSDate().timeIntervalSinceReferenceDate*1000) \(++i)")
}

10995
2018-04-12 18:25


起源

好。我找到了这个。 stackoverflow.com/a/27135836/893771 。一段代码可以帮助很多。 - CppChase
这个答案 类似的问题也显示了如何保持强烈的参考 queryOperation 为了防止你的提取结束不完整。 - Imanou Petit
此解决方案将导致操作仅运行3次!,请使用此代码: stackoverflow.com/a/31664231/530884 - Shaybc


答案:


100是标准查询的默认限制。这个数额不固定。它可能会根据iCloud的总负载而有所不同。如果你想影响那个数量,那么你需要使用CKQueryOperation并设置resultsLimit如下:         operation.resultsLimit = CKQueryOperationMaximumResults; CKQueryOperationMaximumResults是默认值,并将其限制为100(大部分时间)。不要将该值设置得太高。如果需要更多记录,请使用queryCompletionBlock的光标继续读取更多记录。


8
2018-04-12 18:35



大埃德温。我联系你回答以前:)。你有任何示例代码如何做'光标'?这样可以节省一些时间。 - CppChase
对不起,我没有准备好的样品。但这并不困难。你可以像这样开始一个新的queryOperation:var operation = CKQueryOperation(cursor:cursor) - Edwin Vermeer
我接受了你的答案并用示例代码更新了我的问题。 Tnx老兄。 - CppChase
很好,但在Objective C中有这样的例子吗? - user3069232
感谢@EdwinVermeer的精彩讨论并提供示例代码@CPPChase!对于“newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock”这一行,它感觉不是正确的做法,但我找不到更好的方法 - 想法? - hyouuu


我在我的项目中使用此代码从记录类型中获取所有记录,它在目标c中。我根据需要使用“Entry”键。

+ (void)fetchRecordsWithType:(NSString *)recordType
           completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {

    NSPredicate *truePredicate = [NSPredicate predicateWithValue:YES];

    CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType
                                               predicate:truePredicate];

    CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
    queryOperation.desiredKeys = @[@"Entry"];

    NSMutableArray *results = [NSMutableArray new];

    queryOperation.recordFetchedBlock = ^(CKRecord *record) {

        [results addObject:record]; };

    queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {

        [self retrieveNextBatchOfQueryFromCursor:cursor
                                         results:results
                                           error:error
                               completionHandler:completionHandler]; };

    [[self CloudKitContainer].privateCloudDatabase addOperation:queryOperation]; }


+ (void)retrieveNextBatchOfQueryFromCursor:(CKQueryCursor *)cursor
                                   results:(NSMutableArray *)results
                                     error:(NSError *)error
                         completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {

    // CloudKit apparently has query limit

    if (cursor != nil
        && !error) {

        CKQueryOperation *nextOperation = [[CKQueryOperation alloc] initWithCursor:cursor];

        nextOperation.recordFetchedBlock = ^(CKRecord *record) {

            [results addObject:record]; };

        nextOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {

            [self retrieveNextBatchOfQueryFromCursor:cursor
                                             results:results
                                               error:error
                                   completionHandler:completionHandler]; };

        [[self CloudKitContainer].privateCloudDatabase addOperation:nextOperation]; }

    else {

        dispatch_async(dispatch_get_main_queue(), ^(void){

            completionHandler(results, error); }); }}

1
2018-01-03 23:17





在具有完成处理程序的函数内运行它的另一种方法是在获取所有记录之前不会停止。这可以由应用程序中的不同视图控制器重用。

询问

func cloudKitLoadRecords(result: (objects: [CKRecord]?, error: NSError?) -> Void){

    // predicate
    var predicate = NSPredicate(value: true)

    // query
    let cloudKitQuery = CKQuery(recordType: "ClassName", predicate: predicate)

    // records to store
    var records = [CKRecord]()

    //operation basis
    let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase

    // recurrent operations function
    var recurrentOperationsCounter = 101
    func recurrentOperations(cursor: CKQueryCursor?){
        let recurrentOperation = CKQueryOperation(cursor: cursor!)
        recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
            print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter++)")
            records.append(record)
        }
        recurrentOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
            if ((error) != nil)
            {
                print("-> cloudKitLoadRecords - recurrentOperations - error - \(error)")
                result(objects: nil, error: error)
            }
            else
            {
                if cursor != nil
                {
                    print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
                    recurrentOperations(cursor!)
                }
                else
                {
                    print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
                    result(objects: records, error: nil)
                }
            }
        }
        publicDatabase.addOperation(recurrentOperation)
    }

    // initial operation
    var initialOperationCounter = 1
    let initialOperation = CKQueryOperation(query: cloudKitQuery)
    initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
        print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter++)")
        records.append(record)
    }
    initialOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
        if ((error) != nil)
        {
            print("-> cloudKitLoadRecords - initialOperation - error - \(error)")
            result(objects: nil, error: error)
        }
        else
        {
            if cursor != nil
            {
                print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
                recurrentOperations(cursor!)
            }
            else
            {
                print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
                result(objects: records, error: nil)
            }
        }
    }
    publicDatabase.addOperation(initialOperation)
}

用法

cloudKitLoadRecords() { (queryObjects, error) -> Void in
            dispatch_async(dispatch_get_main_queue()) {
                if error != nil
                {
                    // handle error
                }
                else
                {
                    // clean objects array if you need to
                    self.objects.removeAll()

                    if queryObjects!.count == 0
                    {
                        // do nothing
                    }
                    else
                    {   
                        // attach found objects to your object array
                        self.objects = queryObjects!
                    }
                }
            }
        }

1
2018-04-12 04:17



功能卓越,谢谢! - William T.


Swift最简单的例子:

func fetchServices(completion: ErrorHandler? = nil) {

    var records = [CKRecord]()

    let query = CKQuery(recordType: "Service", predicate: NSPredicate(value: true))
    let queryOperation = CKQueryOperation(query: query)

    queryOperation.recordFetchedBlock = { record in
        records.append(record)
    }

    queryOperation.queryCompletionBlock = { cursor, error in
        self.fetchServices(with: cursor, error: error, records: records, completion: completion)
    }

    database.add(queryOperation)
}

private func fetchServices(with cursor: CKQueryCursor?, error: Swift.Error?, records: [CKRecord], completion: ErrorHandler? = nil) {

    var currentRecords = records

    if let cursor = cursor, error == nil {

        let queryOperation = CKQueryOperation(cursor: cursor)
        queryOperation.recordFetchedBlock = { record in
            currentRecords.append(record)
        }

        queryOperation.queryCompletionBlock = { cursor, error in
            self.fetchServices(with: cursor, error: error, records: currentRecords, completion: completion)
        }

        database.add(queryOperation)

    } else {
        parseAndSaveServices(with: records, completion: completion)
    }
}

0
2018-01-10 13:39