简体   繁体   English

如何在使用Core Data时正确设置NSPredicate以实现多对多关系?

[英]How to correctly setup a NSPredicate for a to-many relationship when using Core Data?

I have a Core Data model in which a Task entity includes an optional to-many relationship ExcludedDays to the ExcludedDay entity. 我有一个Core Data模型,其中Task实体包含ExcludedDay实体的可选多对多关系ExcludedDays。 One of the properties of ExcludedDay is day, which is an NSDate object. ExcludedDay的一个属性是day,它是一个NSDate对象。 The ExcludedDay entity has an inverse mandatory to-one relationship to the Task entity. ExcludedDay实体与Task实体具有反向强制关系。

In order to fetch the tasks for a specified day, I need to make sure that the specified day does not appear as the day property of any ExludedDay entity. 为了获取指定日期的任务,我需要确保指定的日期不会显示为任何ExludedDay实体的day属性。

I started trying 我开始尝试了

NSPredicate *dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: @"ALL excludedDays.day != %@", today];

However, despite what the documentation says, ALL does not work and the application throws an exception: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate. 但是,尽管文档说的是,ALL不起作用,应用程序抛出异常:由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'不支持的谓词。

After posting the same question in this forum, I was able to devise the following predicate with the help of various people: 在此论坛中发布相同的问题后,我能够在各种人的帮助下设计以下谓词:

NSPredicate * dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: @"excludedDays.@count == 0 || (excludedDays.@count > 0 && NONE excludedDays.day == %@))", today];

While this worked at first, I have just discovered that this only works when the ExcludedDay entity contains ONLY one day. 虽然这首先起作用,但我刚刚发现,只有当ExcludedDay实体仅包含一天时,这才有效。 As soon as the ExcludedDay entity contains more than one day for the same task, this predicate stops working. 一旦ExcludedDay实体包含同一任务的一天以上,该谓词就会停止工作。 As a result, a task is selected for a day even though the day appears as a day in the ExcludedDay entity, which is of course wrong. 因此,即使当天在ExcludedDay实体中显示为一天,也会选择一天的任务,这当然是错误的。 The problem is not tied to the property day being a NSDate object: replacing day with the corresponding NSString or equivalently with an integer, I still face the same issue and incorrect behaviour. 问题与作为NSDate对象的属性日无关:用相应的NSString替换day或者用整数替换,我仍然面临同样的问题和不正确的行为。

What is the correct way to implement the predicate in this case? 在这种情况下实现谓词的正确方法是什么? May this be a bug related to the ANY aggregate operator when using core data? 在使用核心数据时,这可能是与任何聚合运算符相关的错误吗? Thank you in advance, this is now driving me crazy. 提前谢谢你,这让我发疯了。

It turns out this is yet another problem with missing and/or inconsistent documentation. 事实证明,这是文档丢失和/或不一致的另一个问题。

The correct predicate in this case is the following one: 在这种情况下,正确的谓词如下:

[NSPredicate predicateWithFormat:@"(excludedOccurrences.@count == 0) OR (0 == SUBQUERY(excludedOccurrences, $sub, $sub.day == %@).@count)", today]

In the predicate, a subquery is used to test if the number of related excludedOccurrences with a date matching your test date is equal to zero. 在谓词中,子查询用于测试具有与您的测试日期匹配的日期的相关excludedOccurrences的数量是否等于零。 However, the documentation is misleading. 但是,文档具有误导性。 Here is what the Predicate Programming Guide says regarding the use of predicates in conjunction with to-many relationships. 下面是谓词编程指南关于谓词与多对多关系一起使用的内容。

Using Predicates with Relationships 使用具有关系的谓词

If you use a to-many relationship, the construction of a predicate is slightly different. 如果使用to-many关系,则谓词的构造会略有不同。 If you want to fetch Departments in which at least one of the employees has the first name "Matthew," for instance, you use an ANY operator as shown in the following example: 如果要获取其中至少有一个员工的名字为“Matthew”的部门,您可以使用ANY运算符,如以下示例所示:

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"ANY employees.firstName like 'Matthew'"];

If you want to find Departments in which all the employees are paid more than a certain amount, you use an ALL operator as shown in the following example: 如果要查找所有员工的支付金额超过一定金额的部门,请使用ALL运算符,如以下示例所示:

float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"ALL employees.salary > %f", salary];

Quite curious how to solve this I setup a little project and created the context you are using. 非常好奇如何解决这个问题我设置了一个小项目并创建了你正在使用的上下文。

NSDate *today = [NSDate date];
NSMutableArray *objects = [NSMutableArray array];

{
    NSArray *day = [NSArray arrayWithObjects:today, [today dateByAddingTimeInterval:20.0f], nil];
    NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:@"day"];
    NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:@"excludedDay"];

    [objects addObject:object];
}

{
    NSArray *day = [NSArray arrayWithObjects:[today dateByAddingTimeInterval:20.0f], nil];
    NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:@"day"];
    NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:@"excludedDay"];

    [objects addObject:object];
}


NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE excludedDay.day == %@", today];
NSArray *filtered = [objects filteredArrayUsingPredicate:predicate];

NSLog(@"%@", filtered);

This gives the object when: 这给出了以下对象:

  1. The day array is empty 日数组为空
  2. The day array does not contain the 'today' date 日期数组不包含“今天”日期

This does not give the object when: 在以下情况下,这不会给出对象:

  1. The day array contains the 'today' day数组包含'today'
    • It doesn't matter how many objects in the day array are 日数组中有多少个对象并不重要

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM