核心数据:在两个实体中查找的 NSPredicate

Posted

技术标签:

【中文标题】核心数据:在两个实体中查找的 NSPredicate【英文标题】:Core Data: NSPredicate that looks in two Entities 【发布时间】:2013-01-04 09:37:56 【问题描述】:

问题是我不知道如何制作一个在两个实体中查找的NSPredicate。有什么想法吗?

假设我有 2 个实体:

Library:
1 lib_id
2 name
3 address

和:

Books
1 book_id
2 book_name
3 book_author
4 lib_id

它们之间没有任何核心数据关系。我想发出一个获取请求,该请求将返回一个 NSArray,其中包含至少一本书的所有库。

我认为它是这样的:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescriptor *entity = [NSEntityDescriptor entityForName:@"Books" inManagedObjectContext:context];
 // some NSPredicate...
array = [context executeFetch ...];

【问题讨论】:

不要将 Xcode 标签用于与 Xcode 无关的问题! 在两个实体之间建立关系。使用books.@count > 0 之类的谓词创建针对Library 的请求。这样,您可以节省时间;) 由于某些原因我无法建立关系... 如果您不能按照我的建议进行操作,您需要创建两个不同的请求:一个用于Books,一个用于Library。您需要在内存中加载这两种类型的对象,然后针对lib_id 执行一些检查。也许其他人可以提出其他方法。 【参考方案1】:

这比你想象的要复杂,但也不是不可能。

您的数据模型在两个实体上都使用lib_id,以及您无法在实体之间添加Core Data 关系的评论表明您陷入了一个经典陷阱:您正在处理Core像关系数据库这样的数据。 Core Data 的 API 不像关系数据库那样设计。你可以让它工作,但你应该意识到你会不遗余力地让 Core Data 变得比必要的更困难。如果下面的解决方案看起来很复杂,那是因为你做错了 Core Data

如果您使用 Core Data 的预期用途——在这种情况下是两个实体之间的关系——查找会很简单。您可以对每个 Library 进行一次提取,而其 books 关系中没有任何条目。

话虽如此,您分两步解决眼前的问题。

首先,获取用于Books 实例的lib_id 的每个唯一值。以下将为您提供一个NSArray,其中包含与唯一值匹配的字符串:

NSFetchRequest *bookRequest = [NSFetchRequest fetchRequestWithEntityName:@"Books"];
[bookRequest setPropertiesToFetch:@[@"lib_id"]];
[bookRequest setResultType:NSDictionaryResultType];
[bookRequest setReturnsDistinctResults:YES];

NSError *error = nil;
NSArray *libIdsInBooksDicts = [[self managedObjectContext] executeFetchRequest:bookRequest error:&error];
if (libIdsInBooksDicts == nil) 
    NSLog(@"Fetch error: %@", error);
    return;


NSArray *libIdsInBooks = [libIdsInBooksDicts valueForKey:@"lib_id"];

上面的最后一行是因为libIdsInBooksDicts 实际上包含一个字典数组,而每个字典又都有一个名为lib_id 的键和一个作为实际ID 的值。您只需要这些值。

接下来,查找lib_id 在您刚刚获得的列表中的每个Library

NSFetchRequest *libRequest = [NSFetchRequest fetchRequestWithEntityName:@"Library"];
[libRequest setPredicate:[NSPredicate predicateWithFormat:@"lib_id in %@", libIdsInBooks]];

error = nil;
NSArray *librariesWithAtLeastOneBook = [[self managedObjectContext] executeFetchRequest:libRequest error:&error];
if (librariesWithAtLeastOneBook == nil) 
    NSLog(@"Fetch error: %@", error);
    return;

【讨论】:

+1 汤姆。好答案。我已经在评论中指出了;)【参考方案2】:

NSFetchedResultsController 可以帮助使用 sectionNameKeyPath 获取结果。

- (id)initWithFetchRequest:(NSFetchRequest *)fetchRequest 
      managedObjectContext:(NSManagedObjectContext *)context 
        sectionNameKeyPath:(NSString *)sectionNameKeyPath 
                 cacheName:(NSString *)name;

如果将 lib_id 作为 sectionNameKeyPath 传递,您将获得多个部分中的数据,每个部分关联不同的 lib_id。

【讨论】:

【参考方案3】:

一次提取 == 一个 DISTINCT 实体。

但您可以拥有 N 个相关实体 => 让一个实体相互继承

所以 .. 你可以让 A1 扩展 A 和 A2 扩展 A: => A1 & A2 存储在一个表中,可以一次获取它们(获取所有 As)

但是..您无法获取不相关的 A 和 B: => 一次获取无法做到这一点

【讨论】:

以上是关于核心数据:在两个实体中查找的 NSPredicate的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 获取一对多关系的数据

如何快速比较核心数据中同一实体的两个属性值

按两个依赖属性对核心数据实体进行排序

核心数据 - 不能存储两个单独的实体

两个日期之间实体属性的核心数据总和

使用表格视图将两个属性的信息添加到单个核心数据实体中的有效方法