8

Is there any way to limit the amount of results that are returned in a CKQuery?

In SQL, it is possible to run a query like SELECT * FROM Posts LIMIT 10,15. Is there anything like the last part of the query, LIMIT 10,15 in CloudKit?

For example, I would like to load the first 5 results, then, once the user scrolls down, I would like to load the next 5 results, and so on. In SQL, it would be LIMIT 0,5, then LIMIT 6,10, and so on.

One thing that would work is to use a for loop, but it would be very intensive, as I would have to select all of the values from iCloud, and then loop through them to figure out which 5 to select, and I'm anticipating there to be a lot of different posts in the database, so I would like to only load the ones that are needed.

I'm looking for something like this:

var limit: NSLimitDescriptor = NSLimitDescriptor(5, 10)
query.limit = limit

CKContainer.defaultContainer().publicCloudDatabase.addOperation(CKQueryOperation(query: query)
//fetch values and return them
Jojodmo
  • 23,357
  • 13
  • 65
  • 107

1 Answers1

10

You submit your CKQuery to a CKQueryOperation. The CKQueryOperation has concepts of cursor and of resultsLimit; they will allow you to bundle your query results. As described in the documentation:

To perform a new search:

1) Initialize a CKQueryOperation object with a CKQuery object containing the search criteria and sorting information for the records you want.

2) Assign a block to the queryCompletionBlock property so that you can process the results and execute the operation.

If the search yields many records, the operation object may deliver a portion of the total results to your blocks immediately, along with a cursor for obtaining the remaining records. If a cursor is provided, use it to initialize and execute a separate CKQueryOperation object when you are ready to process the next batch of results.

3) Optionally configure the return results by specifying values for the resultsLimit and desiredKeys properties.

4) Pass the query operation object to the addOperation: method of the target database to execute the operation against that database.

So it looks like:

var q   = CKQuery(/* ... */)
var qop = CKQueryOperation (query: q)

qop.resultsLimit = 5
qop.queryCompletionBlock = { (c:CKQueryCursor!, e:NSError!) -> Void in
  if nil != c {
    // there is more to do; create another op
    var newQop = CKQueryOperation (cursor: c!)
    newQop.resultsLimit = qop.resultsLimit
    newQop.queryCompletionBlock = qop.queryCompletionBlock

    // Hang on to it, if we must
    qop = newQop

    // submit
    ....addOperation(qop)
  }
}

....addOperation(qop)
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • I've read about something like this, but can I do something like select the first 5 values, and then, with a new operation, skip the first 5 values and select the next 5? – Jojodmo Jan 01 '15 at 06:20
  • I've updated the answer (give me five minutes). It is a little unrealistic because it fetches five, and then immediately five more. You'd probably want to fetch the next five after the user scrolls or some such. – GoZoner Jan 01 '15 at 06:22
  • Ok, thanks, so I'll just call this once, and then add the new operation when I need to load the next 5? – Jojodmo Jan 01 '15 at 06:33
  • Right - making sure that the 'new operation' uses the `CKQueryCursor` from the completion handler of the 'old operation' – GoZoner Jan 01 '15 at 06:35
  • `qop = newQop` is crucial. I couldn't make it work without it: `qop` would become `nil` at some point in the process and the fetch would be incomplete... – Imanou Petit Aug 18 '15 at 22:35
  • setting resultLimit = 1 has no effect for me ! Anyone else experience this ? – Duncan Groenewald Nov 22 '16 at 00:47
  • So, basically, this above code create query with limit returning result and currsor of the last query. Its different with SQL with offset above. It works with sequential fetching but doesn't work with pagination mechanism? – TomSawyer Apr 05 '17 at 03:37