Realm Cocoa:通过 PK 查找多个对象

Posted

技术标签:

【中文标题】Realm Cocoa:通过 PK 查找多个对象【英文标题】:Realm Cocoa: finding multiple objects by PKs 【发布时间】:2015-01-06 12:40:30 【问题描述】:

潜伏已久,第一次问。

我在一个项目中使用 Realm Cocoa(来自 Realm.io)并且正在努力通过 PK 执行搜索。 假设我有一个名为RLMFoo 的实体,它有一个名为bar 的主键。我还有一个 PK 列表,假设存储在一个数组中:

NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]

有没有办法在一个查询中从我的领域中检索所有 RLMFoo 类的实体?

到目前为止我已经尝试过:

    谓词格式:[RLMFoo objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:@"bar IN %@", primaryKeys]]; 领域在哪里:[RLMFoo objectsInRealm:realm where:@"bar IN %@", strippedIds]; 带有块的谓词:

.

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id
evaluatedObject, NSDictionary *bindings) 
                    RLMFoo *foo = (RLMFoo *)evaluatedObject;
                    return [primaryKeys containsObject:foo.bar];
                ];
        [RLMFoo objectsInRealm:realm withPredicate:predicate];

但到目前为止,我发现唯一可行的方法是查询每个主键值的领域并聚合结果,这似乎很慢:

NSMutableArray *results = [NSMutableArray new];
for (NSString *primaryKey in primaryKeys) 
    RLMFoo *obj = [RLMFoo objectInRealm:realm forPrimaryKey:primaryKey];
    if (obj) 
        [results addObject:obj];
    

有人知道更好的方法吗?

========= 解释为什么前三种方法不起作用 =======

1) 这不起作用的原因从异常消息中似乎很明显:IN 只取 2 个值,尽管 IN 的 SQL 版本应该取尽可能多的值。从RLMQueryUtil.mm的源码可以看出,BETWEEN操作符也是如此:

        if (compp.predicateOperatorType == NSBetweenPredicateOperatorType || compp.predicateOperatorType == NSInPredicateOperatorType) 
        // Inserting an array via %@ gives NSConstantValueExpressionType, but
        // including it directly gives NSAggregateExpressionType
        if (exp1Type != NSKeyPathExpressionType || (exp2Type != NSAggregateExpressionType && exp2Type != NSConstantValueExpressionType)) 
            @throw RLMPredicateException(@"Invalid predicate",
                                         @"Predicate with %s operator must compare a KeyPath with an aggregate with two values",
                                         compp.predicateOperatorType == NSBetweenPredicateOperatorType ? "BETWEEN" : "IN");
        

这是堆栈跟踪:

*** Terminating app due to uncaught exception 'Invalid predicate', reason: 'Predicate with IN operator must compare a KeyPath with an aggregate with two values'
*** First throw call stack:
(
    0   CoreFoundation      0x03334946 __exceptionPreprocess + 182
    1   libobjc.A.dylib     0x0292aa97 objc_exception_throw + 44
    2   <redacted>          0x00190569 _ZN12_GLOBAL__N_127update_query_with_predicateEP11NSPredicateP9RLMSchemaP15RLMObjectSchemaRN7tightdb5QueryE + 2553
    3   <redacted>          0x0018f7ea _Z27RLMUpdateQueryWithPredicatePN7tightdb5QueryEP11NSPredicateP9RLMSchemaP15RLMObjectSchema + 378
    4   <redacted>          0x0018b23c _Z13RLMGetObjectsP8RLMRealmP8NSStringP11NSPredicate + 748
    5   <redacted>          0x0017d721 +[RLMObject objectsInRealm:withPredicate:] + 161

2)这在原因和堆栈跟踪上非常相似:

*** Terminating app due to uncaught exception 'Invalid predicate', reason: 'Predicate with IN operator must compare a KeyPath with an aggregate with two values'
*** First throw call stack:
(
    0   CoreFoundation      0x03328946 __exceptionPreprocess + 182
    1   libobjc.A.dylib     0x0291ea97 objc_exception_throw + 44
    2   <redacted>          0x00184599 _ZN12_GLOBAL__N_127update_query_with_predicateEP11NSPredicateP9RLMSchemaP15RLMObjectSchemaRN7tightdb5QueryE + 2553
    3   <redacted>          0x0018381a _Z27RLMUpdateQueryWithPredicatePN7tightdb5QueryEP11NSPredicateP9RLMSchemaP15RLMObjectSchema + 378
    4   <redacted>          0x0017f26c _Z13RLMGetObjectsP8RLMRealmP8NSStringP11NSPredicate + 748
    5   <redacted>          0x00171751 +[RLMObject objectsInRealm:withPredicate:] + 161
    6   <redacted>          0x00171465 +[RLMObject objectsInRealm:where:args:] + 213
    7   <redacted>          0x001712f3 +[RLMObject objectsInRealm:where:] + 419

3) 再次,这个非常相似。归根结底是领域缺乏对 NSPredicate 的完整功能集的支持。

