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

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

[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 к объекту под названием «Ключевые слова». Приведенный выше запрос работает, но на моем iPhone4 требуется около 7 секунд. Я хочу посмотреть, что я могу сделать, чтобы сократить время отклика до доли секунды.

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


person Michael Nguyen    schedule 21.04.2011    source источник
comment
Если вы просто запросите ключевые слова для данного A, как тогда будет производительность? Я имею в виду, можете ли вы переписать предикат, чтобы сделать подзапрос основным запросом.   -  person Nick Weaver    schedule 22.04.2011


Ответы (2)


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

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

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

person Michael Nguyen    schedule 23.04.2011
comment
Я объединил вашу другую учетную запись с зарегистрированной, чтобы вы могли редактировать вопрос и оставлять комментарии. Пожалуйста, удалите этот ответ, когда закончите. Спасибо и добро пожаловать в Stack Overflow. - person Bill the Lizard; 23.04.2011

Мне кажется, вы подходите к проблеме с обратной стороны. Если у вас есть ключевые слова, вам следует выполнить поиск объектов 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, которые имеют общие ключевые слова.
Обновление:

От автора ОП:

Это не совсем решает проблему, так как результаты, которые возвращаются из пересечения 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
person TechZen    schedule 22.04.2011
comment
Спасибо. Ваш первоначальный комментарий заставил меня задуматься. В итоге я переделал свой дизайн. Мой поиск теперь многопроходный поиск. Первый сканирует нормализованную таблицу ключевых слов и генерирует набор идентификаторов записей, соответствующих объектам «А». Второй проход запрашивает все объекты «A» с идентификаторами в наборе из первого запроса. - person Michael Nguyen; 24.04.2011
comment
Поиск теперь менее секунды на моем iPhone4. Спасибо за помощь! В итоге я удалил связь между A и ключевыми словами. Это также решило для меня еще одну проблему, связанную с загрузкой. Без моделирования отношений время загрузки значительно сократилось. - person Michael Nguyen; 24.04.2011
comment
Обновление моей текущей ситуации ... Для тех, кто может быть заинтересован или может предложить возможное улучшение. Моя модель теперь выглядит так: Ключевые слова и A больше не связаны отношениями. Кажется, что когда я импортирую данные, используя отношения, когда я импортирую объекты A, время импорта значительно увеличивается. Каждый объект Keyword имеет ключевое слово и строку, содержащую все идентификаторы записей A, связанные с ключевым словом, разделенные знаком ^. Я воздержался от хранения их в виде набора, так как не планирую поиск по идентификатору записи. - person Michael Nguyen; 25.04.2011
comment
Теперь я делаю двухпроходный поиск. 1) получить все объекты Keyword, соответствующие ключевым словам, переданным в качестве входных данных для поиска. создать набор, содержащий пересечение всех идентификаторов записей из этого поиска 2) запрос A для всех объектов в наборе из 1 есть около 18 тыс. ключевых слов, причем каждое ключевое слово имеет примерно 11 объектов, с которыми они связаны. Максимальное значение равно 156. Это делает мои поиски не более чем за 0,8 секунды, что является огромным улучшением по сравнению с тем, что у меня было раньше, когда поиск мог занимать до 8 секунд. - person Michael Nguyen; 25.04.2011