Core Data: Parent Entity Vs Relationships - ios

I'm working on a data-collecting app and I'm having trouble gaining an understanding of how these concepts connect? Here is my scheme:
Site <----->> Station <------->> Observation Event
Site has one attribute, the name of a Site, and can containing multiple stations. Each station will have multiple observations over time. I have these set with the Event to the left as a parent event and created one-to-many relationships as diagrammed, Since each observation event will need to be tagged with site and station.
I'm assuming the parent entity is the best way to create this, or is that what a relationship would do? I expect the user would setup site/station data ahead of time and then observation data would be filled in as they were made.
In short, I just can't wrap my head around what a relationship does in core data and if a parent entity would be redundant. The core data documentation is just not clear to me on this. Any help would be vastly appreciated!!

In essence, what you're going to see when you generate your entity classes, is that in addition to the attributes of each entity you'll have an NSSet for the "to-many" relationship. You can reference any of the "records" in the to-Many relationship by the values in the set.
It seems complicated at first but then it makes total sense. So, if you want to look at the stations, you'll maybe have a "stations" set that includes a list of managed objects for each of the station entities for that site. Each station will contain a set with the managed objects for each of the related observations.
So, once you have a Site entity, you could look at all the stations for that site with something like this:
Site *site = (Site *) managedObjectForSite;
for (NSManagedObject *station in site.stations)
{
Station *stat = (Station *) station;
(do what you need to with the station record)
}
You "link" sites with stations by adding members to the stations set of a given site record, where each member is a station's managed object. You are relieved of the responsibility of "reading" station records -- once you have the members of the set which are loaded with the site, each of those is effectively a managed object for the related stations.
When the light comes on it will all be crystal clear at once. You have to work through it once then you'll pretty much know what's happening in there..
Please also see this as it may help: One-to-Many Relationship: CoreData

In Core Data, relationships have a similar function a foreign keys in a classic relational database setup.
In a database, you would "connect" the Site, Station and Event entities with a foreign key:
Site .id = Station .siteID
Station .id = Event .stationID
In core data this is not necessary. Neither of the two entities needs an extra ID attribute, instead you just define one-to-many relationships.
Site <--->> Station <---->> Event
The advantage: you can access the site from the station, or all the stations from the site with transparent and highly legible dot-notation as you would expect from an object graph. You can even conveniently get the site from an event object, etc.
Site *aStationsSite = station.site;
NSSet *aSitesStations = site.stations;
NSSet *aStationsEvents = station.events;
NSSet *sisterStations = station.site.stations;
Site *siteFromEvent = event.station.site;

Related

How do entities in relationship know what to join on

