CoreData - Modeling mapping table - ios

Im planning on using CoreData to model a “News” iOS app. This’ll have many sections (read a UITabBarItem) which will contain many articles (in a UITableView). The articles might appear in other sections too. Also the sort order of each article is another important factor.
In database lingo I'd have
"Article" table
Sections" table and a
"Article_Sections" table to map an article to a section with additional sort order field.
I have tried and can’t model this in CoreData. Im stuck with a
“Section” class having a many-to-many relationship with
a “Article” class (with an inverse many-to-many relationship to the “Section” class).
However with this I don’t know how to derive the sort order. Any hint on how to do this is very much appreciated. Thanks in advance.
Sandeep

I had to stop thinking in terms of database to sort this out. My solution involved 2 Classes:
ArticleList
Properties:
* List name
* Article Id
* Sort order
Relationship:
* Article ; one-to-one
Article
Properties:
* Article Id
* ....
Relationship:
* List ; one-to-many
I query the ArticleList and sort on sortOrder to get a list of articles for a particular feed/section. Then use the valueForKeyPath: to fetch article properties (such as title etc).

I think that you need to remove the direct relationship between section and article and just have a relationship from your Article_Sections table back to the section and article tables.

You can add an entity "ArticleSectionPair" as an intermediary between "Section" and "Article" with:
an attribute "sortValue"
a one-to-many relationship "section" to the "Section" entity (with its reverse "articlePairs")
a one-to-many relationship "article" to the "Article" entity (with its reverse "sectionPairs")
Using the attribute "sortValue" and th, you can get a sorted collection of ArticleSectionPairs from a section and get the articles with [sortedArticlePairs valueForKeyPath:#"article"].

Related

Creation of relationship with attribute in CoreData with iOS in Objective C

I would like to create a model with two entities Orders and Products. They are linked by a relationship 'Contain' that has an attribute 'quantity'. How can I represent that in CoreData ? (Do not send me the ray tutorial, or any tutorial on youtube, I think I have done every thing). It is very important the relationship with attribute and not something general. (I know that it is not a database, but it is a Conceptual data model/Conceptual Schema as it is named by "entities" and "relationship" so if there is relationships, there must be a way to have relationships with attribute).
EDIT :
Am I doing the right thing by not adding id_order and id_product to the Contain entity ?
In CoreData, Contain would be another entity. It would have relationships to Orders and Products and a quantity attribute.
You cannot add attributes to a relationship in CoreData.

CoreData count for to-many relationships of all children

I have an table/entity "Level" that is a "level" of a law.
Each instance of Level can have two relevant relationships:
One or more sublevels (to-many relationship to same entity) and/or
One or more Articles (to-many relationship to "Article" entity).
For each Level I want to show the number of Articles for that Level and all it's related sublevels in total.
I can do this by looping through the various levels in the sublevels set but I would using a fetched property instead be better?
I guess I need something like
totalArticles = articles.count + sublevels.articles.count
Or should I be storing the count for each sublevel as an attribute/fetched property and then using #sum on the parent level?
Can anyone help with how I might achieve this?

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.

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.

Hibernate many-to-many mapping

I am one of hibernate user in Australia. Recently, I have been dealing with hibernate many-to-many mapping and not going well though.
I got in trouble with "join/associate table with extra columns mapping".
Let`s say there are three tables called Product Order and OrderProduct (includes extra column quantity). Product holds many-to-many relationship with Order.
My confusion is that do we have to consider both ends of associate table when we are writing mapping files? or just write either side?
Also, is it necessary to produce mapping file for the associate table as well?
Any suggestions will be appreciated!!
create a view to join two tables. this view and rest table is many to one relationship
Hope it's userful
You should have an association mapping and entity because you need to create them just like an other entity.

Resources