Class

CKQuery

A CKQuery object manages the criteria to apply when searching for records in a database. You create a query object as the first step in the search process. The query object stores the search parameters, including the type of records to search, the match criteria (predicate) to apply, and the sort parameters to apply to the results. The second step is to use the query object to initialize a CKQueryOperation object, which you then execute to generate the results.

Overview

Always designate a record type and predicate when you create a query object. The record type narrows the scope of the search to one type of record, and the predicate defines the conditions for which records of that type are considered a match. Predicates usually compare one or more fields of a record to constant values, but you can create predicates that return all records of a given type or perform more nuanced searches.

Because the record type and predicate cannot be changed later, you can use the same CKQuery object to initialize multiple CKQueryOperation objects, each of which targets a different database or zone.

Building Your Predicates

An NSPredicate object defines the logical conditions for determining whether a record is a match for a query. The CKQuery class supports only a subset of the predicate behaviors offered by the full NSPredicate class.

Predicate Rules for Query Objects

The predicates you create for your query objects must follow these rules:

  • Predicates are based on a format string. You cannot use value or block-based predicates.

  • Predicates use only the operators listed in Table 2.

  • Predicates operate only on fields containing the following types of data:

  • Key names used in predicates correspond to fields in the currently evaluated record. Key names may include the names of the record’s metadata properties such as "creationDate” or any data fields you added to the record. You cannot use key paths to specify fields in related records.

  • Predicates support the following variable substitution strings:

    • Use %@ for value objects such as strings, numbers, and dates.

    • Use %K for the name of a field. This substitution variable indicates that the substituted string should be used to look up a field name.

  • With one exception, the CONTAINS operator can be used only to test list membership. The exception is when you use it to perform full-text searches in conjunction with the self key path. The self key path causes the server to look in searchable string-based fields for the specified token string. For example, a predicate string of @"self contains 'blue'" searches for the word “blue” in all fields marked for inclusion in full-text searches. You cannot use the self key path to search in fields whose type is not a string.

  • The ANY and SOME aggregate operators may be combined with the IN and CONTAINS operators to perform list membership tests.

  • The distanceToLocation:fromLocation: operator function performs a radius-based location comparison and that comparison must determine whether the location value is inside the circular area you provide. You cannot use it to search for locations outside of the specified circular area. Locations are indexed at a resolution of no less than 10 km.

  • The ALL aggregate operator is not supported.

  • The NOT compound operator is not supported in the following cases:

    • You cannot use it to negate an AND compound predicate.

    • You cannot use it in tokenized queries such as self CONTAINS 'value'.

    • You cannot use it with the distanceToLocation:fromLocation: function.

    • You cannot use it in BETWEEN queries.

Supported Predicate Operators

Table 2 lists the operators you may use in predicates associated with a CKQuery object.

Table 2

Supported predicate operators

Operation

Supported operators

Basic comparisons

=, ==

>=, =>

<=, =<

<

>

!=, <>

BETWEEN

Boolean value predicates

TRUEPREDICATE

FALSEPREDICATE

Basic compound predicates

AND, &&

NOT

String comparisons

BEGINSWITH

Aggregate Operations

IN

CONTAINS

Functions

distanceToLocation:fromLocation:

now

tokenize:using:

Specifying an unsupported operator or data type in your query’s predicate results in an error when you execute the query. For more information about creating predicate objects, see Predicate Programming Guide.

Sample Predicate Format Strings

To match records that link to a different record whose ID you know, create a predicate that matches a field containing a reference object as shown in Listing 1. In the example, the employee field of the record contains a CKReference object that points to another record. When the query executes, a match occurs when the ID in the locally created CKReference object is the same ID found in the specified field of the record.

Listing 1

Matching the ID of a record

CKReference* recordToMatch = [[CKReference alloc] initWithRecordID:employeeID action:CKReferenceActionNone];
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"employee == %@", recordToMatch];

To match the contents of a field to a specific value, use a predicate similar to the ones shown in Listing 2. All of the listed predicates generate the same set of results, which in the example means that the favoriteColors field contains the value red. The value in the field must match the value you specify in the predicate exactly. String-based comparisons are case insensitive but otherwise all comparisons must be an exact match of the specified value.

Listing 2

Matching a field to a specific value

NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"ANY favoriteColors = 'red'"];
predicate = [NSPredicate predicateWithFormat:@"favoriteColors CONTAINS 'red'"];
predicate = [NSPredicate predicateWithFormat:@"'red' IN favoriteColors"];
predicate = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", @"favoriteColors", @"red"];

