How can an entity inherit a property in a recursive model structure? - ios

I have a structure identical to this post here:
How do I create a recursive one-to-many relationship using core data?
It looks something like this:
cage ---------- animal 1
|
|_____ animal 2
|
|_____ animal 3 ____ animal 4
|
|__ animal 5
|
|_____ animal 6
And I have implemented my models exactly as the correct answer has done, i.e.
The problem for me, is that with this structure, only animal 1 has a non-nil property cage, but I would like ALL descendent animals to have this property cage so that i could query the cage property animal6.cage.
I've tried setting this manually but the inverse relationship causes any animal with a cage property to be a direct child of that cage, which I don't want.
Is it possible to inherit the cage property for each animal?

You're using terms like "inherit" and "child" in ways that don't have meaning to Core Data. You have Cage which is related to Animal. Animal has a relationship to itself.
There's no parent/child relationship or inheritance here as far as Core Data is concerned. If one Animal is related to another, they're just two instances with a relationship. One can't inherit a value from the other because one is not the "parent" in any sense that Core Data uses. The two instances are two independent objects and they don't inherit anything any more than any two non-Core Data objects would.
Following from that, setting the cage property doesn't make an Animal a "direct child" of the cage, it just says it's related to the cage. If you want to find the cage for any arbitrary Animal without setting cage on every instance, you need to do something like (Swift-ish pseudocode):
func cage(for animal:Animal) -> Cage {
var currentAnimal = animal
var cage = currentAnimal.cage
while cage == nil && currentAnimal.parent != nil {
currentAnimal = currentAnimal.parent
cage = currentAnimal.cage
}
return cage
}
That's fine if you just want to find the cage for an animal, but you can't use it in a fetch request. If you need something you can use when fetching, you probably need to add a second relationship from Animal to Cage so that you can distinguish the "parent" animal from any others. Every Animal would have a value for one of the relationships, and the other relationship would be reserved for the parent.

Related

Parse: Create 1-to-1 relation on PFObject with support for multiple class types?

