2011-06-30 93 views
8

考虑下面的人为的例子多个子实体类型的所有对象:查询核心数据

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

我想查询我的数据项目,是指猫或狗的所有对象。我希望结果集按名称排列,不管物种如何,因此抓取所有的猫然后抓取所有的狗都不行。我想在单个查询中执行此操作。要做到这一点

一个办法是到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”! :)

回答

3

当你找到了,你不能在SQLite的商店中的抓取谓语使用entity.name(你可以使用它在其他商店类型)。正如你所建议的那样,你可以添加一个petType,这个效果不错,但也让我感到不寒而栗。作为替代,您可以获取所有Pets,然后根据entity.name筛选获取的结果。但是这样做效率不高,因为你正在加载比你需要的更多的实体,并且你正在进行内存中的过滤,SQLite可能会代表你进行内存过滤。

我认为真正的问题是:你想要做什么?如果您确实需要取CatsDogs而没有Goldfish,那么您的模型应该反映这一点。您可以插入一个FourLeggedPet抽象实体作为CatDog的父项。然后,在您的获​​取请求中,使用FourLeggedPet作为setIncludesSubentities:YES的实体。

+1

+1通常情况下,像这样的问题是由于糟糕的模型设计造成的。在这种情况下,似乎宠物对象会与其他东西(目前未定义)相关,例如一个所有者。如果你有一个所有者关系,那么你可以问问主人的宠物和你的完成。数据模型可能非常复杂,您应该利用该模型来紧密模拟/模拟应用程序处理的实际对象,条件和事件。一旦你这样做,你通常会发现你的排序,谓词和提取自动简化。 – TechZen

+0

谢谢!我所做的例子只是反映了我的现实世界的用例,但中间FourLeggedPet实体的想法对我来说很好,我认为。 (顺便说一句,'includesEntities'默认为'YES',不需要设置它。) –

+0

@SimonWhitaker,就我而言,我想获取所有子实体(GoldFish,cats和dogs),但是要根据“邪恶”。有没有办法做到这一点?我正在使用fetchedresultsController,所以我必须设置一个fetchRequest。 – akshaynhegde

2

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

(类型快速和肮脏成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]的实体:( “(实体:猫; ID:0x10025f740 ;数据:{\ n命名=手套; \ N})”, “(实体:狗; ID:0x1002914e0 ;数据:{\ n name = Rover; \ n})“)

注意金鱼的预期消失。

+0

NSInvalidArgumentException',原因:'keypath entity.name not found in entity

+0

啊,很好 - 谢谢。可悲的是,你不能在SQLite商店中使用块谓词。 :( –

+0

你不能在SQLite存储中使用entity.name,即使是非块谓词。 –

0

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

根据您希望显示/使用数据的方式定义所需的分类后,将模型调整为这些分类。拥有一个良好的期待,通用模型,往往可以在对接一个痛苦的使用...

0

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

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

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

    -(void) setPetType:(NSString *) petType{ 
        return; 
    } 
    

现在发现所有的狗和猫只是成为获取实体设置为Pet,然后为IN运营商提供和宠物类型名称的数组的问题。

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

虽然这会工作,我认为Dave Dribin made the best point.时,你有没有适当细化你的数据模型,通常需要这种破解的。如果您需要通过宠物类型对各种宠物进行分组,则该分组可能属于另一个真实世界的对象,条件或事件,例如所有者,这反过来应该与关系到特定的宠物实例建模。如果你这样做,那么你的分组会自动发生,你不必混淆上述所有内容。