2013-11-27 3 views
3

У меня есть объект JSON, содержащий 200 000 предметов. Мне нужно перебирать эти объекты и определять, существуют они или нет, и выполнить соответствующее действие (вставить/обновить/удалить). Оболочка для этого показана ниже. Конечно, это пока ничего не спасает. Это было больше, чтобы посмотреть, как долго это будет происходить. Это действие занимает около 8 минут для обработки на iPhone 4, что кажется безумным, учитывая, что еще нет никаких изменений.Эффективный способ выполнения массива INSERT/UPDATE/DELETE в CoreData.

Есть ли более эффективный способ справиться с этим?

Любые советы или указатели будут очень признательны.

- (void) progressiveInsert 
{ 
    prodAdd = 0; 
    prodUpdate = 0; 
    prodDelete = 0; 

    dispatch_queue_t backgroundDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 

    dispatch_async(backgroundDispatchQueue, 
        ^{ 
         _productDBCount = 0; 

         NSLog(@"Background Queue"); 
         NSLog(@"Number of products in jsonArray: %lu", (unsigned long)[_products count]); 

         NSManagedObjectContext *backgroundThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; 
         [backgroundThreadContext setPersistentStoreCoordinator:_persistentStoreCoordinator]; 
         [backgroundThreadContext setUndoManager:nil]; 

         [fetchRequest setPredicate:predicate]; 
         [fetchRequest setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:_managedObjectContext]]; 
         [fetchRequest setIncludesSubentities:NO]; //Omit subentities. Default is YES (i.e. include subentities) 
         [fetchRequest setFetchLimit:1]; 

         [_products enumerateObjectsUsingBlock:^(id product, NSUInteger idx, BOOL *stop) { 

          predicate = [NSPredicate predicateWithFormat:@"code == %@", [product valueForKey:@"product_code"]]; 
          [fetchRequest setPredicate:predicate]; 

          NSError *err; 
          NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&err]; 

          if (fetchedObjects == nil) { 

           if ([[product valueForKey:@"delete"] isEqualToNumber:[NSNumber numberWithBool:TRUE]]){ 
            prodDelete += 1; 
           } else { 
            prodAdd += 1; 
           } 

          } else { 

           if ([[product valueForKey:@"delete"] isEqualToNumber:[NSNumber numberWithBool:TRUE]]){ 
            prodDelete += 1; 
           } else { 
            prodUpdate += 1; 
           } 

          } 

          dispatch_sync(dispatch_get_main_queue(),^
             { 

              self.productDBCount += 1; 
              float progress = ((float)self.productDBCount/(float)self.totalCount); 
              _downloadProgress.progress = progress; 

              if (_productDBCount == _totalCount){ 
               NSLog(@"Finished processing"); 
               _endProcessing = [NSDate date]; 
               [_btn.titleLabel setText:@"Finish"]; 
               NSLog(@"Processing time: %f", [_endProcessing timeIntervalSinceDate:_startProcessing]); 
               NSLog(@"Update: %i // Add: %i // Delete: %i", prodUpdate, prodAdd, prodDelete); 
               [self completeUpdateProcess]; 

              } 

             }); 


         }]; 


        }); 
} 
+0