*** Terminating app due to uncaught exception 'Invalid predicate', reason: 'Only support compound and comparison predicates'
*** First throw call stack:
(
    0   CoreFoundation      0x03308946 __exceptionPreprocess + 182
    1   libobjc.A.dylib     0x028fea97 objc_exception_throw + 44
    2   <redacted>          0x001638bd _ZN12_GLOBAL__N_127update_query_with_predicateEP11NSPredicateP9RLMSchemaP15RLMObjectSchemaRN7tightdb5QueryE + 4397
    3   <redacted>          0x0016240a _Z27RLMUpdateQueryWithPredicatePN7tightdb5QueryEP11NSPredicateP9RLMSchemaP15RLMObjectSchema + 378
    4   <redacted>          0x0015de5c _Z13RLMGetObjectsP8RLMRealmP8NSStringP11NSPredicate + 748
    5   <redacted>          0x00150341 +[RLMObject objectsInRealm:withPredicate:] + 161

【问题讨论】:

【参考方案1】:

您的前两个选项应该没有问题。编写此查询的最简单方法是:

NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
RLMResults *results = [RLMFoo objectsWhere:@"id IN %@", primaryKeys];

一个名为 objectsWithPredicate 的方法也可以工作。使用前几种方法时,您能否分享更多代码/堆栈跟踪?我认为其他地方发生了问题

【讨论】:

您好,感谢您的回答。我已经更新了原始问题。简而言之:IN 被 Realm 用作 BETWEEN 的别名,因此只能取 2 个值。而完全不支持块谓词。 嘿,没问题!你能更新你实际测试这些方法的初始值和对象吗? foo bar 示例与 IN 运算符一起使用。我正在尝试重现您看到的错误 应该有更快更方便的方法来做到这一点 谢谢Yoshyosh【参考方案2】:

在您的帮助下,我找到了这种行为的原因。 我的真实代码更接近于:

  NSArray *primaryKeys = @[ @"bar1", @"bar2", @"bar3" ];

  NSString *primaryKeyName = @"bar";
  RLMResults *results =
      [RLMFoo objectsInRealm:self.realm
               withPredicate:[NSPredicate predicateWithFormat:@"%@ IN %@",
                                                              primaryKeyName,
                                                              primaryKeys]];

在方法中,PK 和 PK 名称是方法调用的参数。从理论上讲,这将允许我的班级检索具有任何 PK 名称的任何对象。 原来我的错误是假设 predicateWithFormat 在这种情况下会以与 stringWithFormat 类似的方式工作,并正确地将 PK 名称替换为第一个参数,并将 PK 数组作为第二个参数。然而解决方案是先组装格式字符串,然后组装谓词:

  RLMResults *results = [RLMFoo
      objectsInRealm:self.realm
       withPredicate:[NSPredicate
                         predicateWithFormat:
                             [NSString stringWithFormat:@"%@ IN %%@", primaryKeyName],
                             primaryKeys]];

感谢 Yoshyosh 抽出宝贵时间。

【讨论】:

太棒了,很高兴听到您找到了解决方案。如果将@"%@ IN %@" 替换为@"%K IN %@",也可以使用第一个示例。 %K 让带有格式的谓词知道您要将字符串作为属性而不是纯值放入字符串 这是一个更好的解决方案。 非常感谢 user3099609。首先我不明白你过去 3 天的回答,尤其是primaryKeyName。我不知道这是什么,我输入输入文本作为主键名称。所以它是最终错误并且在primaryKeys中我有对象而不是输入输入的文本。根据我的项目primaryKeyName是@“name”,peimaryKeys是NSArray *primaryKeys = @[textField.text];今天只有我在Senior的帮助下解决了这个问题。 我保存了 name:@"user3182143",empid:@"001" 和 bloodGp:@"AB+" 等数据。这里我的primaryKeyName 是name、empid 和bloodGP。我必须将其作为primaryKeyName 传递,而不是textField、textView、label 文本或其他数据。在primaryKeys 中我必须传递textField、textView、label 文本或其他数据。

以上是关于Realm Cocoa:通过 PK 查找多个对象的主要内容,如果未能解决你的问题,请参考以下文章

使用 Realm 进行 Swift XCTest UI 测试

Realm Swift - 按 ID 查找

多个Realm对象为JSON

我应该为 Realm 中的每个实体定义主键吗?

用 ModelViewSet 的多个查找字段替换 Default PK slug

Core Data Cocoa 错误 1570。不能保存多个实体对象