Master data: import tree structure using search or insert / duplicate elements

I have a list of places from a rails application that I am trying to import into an iOS5 application. Each place has a parent, which is the place itself.

I am trying to import JSON data using Core Data using a dictionary

- (void)initWithDictionary:(NSDictionary *)dictionary { self.placeId = [dictionary valueForKey:@"id"]; id parent = [dictionary objectForKey:@"parent"]; if (parent && parent != [NSNull null]) { NSDictionary *parentDictionary = parent; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeId = %@", [parentDictionary objectForKey:@"id"]]; NSArray *matching = fetchedWithPredicate(@"Place", self.managedObjectContext, predicate, nil); if ([matching count] > 0) { self.parent = [matching objectAtIndex:0]; } else { self.parent = [NSEntityDescription insertNewObjectForEntityForName:@"Place" inManagedObjectContext:self.managedObjectContext]; [self.parent initWithDictionary:parentDictionary]; } } } 

fetchedWithPredicate is a method defined as such

 NSArray* fetchedWithPredicate(NSString *entityName, NSManagedObjectContext *context, NSPredicate *predicate, NSError **error) { NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setIncludesPendingChanges:YES]; NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context]; [request setEntity:entity]; [request setPredicate:predicate]; NSArray *result = [context executeFetchRequest:request error:error]; return result; } 

I also have a validation method in Place.m to make sure that I am not creating an ID for placement with the same place (placeId is the server-side identifier).

 - (BOOL)validatePlaceId:(id *)value error:(NSError **)error { if (*value == nil) return YES; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"placeId = %@ AND (NOT self IN %@)", *value, [NSArray arrayWithObject:self]]; NSArray *matching = fetchedWithPredicate(@"Place", self.managedObjectContext, predicate, error); if ([matching count] > 0) { return NO; } else { return YES; } } 

To import data, I retrieve all the places from the server returned in JSON format. Each place has its own information, as well as a node child with parent information, which means that each parent of several children will be displayed several times. He looks like

 { "id": 73, "name": "Some place", "parent": { "id": 2, "name": "Parent name"} } 

I thought that the code above that pretends to β€œfind or create”, with fetching, including unsaved changes, would be okay. But he is still trying to create some records for some places (and since then there is no validation). Looking deeper, it really inserts different main data objects for the same place ID (different pointers), but I don't know why.

thanks

+4
source share
2 answers

It looks like you already have a unique index on id (which is good, obviously). I think that you do not save the newly inserted creations in the master data before you expect it to be returned through the selection. A simple one (if possible, not very efficient depending on the presence of a large number of lines) would be to add a saveContext call immediately after each object is inserted / included.

Another way would be to do this in two passes, first completely in memory, where you create a separate dictionary, where the key is an identifier and the object is a value. This way you can ensure that each identifier is only there. Once they are all in this dictionary, you can easily (or easier, perhaps) add them all to Core Data.

+3
source

So, after a bit more research, this is because I sorted my data by name ...

So, if place A had 5 children, and 3 of them had a name that was before the name A, code:

  • create these 3 children with a parent that does not have the parent itself (since my json does not return parent information)
  • create a
  • create 2 other children with A as the parent (probably due to the sorting method, but that doesn't change the output), so a parent that has a parent

Now we have 2 objects A, one with a parent and one without a parent, which, according to Core Data, has 2 objects.

Easy way out: my tree is a nested set, so I just need to sort the places by the left value, and this way I will always create parents in front of the children.

"Sort by name" was not part of my description, so I left the scc answer as accepted :)

0
source

Source: https://habr.com/ru/post/1399746/


All Articles