I am trying to create a PFObject subclass in Parse that has a one-to-one relation to another PFObject in Parse, but I would like this relation to support any PFObject subclass. Currently, as soon as I create and save my first PFObject to Parse, whatever that PFObject is connected to is set as that relation's one and only accepted type, and creating a second object that connects to a different PFObject subclass for that relation generates an error.
Is there a way either to...
Specify the desired class that a one-to-one relation (i.e., really a pointer) should connect to? And if so, will it support connecting to subclasses of that specified class?
Create a one-to-one relation easily that supports any kind of PFObject?
Example:
For example, let's say I needed to create a subclass of PFObject called Zookeeper in addition to multiple subclasses of PFObject for various types of animals: Armadillo, Bear, Camel, etc. (and for reasons that we must accept, each animal has to have its own PFObject subclass rather than using a generic Animal class).
Then let's say that each Zookeeper is responsible for exactly one animal. To do this, I want to create a one-to-one relation on Zookeeper called animal so that I can specify a single animal object that each Zookeeper is responsible for.
I create multiple Zookeeper and multiple animals. I successfully save my first animal (an Armadillo) and my first Zookeeper object ("Alice") and my second animal (a Bear), but when I try to save my second Zookeeper object ("Bill") to Parse, I get the following error:
invalid type for key animal, expected *Armadillo, but got *Bear
I am not sure if this is exactly what you want, but could you:
Create an Animal subclass in Parse, with a text field called "type", and one column for each of the different types of animals (yeah, I know this is isn't that elegant).
The column matching the "type" column contains a pointer to the specific animal object, while the other columns are nil.
To get the specific animal object, you need to get the Animal object, look at the type column, and then look at the corresponding pointer column to get the specific animal object.
For example, the Zookeeper object contains a pointer to an Animal object. The "type" column of Animal object contains the text "bear". You then retrieve the object in the "bear" column of the Animal object, which is a Bear object. Does this meet your specification? Yes there is an Animal object, but there is still a notion of specific animal objects like Bear, etc.
What I ended up doing was saving the class name and object ID of the desired target object to my object rather than saving it as a pointer; so in the example above, that would mean creating the properties animalType and animalID on Zookeeper and using those properties to query the relevant animal with objectId = animalID. That way I don't have to create any new class types that are purely relational.

How do I create a recursive one-to-many relationship using core data?

I have used core data before, but this database I have to construct has a particularity that will require a special relationship that is melting my brain.
I have 2 entities, lets call them Cage and Animal.
Cage has one attribute name
Animal has a name and image attributes and must keep track of its children.
A possible structure can be something like
cage ---------- animal 1
|
|_____ animal 2
|
|_____ animal 3 ____ animal 4
|
|__ animal 5
|
|_____ animal 6
Looking at this structure you see that Animal 1, Animal 2 and Animal 3 have Cage as parent or are "children" objects of Cage if you will. On the other hand Animal 3 has 2 children objects (4 and 5) and Animal 5 has one children object.
I need Cage and Animal to be different entities.
So, you see that Animal objects can have other Animals as children. An animal can have one parent but multiple children. A Cage object can only have children.
I have tried to add a children relationship with destination equal to Animal and inverse equal to children (toMany) but this is what is melting my brain, because if I do this, this will be parent at one time and children at others, not to mention that the name children will make coding difficult to wrap the head around...
How do I have to build the relationships between the entities to make this work?
First create your entities and attributes. Then create your relationships. The relationships for "animals" and "children" shout be set to "to many". Leave "cage" and "parent" set to "to one" relationship. Finally, set the destination and inverse for each relation.
Relations "animals" and "cage" should be inverse.
Relations "parent" and "children" should be set to inverse.
Should look like this when you're done:
Animals live on cage and two animals can have relationships.

Performing the equivalent of a union with Core Data for a UITableViewController

I know union is a SQL construct, but it's the best analogue for what I'm trying to do.
I have multiple groups of data that I'm receiving from an external source. I'm maintaining them as separate entities in Core Data (they only have some attributes in common (e.g. name)), but I want to present them in the same tableView.
Say I have an entity Food that has relationships with FruitGroup and VegetableGroup. The FruitGroup has a relationship with Fruit which has a relationship with FruitType. The VegetableGroup is similar.
How can I use FruitGroup.Fruit.name and VegetableGroup.Vegetable.name as sectionTitles? And FruitGroup.Fruit.FruitType.name and VegetableGroup.Vegetable.VegetableType.name for row data. (I tried coming up with a predicate that walks down from Food, but that doesn't appear to be workable)
Example modeled data (my groups are far more disparate than fruits and veggies, so re-doing my data model is not an option):
Food
FruitGroup
Apple
Macintosh
Granny Smith
Pear
Bartlett
Asian
Anjou
VegetableGroup
Asparagus
white
wild
Peas
baby
split
Which I would like to appear as:
Apple [section]
Macintosh [row]
Granny Smith
Pear
Bartlett
Asian
Anjou
Asparagus
white
wild
Peas
baby
split
I could use multiple NSFetchedResultsControllers in the UITableViewController and conditionally select the FRC within each of the UITableViewDataSource methods, but that doesn't feel clean.
I'm thinking about subclassing NSFetchedResultsController and, internal to my subclass, merging the results of multiple private NSFetchedResultsControllers that each represent one of the entities. (e.g. sections returns a concatenation of the returns from the sections calls of the internal FRCs)
Does this make sense - or is there a better way? (I saw Core Data Union Query Equivalent but since there are relationships among my entities, I wanted to seek alternatives)
While you can do this as described in the other answers (via creating an abstract Parent entity), I would not recommend it. The performance when it comes to dealing with abstract parents gets bad very quickly. The reason for this is that Core Data will put all of the children into a single table in the underlying SQLite file.
I would suggest going a different route. Have a single entity called Food with attributes describing if it is a vegetable or fruit. Then you have one NSFetchedResultsController which has the type of the food item as the sectionPath and you will get your display the way that you want it.
I recommend creating entities in Core Data based on what the objects are as a very loose level. I would not create entities for Honda, Ford and Dodge, but create an entity for Car and perhaps type or a relationship to a manufacturer.
While Core Data can be backed by a database, at the end of the day it is not a database but an object graph and should be treated as such. Trying to normalize the database will result in poor performance of the object graph.
You should probably look into abstract entities. For example, you could create an abstract entity called Food. Then you're able to create Fruit and Vegetables, which inherits the abstract entity. You'll have to set Food as the "Parent Entity".
Then you could fetch all the items with the entity Food, which includes both Fruit and Vegetables. Based on your post, you'll probably will have a relation from Food to FoodGroup.
To answer your question:
You cannot unify different entity types (if they are not subclasses of the same entity) under a single fetch request. You can define an entity (B) to inherit from another entity (A) and then fetch by the parent entity (A) and get both kind of entities (As and Bs)
You can try and think of it this way:
Item ("Macintosh","White Asparagus",...) has a relationship to Group ("Apple","Asparagus",...), and Group has a relationship to Area (or simply to another parent group).
In this manner you could use a single FRC with sectionNameKeyPath of "group.name" and entity Item (you can filter by "group.area" to only select food items).

is there a way to tell if an nsmanagedobject is in a context but is not part of any or a specific relationship?

For example, say I have the following entities:
Person
House
Car
And a person can have many houses and cars, while cars and houses can only have one owner.
Now consider the following scenario:
I insert a person into the context, along with 3 houses and 3 cars.
Out of these houses and cars, one house and one car already have an
owner.
Then, out of the remaning 2 houses and 2 cars, I add one of each to
the only inserted person.
At this point, if I save the context it'll reflect 3 houses and 3
cars in context.insertedObjects. But I only want to save the objects
that are reachable through the one person I have a handle to (the
idea being that for the house and car with a previous owner, that
owner person is probably a fault and I never fetched it.)
Sure I can manually delete these objects before saving, but I'm wondering if I can come up with a generalized solution, considering object reachability from a particular object.
Essentially something along the lines of (pseudo code):
for (NSManagedObject *obj in context.registeredObjects) {
if (Person does not reach obj in any relationship ) {
[context deleteObject:obj];
}
}
By generalized solution I mean something not requiring knowledge of relationships and specific entities (so as not to iterate over a person's houses and cars relationship for every house and car).
You can make the house & car relationship with person non-optional so the save will fail for the objects that have no owner.
You can deal with the validation issue directly on the NSManagedObject level by subclassing and overriding the -(BOOL)validateForInsert:(NSError**)error method.
Check the relationship within that method and if it returns nil you know you can delete that object.
Here's a bit more reading for you https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdValidation.html

Design for when a model can have three different types?

If I had a Dog model and you can choose it to be 1 out of 3 of the Types called Small, Medium and Large. Should these Types be models themselves if I'm going to put logic in them? What would be the model design?
If Small, Medium and Large will each have unique methods but share common attributes you could have a base Dog model and then subclass each of the sizes like class SmallDog < Dog. Use single table inheritance on the dogs table by adding a type column that accepts values like "SmallDog", etc.
Will all the different types have a common interface but just differ in their logic?
If the interface is the same (i.e they all have the same function definitions) then I'd just have subclasses for each of the different types of dog that extend the original Dog model and have some kind of factory class that handles the creation of Dog models and automatically selects the appropriate class based on the type of Dog. By using the factory class with a common interface for Dog types the rest of the application does not need to care about the type of Dog and you can freely add/remove new types by simply modifying the factory class.

Resources