在NSPredicate中组合“AND”和“OR”条件

55

我又回来了,需要更多帮助来构建我的NSPredicate :(

Category
{
   name:string
   subs<-->>SubCategory
}

SubCategory
{
   name:string
   numbervalue:NSNumber
}

我想知道如何创建一个带有ANDOR的谓词。

例如,我想返回每个类别名称为"category_name"且具有子类别数字值为"valueA" "valueB"的类别。

我尝试了我能想到的所有谓词构造函数的可能组合,但是我无法使其正常工作。

这是我迄今为止最好的尝试。

NSPredicate *predicate=[NSPredicate predicateWithFormat@"(name== %@) AND (ANY subs.numbervalue==%@ OR ANY subs.numbervalue==%@)", @"aname", value1, value2];

编辑1

第二次尝试。对于“numbervalue”的过滤仍然没有起作用。

NSNumber valueA=[NSNumber numberWithInt:20];
NSNumber valueA=[NSNumber numberWithInt:90];
NSArray *arr=[NSArray arrayWithObject:valueA,valueB,nil];

predicate=[NSPredicate predicateWithFormat:@"(name==%@)AND(ANY subs.numbervalue IN %@)",@"aname",arr];

编辑2

我已经尝试仅按numberValue过滤,完全忽略了名称。

1)使用这种方法会返回整个集合,即使集合中只有一个项目具有该值。

predicate=[NSPredicate predicateWithFormat:@"(ANY subs.numbervalue IN %@)",arr];

2) 使用这种方法会遇到相同的问题,即使只有一个项目匹配,也会返回集合中的每个项目。

predicate=[NSPredicate predicateWithFormat:@"((SUBQUERY(subs, $x, $x.numbervalue == %@ or $x.numbervalue == %@).@count > 0)", value1, value2];

同时使用最简单的版本也会导致相同的问题... 我之前没有注意到这一点。

NSPredicate *predicate=[NSPredicate predicateWithFormat@"(ANY subs.numbervalue==%@ ,valueA];

我认为我已经缩小了我的问题。

Category是一个实体(Entity)。 SubCategory也是一个实体。

Category与SubCategory之间具有一对多的关系,并表示为SubCategory的NSSet。

每个SubCategory都有一个名为numbervalue的属性。

编辑3

为了测试,我有2个Category。 两个Category都有10个子类,每个子类的numbervalue都是1-10;

使用此代码将返回两个类别以及每个类别的所有10个子类。

期望的结果是返回两个类别,每个类别只有2个子类。

我已经追踪了所有对象,数据是正确的。问题是我无法返回过滤的子类别的类别。

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context=[[DataSource sharedDataSource]managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

[fetchRequest setFetchBatchSize:20];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

NSPredicate *predicate;
NSNumber *a=[NSNumber numberWithFloat:1];
NSNumber *b=[NSNumber numberWithFloat:2];
predicate=[NSPredicate predicateWithFormat:@"(SUBQUERY(subs,$x,$x.numbervalue==%@ or $x.numbervalue==%@).@count> 0)",a,b];

[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];

NSError *error = nil;
[NSFetchedResultsContoller deleteCacheWithName:nil];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:@"name" cacheName:@"Master"];
aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

if (![self.fetchedResultsController performFetch:&error]) {

   NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   abort();
}

你有查看过NSCompoundPredicate吗?它应该会对你有所帮助。另外尝试使用... AND (ANY subs.numberValue IN %@)", @"fooName", numArray - Eimantas
使用 $x.numbervalue 而不是 $numbervalue。然后尝试先将缓存设置为 nil,查看结果。 - Lorenzo B
@Flex_Addicted。感谢您的所有帮助。最终我不得不在像numberofrowsinsection这样的函数中过滤我的category.subs。虽然有点像黑客,但似乎它正在起作用。 - dubbeat
4个回答

92

除了@Stuart的答案,您还可以像这样使用NSCompoundPredicate进行OR和AND操作。

Obj-C - OR

// OR Condition //

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[predicate1, predicate2]];

Obj-C - AND

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[predicate1, predicate2]];

Swift - OR

let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate  = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate1,predicate2] )

Swift - AND

let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate  = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2] )

Swift 3 - OR

    let predicate1 = NSPredicate(format: "X == 1")
    let predicate2 = NSPredicate(format: "Y == 2")
    let predicateCompound = NSCompoundPredicate.init(type: .or, subpredicates: [predicate1,predicate2])

Swift 3 - AND

    let predicate1 = NSPredicate(format: "X == 1")
    let predicate2 = NSPredicate(format: "Y == 2")
    let predicateCompound = NSCompoundPredicate.init(type: .and, subpredicates: [predicate1,predicate2])

1
太好了!我正需要这样的东西。 - Prince Kumar Sharma

39

我将使用 SUBQUERY 进行以下操作。

[NSPredicate predicateWithFormat@"(name == %@) AND (SUBQUERY(subs, $x, $x.numbervalue == %@ or $x.numbervalue == %@).@count > 0)", @"aname", value1, value2];

试一试,然后告诉我。

另一个解决方案可以是获取整个集合subs,然后使用谓词筛选检查元素是否与value1value2匹配。

希望有所帮助。

编辑

如果按照 Eimantas 的建议进行,请确保创建正确的数组:

NSNumber valueA = [NSNumber numberWithInt:20];
NSNumber valueB = [NSNumber numberWithInt:90];
NSArray *arr = [NSArray arrayWithObjects:valueA, valueB, nil];

似乎发生的情况是,如果一组子项的数字值为90,但其余子项为0,则整个子项集合仍会被返回。 - dubbeat
尝试在谓词中仅使用*(SUBQUERY(subs, $x, $x.numbervalue == %@ or $x.numbervalue == %@).@count > 0)*,并使用额外的谓词过滤数组以找到该名称。 - Lorenzo B
仅使用子查询,您可以检索到什么结果? - Lorenzo B
我更新了我的回答。无论我做什么,每个子项中的每个条目都会被返回,即使只有其中一个具有正确的值。 - dubbeat
1
@dubbeat 这很奇怪。NSPredicate 是正确的。我在一个项目中尝试过,它可以工作。如果您使用 slqlite 作为支持存储,请先启用跟踪 sql 操作 debugging-core-data-on-the-iphone。然后尝试探索您插入到数据库中的数据。有两种方法:加载托管对象并使用 NSLog 打印它们,或直接探索您的 sqllite 文件。也许您会发现一些奇怪的东西,可能是因为您以错误的方式创建了数据。这只是一个假设。希望能帮到您。 - Lorenzo B
我已经不断尝试这段代码,但真的不知道哪里出了问题。我们是在谈论同一件事吗?我已经尽可能清晰地更新了我的问题。 - dubbeat

13

对于Swift,您可以这样做:

SWIFT 4.x - AND

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])

SWIFT 4.x - OR

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [p0, p1])

SWIFT 4.x - NOT

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(notPredicateWithSubpredicates: [p0, p1])

示例

这是我其中一段代码的示例,我在其中寻找uid和sync:

        let p0 = NSPredicate(format: self.uid + " = '" + uid + "'")
        let p1 = NSPredicate(format: self.sync + " = %@", NSNumber(value: true as Bool))
        fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])

我的回答被降低评分,被视为破坏行为。但这确实是正确的答案。 - zeeshan
NOT 将只有一个谓词。例如,请参阅 Foundation 文档中的 'NSCompoundPredicate''init(notPredicateWithSubpredicate:)' - l --marc l

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接