Основные данные: проблема с производительностью подзапроса

#iphone #objective-c #core-data #subquery #aggregate-functions

#iPhone #objective-c #core-data #подзапрос #агрегатные функции

Вопрос:

Я пытался выяснить, есть ли какой-либо способ улучшить производительность следующего предиката:

 [NSPredicate predicateWithFormat:@"A=%@ and SUBQUERY($self.words,$k,$k.keyword IN %@).@count==%d", 
  <value for A>, 
  keywordList, 
  [keywordList count]];
  

Что я пытаюсь сделать, так это вернуть все записи A, содержащие ключевые слова, которые ВСЕ содержатся в предоставленном массиве (keywordList). У меня небольшая база данных, содержащая около 2000 записей. Однако ключевые слова для каждого объекта варьируются от 40 до 300 ключевых слов. Ключевые слова представлены как отношение «ко многим» от A к объекту, называемому Keywords. Приведенный выше запрос работает, но для его выполнения на моем iPhone4 требуется около 7 секунд. Я хочу посмотреть, что я могу сделать, чтобы сократить этот ответ до подсекунды.

Спасибо, Майкл

Комментарии:

1. Если вы просто запрашиваете ключевые слова для данного A, то какова тогда производительность? Я имею в виду, можете ли вы переписать предикат, чтобы выполнить подзапрос в качестве основного запроса.

Ответ №1:

Это не совсем решает проблему, так как результаты, которые возвращаются при пересечении fetchedObjs, неверны. Это не гарантирует, что все объекты ‘A’ содержат все предоставленные ключевые слова. Фактически, возвращается пустой список, и весь процесс на самом деле занимает больше времени.

Должно быть, что-то не так с моей моделью или предыдущим ответом. Я не получаю тех результатов, которых ожидаю. Одной из альтернатив возврату списка ‘A’ было бы вернуть список ManagedObjectIDs ‘A’. [mo valueForKey: (NSString *)] возвращает объект отношения.

Примечание: Первоначально я опубликовал этот вопрос анонимно, думая, что я вошел в систему, поэтому, похоже, я не могу прокомментировать чей-либо ответ.

Комментарии:

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

Ответ №2:

Я думаю, вы подходите к проблеме наоборот. Если у вас есть ключевые слова, вам следует выполнить поиск по Keyword объектам, а затем просмотреть их A взаимосвязи, чтобы найти связанные A объекты. Затем проверьте, нет ли перекрытия в наборах отношений.

Итак, что-то вроде:

 NSFetchRequest *fetch=//...set up fetch
[fetch setEntity:Keyword_entity];
NSPredicate *p=[NSPredicate predicateWithFormat:@"keyword IN %@",keywords];
[fetch setPredicate:p];
NSArray *fetchedObj=//... execute fetch

NSMutableSet *inCommon=[NSMutableSet setWithCapacity:[fetchedObjs count]];
for (NSManagedObject *mo in fetchedObjs){
  if ([inCommon count]==0){
    [inCommon addObjects:[mo valueForKey:@"aRelationshipName"]];
  }else{
    [inCommon intersectSet:[mo valueForKey:@"aRelationshipName"]];
  }
}
  

inCommon затем будет содержать набор всех уникальных A объектов, которые совместно используют все ключевые слова.
Обновить:

От автора OP:

Это не совсем решает проблему, поскольку результаты, которые возвращаются при пересечении fetchedObjs, неверны. Это не гарантирует, что все объекты ‘A’ содержат все предоставленные ключевые слова.

Хорошо, давайте перейдем к другому подходу. Предполагая, что у вас есть модель данных, которая выглядит примерно так:

 A {
    keywords<<-->>Keyword.as
}

Keyword{
    keyword:string
    as<<-->>A.keywords
}
  

Тогда должно сработать что-то вроде этого:

 NSFetchRequest *keyWordFetch=//...set up fetch
[keyWordFetch setEntity:Keyword_entity];
NSPredicate *p=[NSPredicate predicateWithFormat:@"keyword IN %@",keywords];
[keyWordFetch setPredicate:p];
NSArray *fetchedKeywords=//... execute keyWordFetch


NSFetchRequest *entityAFetch=//...set up fetch
[entityAFetch setEntity:A_entity];
NSPredicate *pred=[NSPredicate predicateWithFormat:@"ALL keywords IN %@", fetchedKeywords);
[entityAFetch setPredicate:pred];
NSArray *fetchedAs=//... execute fetch
  

Комментарии:

1. Спасибо. Ваш первоначальный комментарий заставил меня задуматься. В итоге я переделал свой дизайн. Мой поиск теперь является многопроходным поиском. Первый сканирует нормализованную таблицу ключевых слов и генерирует набор идентификаторов записей, которые соответствуют объектам «A». Второй передает запросы для всех объектов ‘A’ с идентификаторами в наборе из первого запроса.

2. На моем iPhone4 поиск занимает меньше секунды. Спасибо за помощь! В итоге я удалил связь между A и ключевыми словами. Это также решило для меня другую проблему, которая заключалась в загрузке. Без необходимости моделировать взаимосвязь время загрузки было значительно сокращено.

3. Обновите мою текущую ситуацию… Для тех, кто может быть заинтересован или может предоставить возможное улучшение.. Моя модель теперь выглядит так: ключевые слова и A больше не связаны через отношения. Кажется, что когда я импортирую данные, используя отношения при импорте объектов, время импорта значительно увеличивается. Каждый объект Keyword имеет ключевое слово и строку, содержащую все идентификаторы записи A, связанные с ключевым словом, разделенные знаком «^». Я воздержался от сохранения их в виде набора, поскольку я не планирую выполнять поиск на основе recordid.

4. Теперь я провожу поиск в два прохода. 1) получите все объекты ключевых слов, соответствующие ключевым словам, переданным в качестве входных данных для поиска. создайте набор, содержащий пересечение всех идентификаторов записей из этого поиска 2) запросите A для всех объектов в наборе из 1, в котором содержится около 18 ключевых слов, причем каждое ключевое слово содержит примерно 11 объектов A, с которыми они связаны. Максимальное значение — 156. это приводит к тому, что мои поиски занимают максимум 0,8 секунды, что является огромным улучшением по сравнению с тем, что у меня было раньше, когда поиск мог занимать до 8 секунд