10

Trong ứng dụng của chúng tôi đang được phát triển, chúng tôi đang sử dụng Dữ liệu cốt lõi với một cửa hàng sao lưu sqlite để lưu trữ dữ liệu của chúng tôi. Mô hình đối tượng cho ứng dụng của chúng tôi rất phức tạp. Ngoài ra, tổng số lượng dữ liệu do ứng dụng của chúng tôi phân phối quá lớn để phù hợp với gói ứng dụng iOS (iPhone/iPad/iPod Touch). Do thực tế là người dùng của chúng tôi thường chỉ quan tâm đến một tập hợp con dữ liệu, chúng tôi đã phân đoạn dữ liệu của mình theo cách mà ứng dụng gửi cùng với tập hợp con (mặc dù, ~ 100 MB) đối tượng dữ liệu trong gói ứng dụng. Người dùng của chúng tôi có tùy chọn tải xuống các đối tượng dữ liệu bổ sung (có kích thước ~ 5 MB đến 100 MB) từ máy chủ của chúng tôi sau khi họ thanh toán cho các nội dung bổ sung thông qua mua hàng trong ứng dụng iTunes. Tệp dữ liệu gia tăng (hiện có trong các cửa hàng sao lưu sqlite) sử dụng cùng phiên bản xcdatamodel làm dữ liệu đi kèm với gói; không có thay đổi đối với mô hình đối tượng. Các tệp dữ liệu gia tăng được tải xuống từ máy chủ của chúng tôi dưới dạng các tệp sqlite được nén. Chúng tôi không muốn bloat gói ứng dụng của chúng tôi bằng cách vận chuyển các nội dung gia tăng với ứng dụng. Ngoài ra, chúng tôi không muốn dựa vào các truy vấn trên webservice (vì mô hình dữ liệu phức tạp). Chúng tôi đã thử nghiệm tải xuống dữ liệu sqlite gia tăng từ máy chủ của chúng tôi. Chúng tôi đã có thể thêm kho lưu trữ dữ liệu đã tải xuống vào persistentStoreCoordinator được chia sẻ của ứng dụng. Cách hiệu quả để kết hợp hai Cửa hàng liên tục dữ liệu lõi iOS là gì?

{ 
       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(); 
           }    
       } 
} 

Tuy nhiên, có hai vấn đề với làm nó theo cách này.

  1. dữ liệu lấy kết quả (ví dụ, với NSFetchResultController) xuất hiện với dữ liệu từ incrementalStoreURL nối vào cuối của dữ liệu từ defaultStoreURL.
  2. Một số đối tượng được sao chép. Có nhiều thực thể với dữ liệu chỉ đọc trong mô hình dữ liệu của chúng tôi; chúng được nhân đôi khi chúng ta thêm persistent thứ haiCài đặt vào persistentStoreCoordinator.

Lý tưởng nhất, chúng tôi muốn Core Data hợp nhất đồ thị đối tượng từ hai cửa hàng liên tục thành một (không có mối quan hệ chia sẻ giữa hai cửa hàng tại thời điểm tải xuống dữ liệu). Ngoài ra, chúng tôi muốn xóa các đối tượng trùng lặp. Tìm kiếm trên web, chúng tôi đã thấy một số câu hỏi của những người cố gắng làm điều tương tự mà chúng tôi đang thực hiện - chẳng hạn như this answerthis answer. Chúng tôi đã đọc Marcus Zarra's blog on importing large data sets in Core Data. Tuy nhiên, không có giải pháp nào mà chúng tôi đã thấy có hiệu quả đối với chúng tôi. Chúng tôi không muốn đọc theo cách thủ công và lưu dữ liệu từ cửa hàng gia tăng vào cửa hàng mặc định vì chúng tôi nghĩ điều này sẽ rất chậm và dễ xảy ra lỗi trên điện thoại. Có cách nào hiệu quả hơn để thực hiện hợp nhất không?