This is a basic question from someone transitioning from SQL-based databases that I normally sweep under the carpet but would really like to understand. When two entities are joined in a relationship, how does Core Data figure out what attribute to join on? Does it figure it out by matching attribute names, or just how does it know?
I'm asking to understand why following code is not working.
I have one entity, Books with attributes as follows:
aid|authorname
I have another entity, Authors with attributes
bid|bookname|authorid
//note authorid here is spelled differently than in the author entity.
Authors has a Many relationship to Books named book
Books has One relationship to Authors named author.
In the books VC, in the .h file I have
#property (nonatomic,weak) Books * book;
In the .m file I have following code in ViewDidLoad
NSString *authorname = self.book.author.authorname;
//this is supposed to be book-object.author-relationship,authorname-attribute
//however it displays blank
NSLog(#"author name%#",authorname); //displays blank
I have very similar code working elsewhere in the app that works fine (though both relationships are one-to-one) so I think I must be missing something dumb somewhere.
However, it has prompted me to wonder exactly how does core-data figure out which author goes with which books?
Are the attribute names supposed to match up?
Thanks for any insights.
CoreData "knows" that two objects are related if and only if you tell it, which you do by (assuming you have defined the relationship in the data model editor) assigning one object to the relationship property of the other object:
myBook.author = myAuthor;
or equivalently
[myAuthor addBooksObject:myBook];
Once you've done that, CoreData will "know" that those two objects are related (even if you save the data then reload it). This is completely independent of the attributes (such as aid and authorid) that you might think indicate that two objects are related.
If you want Books to be related to the Author with aid equal to the Book's authorid, you have program it that way. So, if for example you sync an Author from your server, with aid = 123, and then sync some Books with authorid = 123, you will need to fetch (unless you already have a reference to it) the Author with aid = 123, and set the relationship with code similar to the above. I suspect it is this step which you have missed, and is causing the blank author name.
If you do this during your sync process, you can thereafter just rely on the relationship, with no need to worry about primary keys, foreign keys, joins, etc. Behind the scenes, CoreData maintains a unique primary key for each entity, and foreign keys for each relationship. You can see these if you directly inspect the underlying SQLite database, and/or if you activate SQLDebug.

Core Data Model

I'm struggling with creating a suitable Core Data model for my app. I'm hoping someone here can provide some guidance.
I have two entities -- "Goals" and "Items". The Goals entity contains only a goal description, but any goal may have any number of subgoals, and these may extend multiple levels in a tree structure. Subgoals are to be contained within the same entity, so presumably the Goal entity will contain a pointer to "parent" which will be the parent goal of any subgoal.
There will also be an "Items" entity that contains a couple of text fields and a couple of binary items, and must be linked (ideally, by a unique identifier, perhaps objectID) to the particular goal or subgoal the item(s) are related to.
I am totally fumbling with how to set this model up. I know what attributes need to be in each entity, but the relationships, particularly between goals and "subgoals", has me stumped. I don't seem to be able to turn up any good examples of tree structures in Core Data on the Internet, and even the couple of books I have on Core Data don't seem to address it.
Can anyone here help an old SQL programmer get headed the right direction with these relationships in Core Data? Thanks.
Have you tried creating a one-to-many from Goal to itself, and a one-to-one from Goal to Item? The only thing I would worry about here is circular references.
Also, read Relationships and Fetched Properties in the CoreData Programming Guide.
Here is how it is done:
You set up a to-many relationship from Goal to Item in the model editor. Don't use any ids, foreign keys etc. This is old-fashioned database thinking - you can forget about it. Here we are only dealing with an object graph. The database layer is just an implementation detail for persisting the data.
Make two more relationships in entity Goal to itself: a to-one called parent, a to-many called subGoals. Make them the inverse of each other. Simple!
QED is correct, you can create a to many relationship on goal (call it subgoals) as well as a to-one relationship on goal (call it parentGoal) and set them as inverses to each other.
Then create another to many relationship (call it items) on the goal entity, with the inverse being a to one relationship on the item entity (call it goal). Then you're all set. You don't need to link items with a unique id, just add them to the items relationship.
Also note that if you did want to give items a unique id, do not use the objectID. The objectID should only be used as a temporary id as they are not guaranteed to remain the same. In fact they will change if you ever do a Core Data migration.
One way, though not really great, is to create a another entity, say subGoal, and each goal has one subGoal and each object of subGoal has many goal.

How to force UNIDIRECTIONAL to-many relationship to persist

There is a problem with core data when a to-many relationship has no inverse. Changes made to the related property do not persist. This is a problem many of us have faced, as it can be found by googling.
This is to ask if some of you found a trick/workaround to achieve persistence, beside the obvious answer or adding an inverse relationship.
Background:
Even if unidirectional relationship are discouraged in the documentation, they are not forbidden. The doc only insists on responsibility incurred when having no inverse.
The reason of not wanting an inverse is outlined in the core-data doc: when you have a large number of items linked to one entity the inverse relationship is loading a large NSSet each time an item is added. Consuming memory, possibly more than allowed for no reason.
Example
In employees/department typical paradigm, if you have a huge number of employees able to belong to several departments, you need a to-many relationship from employee to department. You do not want the inverse because each time an employee is linked to a department, a (very) large NSSet must be loaded, updated and saved. Moreover if the department entity is never deleted, graph integrity is easy to maintain.
Please do not reply that this is a feature of core-data and that inverse relationship is mandatory. This is not stated as such and is more like a bug than a feature. Posting a bug report is not solving the point for current deployed systems.
Edit: The Join entity solution
This edit is to give more light and discussion to Dan Shelly's answer proposal below.
First, to reply to your first, I'm not trying to have a many-to-many but a true unidirectional to-many. The very same page your linked has this text a bit below the one you quoted:
Unidirectional Relationships
It is not strictly necessary to model a relationship in both directions. In some cases it may be useful not to, for example when a to-many relationship may have a very large number of destination objects and you are rarely likely to traverse the relationship (you may want to ensure that you do not unnecessarily fault in a large number of objects at the destination of a relationship). Not modeling a relationship in both directions, however, imposes on you a great number of responsibilities, to ensure the consistency of the object graph, for change tracking, and for undo management.
That said your proposed solution of adding an join entity is a way to go if there is no solution to force core-data to generates and updates it automatically.
IMO, and for my use case, the join entity does not even need to have the relationship to Department. This to-one is useless and may be replaced by a property of the join entity keeping related Department information, like its objectID or other indexed property to reach it.
i.e:
DepartmentEmployee:
Properties: Dept_ix (integer)
Relationships: employee (to-one,nullify)
This is a great question.
ButFirst thing first:It clearly state in the documentation:
"Important: You must define many-to-many relationships in both directions—that is, you must specify two relationships, each being the inverse of the other. You can’t just define a to-many relationship in one direction and try to use it as a many-to-many. If you do, you will end up with referential integrity problems."
Never the less, Lets describe the issue (resulting database)
When defining a to-many relationship, the resulting database does not add an additional table to map the relationship.
It only sets a property on the entity at one end of the to-many relationship equal to the last item that referenced it.
Example:
Model:
Entity: Department
Relationships: NONE
Properties: name (string)
Entity: Employee
Relationships: departments (to-many,no-action)
Properties: name
Resulting Database:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
Z2DEPARTMENTS (int)
ZNAME
ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZNAME
This structure will obviously result in data inconsistency.
The solution will be to hold an entity: DepartmentEmployee modeling the to-many relationship in both directions but one of them would be unidirectional (Department -> DepartmentEmployee):
DepartmentEmployee:
Relationships: department (to-one,no-action), employee (to-one,nullify)
and you will have to maintain the table upon deletion of a department object.
Hope this made some sense :)
First a reply for your comment:
IMO, and for my use case, the join entity does not even need to have the relationship to Department. This to-one is useless and may be replaced by a property of the join entity keeping related Department information, like its objectID or other indexed property to reach it.
This is exactly what the department property is doing in the joined relationship.
If you would look at the generated SQLite structure, you will see and additional mapping table between the Employee entity and the Department entity, holding only their int64 ids.
Now, the given example was:
Example
In employees/department typical paradigm, if you have a huge number of employees able to belong to several departments, you need a to-many relationship from employee to department. You do not want the inverse because each time an employee is linked to a department, a (very) large NSSet must be loaded, updated and saved. Moreover if the department entity is never deleted, graph integrity is easy to maintain.
A simple ONE-to-many relationship with no inverse could be easily implemented.
You can look at it as just another property on the object in the 'many' side of the relationship.
This example request a ONE-to-many relationship of the kind:
Employee-->>Department (an Employee may belong to many departments)
The inverse is:
Department-->Employee
Since we must not implement a many-to-many relationships without an inverse, we must implement the to-ONE side of the relationship, just to make sure we comply with the implementation of the framework.
Re-iterating:
By the documentation we know that no many-to-many relationship will NOT persist without an inverse relationship being defined.
==>
Since we like to model the relationship without an inverse we will model it only as the to-ONE part of the coupling (modelling it as a to-many will violate the persistency promised by the framework)
Think of it as useful for defining files in a folder (a file may not belong to more than one folder), or parent child relationship.
==>
We must define the relationship as:
Department-->Employee (Which does not make much sense since a department that can hold only one employee is not really a department is it)
To look at it from another angel (negative proof):
Suppose we would like to go against the framework and define a MANY-to-many relationship with no inverse.
==>
That would mean that we will only implement it in one direction leaving a ... to-many relationship or ... MANY-to relationship
==>
this is the same thing isn't it (a to-many relationship from and entity1 to entity2)
==>
NOW, if we have a ONE-to-many relationship and we choose to not implement the inverse of it, we can choose to implement the to-many part? NO WE CANNOT, this will look as only half of a MANY-to-many relationship
==>
We MUST implement the ONE-to part of it.
For making some more sense, I will show the more logical:
Department-->>Employee
So our implementation for this ONE-to-many relationship would be:
Department<--Employee
This will result in the following SQLite DB structure:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
ZNAME
ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZDEPARTMENT (int)
ZNAME
We could now define a fetched property on Department to fetch all the employees belonging to it:
employees predicate: department == $FETCH_SOURCE
You can enforce this relationship in the prepareForDeletion method of Department (not tested):
(You will first set the userInfo dictionary on Department to hold the type of enforcement)
(I left the implementation of the 'Deny' rule to the reader :D )
- (void) prepareForDeletion
{
[super prepareForDeletion];
NSEntityDescription* entity = [self entity];
NSDictionary* dict = [entity userInfo] ;
if ([dict count]) {
[dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL *stop) {
NSArray* arr = [self valueForKey:key];
if( [value isEqualToString:#"cascade"]) {
for (NSManagedObject* obj in arr) {
[[self managedObjectContext] deleteObject:obj];
}
} else if ( [value isEqualToString:#"nullify"] ) {
NSArray* arr = [self valueForKey:key];
for (NSManagedObject* obj in arr) {
[obj setValue:nil forKey:#"department"];
}
}
}];
}
}
As I see it, this is all you can do with regard to inverse relationships.
If you still believe you need a many-to-many relationship, please refer to my other answer.
Regards,
Dan.
Have you considered doing away with the relationship entirely and programmatically managing the foreign key on employee?
If you have a UI which sets the property from a list of existing Departments (a pick list, etc.) you can simply take the primary key from that list and assign it as the departmentID property on your Employee.
You should then be able to implement a validateDepartmentID:error method on your Employee object which checks that the given departmentID is valid (i.e. is in a fetched list of departments) and/or is not null so that you maintain referential integrity between the Employee and Department.
When fetching the list of Employees in a Department, you can either use fetched properties or add an instance method to the Department which returns an instance of NSFetchedResultsController containing the Department's employee list.
The only other thing you'd need to do is inject some deletion logic in your Department class (likely on -prepareForDeletion) to update the departmentID on any affected child records. That one depends on your business logic.
The Apple docs on property validation cover -prepareForDeletion and -validateValue:forKey:error if you're not familiar with them.

Core Data multiple relationships to same entity

I've been studying Core Data quite a bit now, and I've now decided it's time to use it in a new project I'm doing.
Having never use it in a working project I've just come across a few issues I would like to get the communities feedback on.
I'm doing a location based application and I would like to store "outings" in my Core Data model, so for each trip I have some traditional information such as date, distance, description etc... But I also need to save location information which I'll need to plot some points on a map.
So I have a "to" and "from" object per trip, I've created a MapPoint entity with latitude, longitude and location name attributes. On my Trip entity, I've added a "to" and a "from" relationship who's destination is MapPoint.
But what do I do with the inverse property?
Because Xcode seems to give a warning it I leave it as "No inverse".
I needed to create 2 relationships on MapPoint to reference back to the Trip to the "to" and another relationship referencing the "from" relationship of Trip.
Is this correct ? I can't quite understand.
I have a similar issue with a User Entity where this is being used in several other Entities, should I be implementing an inverse relationship back to each Entity which uses User?
To keep Xcode happy it seems I need to create a relationship on User back to Trip, and back to other Entities I'm using such as an Upload, Picture entities etc... it seems to me disturbing to think a Trip has a User object, which would then have prepared to link back to an Upload/Photo... which has nothing to do with that Trip.
If you want to support inverse relationships for your to and from relationships, you can just add appropriate relationships to your MapPoint entity. Call them tripTo and tripFrom, or whatever seems appropriate to you, and set those as the inverse relationships for your to and from relationships, respectively.
As the docs explain, you're not required to model a relationship in both directions, but doing so makes life easier. What happens, for example, when a user is deleted? If you have a number of other entities related to User, then you need some way to figure out which objects were related to that user so that you can update them. If you have inverse relationships, Core Data can automatically update any related objects using the deletion rule (like nullify) that you choose. Without inverse relationships, it's up to you to fix up any related objects.
I'm not entirely familiar with Core Data, but I believe it has a form of entity inheritance.
You could make your MapPoint entity abstract and create a FromMapPoint and a ToMapPoint which inherit their attributes from the MapPoint entity.
Your Trip entity can then have two separate relationships - one to FromMapPoint and one to ToMapPoint with the appropriate inverses.
As I said - I'm no CD expert, so hopefully someone else can come along and validate/shoot-down this suggestion?
With a bit of digging I found that you can set the parent entity through the Data Model Inspector. I created this quick representation of what you've been talking about.
In my experience Core Data doesn't "require" you to have inverse relationships, but not having them leads to mysterious bugs, even if you make sure to keep your object graph consistent manually. At least I think that's what was causing the mysterious bugs.
The SQLite store uses inverse relationships to represent to-many relationships. For a to-many relationship foo from entity A to entity B, I would have thought it would create a separate table "foo" with a column A and a column B, with object ids appearing more than once in column A. Nope. It doesn't represent one-to-many relationships at all, it represents their inverses only, which are to-one relationships. It represents fooInverse as a column in entity B's table, containing object ids that correspond to A-type entities. So you must have an inverse. It seems that in simple cases Core Data can deduce what the inverse should be if you don't define it, and your to-many property works correctly. However in more complicated cases such as the one you describe, it falls over.

Traversing one-to-many relationships with NSFetchedResultsControllers

I am creating an app that navigates through multiple levels of one-to-many relationships. So for example, pretend that the CoreDataBooks code sample starts with a list of genres, you click on a genre and then get the list of books organized by author as seen in Apple's code sample.
Here is my problem: the Apple documentation tells me I should use a FetchedResultsController to help organize my list of books into sections (among other reasons). But when trying to figure out how to get from "one" genre to my "many" books, the Core Data FAQ tells not to use a fetch. From the FAQ:
I have a to-many relationship from Entity A to Entity B. How do I fetch the instances of Entity B related to a given instance of Entity A?
You don’t. More specifically, there is no need to explicitly fetch the destination instances, you simply invoke the appropriate key-value coding or accessor method on the instance of Entity A.
The problem, of course, is I now have my books in a set, but I want them to get them from a fetched results controller.
What is the best way to proceed here? Should I follow the FAQ, and if so, how do I manage dividing my books up into sections by author?
Or do I use a fetched results controller (which I suspect is better), in which case how do I traverse the one-to-many relationship (since Apple's oh-so-helpful answer is simply "don't")?
Many thanks for your help.
Sasha
You have a data model that looks roughly like this:
Genre{
name:
books<-->>Book.genre
}
Book{
name:
genre<<-->Genre.books
}
In your master table, you run a fetched results controller to get table of Genre objects. Then the user selects one of the row which behind the scenes selects a particular Genre object.
Since every Genre object has a books relationship that points to the related Book objects, you have automatically got a reference to all the related book objects so you don't have to fetch anything. For your book tableview you just create a sorted array of the Book objects in the selected Genre object's books relationship.
Think of a Core Data object graph as a clump of bead strings all woven together in a web or fabric. The beads are objects and the strings are relationships. A fetch plucks one of the bead/objects from the clump but once you have that bead/object in hand, then you can just pull on its string/relationship to pull out all the beads/objects related to the bead in your hand.
So, fetches are used in most cases just to find the starting objects, then you walk relationships to find most of the other objects.
That is why the Apple docs say you don't need a second fetch.

Resources