10

開発中のアプリケーションでは、データを保存するためにsqliteバッキングストアでコアデータを使用しています。私たちのアプリのオブジェクトモデルは複雑です。また、アプリで配信されるデータの総量が大きすぎて、iOS(iPhone/iPad/iPod Touch)アプリのバンドルに収まらない場合もあります。ユーザーは、通常、データのサブセットのみに関心があるため、データオブジェクトのサブセット(〜100 MB)を同梱するようにデータを分割しています。アプリバンドル。当社のユーザーは、iTunesのアプリ内購入による追加コンテンツの支払い後に、サーバーから追加データオブジェクト(サイズが5 MB〜100 MB)をダウンロードすることができます。 (sqliteバッキングストアに存在する)インクリメンタルデータファイルは、バンドルに同梱されているデータと同じxcdatamodelバージョンを使用します。オブジェクトモデルにはゼロの変更があります。インクリメンタルデータファイルは、gzip形式のsqliteファイルとして当社のサーバーからダウンロードされます。インクリメンタルなコンテンツをアプリと共に出荷することで、アプリケーションバンドルを膨らませたくありません。また、(複雑なデータモデルのため)Webサービスでのクエリに頼ることも望まない。 私たちはサーバーからインクリメンタルsqliteデータのダウンロードをテストしました。ダウンロードしたデータストアを、アプリケーションの共有persistentStoreCoordinatorに追加することができました。 2つのiOSコアデータ永続ストアを効率的にマージする方法は何ですか?

{ 
       NSError *error = nil; 
       NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
                                [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
                                [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 

       if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:defaultStoreURL options:options error:&error]) 
       {            
           NSLog(@"Failed with error:  %@", [error localizedDescription]); 
           abort(); 
       }    

       // Check for the existence of incrementalStore 
       // Add incrementalStore 
       if (incrementalStoreExists) { 
           if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:incrementalStoreURL options:options error:&error]) 
           {            
               NSLog(@"Add of incrementalStore failed with error:  %@", [error localizedDescription]); 
               abort(); 
           }    
       } 
} 

しかし、このようにそれをやって2つの問題があります。

  1. データはdefaultStoreURLから データの末尾に付加incrementalStoreURLから データを表示(NSFetchResultControllerと、例えば)結果をフェッチします。
  2. 一部のオブジェクトが複製されています。データモデルには、 の読み取り専用データを持つ多くのエンティティがあります。 2番目のpersistentStoreを のpersistentStoreCoordinatorに追加すると、これらは重複してしまいます。

コアデータは、2つの永続ストアのオブジェクトグラフを1つにマージすることが理想的です(データのダウンロード時に2つのストアのデータの間に共有関係はありません)。また、複製オブジェクトを削除したいと考えています。 ウェブを検索したところ、this answerthis answerなど、私たちがやっているのと同じことをやろうとしている人たちからいくつか質問がありました。我々はMarcus Zarra's blog on importing large data sets in Core Dataを読んだ。しかし、私たちが見た解決策のどれもが私たちのために働いていませんでした。インクリメンタルストアのデータを手動で読み込んでデフォルトのストアに保存することは望ましくありません。これは、これが非常に遅く、電話でエラーが発生しやすいと考えているからです。マージを行うより効率的な方法はありますか?

私たちは、次のように手動移行を実装することで問題を解決しようとしました。しかし、我々はマージを成功させることができませんでした。私たちは、上で参照された回答1と2で提案された解決策については、本当に明確ではありません。 Marcus Zarraのブログでは、私たちのプロジェクトの初めに大規模なデータセットをiOSにインポートしていたいくつかの問題に取り組んでいました。

