问题
Long time lurker, first time asker.
I'm using Realm Cocoa (from Realm.io) in a project and am struggling to perform searches by PKs.
Let's say I have an entity called RLMFoo which has a primary key called bar. I also have a list of PKs, let's say stored in an array:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
Is there any way to retrieve all entities of class RLMFoo from my realm in one single query?
I've tried so far:
- Predicate with format:
[RLMFoo objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:@"bar IN %@", primaryKeys]]; - Realm's where:
[RLMFoo objectsInRealm:realm where:@"bar IN %@", strippedIds]; - Predicate with block:
.
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id
evaluatedObject, NSDictionary *bindings) {
RLMFoo *foo = (RLMFoo *)evaluatedObject;
return [primaryKeys containsObject:foo.bar];
}];
[RLMFoo objectsInRealm:realm withPredicate:predicate];
But the only thing that I've found to work so far, is to query the realm for each primary key value and aggregate the results, which seems slow:
NSMutableArray *results = [NSMutableArray new];
for (NSString *primaryKey in primaryKeys) {
RLMFoo *obj = [RLMFoo objectInRealm:realm forPrimaryKey:primaryKey];
if (obj) {
[results addObject:obj];
}
}
Does anyone know of a better way to do it?
========= Explaining why the first three methods don't work =======
1) The reason why this doesn't work seems apparent from the exception message: IN only takes 2 values, although the SQL version of IN should take as many as necessary. As we can see from the source code of RLMQueryUtil.mm, the same applies to the BETWEEN operator:
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");
}
Here's the stack trace:
*** 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) This is very similar in reason and stack trace:
*** 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) This one is, again, very similar. What it boils down to is lack of support from realm for the full feature set of 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:
your first two options should work no problem. The simplest way to write this query is:
NSArray *primaryKeys = @[@"bar1", @"bar2", @"bar3"]
RLMResults *results = [RLMFoo objectsWhere:@"id IN %@", primaryKeys];
A method called objectsWithPredicate works too. Can you share more of your code/stack trace when using your first few methods? I think there are issues happening elsewhere
回答2:
With your help, I was able to find the reason for this behaviour. My real code was something closer to:
NSArray *primaryKeys = @[ @"bar1", @"bar2", @"bar3" ];
NSString *primaryKeyName = @"bar";
RLMResults *results =
[RLMFoo objectsInRealm:self.realm
withPredicate:[NSPredicate predicateWithFormat:@"%@ IN %@",
primaryKeyName,
primaryKeys]];
Which was in a method and the PKs and the PK name were parameters to the method call. In theory this would allow my class to retrieve whatever objects with whatever PK name. Turns out my mistake is in assuming predicateWithFormat would, in this case, work in a similar way to stringWithFormat and properly replace the PK name as the first argument and take the PK array as the second one. However the solution turned out to be first assembling the format string, and then the predicate:
RLMResults *results = [RLMFoo
objectsInRealm:self.realm
withPredicate:[NSPredicate
predicateWithFormat:
[NSString stringWithFormat:@"%@ IN %%@", primaryKeyName],
primaryKeys]];
Thanks Yoshyosh for your time.
来源:https://stackoverflow.com/questions/27798898/realm-cocoa-finding-multiple-objects-by-pks