Предикаты и сортировка в Core Data для Mac и iOS

Предикат это логический оператор, который вычисляет Boolean значение - true или false. Например в SQL:
SELECT name, address FROM employee WHERE department = ‘research’;
department = ‘research’ - это предикат. Он вычисляется для каждой записи возвращаемой оператором SELECT.

В СУБД предикаты применяются в индексах, т.е. вместо того чтобы получить данные и пропустить их через предикат, СУБД проверяет индексы для поиска всех записей удовлетворяющих предикату.

Предикаты могут использоваться для:
  • filter arrays, 
  • key paths, 
  • Cocoa bindings,
  • и controller data.


Key-value пары хранятся в dictionary (NSDictionary); каждая пара называется entry.
Чтобы получить значение для ключа используется метода:
- (id)objectForKey:(id)aKey

Создание read-only словаря:
  • - (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
  • NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@”value1”, @”key1”, @”value2”, @”key2”, nil];

NSMutableDictionary - read/write структура.
  • - (void)setValue:(id)value forKey:(NSString *)key
  • - (void)setObject:(id)anObject forKey:(id)aKey

Синтаксис предикатов состоит из:

  • литералов и переменных;
  • операторов сравнения;
  • литералов и идентификаторов.

Литералом может быть строка в кавычках "committee" или определенное значение TRUEPREDICATE или FALSEPREDICATE, которые вычисляется в TRUE или FALSE соответственно.
Переменные обозначаются знаком $, например $lastName.



После создания NSPredicate объекта, можно оценить с его помощью любой объект:
- (BOOL)evaluateWithObject:(id)object

Обратите внимание что в Objective-C используется YES и NO, вместо TRUE и FALSE.

У NSArray есть свой метод для возвращения элементов соответствующих предикату:
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
Например:
NSArray *myNewArray = [myArray filteredArrayUsingPredicate:myPredicate];

У NSMutableArray метод filterUsingPredicate возвращает matching objects (не новый массив объектов):
(void)filterUsingPredicate:(NSPredicate *)predicate
Например:
[myArray filterUsingPredicate:myPredicate];

Помните что Core Data может работать с SQLite (Mac OS X, iOS), Spotlight (Mac OS X), XML документами (Mac OS X), in-memory хранилищами данных. Enterprise Objects Framework это то что сейчас Core Data, а значит ничто не мешает использовать Oracle, OpenBase, и другие хранилища данных.

Предикаты опционально добавляются к fetch запросам. Если не использовать предикат то возвращаются все данные:
[request setPredicate:predicate];

Способы конструирования предикатов:
  • Xcode и шаблоны предикатов - имеется встроенные редактор предикатов;
  • Format strings - методы: predicateWithFormat:, predicateWithFormat :arguments, predicateWithFormat :argumentArray;
  • Code - NSExpression будет сердцем предиката, далее они комбинируются в NSComparisonPredicate и потом в NSCompoundPredicate.

Создание fetch запроса и предиката:
1) Выделите одну или несколько сущностей для создания fetch запроса:


2) После создания запроса можно изменить получаемую сущность:

3) Определитесь должны ли быть все компоненты предикаты истины или любых из них достаточно:

4) Выберите атрибут, оператор сравнения, и значение для проверки предиката:

5) Можно переключиться на текстовую версию редактора:
По умолчанию первый предикат это NOT FALSEPREDICATE, которые разрешается в true для каждой записи.

6) Можно снова переключиться в графический режим:

7) Установите имя запроса


Использование шаблона предиката с hard-coded данными:
// get the data model -- perhaps from the persistent store coordinator
NSManagedObjectModel *model = myModel;

// create the error object and set to nil
NSError *error = nil;

// create the fetch request from the template using the name you set
// in the data model inspector in utilities
// pass nil for the substitution variables
NSFetchRequest *fetchRequest =
[model fetchRequestFromTemplateWithName:@”myFetchRequest”
substitutionVariables:nil];

// execute the fetch request
NSArray *results =
[aManagedObjectContext executeFetchRequest:fetchRequest error:&error];


Использование шаблона предиката с runtime данными:
// get the data model -- perhaps from the persistent store coordinator
NSManagedObjectModel *model = myModel;

// create the error object and set to nil
NSError *error = nil;

// create a dictionary for the runtime data
NSDictionary *substitutionDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
manager.salary, @”salaryComparison”, nil];

// create the fetch request from the template using the name you set
// in the data model inspector in utilities
// pass in the dictionary you just created
NSFetchRequest *fetchRequest
[model fetchRequestFromTemplateWithName:@”myFetchRequest”
substitutionVariables:substitutionDictionary];

// execute the fetch request
NSArray *results =
[aManagedObjectContext executeFetchRequest:fetchRequest error:&error];


Создание предиката с помощью format string:
NSPredicate *myPredicate =
[NSPredicate predicateWithFormat:@”City == Plattsburgh
OR Address CONTAINS Champlain”];


Создание предиката с помощью format string и runtime данными:
NSPredicate *myPredicate =
[NSPredicate predicateWithFormat:@”City == %
OR Address CONTAINS %@”, @”Plattsburgh”, @”Champlain”];

Можно расширить данную структуру условиями (clauses) типа: %K LIKE %@
в которых NSString объекты заменяются на %K и %@


Сортировка данных:
Класс NSSortDescriptor позволяет создать объект инкапсулирующий функциональность сортировки. Такие объекты могут быть использованы для сортировки массивов, элементов в таблице или контроллере, и элементов получаемых в fetch запросе. Сортировка это еще одна область где применяется key-value coding. При создании sort descriptor указывается ключ который будет использоваться. Один из методов создания sort descriptor:
+ (id)sortDescriptorWithKey:(NSString *)key ascending:(BOOL)ascending

Использование sort descriptor:
// create a sort descriptor
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@”employeeID” ascending:YES];

// add it to the fetch request
[myFetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];