Основные данные: импортировать древовидную структуру с поиском или вставкой / дублированием записей - PullRequest
4 голосов
/ 05 марта 2012

У меня есть список мест из приложения rails, который я пытаюсь импортировать в приложение iOS5. У каждого Места есть родитель, который сам является Местом.

Я пытаюсь импортировать эти данные JSON с Core Data, используя словарь

- (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 - метод, определенный как таковой

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;
}

У меня также есть метод проверки в Place.m, чтобы убедиться, что я не создаю место с тем же placeId (placeId - это идентификатор на стороне сервера).

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

Чтобы импортировать данные, я извлекаю все места с сервера, возвращенные в формате JSON. Каждое место имеет свою собственную информацию, а также дочерний узел с информацией о родителе, что означает, что каждый родительский объект с несколькими дочерними элементами появится несколько раз. Похоже

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

Я подумал, что приведенный выше код, который выполняет своего рода «поиск или создание» с извлечением, включающим несохраненные изменения, будет в порядке. Но он все еще пытается создать несколько записей для некоторых мест (и не удается, так как на месте есть проверка). Если посмотреть глубже, он действительно вставляет разные объекты основных данных для одного и того же placeId (разные указатели), но я не знаю почему.

Спасибо

Ответы [ 2 ]

3 голосов
/ 05 марта 2012

Похоже, у вас уже есть уникальный индекс на id (что, очевидно, хорошо).Я думаю, что вы не сохраняете вновь вставленные творения в базовые данные до того, как ожидаете, что они будут возвращены через fetch.Простым (если, возможно, не слишком производительным в зависимости от наличия большого количества строк) было бы добавить вызов saveContext сразу после вставки / инициализации каждого объекта.

Другим способом будет сделать это за два проходасначала полностью в памяти, где вы создаете отдельный словарь, где ключ - это идентификатор, а объект - это значение.Таким образом, вы сможете убедиться, что каждый идентификатор был там только один раз.После того, как они все в этом словаре, вы можете легко (или, может быть, проще) добавить их все в Базовые данные.

0 голосов
/ 07 марта 2012

Итак, после более подробного расследования, это связано с тем, что я сортировал свои данные по имени ...

Так что если у места А было 5 детей, и у 3 из них было имя, котороеперед именем А, код будет:

  1. создать тех 3 детей с родителями, у которых нет родителя (потому что мой json не возвращает информацию о родителе родителя)
  2. create A
  3. create 2 других потомка с A в качестве родителя (вероятно, из-за способа сортировки, но это не меняет вывод), поэтому родитель, у которого есть родитель

Теперь у нас есть 2 объекта A, один с родителем, а другой без родителя, который, как считают в Core Data, имеет 2 объекта.

Простой выход: мое дерево является вложенным множеством,поэтому мне просто нужно отсортировать места по левому значению, и таким образом я всегда буду создавать родителей перед детьми.

«Сортировка по имени» не была частью моего описания, поэтому я оставлю scc'sответ в принятом виде:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...