CKError: Query filter exceeds the limit of values: 250 for container

CKError: Query filter exceeds the limit of values: 250 for container

我想从 public 数据库中提取大约 500 "Visit" 条记录。 CloudKit 一次只给你 100 条记录,所以我只使用如下所示的 CKQueryCursor 来获取我想要的所有记录。

func fetchVisits(_ cursor: CKQueryCursor? = nil) {
    print("fetchVisits \(cursor)")
    var operation: CKQueryOperation!
    if let cursor = cursor {
        operation = CKQueryOperation(cursor: cursor)
    } else {
        let query = CKQuery(recordType: "Visit", predicate: NSPredicate(format: "Client IN %@ AND ANY Students IN %@", visitClients, visitStudents))
        operation = CKQueryOperation(query: query)
    }
    operation.recordFetchedBlock = {
        (record) in
        totalVisits.append(record)
    }
    operation.queryCompletionBlock = {
        (cursor, error) in
        if let error = error {
            //handle error
        } else if let cursor = cursor {
            self.fetchVisits(cursor)
        } else {
            //all done!
        }
    }
    CKContainer.default().publicCloudDatabase.add(operation)
}

我这样调用函数:

fetchVisits()

效果很好,它获得了我需要的所有访问。控制台日志

fetchVisits nil
fetchVisits Optional(<CKQueryCursor: 0x174228140; id=4bb7887c326fc719, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x17422a320; id=f67fb25669486da9, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x174228380; id=7e87eb8b7cfe1a74, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x17422cc80; id=e77e47ef2b29c8a4, zone=(null)>)

但现在的问题是我想在按下按钮时刷新,但现在却出现此错误:

"Service Unavailable" (6/2022); "Request failed with http status code 503"; 30.0 秒后重试

这是不言自明的,我想我通过请求区区 500 条记录让服务器不堪重负?所以我等待 30 秒并再次调用该函数,现在我得到了这个错误。

"Limit Exceeded" (27/2023); server message = "Query filter exceeds the limit of values: 250 for container

出于某种原因,我无法再次 运行 该功能。如果我重新启动应用程序,它会再次正常工作,但只是第一次。这个问题似乎特定于任何 table that returns CKQueryCursor。我还有其他 table 我正在点击的记录少于 100 条(因此光标为零),我可以多次拉动这些记录而不会出现任何问题。

在查询操作完成之前,您正在向 iCloud 请求更多 CKRecords。

...
operation.queryCompletionBlock = {
    (cursor, error) in
    if let error = error {
        //handle error
    } else if let cursor = cursor {
        self.fetchVisits(cursor)
    } else {
        //all done!
    }
}
...

函数 self.fetchVisits(cursor) 调用在完成块内完成,这意味着您在当前操作完成之前请求更多记录。

可能的解决方案是使用一个包含 CKQueryCursor 的闭包 (completionHandler),当用户需要更多记录(滚动 table 或其他)时,您再次调用一个 self.fetchVisits 传递 cursos在您关闭时收到。

好的,所以我以前见过这个,我认为问题是 CloudKit 服务器中的错误。根据我的经验,它与复杂的查询有关。

如果您尝试将谓词更改为:

NSPredicate(value: true)

或者甚至通过删除 ANY 部分来简化现有的,这可能足以修复它。

编辑:我想现在我找到了原因:

虽然我之前的回答(如下)是正确的,但它不适用于这个问题。
至此,我自己遇到了问题中提到的两个服务器错误,并调查了情况。

我的设置如下:
我想根据谓词中指定的条件下载 iCloud 记录的子集:

let doNotDownloadIfOlderFormat = "NOT " + iCloudRecordKeyName + " IN %@"
let doNotDownloadIfOlderPredicate = NSPredicate.init(format: doNotDownloadIfOlderFormat, doNotDownloadIfOlder)  

这里,doNotDownloadIfOlder是一组自定义对象。

在某些测试用例中,它们的数量超过 300。我怀疑这可能是错误消息的原因 Query filter exceeds the limit of values: 250 for container .

我因此修改了我的代码:

let doNotDownloadIfOlderFormat = "NOT " + iCloudRecordKeyName + " IN %@"
let limit = min(doNotDownloadIfOlder.count, max)
let shortened = Set(Array(doNotDownloadIfOlder)[0 ..< limit])
let doNotDownloadIfOlderPredicate = NSPredicate.init(format: doNotDownloadIfOlderFormat, shortened)  

我将 max 设置为低于 300 的测试值。
这是结果:

  • max <= 140: 没有错误
  • 141 <= 最大值 <= 250:"Service Unavailable" (6/2022); "Request failed with http status code 503"; 30.0 秒后重试>
  • 最大 > 250:"Limit Exceeded" (27/2023); server message = "Query filter exceeds the limit of values: 250 for container

显然,查询可能存在未记录的服务器限制。奇怪的数字 140 可能与我的自定义对象的大小有关。

上一个回答:

docs 说:

The server can change its limits at any time, but the following are general guidelines:

  • 400 items (records or shares) per operation
  • 2 MB per request (not counting asset sizes)

If your app receives CKError.Code.limitExceeded, it must split the operation in half and try both requests again.