Core Data's inverse relation in detail - ios

I am having hard time wrapping my head around Core Data's inverse relationship. For example let's say there are two entities: post and comment, where
post has_many comments and
comment belongs_to post
Using xcode's visual coredata editor I would create both entities and then link them by:
Connecting post to destination comment and selecting to many
Connecting comment to destination post and not selecting to many
Make sure the relationship from #2 above is an inverse of #1.
Here's my question:
What is actually going on behind the scenes when you select inverse. What is this equivalent to in other ORMs, say ActiveRecord.
Will it have the same effect if instead of specifying #2's inverse as #1, I went to #1 and said its inverse is #2?

The phrase I hear most often is that CoreData is about maintaining an object graph foremost, and an ORM second. So with that in mind, I'd say that behind the scenes when you set an inverse relationship you are noting that it is a bi-directional relationship and not two, one way relationships. Without that, you may be able to specify that a comment has many posts but if you add a post to a comment you may not be able to traverse the graph from the comment to the post and then back.
Coming from ActiveRecord that may not make much sense if you think about it as keeping keys in columns and noting the relationships - but CoreData can have many different data storage formats and an inverse relationship is not implied by storing the data.
It should have the same effect if you go to #1 and said its inverse is #2 - but that's easy to test and see.

Setting an inverse means that if you change one relationship then the inverse is kept in sync. For example if you add a Comment to a Post then the Comment's "post" relationship will automatically be set. Similarly if you do set a Comment's post property then that Comment will automatically be added to the Post's comments collection. So the following two lines of code are equivalent:
newComment.post = aPost;
[aPost addCommentsObject:newComment];
If you did not specify the inverse then you would have to include BOTH lines to create the relationships. Inverses are recommended because it is easier to maintain the integrity of the object graph. How this is actually persisted in the store depends on the store type, but if you are using a sqlite store then it's easy to peek inside to see what Core Data is doing.
They are equivalent.

Related

Core Data Relationship For Unidirectional One to Many

What is the best practice for creating Unidirectional One to Many Relationships in Core Data?
For example...
Lets take two classic entity examples, "teacher" and "student".
Each student has one teacher, and each teacher has many students.
In CoreData right now you are forced to provide an inverse such that teacher is forced to have a reference to a 'student'. If you don't you get this nice warning that says something along the lines of...
file:///Users/josephastrahan/Documents/VisualStudioProjects/Swift3WorkOrders/WorkOrders/WorkOrders/WorkOrders.xcdatamodeld/WorkOrders.xcdatamodel/: warning: Misconfigured Property: Teacher.student should have an inverse
What if I don't want teacher to have a reference to student?
Some other posts have brought up that I should just allow the inverse anyways but I think this inverse may be causing an issue with one of my projects.
That said let me explain my exact issue.
Lets say that our teacher has a unique attribute int64 called 'id'. Lets say the students also have unique attribute int64 called 'id'.
The int64 is enforced to be unique by adding a constraint on the model for teacher on id. (refer to image below to see how that is done)
Every year there is new students but the teachers stay the same. So I decided that I want to delete all the students without deleting the reference to the teacher. So I set the delete rule to 'nullify' for the relationship for the teacher to student and 'nullify' for the student to teacher.
Now when I create a new student I want to assign one of the existing teachers to that student... (something like student.teacher = teacher object with id of 1 or the same id as before) however!! , because the teacher has the inverse relationship to a student that no longer exists (which in theory should be null) the program crashes!
I know this is the case as I've used print console logs to narrow it down the exact point that it occurs. Also I know this because if I add the delete rule of cascade for student the crash will go away but...then I lose my teacher! which I don't want...
Some things that I think might be the issue:
1.) When I do my testing I do it at the startup of the program which creates a new context everytime. Could it be that because I never deleted teacher it still thinks it refers to a student from a context that no longer exists? (if I'm even saying this right...)
I'm not sure the best solution to acheive what I'm trying to do with Coredata and any advice is much appreciated!
Note:
Forgot to mention I also have the Merge Policy of: NSMergeByPropertyObjectTrumpMergePolicy, which will overwrite the old data with the new. When I'm creating new students I'm creating new teachers also just using the same id which should follow this policy.
You are almost there.
The advice to keep the inverse relationship is a good one. Keep it.
Your issue is likely caused by different contexts. Instead of holding on to a teacher object in memory, you should fetch the teacher (based on the id) in the context in which you intend to use it.
Your nullified students should not have any impact. A to-many relationship is really a Set<Student>. Make sure the set is empty.
NB:
If you want to keep the student in the database (for historical purposes) - it seems from your description that this is the case - you might also consider another scheme: give your students another attribute (such as a year) and use that to filter the student list. You would not have to delete or nullify anything. You could also do some more interesting time-based queries on the data.
Unique Constraints are available with iOS9. Which have helped iOS Developers with adding and updating records in CoreData.
Unique Constraints make sure that records in an Entity are unique by the given fields. But unique constraints along with To-Many relationship leads to a lot of weird issues while resolving conflicts.
e.g. “Dangling reference to an invalid object.”
This post is basically focused to a small problem that may take days to fix.
http://muhammadzahidimran.com/2016/12/08/coredata-unique-constraints-and-to-many-relationship/

Rails: storing arrays in DB vs. setting up relationship

Setting: There are two models with the following attributes.
Person: name, age, work_experience, available_dates
WorkExperience: job_title, starting_date, ending_date
Then, I can do the following
Person
has_many work_experiences
WorkExperience
belongs_to Person
Q1: Should available_dates be an attribute (array of dates) or a child (e.g. Person has_many available_dates)?
Q2: Can Person have work_experiences field that's just an array of objects instead of an individual table in DB?
TL;DR: As in all design questions, I think the answer is "it depends."
On Q1:
I've done both ways before. If I were making this decision, I would consider the following:
Do I see myself adding more information to available_dates in a very near future? For example, it could be location, timezone, commute distance, and so on that would make this data into "availability" in general. In that
case, I'd rather create separate objects with association.
Do I see myself reusing these dates in some other place in my code? Then I'd rather make them into separate objects and persist.
On Q2:
I don't think it'd be good for your sanity. DB has simple datatypes and are not designed to retain object models.
On Q1 I would go with an association instead of an array as it provides more flexibility. So a child, as you call it is what I would do.
On Q2 I would also go with a separate WorkExperience Model as it also provides more flexibility and room for growth. Also the array might get a bit messy as a Person starts gathering work experiences and querying for information might get more complicated than it needs to be with an array. With a separate model Active Record will give you a lot of tools you can use to dig into a person's work experiences.
Hope that helps.

CoreData dilemma: entity without inverse relationship

I have entities Image and Post. Post has a uni-directional Nullify relationship to Image. It's a relationship and not an attribute because I want to benefit from lazy loading of relationships, making initial fetches of Post entities very fast. As you can guess, Image contains image data.
Apple generally discourages uni-directional relationships (Link) unless there a good reason not to have an inverse relationship.
My reason for not having it is Image is a generic entity, so having inverse relationships to all entities that use it seemed like overkill. However, I wonder if there is a better way to go about this without violating Apple's recommendation.
Parent entities.
Comment, BlogPost and Tweet all inherit the images relationship. And the inverse relationship points to a TextContent object, which can be a Comment, BlogPost, Tweet or TextContent itself.
As Apple says... Unidirections Relationships
It is not strictly necessary to model a relationship in both
directions
BTW I think attributes also could be loaded lazily (by system) if CoreData detects that is better to do this...
You can be interested in this then to
avoid warnings in your project

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.

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.

Resources