Chúng tôi đã cố gắng giải quyết sự cố bằng cách triển khai quá trình di chuyển thủ công như sau. Tuy nhiên, chúng tôi đã không thể thành công để hợp nhất xảy ra. Chúng tôi không thực sự rõ ràng về giải pháp được đề xuất bởi câu trả lời 1 và 2 được tham chiếu ở trên. Blog của Marcus Zarra đã giải quyết một số vấn đề mà chúng tôi đã có ngay từ đầu dự án của chúng tôi, nhập dữ liệu lớn của chúng tôi vào 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(); 
       } 
} 

Dường như tác giả của câu trả lời 1 đã đọc dữ liệu của anh ấy từ cửa hàng gia tăng và lưu vào cửa hàng mặc định. Có lẽ, chúng tôi đã hiểu lầm giải pháp được đề xuất bởi cả hai bài viết 1 & 2. Kích thước dữ liệu của chúng tôi có thể ngăn cản chúng tôi đọc thủ công và chèn lại dữ liệu gia tăng của chúng tôi vào cửa hàng mặc định. Câu hỏi của tôi là: cách hiệu quả nhất để có được đồ thị đối tượng từ hai persistentStore (có cùng objectModel) để hợp nhất vào một persistentStore là gì?

Tự động di chuyển hoạt động khá tốt khi chúng tôi thêm thuộc tính thực thể mới vào biểu đồ đối tượng hoặc sửa đổi mối quan hệ. Có một giải pháp đơn giản để hợp nhất dữ liệu tương tự vào cùng một kho lưu trữ bền vững sẽ đủ kiên trì để dừng và tiếp tục lại - khi quá trình di chuyển tự động được thực hiện?

+0

Marcus Zarra ở đâu khi tôi cần anh ấy? Tôi đã thực hiện một số tiến trình bằng cách sử dụng [NSPersistentStore migratePersistentStore: toURL: options: withType: error] method. Tôi chỉ cần thêm một vài mã để có được nơi tôi cần. – Sunny

+0

Tôi đang vật lộn với cùng một thứ. Bạn có thể đăng những gì bạn đã đưa ra cho đến nay? Tôi bị lạc. – damon

+0

Xong! Hãy cho tôi biết làm thế nào nó quay ra cho bạn. – Sunny

Trả lời

6

Sau nhiều nỗ lực, tôi đã tìm ra cách để làm cho công việc này. Bí quyết đầu tiên là tạo dữ liệu lưu trữ gia tăng mà không có bất kỳ dữ liệu nào cho các thực thể chỉ đọc. Không để dữ liệu chỉ đọc ra khỏi các cửa hàng gia tăng, các cá thể thực thể cho các cửa hàng này sẽ bị trùng lặp sau khi di chuyển và hợp nhất dữ liệu. Do đó, các cửa hàng gia tăng sẽ được tạo mà không có các thực thể chỉ đọc này. Cửa hàng mặc định sẽ là cửa hàng duy nhất có chúng.

Ví dụ: tôi có các thực thể "Quốc gia" và "Tiểu bang" trong mô hình dữ liệu của mình. Tôi chỉ cần có một ví dụ về Quốc gia và Nhà nước trong biểu đồ đối tượng của mình. Tôi giữ các thực thể này ra khỏi các cửa hàng gia tăng và tạo chúng chỉ trong cửa hàng mặc định. Tôi đã sử dụng Thuộc tính đã tìm nạp để liên kết lỏng lẻo đồ thị đối tượng chính của tôi với các thực thể này. Tôi đã tạo cửa hàng mặc định với tất cả các cá thể thực thể trong mô hình của tôi. Các cửa hàng gia tăng hoặc không có các thực thể chỉ đọc (tức là, Quốc gia và Nhà nước trong trường hợp của tôi) để bắt đầu hoặc xóa chúng sau khi tạo dữ liệu hoàn tất.

Bước tiếp theo là thêm cửa hàng gia tăng vào persistentStoreCoordinator (không giống như điều phối viên cho cửa hàng mặc định mà chúng tôi muốn di chuyển tất cả nội dung) trong khi khởi động ứng dụng.

Bước cuối cùng là gọi phương thức migratePersistentStore trên cửa hàng gia tăng để hợp nhất dữ liệu của nó với cửa hàng chính (tức là, mặc định). Mau!

