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