#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 секунд