[Leopard] SUBQUERY predicate with CoreData

  • I have an example CoreData managed object model that contains an
    entity Foo{fooProp:string; keyValues:{to- m,eany relationship to
    KeyValue}) and an entity KeyValue(key:string,value:float; foo:{to-one
    relationship to Foo}). I'm having trouble executing a fetch request
    with a predicate that contains a SUBQUERY expression on a managed
    object context using this model. I've included the .xcdatamodel (zip'd
    with an added extension -- hopefully it will make it pass the list
    filter). In the Xcode "CoreData application", using the included
    .xcdatamodel and adding this method to the app delegate:

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

        // add some examples to the XML store
        id foo = [NSEntityDescription insertNewObjectForEntityForName:@"Foo"

    inManagedObjectContext:[self managedObjectContext]];
        [foo setFooProp:@"fooProp1"];

        id kv = [NSEntityDescription insertNewObjectForEntityForName:@"KeyValue"
                                              inManagedObjectContext:[self
    managedObjectContext]];

        [kv setKey:@"key1"];
        [kv setValue:[NSNumber numberWithFloat:1.0] forKey:@"floatValue"];

        [foo addKeyValuesObject:kv];

        foo = [NSEntityDescription insertNewObjectForEntityForName:@"Foo"

    inManagedObjectContext:[self managedObjectContext]];
        [foo setFooProp:@"fooProp2"];

        kv = [NSEntityDescription insertNewObjectForEntityForName:@"KeyValue"
                                              inManagedObjectContext:[self
    managedObjectContext]];

        [kv setKey:@"key2"];
        [kv setValue:[NSNumber numberWithFloat:2.0] forKey:@"floatValue"];

        [foo addKeyValuesObject:kv];

        NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
        [fetch setEntity:[NSEntityDescription entityForName:@"Foo"
                                    inManagedObjectContext:[self
    managedObjectContext]]];

        [fetch setPredicate:[NSPredicate
    predicateWithFormat:@"(SUBQUERY(self.keyValues, $kv, $kv.key like
    'key1' && $kv.floatValue==1.0).count > 0) && fooProp like
    'fooProp1'"]];

        id result = [[self managedObjectContext] executeFetchRequest:fetch

    error:nil]; //app crashes here

        NSLog(@"%@", result);

    }

    produces the following (relevant) output at the console

    *** -[NSCFArray compare:]: unrecognized selector sent to instance 0x195220

    and the application crashes during the  [[self managedObjectContext]
    executeFetchRequest:fetch error:nil] method.

    I know the documentation on using SUBQUERY is still on the way, but I
    was hoping that one of the CoreData gurus could suggest what I'm doing
    wrong.

    Thanks!

    Barry
  • On Feb 4, 2008, at 14:02, Barry Wark wrote:
    > "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    > $kv.floatValue==1.0).count > 0) && fooProp like 'fooProp1'"]];
    >

    should be:

    "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    $kv.floatValue==1.0)<....> > 0) && fooProp like 'fooProp1'"]];
  • > [fetch setPredicate:[NSPredicate
    > predicateWithFormat:@"(SUBQUERY(self.keyValues, $kv, $kv.key like
    > 'key1' && $kv.floatValue==1.0).count >  0) && fooProp like
    > 'fooProp1'"]];

    A SUBQUERY expression produces an array of results, a lot like
    NSArray's -filteredArrayWithPredicate:

    Tacking on .count at the end is adding a keypath.  Basically it's like
    [subqueryResults valueForKeyPath:@"count"]

    NSArray responds to the keypath .count by invoking -count on each
    element and giving you back an array of all the counts.  This array
    is not happy being compared to a scalar 0.

    NSArray responds to the keypath "@count" in the manner you want.  You
    can use either

    "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    $kv.floatValue==1.0)<....> > 0)"

    or

    "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    $kv.floatValue==1.0)[size] > 0)"

    where the second form is using some of the built-in predicate
    operators for aggregates.
    --

    -Ben
  • Thank you to Ben and Melissa for setting me straight. I notice that
    the NSExpression documentation gives

    "SUBQUERY(residents, $x, $x.firstname == "Jane" && $x.lastname ==
    "Doe").count != 0)"

    as an example subquery for a similar use case. Besides the missing
    opening '(', this example has the, as I know now, incorrect use of
    .count != 0 pattern. Is this well known, or worth filing a bug for the
    documentation?

    Thanks,
    Barry

    On Feb 4, 2008 3:29 PM, Ben Trumbull <trumbull...> wrote:
    >> [fetch setPredicate:[NSPredicate
    >> predicateWithFormat:@"(SUBQUERY(self.keyValues, $kv, $kv.key like
    >> 'key1' && $kv.floatValue==1.0).count > 0) && fooProp like
    >> 'fooProp1'"]];
    >
    > A SUBQUERY expression produces an array of results, a lot like
    > NSArray's -filteredArrayWithPredicate:
    >
    > Tacking on .count at the end is adding a keypath.  Basically it's like
    > [subqueryResults valueForKeyPath:@"count"]
    >
    > NSArray responds to the keypath .count by invoking -count on each
    > element and giving you back an array of all the counts.  This array
    > is not happy being compared to a scalar 0.
    >
    > NSArray responds to the keypath "@count" in the manner you want.  You
    > can use either
    >
    > "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    > $kv.floatValue==1.0)<....> > 0)"
    >
    > or
    >
    > "(SUBQUERY(self.keyValues, $kv, $kv.key like 'key1' &&
    > $kv.floatValue==1.0)[size] > 0)"
    >
    > where the second form is using some of the built-in predicate
    > operators for aggregates.
    > --
    >
    > -Ben
    >
  • On Feb 4, 2008, at 8:00 PM, Barry Wark wrote:

    > Is this well known, or worth filing a bug for the
    > documentation?

      There's no problem with always filing a bug if you notice it. That
    way there's no question as to whether the doc team knows about it.

    --
    I.S.