Сбой при использовании агрегированной операции: «ВСЕ» в iOS-приложении с базовыми данными - PullRequest
8 голосов
/ 07 мая 2011

Я работаю над приложением для iphone, и у меня есть простое отношение многие ко многим, настроенное с объектами Group и Contact.Группа может иметь много контактов, и контакты могут принадлежать нескольким группам.

Я пытаюсь выбрать все группы, к которым конкретный контакт НЕ принадлежит, используя следующий предикат.(Примечание: поле uid является строковым полем, которое я использовал для уникальной идентификации контактных объектов)

[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]

Согласно руководству по программированию предиката Apple, агрегатная операция ALL действительна, но я получаю следующее исключение, указывающее, что этоявляется неподдерживаемым предикатом:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate (null)'

Я могу использовать аналогичный предикат, чтобы выбрать все группы, к которым контакт уже принадлежит, с использованием этого предиката, поэтому кажется, что у меня все отношения и поля определены правильно.

[NSPredicate predicateWithFormat:@"ANY contacts.uid == %@", contactUId]

Исключение выдается при построении предиката, а не когда я пытаюсь фактически выполнить запрос на выборку, поэтому, похоже, он связан с синтаксисом, который я использую, а не с поддержкой Core Data.Что я делаю не так?

Ответы [ 3 ]

5 голосов
/ 25 января 2013

В Руководстве по программированию основных данных написано

Есть некоторые взаимодействия между извлечением и типом магазина. В хранилищах XML, двоичных данных и в памяти оценка дескрипторов предикатов и сортировки выполняется в Objective-C с доступом ко всем функциям Cocoa, включая методы сравнения на NSString. Хранилище SQL, с другой стороны, компилирует предикаты и сортирует дескрипторы в SQL и оценивает результат в самой базе данных.

Далее описываются некоторые другие ограничения использования NSPredicate с NSSQLiteStoreType, но использование здесь «ALL» является (недокументированным) ограничением, связанным с тем, как запрос на выборку выдает SQL.

Под капотом CoreData генерирует три таблицы для вашей схемы:

  • таблица для контактов
  • стол для группы
  • таблица соединений, которая их связывает

И когда вы вызываете myGroup.contacts, запускается что-то вроде этого:

select * from Group join JOIN_TABLE on Group.pk == JOIN_TABLE.group_pk join Contact on JOIN_TABLE.contact_pk == Contact.pk where Group.pk == 12

За одним точечным символом происходит много всего!

В любом случае, чтобы действительно выполнить ваш запрос, вам нужно что-то вроде этого. Я проверил это на реальной базе данных SQLite CD, поэтому имена таблиц выглядят странно, но все равно должно быть понятным:

select ZGROUP.Z_PK as outer_pk from ZGROUP where "myUID" not in 
(select ZCONTACT.ZUID as contact_uid from ZGROUP join Z_1GROUPS on Z_1GROUPS.Z_2GROUPS == ZGROUP.Z_PK join ZCONTACT on Z_1GROUPS.Z_1CONTACTS == ZCONTACT.Z_PK where ZGROUP.Z_PK == outer_pk)

Я не эксперт по SQL, но мои наблюдения состоят, во-первых, в том, что этот запрос будет медленным, и, во-вторых, в том, что он от далекого предиката NSP, с которого мы начали, довольно далек Таким образом, только благодаря огромным усилиям CD сможет выполнить запрос SQL для того, что вы хотите сделать, и запрос, который он придет, будет не намного лучше, чем наивная реализация в ObjC.

Как бы то ни было, разработчик Apple говорит здесь , что ALL не поддерживается в SQLite, а документация об обратном неверна. Эта документация все еще там в 2013 году, так что, похоже, никто ничего с этим не сделал.

Во всяком случае, то, что вы должны сделать, это что-то вроде этого:

NSFetchRequest *fetchRequest = ...
NSArray *result = [moc executeFetchRequest:fetchRequest error:&err];
result = [result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]];

Это позволит оценить предикат в программном обеспечении.

0 голосов
/ 14 июня 2013

Агрегатные выражения не поддерживаются Базовыми данными.

Для записи, приведенное выше предупреждение взято из (2013) ссылки на класс NSExpression, в параграфе относительно АгрегатВыражения .На самом деле некоторые операторы поддерживаются (ЛЮБЫЕ, НЕТ), когда они переводимы в SQL.

0 голосов
/ 08 мая 2011

Основной синтаксис в порядке. Это компилируется и запускается в моем тесте:

NSString *contactUId=@"steve";
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId];
NSLog(@"p = %@",p);

//=> p = ALL contacts.uid != "steve"

Скорее всего, проблема связана с переменной contactUid. Если оно не имеет значения или значения, которое не преобразуется в чистую строку, понятную NSPredicate, это вызовет сбой. Например.

NSString *contactUId;
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId];
NSLog(@"p = %@",p);

... вызывает именно тот сбой, который вы описываете.

Я бы записал contactUid непосредственно перед тем, как присвоить его предикату, чтобы увидеть его действительное, конвертируемое в строку значение. То, как оно отображается в NSLog, - это то, как оно будет отображаться в предикате, потому что оба используют одинаковое форматирование строки.

...