How to have repeated items in an Ordered To-Many relationship? - ios

In Core Data of Xcode 4.3.2, an Ordered To-Many relationship is modelled with NSOrderedSet. It works well until I found the need to have repeated items in the relationship; it should really be modelled in a NSArray.
For example, in a music app, I have the following songs: SongA, SongB, SongC ,
I may want a party play list where people insert songs in any order and could be repeated. The list may look something like:
[SongC, SongC, SongA, SongC]
The way Core Data currently works, the list would become:
[SongC, SongA]
I.e., all repeated items are dropped, as it is modelled with sets.
So, coming back to my question: what is a good way to model repeated items in order in a relationship in Core Data?

Relationships are sets (and ordered relationships are ordered sets, but still sets); sets by definition contain unique objects. So you can't put duplicate objects into a relationship either way.
Whether you use the ordered-relation feature or not, you'll want to go back to the abstract ER model to find another way to turn your conceptual relationships into a Core Data model... it might help to think about how you'd do it in a plain SQL (or SQL-like) database and then come back to what Core Data does beyond SQL.
It sounds like you're making something akin to iTunes playlists, no? A model that might work for that would go something like:
Playlist <--->> PlaylistEntry
PlaylistEntry <<---> Song
The PlaylistEntry entity represents one instance of a Song's inclusion in one Playlist. You can have multiple PlaylistEntrys that reference the same Song in a single Playlist, and you can add other attributes to the PlaylistEntry to keep track of other things (like song order, if you're not using an ordered relationship). As a bonus, you can use that to add other features if you like -- say, to make a playlist that plays three different snippets out of one long track.

As rickster said, relationships as managed by Core Data uniquely associate entities between them. Even if Lion's Core Data (is supposed to) supports ordered relationships (supposed to, because in practice it won't work, the feature is buggy, barely usable), they still are relationships that follow the relational database model.
And so you have to manage the association by yourself, and you most certainly have to manage the ordered part of the association by yourself too.
Score <->> ScoreSong
ScoreSong <<-> Song
With ScoreSong having the following properties:
ScoreSong:
- score: -> Score
- song: -> Song
- order: integer, indexed
Then you have to use a Fetch Request with a sort descriptor for the key order, which will return an ordered NSArray of ScoreSong. You can ask the fetch request to prefetch the songs, then you can create the songs array, still properly ordered, with a single call to valueForKey: #"song".
Of course you have to create different ScoreSong for a single Song when you need to include than song more than once in your Score. That's the whole point of the added indirection.

Related

Realm: Best practice to link object with a property associated with the link

Say we have an object A and we want to link it with a sortable list of Publications. But also, we have another object B which also has a sortable list of Publications. Publication objects may be shared between the lists.
Sorting order is defined in the backend and results in some integer property sort. Obviously, we cannot store this property as part of Publication object, because its’ sorting order is different in relation to different objects (A & B) and we’ll just needlessly update these objects every time we update lists, resulting in unwanted notifications.
I see a couple of ways to achieve this:
In SQL world, we can make many-to-many link via an intermediary table, where we can also store some properties, associated with a link, like sort. We can do something like this with Realm, but it’s a bit inconvenient and feels like API misuse.
We can make another List property with sorting order for the list stored in it. Though it’s even more inconvenient and strange.
Thanks in advance for clarification.
After some experiments, I concluded that this way feels as the most natural one, if you only want an ordered list of dependencies:
Make a List of ordered primary keys for the entity you want to "join".
Throw in some accessors to fetch necessary objects.
Still clumsy and you can't query dependencies.

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.

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.

How to store many item flags in core data