Đoạn mã sau minh họa hai bước cuối cùng tôi đã đề cập ở trên. Tôi đã thực hiện các bước này để thực hiện cài đặt của mình để hợp nhất dữ liệu gia tăng vào kho dữ liệu chính để hoạt động.

{ 
    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

Bạn có thể nhận xét về hiệu suất của giải pháp này để giúp bất kỳ ai đang xem xét tùy chọn này so với việc đọc và ghi dữ liệu từ cửa hàng này sang cửa hàng khác theo cách thủ công không? –

1

Lý do di chuyển của bạn không hoạt động là do mô hình đối tượng được quản lý giống hệt nhau.

Về mặt kỹ thuật, bạn đang nói về "di chuyển dữ liệu" chứ không phải "di chuyển giản đồ". API di chuyển của CoreData được thiết kế để di chuyển lược đồ, đó là xử lý các thay đổi đối với mô hình đối tượng được quản lý.

Theo như chuyển dữ liệu từ cửa hàng này sang cửa hàng khác, bạn sẽ tự mình thực hiện. CoreData có thể giúp bạn có hiệu quả bằng cách sử dụng tính năng tạo hàng loạt và tìm nạp giới hạn cho các yêu cầu tìm nạp của bạn, nhưng bạn cần tự triển khai logic.

Có vẻ như bạn có hai cửa hàng liên tục, cửa hàng lớn và cửa hàng nhỏ. Sẽ là hiệu quả nhất để tải một tệp nhỏ và phân tích nó, khám phá tập hợp khóa chính hoặc số nhận dạng duy nhất bạn cần truy vấn trong cửa hàng lớn hơn.

Sau đó bạn có thể loại bỏ dễ dàng bằng cách truy vấn cửa hàng lớn hơn cho những số nhận dạng đó.

Các tài liệu cho NSFetchRequest có API cho Phạm vi truy vấn của bạn:

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

+0

Cảm ơn bạn đã trả lời câu hỏi của tôi. Về mặt kỹ thuật, việc di chuyển dữ liệu lõi dường như làm nhiều hơn việc di chuyển lược đồ. Câu hỏi của tôi là cố gắng tìm ra đường dây ở đâu và cách tôi có thể tận dụng lợi thế của những gì đã có để thực hiện công việc. Tôi muốn tránh sức mạnh vũ phu - vì điều này có thể sẽ dẫn đến khó duy trì mã khi Apple giới thiệu nhiều tính năng hơn. Tôi đã thực hiện một số tiến bộ bằng cách sử dụng phương pháp [NSPersistentStore migratePersistentStore ::::]. Tôi gần như có. Tôi ước ai đó có kinh nghiệm làm những gì tôi đang cố gắng làm có thể tư vấn cho tôi. – Sunny

1

Bạn không cần bất kỳ di cư - di cư được thiết kế để mang lại những thay đổi trong NSManagedObjectModel, không phải trong dữ liệu riêng của mình.

Điều bạn thực sự cần là Điều phối viên cửa hàng Pesristent quản lý hai Cửa hàng liên tục. Đó là kinda khó khăn, nhưng không quá khó, thực sự.

Có một câu hỏi tương tự, có thể giải thích cho bạn, những gì bạn thực sự cần làm. Can multiple (two) persistent stores be used with one object model, while maintaining relations from one to the other?

Dưới đây là một arcticle tốt bởi Marcus Zarra

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

+0

Xin chào @Nikita, cảm ơn bạn đã trả lời câu hỏi của tôi. Tôi tin rằng migrationManager thực hiện nhiều hơn việc di chuyển lược đồ. Do đó, dữ liệu của bạn được di chuyển khi bạn thêm một thuộc tính vào lược đồ của mình và bật tự động di chuyển. Về bình luận của bạn về việc sử dụng một persistentStoreCoordinator duy nhất, đó là những gì tôi đang làm. Xem đoạn mã bắt đầu từ "if (incrementalStoreExists) {." Các liên kết bạn cung cấp không giải quyết được vấn đề của tôi. Tôi đã sử dụng nhiều cửa hàng liên tục và sử dụng điều phối viên duy nhất để quản lý chúng. – Sunny

Các vấn đề liên quan