Проблема с SUBQUERY, чтобы найти максимальную (последнюю?) Дату - PullRequest
1 голос
/ 30 июня 2011

У меня есть две сущности 'Times' <* -> 'FileList'. 'FileList' - имеет отношение 'whenDownload', обратное отношение от 'Times' равно 'wichFile'. Каждый файл в FileList может быть загружен несколько раз, и myApp сохраняет несколько версий файлов в файловой системе. «Times» помогает мне найти точную версию и другую информацию о файле (комментарии и т. Д.).
Итак, у меня есть filesArray с несколькими объектами из 'FileList', и я пытаюсь найти последнюю загружаемую версию каждого объекта FileList из массива, чтобы понять, нужно ли загружать новые версии или нет. Серверная часть с файлами не моя.
Мой код:

NSFetchRequest *cutRequest = [[NSFetchRequest alloc] init];
cutRequest.entity = [NSEntityDescription entityForName:@"Times" inManagedObjectContext:localContext];
(NSArray *) listFilesToDownload = [self getListFilesToDownload]; // array with 30-90 files from 10k
NSPredicate * filePredicate = [NSPredicate predicateWithFormat:@"whichFile IN %@", listFilesToDownload];

// doesn't work:
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "SUBQUERY(Times, $s, max($s.timeDownload))"]; 
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "max (Times, $s, $s.timeDownload)"];
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "(SUBQUERY (Times, $s, $s.timeDownload).max != 0)"];
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "max (timeDownload)"];
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "max (Times.timeDownload)"];
//NSString  *name1 = @"timeDownload";
//NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "max (%K)", name1];
NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "SUBQUERY (Times, $time, max($time.timeDownload)).@count > 0"];

NSArray *allPredicates  =   [NSArray arrayWithObjects: filePredicate, timePredicate, nil];
cutRequest.predicate    =   [NSCompoundPredicate andPredicateWithSubpredicates:allPredicates];
cutRequest.fetchBatchSize = 300;
NSArray     *arrayRequest   = [localContext executeFetchRequest:cutRequest error:&error];
[cutRequest release];

также я пробовал один предикат:

 NSPredicate * timePredicate =  [NSPredicate predicateWithFormat : @ "(whichFile IN %@) AND (SUBQUERY (Times, $s, max($s.timeDownload))"];

но все еще есть "Unable to parse the format string..."

Я не знаком с основными данными. Некоторые посты ( один , два и три ) были интересными, но для меня этого недостаточно. Я не смог найти достаточно деталей о синтаксисе SUBQUERY с функциями.

1. Не могли бы вы объяснить, как получить правильный предикат с подзапросом в моем случае?
2. Что вы рекомендуете прочитать о подзапросе (кроме Руководства по программированию Predicate:)

Надеюсь, вопрос будет интересен всем.
Ждем ваших советов.
Nik

Ответы [ 2 ]

3 голосов
/ 30 июня 2011

В строке формата предиката есть пара неправильных вещей:

SUBQUERY (Times, $s, max($s.timeDownload))
  1. SUBQUERY возвращает коллекцию (т. Е. Массив).Предикаты хотят выражения, которые оцениваются как YES или NO, а не как массив.Массив не является логическим значением.Обычно вы видите что-то более похожее на SUBQUERY(...).@count > 0, что означает «выполнить этот подзапрос, а затем вернуть YES, если он вернул какие-либо объекты».Это может быть уместно в вашем случае, хотя я не совсем уверен;Я просто оцениваю синтаксическую правильность вашей строки формата.

  2. Третий параметр SUBQUERY сам должен быть выражением предиката.Он должен быть равен YES или NO.max(oneThing) не оценивается как YES или NO;оценивается в oneThing.

Предполагая, что вы все правильно поняли, я не уверен, что Core Data даже сможет его распознать как предикат запроса на выборку.Базовые данные предъявляют довольно строгие требования к тому, что вы можете делать в предикате.Кроме того, я уверен, что SUBQUERY квалифицируется как «агрегатное выражение», которое Core Data поддерживает , а не .

Я бы сказал, что примерно в 99% случаев выдумаю, что вам нужно использовать SUBQUERY, вы не делаете, и вы на самом деле не должны.Это очень редко полезно, и его использование достаточно причудливо, что обычно делает ваш предикат более трудным для понимания, и, следовательно, менее поддерживаемым в будущем.

В двух словах: найдите другой способ сделать это.У вас есть список файлов, и вы хотите получить объект Times, соответствующий времени последней загрузки, верно?Я думаю, что это будет примерно так:

NSPredicate *p = [NSPredicate predicateWithFormat:@"whichFile.name IN %@ AND timeDownload = whichFile.whenDownload.@max.timeDownload", listFilesToDownload];

Это предполагает, что listFilesToDownload является массивом имен файлов, и что ваша сущность FileList имеет свойство name, которое является именемфайл и соответствует одному в списке ...

Интересует (я думаю), что это:

timeDownload = whichFile.whenDownload.@max.timeDownload

Это будет оценивать YES, если загрузкавремя этого файла равно максимальному времени загрузки всех времен этого времени FileList.Или что-то типа того.Ваши названия довольно ужасны (простительно, поскольку английский не является вашим родным языком), и это определенно мешает моим объяснениям, но вы идете.

Наслаждайтесь.

0 голосов
/ 30 июня 2011

Как правило, когда вы сталкиваетесь с необходимостью использовать подзапрос в предикате, который часто является признаком того, что вы столкнулись с проблемой проектирования. Обычно это означает, что (1) ваша выборка против неправильной сущности или (2) ваша модель данных является неоптимальной.

В этом случае, я думаю, что это (2).

Давайте сделаем следующие предположения относительно ваших требований.

  • Вы загружаете несколько версий одного и того же имени файла с сервера.
  • Каждый файл хранится вне Core Data, и вам нужно только указать имя файла, путь к файлу на диске и время загрузки в самих Core Data.

Затем вы можете упростить модель данных до:

DownFile{
  name:string
  path:string
  downloadTime:date
}

Каждый экземпляр DownFile хранит информацию для одного загруженного файла. У вас может быть много файлов с одинаковым именем (или номером идентификатора, или другим идентификатором), но каждая версия имени файла будет иметь уникальный путь на диске и уникальную метку времени загрузки.

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

NSPredicate *p=[NSPredicate predicateWithFormat:@"name == %@", soughtName];

Затем вы можете предоставить дескриптор сортировки, который сортирует по дате, помещая самую последнюю дату вверху:

NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey: @ "downloadTime" по возрастанию: НЕТ];

Когда вы запускаете выборку с указанным выше предикатом и сортируете, он возвращает массив всех экземпляров DownFile, которые имеют то же имя, что и переменная soughtName, и все они отсортированы по убыванию. Чтобы найти последнюю загруженную версию, просто получите нулевой элемент массива.

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

Даже если эта конкретная модель не будет работать для вас, она должна предоставить вам отправную точку. Базовые данные предназначены для моделирования / симуляции реальных объектов, условий или событий и отношений между ними. Поэтому ваша модель должна отражать то, что действительно происходит. В этом случае у вас есть

downloaded files -->> Object
download time -->> Event

Поскольку каждый файл уникален только по времени загрузки, вам нужно сделать это главным атрибутом объекта, который моделирует файлы. (Обратите внимание, что объект моделирует единичные экземпляры реального объекта, условия или события.) Объект DownFile позволяет моделировать / моделировать один файл реального мира по названию местоположения и критическому событию его времени загрузки.

Поскольку объект тесно моделирует реальность, это делает выборку реальных экземпляров объекта намного, намного проще

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...