查询Core Data中多个子实体类型的所有对象

时间:2011-06-30 05:32:26

标签: objective-c cocoa core-data

鉴于以下设想的例子:

Core Data entity diagram for my Awesome Pet Shop App (patent pending)

我想查询我的数据,查找猫或狗的所有对象。我希望结果集按名称排序,无论种类如何,所以取出所有的猫然后取出所有的狗都行不通。我想在一个查询中执行此操作。

执行此操作的一种方法是向Pet添加petType字段,为每个记录提供标识其所属子实体的petType值,然后进行查询:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Pet" 
                                          inManagedObjectContext:myMOC];
[fetchRequest setEntity:entity];

// petType values: 1 = dog, 2 = cat, 3 = goldfish. Yuk.
NSPredicate *p = [NSPredicate predicateWithFormat:@"petType = 1 OR petType = 2"]
[fetchRequest setPredicate:p];

// etc...

但仅仅想到这样做会让我不寒而栗。还有更好的方法吗?


更新:感谢所有回复的人 - 这里有一些非常好的,经过深思熟虑的解决方案,我很感激所有这些。

为了给出这个上下文,真正的数据模型比这更复杂(不是它们总是如此),但它组织得非常好。在我的时间里,我设计的数据模式比我公平的数据模式更多,我很高兴实体及其关系得到充分考虑。这个问题的出现是因为(为了扩展已经摇摇欲坠的人为例子)客户原本想要的:

  • 显示所有宠物列表的视图
  • 显示金鱼列表的视图
  • 显示猫列表的视图
  • 显示狗列表的视图

到目前为止,这么好。但他们也想要一个显示所有猫和狗的综合列表的视图“因为小女孩喜欢猫和狗”。 (最初它是猫和金鱼,出于同样的原因。)实际上并没有一种方法可以自然地对那些具体实体的子集进行分组;它真的很随意。

到目前为止,Dave Dribin的“抽象中间实体”方法似乎是最干净的解决方案,尽管在我的情况下我认为它会有点人为;真正唯一可以如实标记中间实体的方式就是“ThingLittleGirlsLike”! :)

4 个答案:

答案 0 :(得分:3)

正如您所知,您不能在SQLite存储的fetch谓词中使用entity.name可以在其他商店类型上使用它)。你可以按照你的建议添加一个petType,它运作得很好,但也让我不寒而栗。或者,您可以获取所有Pets,然后根据entity.name过滤提取的结果。但这样做效率有点低,因为您加载的实体数量超出了您的需求,并且您正在进行SQLite可能代表您进行的内存中过滤。

我认为真正的问题是:你想做什么?如果您确实需要在没有Cats的情况下提取DogsGoldfish,那么您的模型应该反映出来。您可以插入FourLeggedPet抽象实体作为CatDog的父级。然后,在您的获​​取请求中,使用FourLeggedPet作为setIncludesSubentities:YES的实体。

答案 1 :(得分:2)

您的托管对象已经拥有宠物类型:他们拥有entity.name。如果您搜索具有实体名称Pet@"Cat"的所有@"Dog"实体,我认为应该这样做。

示例(在Xcode中输入快速和脏,设计很差):

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    NSEntityDescription *catEntity = [NSEntityDescription entityForName: @"Cat" inManagedObjectContext: [self managedObjectContext]];
    NSEntityDescription *dogEntity = [NSEntityDescription entityForName: @"Dog" inManagedObjectContext: [self managedObjectContext]];
    NSEntityDescription *goldfishEntity = [NSEntityDescription entityForName: @"Goldfish" inManagedObjectContext: [self managedObjectContext]];

    id cat = [[NSManagedObject alloc] initWithEntity: catEntity insertIntoManagedObjectContext: [self managedObjectContext]];
    [cat setName: @"Mittens"];

    id dog = [[NSManagedObject alloc] initWithEntity: dogEntity insertIntoManagedObjectContext: [self managedObjectContext]];
    [dog setName: @"Rover"];

    id fish = [[NSManagedObject alloc] initWithEntity: goldfishEntity insertIntoManagedObjectContext: [self managedObjectContext]];
    [fish setName: @"Goldie"];

    [[self managedObjectContext] save: NULL];
    [fish release];
    [dog release];
    [cat release];

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName: @"Pet"];
    NSPredicate *pred = [NSPredicate predicateWithBlock: ^(id evaluatedObject, NSDictionary *bindings) {
        BOOL result = ![[[evaluatedObject entity] name] isEqualToString: @"Goldfish"];
        return result;
    }];
    [fetch setPredicate: pred];

    NSArray *entities = [[self managedObjectContext] executeFetchRequest: fetch error: NULL];
    NSLog(@"Entities: %@", entities);
}

记录以下内容:

  

2011-06-30 14:46:57.435   CDSubentityTest [5626:407]实体:(       “(实体:Cat; id:0x10025f740    ;数据:{\ n name = Mittens; \ n})“,       “(entity:Dog; id:0x1002914e0    ;数据:{\ n name = Rover; \ n})“)

注意金鱼的预期消失。

答案 2 :(得分:0)

我相信你建议的方法会很好用。拥有petType还可以允许您定义Pet s的子类型(即“哺乳动物”,“鸟类”,“爬行动物”,“鱼”等)。当然,你可以为这些中的每一个提供抽象实体,但目前尚不清楚如何从中受益。它最终取决于您的应用程序如何使用该模型。您是否需要获取所有Pet个?您是否计划在一次获取中仅获得CatFish的可能性?您是否关心获取不会立即使用的数据?

根据您希望显示/使用数据的方式定义所需的分类后,请将模型调整为这些分类。拥有一个好看的通用模型,往往是一个痛苦的使用... ...

答案 3 :(得分:0)

谓词不识别实体,因此您无法构造谓词来查找它们。 (更新:这仅适用于SQL商店。)

如果您的心脏设置为按宠物类型提取,那么您没有选择,只能提供一个属性,该属性将提供宠物类型值,并允许谓词和提取操作。但是,您可以制作比您提议的更清洁,更安全的版本。

  1. 为每个实体创建一个petType属性(它不能在实体中继承,但可以在自定义NSManagedObject子类中继承。)
  2. 然后将数据模型编辑器中的默认值设置为物种名称,例如猫,狗,金鱼等(如果使用继承属性,也会继承默认值。)
  3. 重写setter方法,无法有效地使属性只读。 (这可以从一个共同的超类继承。)

    -(void) setPetType:(NSString *) petType{
       return;
    }
    
  4. 现在找到所有的狗和猫只需要将获取实体设置为Pet,然后为IN运算符提供宠物类型名称和数组。

    NSArray *petTypes=[NSArray arrayWithObjects:@"cat",@"dog",nil];
    NSPredicate *p=[NSPredicate predicateWithFormat:@"petType IN %@", petTypes];
    

    虽然这会有效,但我认为Dave Dribin made the best point.当你没有正确改进数据模型时,通常需要这种黑客攻击。如果您需要按宠物类型对各种宠物进行分组,那么该分组可能属于另一个真实世界的对象,条件或事件,例如所有者,反过来应该建立与特定宠物实例的关系建模。如果你这样做,那么你的分组会自动发生,你不必乱用上述所有内容。