Быстрые примеры для [пакетной вставки] (http://stackoverflow.com/a/32034101/3681880) и [пакетного удаления] (http://stackoverflow.com/a/32031690/3681880) – Suragch

ответ

6

Посмотрите Implementing Find-or-Create Efficiently в "Руководстве по программированию Core Data".

(Update: В этой главе больше не существует в текущем руководстве по программированию Core Data Архивной версию можно найти на http://web.archive.org/web/20150908024050/https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html.).

Одна из ключевых идей не выполнить один выборку запроса для каждого продукта, но выполнить «основная выборка» с предикатом, как

[NSPredicate predicateWithFormat:@"code IN %@", productCodes] 

где productCodes является массив «много» кодов продуктов из ваших данных в формате JSON. Конечно, вам нужно найти оптимальный «размер партии».

+0

А, ок. В этом есть смысл. Будет ли это что-то, что я должен делать в рамках существующей итерации «_products» или по-другому? Например, добавление кода продукта в массив. Затем, когда количество массивов достигает X (100), оно выполняет пакетную выборку, а затем действия CoreData (вставка/обновление/удаление). –

+0

@ LukeSmith: Я не думаю, что есть стандартный способ сделать это. То, что вы описали, является одним из возможных решений. –

+0

@Martin R, ссылка не работает – RaffAl

0

Не работайте с отдельными изделиями, заказывайте их. В настоящее время вы делаете много запросов на выборку в контексте, и это требует времени (используйте инструмент Core Data Instruments, чтобы посмотреть). Если сначала установить размер партии для вашей обработки на 100, затем выберите эту группу идентификаторов, а затем локально проверьте наличие в массиве результатов выборки.

1

С учетом того, что многие объекты, я думаю, вам нужно начинать быть очень умными в отношении ваших данных и системы и искать другие способы обрезать предметы до извлечения объектов 200K JSON. Вы говорите, что используете Core Data и находитесь на iPhone, но не указываете, является ли это клиентским/серверным приложением (ударяя веб-сервер с телефона). Я постараюсь, чтобы мои предложения были общими.

Действительно, вы должны думать за пределами вашего текущего JSON и больше о других данных/метаданных, которые могут содержать подсказки о том, что вам действительно нужно, чтобы получить до слияния/обновления. Похоже, вы синхронизируете две базы данных (телефон &) и используете JSON в качестве средств передачи.

  1. Можете ли вы установить временные данные? Если вы знаете, что в последний раз, когда вы обновили свою телефонную БД, вам нужно только вытащить данные после этого изменения.
  2. Можете ли вы отправить свои данные в разделах/разделах? Группировка 1000-10000 может быть намного более управляемой.
  3. Можете ли вы разделить свои данные на разделы, более или менее относящиеся к пользователю/приложению? Таким образом, сначала обновляются элементы, которые пользователь прикасается к первому.
  4. Если Ваши данные географические, вы можете сначала отправить данные в интересующий регион?
  5. Если ваши данные являются продуктами, можете ли вы отправить данные, которые пользователь просмотрел совсем недавно?
  6. Если ваши данные являются иерархическими, можете ли вы пометить корневые узлы как измененные (или снова временную метку) и изменить только обновленные поддеревья?

Я бы колебался в любой системе, будь то сетевой или даже локальной БД, чтобы попытаться объединить обновления из списка элементов 200K, если это не очень простой список (например, сортировка с числовым слиянием). Это огромная трата времени и сетевых ресурсов, и это не сделает ваших клиентов очень счастливыми.

+0

Предполагается, что основная часть этих записей будет включена в базу данных, заполненную до заполнения. Однако, поскольку эти записи могут потребовать обновления со скоростью примерно 1000/день, если кто-то не обновлялся более регулярно, чем раз в неделю, это займет много времени. В идеале, никто не должен когда-либо обновлять это множество предметов сразу, это всего лишь предосторожность. Последующие обновления запрашиваются с идентификатором последнего полученного обновления (служит той же цели, что и предложение временной отметки), так что принимаются только соответствующие обновления. –

+0

У меня есть два разных пути обновления. Один из них, когда в CD Entity нет записей, и он просто выполняет массовый импорт «текущих» данных (а не обновлений, последней полной версии набора данных). Это занимает около 4 минут на iPhone 4. Другой - это когда существуют существующие записи, и ему нужно выполнить итерацию INSERT/UPDATE/DELETE (как описано в этом сообщении). –

+0

Люк, даже если вы найдете способ ускорить изменения вашего компакт-диска 10x (что я уверен, вам понравятся небольшие обновления), вы все еще смотрите на 30-60 секунд для 200K. Даже при 1000/день это 7000/wk или 10 секунд без ускорения. Я продолжаю задаваться вопросом о вашем дизайне системы. Каков ваш прецедент? Является ли приложение часто используемым, когда сеть доступна или в основном отключена, и пользователь прикасается к кнопке для обновления данных? Автоматически? Вы действительно нуждаетесь в каждой стоимости, мгновенно обновляемой на телефоне? Google может автозаполнять условия поиска через Интернет, каково ваше стремление к тому, чтобы все сразу присутствовало? –

Смежные вопросы