You can match against more than one value at a time by using a predicate similar to the ones in Listing 3. In the example, the predicates report a match if the value in the favoriteColor field of a record matches either of the values red or green.

Listing 3

Matching a field to one or more values

NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"ANY { 'red', 'green' } = favoriteColor"];
predicate = [NSPredicate predicateWithFormat:@"favoriteColor IN { 'red', 'green' }"];

For fields that contain string values, you can match the beginning portion of the string using the BEGINSWITH operator as shown in Listing 4. You cannot use other string comparison operators such as CONTAINS or ENDSWITH. When using this operator, the field must contain a string value and must start with the string you specified. Matches are case sensitive. In the examples, the predicate matches records whose favoriteColors field contained the strings red, reddish, or red green duct tape.

Listing 4

Matching a field that starts with a string value

NSString* matchString = @"red";
NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"ANY favoriteColors BEGINSWITH 'red'"]
predicate = [NSPredicate predicateWithFormat:@"ANY favoriteColors BEGINSWITH %@", matchString]

To perform a tokenized search of a record’s fields, use the special operator self. A tokenized search searches any fields that have full-text search enabled, which is all string-based fields by default. Each distinct word in the tokenized string is treated as a separate token for the purpose of searching. Comparisons are case- and diacritic-insensitive. These token strings may be found in a single field or in multiple fields.

Listing 5 shows an example that searches the fields of a record for the token strings bob or smith.

Listing 5

Matching a field containing one or more tokens

NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"self contains 'bob smith'"];

To search for multiple tokens present in the fields, use the AND predicate operator, as shown in Listing 6.

Listing 6

Matching a field containing multiple tokens

NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"self contains 'bob' AND self contains ‘smith’”];

To test whether two locations are near each other, create a predicate using the distanceToLocation:fromLocation: function as shown in Listing 7. Predicates that use this function must have the structure shown in the listing. In your code, replace the location variable with a field name from one of your records. (This data type for the field must be a CLLocation object.) Similarly, replace the fixedLoc and radius values with appropriate values from your app. The fixedLoc value is the geographic coordinate that marks the center of a circle with the specified radius. In this example, the predicate returns a match if the location in the record is within 10 kilometers of the specified latitude and longitude.

Listing 7

Matching by distance from a location

CLLocation* fixedLoc = [[CLLocation alloc] initWithLatitude:37.331913 longitude:-122.030210];
CGFloat radius = 10000; // meters
NSPredicate predicate =
   [NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(location, %@) < %f", fixedLoc, radius]];

To retrieve all records of a given type, use the TRUEPREDICATE expression as shown in Listing 8. A predicate with this operator always evaluates to true and thus matches every record. When using such an operator, it is recommended that you use a cursor to batch the results into smaller groups for processing.

Listing 8

Retrieving all records of a given type

NSPredicate predicate = nil;
predicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];

Indexes and Full-Text Search

Indexes make it possible to search the contents of your records efficiently. During development, the server indexes all fields whose data types can be used in the predicate of a query. This automatic indexing makes it easier to experiment with queries during development, but these indexes take up space in a database and take time to generate and maintain. So when migrating to a production environment, remove the indexes for any fields that you do not actually use in queries.

Full-text search is another feature that is on by default for all fields during development. When you move to the production environment, disable full-text search for fields whose contents you do not need to search. As with removing indexes, disabling full-text search improves the performance of your tokenized searches. To configure the indexing and full-text search options for fields in your schema, use CloudKit Dashboard. For more information about disabling unused indexes, read Disable Unused Indexes in CloudKit Quick Start.

In a full-text search, the following words are ignored if they appear in the token strings:

a

by

not

then

an

for

of

there

and

if

on

these

are

in

or

they

as

into

such

this

at

is

that

to

be

it

the

was

but

no

their

will

with

Executing a Search Using Your Query Object

To execute a query based on your query object, do one of the following:

  • Create a CKQueryOperation object using your query object. Run the operation directly or add it to an operation queue to perform the query and deliver the results.

  • Call the perform(_:inZoneWith:completionHandler:) method of CKDatabase to execute the query. Process the results in your completion handler.

Queries always run asynchronously and deliver results to a completion handler that you provide.

Symbols

Initializing a Query Object

init(recordType: String, predicate: NSPredicate)

Initializes and returns a query object with the specified parameters.

Accessing the Query Parameters

var recordType: String

The record type to search.

var predicate: NSPredicate

The search criteria to use when matching records.

var sortDescriptors: [NSSortDescriptor]?

The sort descriptors to use when organizing the query results.

Relationships

Inherits From