views:

803

answers:

1

Hi,

I've run into a problem when I've been porting my code over from a SQLite database to Core Data.

The data that I'm using comes from a existing database and as such has all the relationships defined using the ID of each of the tables (or entities now that I'm using Core Data). My problem is that I want to query against a single table then use that result to propagate up though the data to get all the other data that I require.

The original database looks like:

CREATE TABLE TecAccessPoints (MAC varchar NOT NULL PRIMARY KEY UNIQUE,ZoneID integer NOT NULL,Floor integer);
CREATE TABLE Zones (ID integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,ZoneName varchar NOT NULL,BuildingID integer,ZoneDescription varchar);
CREATE TABLE Buildings (ID integer NOT NULL PRIMARY KEY AUTOINCREMENT,BuildingName varchar NOT NULL,CampusID integer NOT NULL,BuildingDescription varchar,Long float,Lat float);
CREATE TABLE Campuses (ID integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,CampusName varchar NOT NULL,CampusDescription text, Latitude float, Longitude float);

My original SQL query is:

SELECT DISTINCT Zones.ID, Zones.ZoneName, Buildings.ID, Buildings.BuildingName, Campuses.ID, Campuses.CampusName, Rooms.ID, Rooms.NameNumber, Rooms.Floor 
FROM Zones, TecAccessPoints, Buildings, Campuses, Rooms 
WHERE Campuses.ID = Buildings.CampusID 
   AND Buildings.ID = Zones.BuildingID 
   AND Zones.ID = Rooms.ZoneID 
   AND Zones.ID = TecAccessPoints.ZoneID 
   AND TecAccessPoints.MAC = '%@';

Is there any way that I can replicate this query using NSPredicate (or something similar) or is it the case that I'll have to first perform the lookup

TecAccessPoints.MAC == '%@'

Then do another lookup on the data using the returned data like:

Zones.ID == TecAccessPoints.ZoneID

And so on until I have all my results that I need?

Thanks

James

+1  A: 

You can't replicate your query as a Core Data predicate because you can fetch only one entity at a time. However, this may be a case where thinking about things as the object graph rather than the database tables might be much more fruitful. If you have relationships

TecAccessPoints <*-> Zones <*-> Buildings <-> etc.
                           <-*> Rooms

You can easily query TecAccessPoints as you show above then follow the relationships in code, e.g.

TecAccessPoint *tap;
// fetch tap

Campus *campus = tap.zone.building.campus;
NSSet *rooms = tap.zone.rooms;

assuming the relationships are named zone, building, rooms, etc. and you have appropriate NSManagedObject categories to define the associated @properties so that the compiler doesn't complain.

Of course, underneath Core Data is doing a sequence of SQL queries which are each a JOIN across multiple tables. Core Data is quite good about making the performance hit of this minimal, but if you're a DB geek, this may bother you. If so, you'll have to stick with the raw SQLite. Moving to Core Data implies you're willing to think about things at the object graph level and ignore the implementation details for the most part.

Barry Wark
Thats what I was afraid of - making the relationships between the entities (which require it to be linked to an object) when the current relationships are mapped by using the primary keys in the table - I'm keen to use Core Data because it *should* make my code easier to understand to anyone joining development in the future but I do like the ability to perform joins on tables... I guess I'm going to hit a barrier either on import by making all the relationships or on query when I have to call each entity separately...
James Raybould
Like you say, there has to be some pain in translating between database schema, regardless of whether you involve Core Data or not (obviously high-end database systems or ORMs can help mitigate this pain but someone had to endure the pain to code them...). In this case, doing the JOINs on import is the right way to go. First of all, you can take the coding time (and runtime performance hit) once during import rather than at every query. Second, you will be able to use the Core Data framework to its full potential.
Barry Wark
Thanks for your help with this Barry, I've decided to stick with the current SQLite implementation - partly so I don't exclude anyone on 2.x and partly because my import code was starting to get a bit bloated. The other thing I've been seeing as I've been investigating this is that for every entity I grab from the persistent store it core data places it and ALL of its relations in to memory, where as with the SQL query I can select which parts of which tables I want to load. I've kept a core data branch so that I can come back to it in future though. Again thanks.
James Raybould
Like any other, Core Data is just a tool. If the current implementation works for you, that's great! Just a quick note: when you get an instance from the persistent store, you can get it as a fault. When that fault fires, only the attributes are loaded -- relationships are still a fault that doesn't fire until you traverse that relationship. In a fetch request you can specify if you want to pre-fault (i.e. load) the targets of *particular* relationships from the fetch's target entity. You never need to load all the relationships if you don't want to.
Barry Wark