Core Data many-to-many predicate (again) - core-data

Yesterday I asked a question about a many-to-many relationship:
I have two entities which are related by a many-to-many relationship:
Database <<----->> Category
In other words, a database can have many categories and a category can
be associated with many databases.
I need an NSPredicate that will return all Category objects associated
with a given Database object.
...and received some very helpful advice. I would now like to ask a different, but related, question:
I would like an NSPredicate that will return all Category objects NOT associated with a given Database object.
I require this as a predicate since it is being used as part of a fetched results controller.
Any help greatly appreciated. Thanks in advance!

It seems to be a Core Data bug that "NOT ANY" queries do not work correctly
(see Core Data NSPredicate with to-Many Relationship for a similar issue).
As a workaround, you can use the following predicate with "SUBQUERY":
[NSPredicate predicateWithFormat:#"SUBQUERY(databases, $db, $db == %#).#count == 0", theDatabase]

Try to use:
[NSPredicate predicateWithFormat:#"NOT (ANY databases = %#)", database];
Or
[NSPredicate predicateWithFormat:#"NOT(%# IN databases)", database]

Related

NSPredicate and KVC collection operators

Assume I have a Core Data class Employer with a 1-N relationship called employees.
This employees relationship maps to an NSSet. So far so good.
I want to create an NSFetchRequest of Employer with a predicate that requieres that employees > 0.
How can I create this predicate?
I tried "employees.#count", but it seems to consider the whole thing a keyPath.
You got the syntax backwards:
#count.employees

How to filter entities by to-one relationship in Core Data.

In my function unit, I got an entity author's record. I need find out all books written by this author.
The Book has a relationship called belongToAuthor. How to request book records?
Can I use following NSPredicate to filter books by author relationship?
Author *author;
author = _author;
NSPredicate* predicate = [NSpredicate predicateWithFormat:#"ANY book.belongToAuthor == %#", author];
If you've followed the Core Data design principles, your relationship belongToAuthor (by the way, simply calling it author might be a much better name) has an inverse relationship on the Author class pointing the Bookclass - named something simple like books.
Book.belongToAuthor <<-> Author.books
With that you can simply rely on the power of Core Data and get all books written by that author by calling
NSSet *books = author.books;
Related documentation: Core Data Programming Guide - Relationships

NSPredicate for items in two-levels deep to-many relationship, w/o direct relationship

I'm trying to build a single NSFetchRequest predicate for the following (simplified) model:
There is a simple to-many relationship between a Category and a number of Brands.
Each Brand then has a modelNumbersData property which is binary data, a serialisation of an array of NSString modelNumbers which are exposed in a transient property on the Brand objects.
There is no direct relationship between Brands and Models. The relationship is that a Model's modelNumber may be in a Brand's modelNumbers transient property.
I would like to build an NSPredicate query to fetch all of the Model objects under a particular Category.
Fetching the Models for a Brand is easy, I can do "modelNumber IN $FETCH_SOURCE.modelNumbers". How do I now extend this query to originate with the category? It seems I need a SUBQUERY?
Furthermore, I am doing an NSFetchRequest, so unless I'm mistaken I need to start with "SELF.modelNumber IN (...)", so that we select from all Models.
Thanks in advance!
Ok, I got there first. For reference, the following works:
SUBQUERY(%#.brands, $brand, $brand.modelNumbers CONTAINS $modelNumber).#count > 0
I think the problem I was mostly having was neglecting the .#count component. Still don't quite understand this, but it seems to be necessary with all SUBQUERY statements.

Related NSManagedObject in NSPredicate

In my NSFetchedResultsController I want to have an NSPredicate which only fetches objects with certain relationships.
For example:
I have three subclassed NSManagedObjects: Flight, Aircraft, Battery.
Every Flight has one Aircraft. Every Flight can have several Battery objects.
So when I am fetching the Flight objects, this is something I have tried with the predicate: [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"aircraft = %#", aircraftObj]];
So I only want to fetch the Flight objects which are related to that specific Aircraft object. This doesn't work, but how can I do this?
Secondly, building on that, the Flight has batteries which is an NSSet of Battery objects. So if I want to do the same as the Aircraft, but filter the Battery objects, how would I do this?
Thanks, let me know if I need to explain further.
You have a problem of "double formatting" your predicate.
You could simply write:
[NSPredicate predicateWithFormat:#"aircraft = %#", aircraftObj]
the same would go for your Battery objects (but with the proper Flight)

Xcode4 Core Data Relationships

I've once again read through the Apple developer Core Data documentation and found it lacking when it comes to the graphical Xcode 4 editor when creating SQLLite entities much as I found it lacking when IB was separate in Xcode 3.
Three tables:
ZipData
LocationData
CrossReference
CrossReference has the primary key of ZipData and LocationData so I only need to query CrossReference to get all zips for locations or all locations for zips. This means of course a to-many relationship on both ZipData and LocationData (and perhaps on CrossReference?).
What I have (that isn't working) relationship-wise is this :
ZipData has a relationship "locations" that points to LocationData and is inverse
LocationData has a relationship "zipsCodes" that points to ZipData and is inverse
CrossReference table has two relationships, one to ZipData (and is inverse) and one to LocationData (and is inverse).
I'm not sub-classing any of the entities as NSManagedObjects just yet. I am simply doing the code below in the viewDidLoad method, just to see if what I have setup works.
// test/learn the core data frame work
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *locationData = [NSEntityDescription
insertNewObjectForEntityForName:#"LocationData"
inManagedObjectContext:context];
[locationData setValue:#"Testville" forKey:#"City"];
[locationData setValue:#"United Tests" forKey:#"Country"];
[locationData setValue:#"County of Test" forKey:#"County"];
NSManagedObject *zipCodeData = [NSEntityDescription
insertNewObjectForEntityForName:#"ZipCodeData"
inManagedObjectContext:context];
[zipCodeData setValue:[NSNumber numberWithDouble:1111.00] forKey:#"Income"];
[zipCodeData setValue:[NSNumber numberWithDouble:22.00] forKey:#"LandArea"];
[zipCodeData setValue:#"23060" forKey:#"ZipCode"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"CrossReference" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
NSLog(#"LocationId: %#", [info valueForKey:#"LocationDataId"]);
NSManagedObject *details = [info valueForKey:#"details"];
NSLog(#"ZipId: %#", [details valueForKey:#"ZipCodeDataId"]);
}
[fetchRequest release];
I don't understand how to setup these relationships and not sure how to trust that somehow the primary keys are setup and the entities just kind of find their way together.
I'm getting nothing back in the logs even though when I view the simulators sqllite db I see the test entities have been persisted (but nothing in CrossReference). I know I'm missing something relationship wise but I can't put my finger on it.
Your major problem is revealed by the bolded phrases below:
I've once again read through the Apple
developer Core Data documentation and
found it lacking when it comes to the
graphical Xcode 4 editor when creating
SQLLite entities much as I found it
lacking when IB was separate in Xcode
3.
Three tables:
ZipData LocationData CrossReference
CrossReference has the primary key of
ZipData and LocationData so I only
need to query CrossReference to get
all zips for locations or all
locations for zips.
There is no such thing as SQLite entities and Core Data does not have tables or primary keys.
Core Data is not SQL. Entities are not tables. Objects are not rows. Attributes are not columns. Relationships are not joins. Core Data is an object graph management system that may or may not persist the object graph and may or may not use SQL far behind the scenes to do so. Trying to think of Core Data in SQL terms will cause you to completely misunderstand Core Data and result in much grief and wasted time.
You are making a mistake common to people skilled in SQL. You are assuming that Core Data is a lightweight object wrapper around procedural SQL. It is not. The SQLite store is but one of four persistence options and the data model itself is wholly independent of which persistence option you choose i.e. the same model will work with all types of stores. The stores are just different means of archiving and de-archiving (freeze drying and rehydrating) a graph of live objects. How a specific graph gets persisted is a behind the scenes implementation detail you can ignore in the vast majority of cases. Simply forget everything you know about SQL because it won't help you understand Core Data.
Your specific problem here is that you never set the relationships between the objects. You need to create a CrossReference object and set it's relationships, thusly;
NSManagedObject *crossReference = [NSEntityDescription
insertNewObjectForEntityForName:#"CrossReference"
inManagedObjectContext:context];
[crossReference setValue:locationData forKey:#"location"];
[crossReference setValue:zipCodeData forKey:#"zipCode"];
The context will ensure that the reciprocal relationship is set on the related LocationData and ZipData objects.
The key to mastering Core Data is to ignore the form of persistence and to instead think only in terms of objects with attributes and relationships. Once you really internalize that concept, then every thing falls into place easily.

Resources