I am trying to do the following in my iPad app. I have a structure that allows people to create grouped lists which we call "Templates". So The top level CoreOffer(has Title) which can have many groups(has grouptitle/displayorder) which can have many items(has ItemTitle, DisplayOrder). As shown below. This works great, I can create Templates perfectly.
Image link
http://img405.imageshack.us/img405/9145/screenshot20110610at132.png
But once Templates are created people than can use them to map against the Template which I will call an Evaluation. A Template can be used many times. The Evaluation will contain a date(system generated) and which items from this particular Template have been selected.
Example below, people will be able to check particular rows in the screen below, this is then an Evaluation.
Image link
http://img41.imageshack.us/img41/8049/screenshot20110610at133.png
I am struggling to figure out how to create and store this information in the core data model without duplicating the Template. (struggling coming from a SQL background!) In SQL this would involve something like an evaluation table recording each itemid and its selection status.
I expect its quite simple but I just cant get my head around it!
Thanks
The first thing you want to do is clean up the naming in your data model. Remember, you are dealing with unique objects here and not the names of tables, columns, rows, joins etc in SQL. So, you don't need to prefix everything with "Core" (unless you have multiple kinds of Offer, Group and Item entities.)
Names of entities start with uppercase letters, names of attributes and relationships with lower case. All entity names are singular because the modeling of the entity does not depend on how many instances of the entity there will be or what kind of relationships it will have. To-one relationship names should be singular and to-many plural. These conventions make the code easy to read and convey information about the data model without having to see the actual graphic.
So, we could clean up your existing model like:
Offer{
id:string
title:string
groups<-->>Group.offer
}
Group{
title:string
displayOrder:number
offer<<-->Offer.groups
items<-->>Item.group
}
Item{
title:string
displayOrder:number
isSelected:Bool
group<<-->Group.items
}
Now if you read a keypath in code that goes AnOfferObj.groups.items you can instantly tell you are traversing two to-many relationships without knowing anything else about the data model.
I am unclear exactly what you want your "Evaluations" to "copy". You appear to either want them to "copy" the entire graph of any Offer or you want them "copy" a set of Item objects.
In either case, the solution is to create an Evaluation entity that can form a relationship with either Offer or Item.
In the first case it would look like:
Evaluation{
title:string
offer<<-->Offer.evaluations
}
Offer{
id:string
title:string
groups<-->>Group.offer
evaluations<-->>Evaluation.offer
}
... and in the second case:
Evaluation{
title:string
items<<-->>Item.evaluations
}
Item{
title:string
displayOrder:number
isSelected:Bool
group<<-->Group.items
evaluations<<-->>Evaluation.items
}
Note that in neither case are you duplicating or copying anything. You are just creating a reference to an existing group of objects. In the first case, you would find all the related Item objects for a particular Evaluation object by walking a keypath of offer.groups.items. In the second case, you would walk just the keypath of the items relationship of the Evaluation object with items.
Note that how you ultimately display all this in the UI is independent of the data model. Once you have the objects in hand, you can sort or otherwise arrange them as you need to based on the needs of view currently in use.
Some parting advice: 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.
Basically, forget everything you know about SQL. That knowledge won't help you understand Core Data and will actively impede your understanding of it.

Create NSFetchedResultsController managing entities with many to many relationship

I have an entity called Project and another entity called Employee. Employees work on multiple projects.
Project entity has project name.
Employee entity has First name, last name, departmentid number.
I want the data to show up in section header table like this
Project 1
Dept1
-firstname1, lastname1
-firstname2,lastname2
dept 2
firstname3, lastname3
firstname4,lastname4
Project 2
Dept1
-firstname1,lastname1
How can I do this? I don't have to display department names, but it has to be sorted that way.
I am using Core Data & UITableView. I need to construct NSFetchResultsController for this.
I think the root of your question comes from the fact that a to-many relationship results in an NSSet when you access it from the from object of the relationship (ie: Project->Employees - results in an NSSet of employees). NSSets are, of course, unordered.
And the answer is this:
You'll need to sort your employee NSSets by hand. I suggest you convert the NSSet to an NSMutableArray, and then use -sortUsingBlock: or something along those lines to do it. To keep yourself from having to re-sort it every time you need it, store it as a member variable of your Project class. In doing so, it should be pretty easy to create it lazily, and only re-sort it when the dataset changes, which will be better for performance.
You MIGHT be able to do something with a subquery in Core Data... but I think you might find that'll hit the disk more often than you might like. (Just a guess there) The technique I've suggested above is a bit less magical, a bit more brute force, but it'll work, and you'll know exactly how it behaves forever.
Use fetchedResultsController to get your 'Project' entities and you will be able to access and display the Employee NSSet in your tableview datasource methods via their relationships.

Resources