I'm trying to retrieve data from an Entity in Core Data where I know the value of a related Entity.
IE:
Entity1
-attrib1.1
-attrib1.2
-relationship1
Entity2
-attrib2.1
-relationship1
Entity1 has a to-many relationship to Entity2 on relationship1.
I'm trying to get the value of Entity2 where Entity1.attrib1.1 = XXX.
I tried using NSPredicate, but I'm not sure how, if possible, to write the syntax in the predicateWithFormat method.
If this doesn't make sense, sorry. I'll try to clear up if needed.
I have searched google and here, but haven't found anything. Maybe my eyes are giving out? ;)
I think you're trying find instances of Entity2 where Entity1.attrib1.1==XXX and Entity1.relationship1 contains a reference to the Entity2 instance. From your description, it's not clear if Entity2.relationship1 is the inverse of Entity1.relationship1. If not, you really should create the inverse relationship and set it as such in the data modeler. Unless you really know what you're doing and are sure you do not need the inverse relationship, Core Data will not work as you expect unless the inverse relationship exists.
Once you have the inverse relationship from Entity2 to Entity1 (let's call it inverseRelationship for sake of example), you can perform a fetch request on Entity2 using an NSPredicate instance with the format string
inverseRelationship.attrib1.1 == XXX
if the inverse is a to-one relationship or
ANY inverseRelationship.attrib1.1 == XXX
if the inverse is also a to-many relationship.
Related
I have a fairly complex CoreData data model with entities inheriting from others and I'm getting an exception when executing a predicate. For example:
#"player.score > 1000";
Where:
Player (abstract)
- name
- tags -> Tag
LocalPlayer : Player
- score
- lives
VirtualPlayer : Player
- difficultyLevel
Tag : NSManagedObject
- name
- color
- player -> Player
I understand why, Tag has a relationship to Player, and score is an attribute on LocalPlayer, so it isn't valid since it isn't on other Player subclasses. But I really don't want lose the hierarchy of my data model.
Is there a way (subqueries, maybe?) to limit my predicate to only run against LocalPlayer objects in the Tag:player->Player releationship? Any suggestions?
Thanks.
If you are attempting to perform a fetch request using this predicate, it is not possible. The predicate is compiled to an SQL statement, and it is validated before sending to the backing database for execution. Interestingly, Core Data implements inheritance in a single, large table. So the SQL statement would actually not fail and return a correct result. But it is failed before execution by the Core Data predicate parser, which validates it against its model. To overcome this, consider promoting the score property to the abstract class Player. Perhaps, store it as an NSNumber, which would allow having a nil value to indicate irrelevance (in cases of VirtualPlayer objects).
You could also reverse your fetch request, fetching all local players with score of 1000, and then taking a list of all the tags:
NSSet* tags = [[moc executeFetchRequest:localPlayersRequest error:NULL] valueForKey:#"#distinctUnionOfSets.tags"];
Note however, that this is less optimal, and you may consider prefetching the tags relationship for quicker union of sets.
You should not have a predicate like this. From a conceptional standpoint, a Player is not guaranteed to have a score. Instead, you should set the entity of your request to LocalPlayer.
Even better, in my opinion, would be to avoid the inheritance complexity altogether. If the attributes list in your question is exhaustive, I would think that you had better simplify the model to just a Player entity to include all the attributes. You could even add a boolean isVirtual to make query filters easier.
Keep it simple and readable. You may "lose the hierarchy" but you will "gain simplicity".
I had a conceptual problem with Core Data.
I have an entity called OBJ which has an OBJ entity in a relationship. I get very eloquent messages like "An error occured." when I test my model.
For each OBJ entity, I wish to define one or more ACTIONs associated with another OBJ entity. So I guess my mistake comes from a loop, but not sure.
At first, I defined an inverse relationship (as advised by the doc, a relationship should always have its inverse):
ENTITY OBJ
Attributes:
name
status
Relationships:
actions
<-------- >>
ENTITY ACTION
Attributes:
name
Relationships:
obj
Well, it does not work, because the OBJ always refers to itself. I have therefore decoupled relationships. The ACTION points to an OBJ and the OBJ points to several ACTIONs, without reciprocity. Error message.
My basic idea is that each of the ACTIONs associated with a particular OBJ can run if the status of another OBJ allows it.
How to build this to be MVC consistent? The basic idea seems simple, it is his achievement that is less so. Is it a wrong Core Data modeling? Do I make a mistake at the controller level? Interface? Note that at this level I have not written a single line of code.
Thank you!
If you want to
define one or more ACTIONS associated with another OBJ
it means that one OBJ can have many ACTIONS and one ACTION can have many OBJs.
Therefore you should define a relationship between ACTION and OBJ that is to-many in both directions.
OBJ.actions <<-------->> ACTION.obj
Unless you intend to have "sub-objects" (such as CATEGORY and subCATEGORY) you should not have a relationship between OBJ and OBJ.
I found a solution by adding a new relationship between "OBJ" and "ACTION".
OBJ ACTION
… …
Relationships Relationships
targetObject <------->> actions
conditionObject <------->> condition
If I don't, the inverse relationship always refers to the same (self) object. So, by changing a relationship, I was changing the object itself!
By adding a new one-to-many relationship, I can indirectly make a OBJ to OBJ relation (the targetObject and conditionObject may be the same one) AND keep the graph consistent.
Each day, I understand better what "Core Data is not for beginners" means…
Entity B (Book) has a one-to-many relationship with the entity D (Description). The idea is that a book has different descriptions for different languages.
I want to sort books based on their titles (D.title) for a given language (D.languageID)
If B had one-to-one relationship to D, I would do something like:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"d.title" ascending:YES];
Of course, you may say, the model does not prevent a book from having many descriptions with the same languageID. But in this case any (e.g. the first) description would be ok for me.
Is my model wrong? What's the best solution now?
This is logically not possible. If you have many objects B belonging to A, which B is the sort routine supposed to take?
For example you have the entity A "Parent" and the entity B "Child" and the attribute of B "dateOfBirth". You cannot sort all Parent objects according to the lowest birth date because if two different parents have two children each, it is possible that each of them has a child that is older than one of the other's.
Clear?
In my app Core Data model I have Sheet and Text entities. Sheet entity can have two Text's: privacyNotes and termsOfUse.
Both of Text type. So in XCode data modeler I create to-one relationships called "privacyNotes" and "termsOfUse" in Sheet with Text destination. Next goes to-one relationship "sheet" in Text. Then I select that Text.sheet relationship as inverse for Sheet.privacyNotes. So far so good. But when I set same Text.sheet relationship as inverse for Sheet.termOfUse XCode deletes this relationship as inverse Sheet.privacyNotes!
I understand that relationships in DB can be not so simple compared to Objective-C objects relationships, but I really don't get why SQLite or (CoreData) can't reuse one relationship as inverse for FEW other relationships?
A little peek under the abstraction hood might be enlightening*: a relation can only be the inverse for exactly one other relation because, in the backing store, they're represented by the same data. If a Text and a Sheet can have a certain relationship, Core Data does what a good human data modeler would do and stores that relationship as succinctly as possible. The relation properties of the entity objects are just ways of looking at that relationship.
To get the effect of what you're going for: go ahead and give Sheet properties for privacyNote and termsOfUse; but give Text properties like sheetIAmTermsFor and sheetIAmPrivacyNoteFor, and set them as inverses appropriately. Then in the Text class, add a synthetic property along these lines:
// in interface
#property (nonatomic, readonly) Sheet *sheet;
// in impl
-(Sheet *)sheet
{
if ([self sheetIAmTermsFor])
return [self sheetIAmTermsFor];
else
return [self sheetIAmPrivacyNoteFor];
}
If you want to write a setter too, you'll have to decide which role that setter should bestow on the Text (which Core Data can't figure out for you, another reason a property can't be the inverse of two different properties.)
If you need to enforce a constraint that a Text can only ever be a "privacyNote" or a "terms" but never both, override the setters for sheetIAmTermsFor and sheetIAmPrivacyNoteFor, following Apple's pattern in the docs, and have each null the other property when set.
(* Apple regards the SQLite databases Core Data generates as private to their implementation, but inspecting their schemas can be very educational. Just don't be tempted to write shipping code that goes behind CD's back to poke at the db directly.)
You are far better off having a one to many relationship between Sheet and Text with a validation limit of 2. Then you should have a type property in the text which declares it as either a privacyNotes or termsOfUse. From there you can add convenience methods to your Sheet subclass that allows you to retrieve either one.
Suppose I have the following data model:
Entity Person
Attribute name String
Attribute personType String
Attribute dailyRecords
Entity CarpenterDailyRecord
Attribute numberOfNailsHammered Int
Attribute picNameOfFinishedCabinet String
Entity WindowWasherDailyRecord
Attribute nameOfBuildingWashed String
Attribute numberOfWindowsWashed Int
I would like to establish a to-many relationship between the Person.dailyRecords and 1 of the daily record entities (which changes depending on the person type). Of course, i could create a CarpenterPerson and WindowWasher entity which each points to it's unique daily record structure, but i have to group people together in my app somehow.
so if i do a Group Entity:
Entity Group
Attribute people array
i'm still stuck. how do i point to multiple & different Person entities?
There must be an obvious answer, it's just i'm so new to all of this. thanks!
Create a parent (DailyRecord) entity that handles the relationship (Person <-->> DailyRecord). [CarpenterDailyRecord|WindowWasherDailyRecord] then inherits from DailyRecord.
The risk with this, however, is that all of the children (WindowWasherDailyRecord, CarpenterDailyRecord) will be in one table in the underlying sqlite structure and therefore can cause a performance impact. This is not a reason to avoid inheritance, just something to be aware of while designing your data model.