{ 
       NSError *error = nil; 
       NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
                                [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
                                [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];        

       NSMigrationManager *migrator = [[NSMigrationManager alloc] initWithSourceModel:__managedObjectModel destinationModel:__managedObjectModel]; 
       if (![migrator migrateStoreFromURL:stateStoreURL 
                                type:NSSQLiteStoreType 
                             options:options 
                    withMappingModel:nil 
                    toDestinationURL:destinationStoreURL 
                     destinationType:NSSQLiteStoreType 
                  destinationOptions:nil 
                               error:&error]) 
       { 
           NSLog(@"%@", [error userInfo]); 
           abort(); 
       } 
} 

答え1の著者は、増分ストアからの彼のデータを読み込み、デフォルトのストアに保存してしまったようです。おそらく、我々は両方の記事1 &で提案されている解決策を誤解しています。2.私たちのデータのサイズによって、私たちは増分データを手動で読み込んでデフォルトの店に再挿入することができません。私の質問は、(同じobjectModelを持つ)2つのpersistentStoresのオブジェクトグラフを1つのpersistentStoreにマージする最も効率的な方法は何ですか?

オブジェクトのグラフに新しいエンティティ属性を追加したりリレーションシップを変更したりすると、自動移行はかなりうまく機能します。自動移行が実行されるので、停止して再開するのに十分な復元力を持つ同じ永続ストアに類似のデータをマージする簡単なソリューションはありますか?

+0

私が彼を必要とするとき、マーカス・ザラはどこですか?私は[NSPersistentStore migratePersistentStore:toURL:options:withType:error]メソッドを使用していくつかの進歩を遂げました。私は、必要な場所にアクセスするために、いくつかのクリーンアップコードが必要です。 – Sunny

+0

私は同じことをしています。あなたはこれまでに思いついたことを投稿できますか?道に迷いました。 – damon

+0

完了!それがあなたのためにどのように判明したか教えてください。 – Sunny

答えて

6

マーカスZarraにより良いarcticleです。この秘密は、読み取り専用エンティティのデータがないインクリメンタルストアデータを最初に作成することです。インクリメンタルストアから読み込み専用のデータを除外せずに、これらのエンティティインスタンスは、データの移行とマージ後に複製されます。したがって、増分ストアは、これらの読み取り専用エンティティなしで作成する必要があります。既定のストアは、それらを持つ唯一のストアになります。

たとえば、データモデルに「国」と「国」エンティティがあります。オブジェクトグラフに国と州のインスタンスを1つだけ持つ必要がありました。私はこれらのエンティティをインクリメンタル・ストアから外して、デフォルト・ストアでのみ作成しました。フェッチされたプロパティを使用して、メインオブジェクトグラフをこれらのエンティティに疎結合させました。モデル内のすべてのエンティティインスタンスでデフォルトストアを作成しました。インクリメンタルストアには、データの作成が完了した後に読み込み専用のエンティティ(私の場合は国と州)がありませんでした。

次の手順では、アプリケーションの起動時にインクリメンタルストアを独自のpersistentStoreCoordinator(すべてのコンテンツを移行するデフォルトストアのコーディネータと同じではない)に追加します。

最後のステップでは、インクリメンタルストアのmigratePersistentStoreメソッドを呼び出して、そのデータをメイン(つまりデフォルト)ストアにマージします。プレスト!

次のコードは、上記の最後の2つの手順を示しています。増分データをメインデータストアに統合して動作させるために、これらの手順を実行しました。

{ 
    NSError *error = nil; 
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
    [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
    [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 

    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:defaultStoreURL options:options error:&error]) 
    {    
     NSLog(@"Failed with error: %@", [error localizedDescription]); 
     abort(); 
    }  

    // Check for the existence of incrementalStore 
    // Add incrementalStore 
    if (incrementalStoreExists) { 

     NSPersistentStore *incrementalStore = [_incrementalPersistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:incrementalStoreURL options:options error:&error]; 
     if (!incrementalStore) 
     { 
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
      abort(); 
     }  

     if (![_incrementalPersistentStoreCoordinator migratePersistentStore:incrementalStore 
      toURL:_defaultStoreURL 
      options:options 
      withType:NSSQLiteStoreType 
      error:&error]) 
     { 
      NSLog(@"%@", [error userInfo]); 
      abort(); 

     } 

     // Destroy the store and store coordinator for the incremental store 
     [_incrementalPersistentStoreCoordinator removePersistentStore:incrementalStore error:&error]; 
     incrementalPersistentStoreCoordinator = nil; 
     // Should probably delete the URL from file system as well 
     // 
    } 
} 
+0

このオプションを検討しているユーザーが、あるストアから別のストアにデータを手動で読み書きするのに役立つこのソリューションのパフォーマンスについてお聞かせください。 –

1

移行が機能しない理由は、管理対象オブジェクトモデルが同一であるためです。

技術的には、「データ移行」は「スキーマの移行」ではなく、 CoreDataの移行APIは、管理オブジェクトモデルの変更を処理するスキーマ移行用に設計されています。

ある店舗から別の店舗にデータを転送している限り、あなたは自分自身のものです。 CoreDataは、フェッチ要求にバッチ処理とフェッチ制限を使用することで効率的になりますが、ロジックを自分で実装する必要があります。

2つの永続的なストア、大きなものと小さいものがあるようです。小さなものをロードして分析し、大きなストアで照会する必要がある主キーまたは一意のIDのセットを発見するのが最も効率的です。

これらの識別子の大規模なストアに簡単に照会することで、簡単にデュープを削除することができます。

NSFetchRequestのドキュメントは、クエリのスコープのためのAPIを持っています

https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSFetchRequest_Class/NSFetchRequest.html

+0

私の質問にお返事いただきありがとうございます。技術的には、コアデータの移行はスキーマの移行以上の役割を果たすようです。私の質問は、その行がどこにあるのか、すでにその仕事をするためにそこにあるものをどのように活用できるのかを見極めることです。私はブルートフォースを避けたい - アップルがますます多くの機能を導入しているので、コードを維持するのは難しいだろう。私は[NSPersistentStore migratePersistentStore ::::]メソッドを使っていくつかの進歩を遂げました。私はほとんどそこにいる。私は、私がやろうとしていることを経験している人がアドバイスを受けることを願っています。 – Sunny

1

あなたは、任意の移行を必要としない - 移行がNSManagedObjectModelではなく、データ自体の変化をもたらすように設計されています。

あなたが本当に必要とするのは、2つの永続的な店舗を管理するPesristent Store Coordinatorです。ちょっと難しいですが、それほど難しいことではありません。

あなたに本当に必要なことを説明できる似たような質問があります。 Can multiple (two) persistent stores be used with one object model, while maintaining relations from one to the other?

は、ここで私は、この作品を作る方法を考え出した、いくつかの試みの後

http://www.cimgf.com/2009/05/03/core-data-and-plug-ins/

+0

こんにちは@ニキータ、私の質問にお返事いただきありがとうございます。私は、migrationManagerがスキーマを移行する以上のことをしていると信じています。したがって、スキーマに属性を追加して自動移行を有効にすると、データが移行されます。単一のpersistentStoreCoordinatorの使用に関するコメントについては、私がやっていることです。 「if(incrementalStoreExists){。」で始まるコードの断片を参照してください。あなたが提供したリンクは私の問題に対処しませんでした。私はすでに複数の永続ストアを使用しており、それらを管理するために単一のコーディネータを使用しています。 – Sunny

関連する問題