Search code examples
core-dataentity-relationship

Core Data subentity vs. relationship for entities with similar attributes


Planning out the Core Data schema for my iOS app, I found that I have a handful of entities that need the same set of attributes (e.g. descriptive text, a rating, user notes, etc.) and relationships (e.g. for applying tags, attaching parameters, setting parent/child relationships, associating images, etc.). For one entity "Entity A", the shared attributes/relationships are all it needs while the others each have a couple of additional unique attributes and/or relationships. Reading the Core Data documentation and posts here, I decided to set up "Entity A" as the parent of the others.

One additional entity shares all of the same attributes and a subset of the same relationships, and that is the entity for images (note that the images themselves are stored in files, this entity is for metadata and has a key to the image file). "Entity A" and children all need a to-many relationship to the image entity, however, while the image entity does not need a relationship to itself. The image entity also does not need the parent/child relationships of "Entity A". I see four options, but am having trouble determining which way to go.

My question is whether any of these options have a significant pro or con that I'm missing or if one particular option is generally considered the "correct" way of doing things. I've read that in Core Data all subentities will share a single table with the parent entity, which could lead to performance issues for large numbers of objects, but I anticipate my app would only require a couple thousand rows in such a table at most.

Option 1: Set up the image entity as a child of "Entity A" and just ignore the relationships it doesn't need. This is the easiest to set up and takes full advantage of inheritance, but I don't know if ignoring relationships is a good design choice. "Entity A" has existing data that I think would migrate most easily this way, but I can also recreate the data without too much trouble, so that's not a significant consideration. There is no current image entity, so migration of that is not a concern.

Option 2: Create an abstract parent that both "Entity A" and the image entity are children of. The parent would be "Entity A" minus the relationship to images and "Entity A" would now just have the relationship to images. This seems cleaner and is currently where I'm leaning. While this seems functionally good, conceptually I don't know if it's appropriate for an entity with what's essentially metadata to be the parent.

Option 3: Instead of an abstract parent, make a separate new "metadata" entity that is "Entity A" minus the image relationship and add a to-one relationship to the metadata entity from both "Entity A" and the image entity, like making a composite object. This seems conceptually appropriate since the metadata is just one aspect of the main entities, not the defining factor (which is how it feels with Option 2). It also keeps the image entity and "Entity A" in separate tables and should allow searching by metadata to be done more efficiently. The only downside, if it is one, is that it's taking the majority of the attributes and relationships for both "Entity A" and the image entity and tacking them on as a relationship.

Option 4: Ignore the similarity in attributes and just make the image entity completely separate, duplicating all of the attributes from "Entity A". This seems the least desirable due to duplication of effort.


Solution

  • I ended up going with Option 3 (creating a new "metadata" entity) and then created two separate sub-entities of metadata to handle the inverse relationships for "Entity A" and the image entity. This seemed to be the appropriate course of action in terms of object hierarchy. I also added accessors to "Entity A" and the image entity as a convenience to pass through calls for the metadata entity's attributes.

    The resulting Core Data migration required a few steps and some custom coding - probably more work than simply creating an empty database and manually re-populating it, but it was a good learning experience with migrations.

    Having completed the migration I confirmed what I had read about sub-entities sharing a table with the parent entity. In the case of the metadata entity, this meant that every row had columns representing the inverse relationships to both "Entity A" and the image entity. For reference, the metadata table had the following columns:

    Z_PK = row index
    Z_ENT = entity index to distinguish between sub-entities (all entity indices are in the table Z_PRIMARYKEY)
    Z_OPT = count of writes for the given row
    Zxxxx - columns for each attribute and to-one relationship in the parent and sub-entities, apparently ordered with booleans first, then integers, then relationships, dates, and finally strings (from smallest to largest data size)
    

    Note that to-many relationships are handled in separate tables.