Realm Cocoa:通过 PK 查找多个对象
Realm Cocoa: finding multiple objects by PKs
潜伏已久,第一次提问。
我在一个项目中使用 Realm Cocoa(来自 Realm.io)并且正在努力通过 PK 执行搜索。
假设我有一个名为 RLMFoo
的实体,它有一个名为 bar
的主键。我还有一个 PK 列表,假设存储在一个数组中:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
有没有办法在一次查询中从我的领域检索 class 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
您的前两个选项应该没问题。编写此查询的最简单方法是:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
RLMResults *results = [RLMFoo objectsWhere:@"id IN %@", primaryKeys];
一个叫做 objectsWithPredicate 的方法也可以。您可以分享更多使用前几种方法时的 code/stack 轨迹吗?我认为其他地方发生了问题
在您的帮助下,我找到了这种行为的原因。
我的真实代码更接近于:
NSArray *primaryKeys = @[ @"bar1", @"bar2", @"bar3" ];
NSString *primaryKeyName = @"bar";
RLMResults *results =
[RLMFoo objectsInRealm:self.realm
withPredicate:[NSPredicate predicateWithFormat:@"%@ IN %@",
primaryKeyName,
primaryKeys]];
在方法中,PK 和 PK 名称是方法调用的参数。理论上,这将允许我的 class 检索具有任何 PK 名称的任何对象。
原来我的错误是假设 predicateWithFormat 在这种情况下会以与 stringWithFormat 类似的方式工作,并正确地将 PK 名称替换为第一个参数,并将 PK 数组作为第二个参数。然而解决方案原来是首先组装格式字符串,然后是谓词:
RLMResults *results = [RLMFoo
objectsInRealm:self.realm
withPredicate:[NSPredicate
predicateWithFormat:
[NSString stringWithFormat:@"%@ IN %%@", primaryKeyName],
primaryKeys]];
感谢 Yoshyosh 抽出时间。
潜伏已久,第一次提问。
我在一个项目中使用 Realm Cocoa(来自 Realm.io)并且正在努力通过 PK 执行搜索。
假设我有一个名为 RLMFoo
的实体,它有一个名为 bar
的主键。我还有一个 PK 列表,假设存储在一个数组中:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
有没有办法在一次查询中从我的领域检索 class 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
您的前两个选项应该没问题。编写此查询的最简单方法是:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
RLMResults *results = [RLMFoo objectsWhere:@"id IN %@", primaryKeys];
一个叫做 objectsWithPredicate 的方法也可以。您可以分享更多使用前几种方法时的 code/stack 轨迹吗?我认为其他地方发生了问题
在您的帮助下,我找到了这种行为的原因。 我的真实代码更接近于:
NSArray *primaryKeys = @[ @"bar1", @"bar2", @"bar3" ];
NSString *primaryKeyName = @"bar";
RLMResults *results =
[RLMFoo objectsInRealm:self.realm
withPredicate:[NSPredicate predicateWithFormat:@"%@ IN %@",
primaryKeyName,
primaryKeys]];
在方法中,PK 和 PK 名称是方法调用的参数。理论上,这将允许我的 class 检索具有任何 PK 名称的任何对象。 原来我的错误是假设 predicateWithFormat 在这种情况下会以与 stringWithFormat 类似的方式工作,并正确地将 PK 名称替换为第一个参数,并将 PK 数组作为第二个参数。然而解决方案原来是首先组装格式字符串,然后是谓词:
RLMResults *results = [RLMFoo
objectsInRealm:self.realm
withPredicate:[NSPredicate
predicateWithFormat:
[NSString stringWithFormat:@"%@ IN %%@", primaryKeyName],
primaryKeys]];
感谢 Yoshyosh 抽出时间。