Core data cross referencing two relationships - iphone

I have a data structure in Core Data like so...
User
Item
Category
User has a toMany relationship "FavouriteItems" to the Item entity.
Category also has a toMany relationship "Items" to the Item entity.
The user can select favourite items from any categories they wish. At the moment I am listing all the items and then displaying the Category alongside.
What I'd like to do is display all the user's favouriteItems for a selected Category.
i.e. select all the Items that have a relationship with Category x and User y.
I'm currently doing this by getting all the Items through one relationship (i.e. User.favouriteItems) and then filtering the NSSet using a block predicate.
Is it possible though to do this with a simple CoreData predicate?
Hmm... thinking about it would a predicate like this work...
[NSPredicate predicateWithFormat:#"interestedUser.id = %# AND category.id = %#", user.id, category.id];
And then run a fetch request on the item entity?
Would that work?

Shooting pretty blind as that's an awkward scenario to set up just to answer a question but perhaps
If you are filtering an array of Items which has the correct inverse relationships set up.
[NSPredicate predicateWithFormat:#"%# IN interestedUsers AND %# IN categories",
someUser,
someCategory];
Basically the Item has many users (interestedUsers) so we are saying is our user in this collection.
Similarly the Item has many categories (categories) so we are saying AND is our chosen category in this collection.

Related

Core Data query 1<-->>2<<-->3. How get 1 from 3?

For example I have data model like this
Entity: Store Entity: Inventory Entity:Product
Attribute: name Attribute: qty Attribute: name
Relationship: inventories <---->> Relationsips: <<----> Relationship: inventories
stores
products
I wanna know how retrieve from Core Data list of Stores for exact Product.
I have tableView with products and on click on exact product cell I wanna move to another controller with list of stores which contatins this product.
As I understand I can get NSSet of all Inventory entities first and iterate it to fetch stores. But I think Core Data is more intelligent thing and more simple and elegant method exists for it. For example, maybe when I get all inventory objects I can fetch Store with predicate somethink like this "Store.inventories IN (array of my inventories from previous fetch)". Or even more simple way. Thanks!
With Core Data - as a general rule - when you have a managed object in your hands, start with that object and navigate the model to retrieve related entities. Specifically:
// if #"stores" is the name of the relationship in Inventory:
NSSet *stores = [product.inventories valueForKey:#"stores"];

CoreData sort on to-many relationship

I'm writing an iOS app which has store of person records, and needs to display lists them sorted in particular ways. There are a variable number of these orderings, and they are generated on the fly, but I would like them to be stored in the datastore. The SQL way to do this is to have a ListPositions table with a list name, an id into the persons table, and a sort key. Then, to display a particular list, I can select all list ListPositions with a given name, pull in the referenced persons, and sort on the sort key. Trying to do this in CoreDatat, however I run into problems. I am trying to do this using a schema like:
Person:
Name
DOB
etc...
positions -->> ListPosition
ListPosition:
listName
sortKey
person --> Person
Then, I can get all the Persons in a given list with the NSPredicate
[NSPredicate predicateWithFormat:#"ANY positions.listName like %#", someList];
This allows me to dynamically add lists against a large set of Persons. The problem is that I am unable to use the sortKey field of ListPosition to sort the Persons. What NSSortDescriptor will do this? And if it is not possible to sort a fetch on the property of one element of a to-many relationship, what is another way to get multiple, dynamic orderings in coredata? I am displaying the lists with a NSFetchedResultsController, so I can't put the lists together myself in memory. I need to do it with a single NSFetchRequest.
You're right-- following a to-many relationship returns an NSSet, which has no inherent sorting. To get sorted results there are a couple of options:
Assuming that Person/ListPosition is a two-way relationship, do a new fetch request for ListPosition entities. Make the predicate match on the "person" relationship from ListPosition, which would look something like [NSPredicate predicateWithFormat:#"person=%#", myPerson]. Use whatever sort descriptor you need on the fetch request.
Follow the relationship as you're doing, which gives you an NSSet. Then use NSSet's -sortedArrayUsingDescriptors: method to convert that to a sorted array.
I think the best approach in this case would be to fetch on ListPosition entity instead. Add the sort Descriptor for sortKey (it would work in this case because the fetch request is on ListPosition entity) and prefetch the Person associated with the the list name using setRelationshipKeyPathsForPrefetching for "person" on the fetch request.
[NSPredicate predicateWithFormat:#"listName like %#", someList];
If I understand your model correctly, each Person has one ListPosition for each list in which it participates. Let's say we have acsending list by their names, so X people have X list positions with the same listName and sortKey.
I would create entity List, that would contain the sortKey attribute and then use it in sort descriptor.
entity List:
- sortKey : string
- ascending : bool
Create sort descriptor and use it in fetch request:
[NSSortDescriptor sortDescriptorWithKey:chosenList.sortKey ascending:chosenList.ascending];
Then you may have as many Lists as you want and you can easily use its sort key to sort all people.
If you want to store the positions in database (you didn't mention attribute index in your ListPosition, or anything similar), you can create “joint entity”:
entity PersonInList:
- index : integer
- person -> Person
- list –> List
Another idea is having ordered set of Person objects directly in List entity.
Get the ListPosition (it will come as a NSMutableSet). Then do a sort on the Set, like this:
NSMutableSet *positionsSet = [personEntity mutableSetValueForKey:#"positions"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"yourSortKey" ascending:NO];
NSArray *positionsSortedSet = [positionsSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
That will give you a sorted out array according to your key.
I usually add an index field (type NSNumber) to an entity. It's very easy to calculate index in adding item. just by
object.index = person.positions.count
so, actually you don't need positions field but positions relationship. connect person entity to ListPosition entity would be enough.

CoreData Guidance

So I have this problem I am trying to solve - I wonder if anyone can comment on/help me with the approach. The thing is, I have it partly solved, but with the rest I'm not quite sure.
Here's the deal:
I have a fairly large DB online which I want to load on first start of the App. After that I am only going to load it if new versions exist.
I use an xml parser to parse the data and enter all the data to my data model. The database consists of thousands of products, all described by various attributes.
Anyway, it's easy for me to save thousands of products in a database, then retrieving the data on demand.
I have a problem of how to categorize them and how to save the category data. There is a main category i.e. Hi-Fi which has several subcategories- let's say 'stereo', 'tuner', 'phone' and so on....
How to best save this info, that category a has 15 subcategories and each of these categories in turn has 30 products while securing performance and keeping process-time at a minimum. I don't want to check all 2000 Products whether I need to show them in a certain table view each time I open a new table view.
Any hints on the apporach are appreciated.
You'll need two entities: Product and Category.
Category has a to-many relationship called subcategories with a target entity of Category. The inverse relationship can be called parentCategory. Category also has a to-many relationship called products. Product would have an inverse relationship called category (or categories if a product can belong to multiple categories)
Now, you can get all the products for a given category by checking its products property. If you want to include all the products in the subcategories, you can do a fetch request with a predicate like this:
[NSPredicate predicateWithFormat:#"category == %# OR category IN %#", category, category.subcategories];
I think you can solve it by having a Core Data modal consisting of three entities: Product, Category and SubCategory.
Product has a relationship category with destination Category and a relationship subcategory with destination SubCategory.
Category has a to-many relationship products with destination Product and a to-many relationship subcategories with destination SubCategory.
SubCategory has a to-many relationship products with destination Product and a relationship category with destination Category.
When defining these relationships remember to assign the Inverse relationships as well.
Now you get a list of all products belonging to a specific category by just loading the Category in question and accessing the products property. It should also be possible to use NSFetchRequest for Product with a predicate specifying which category you want. Which is best regarding performance and memory requirements I can't say so you just have to test which approach works best.

Core data many-to-many relationship - Predicate question

In my Core Data model I have two entities: List and Patient. List has an attribute called 'name'.
A List can have any number of Patients and each Patient can belong to any number of different lists. I have therefore set a relationship on List called 'patients' that has an inverse to-many relationship to Patient AND a relationship on Patient called 'lists' that has a to-many relationship to List.
What I'm struggling to figure out is how to create a Predicate that will select all Patients that belong to a particular List name.
How would I go about this? I have never used relationships before in Core Data.
This seems to work OK:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY lists == %#)", myList];
Where myList is an actual List entity.
Given a data model like:
List <<——>> Patient,
you can find all Patient instances that belong to a list with a particular name with a fetch request on the Patient entity using a predicate like:
[NSPredicate predicateWithFormat:#"ANY lists.name LIKE[cd] %#", listName]
assuming listName is an NSString instance with the list name you want. LIKE[cd] does a case-insensitive and diacritic-insensitive comparison.
It sounds like your data model is this:
List <<-->> Patient
I would think that if you know the particular list name, then you know the particular list object. If so, you can just grab the patients using the to-many relationship from List to Patient--it is a set of patient objects. For example, if the relationship from List to Patient is named "patients":
NSSet *patientSet = listObject.patients;
Note: this requires that you create subclasses for your managed objects so you can access the attributes and relationships as properties on your objects.
If you only know the list name for some reason, and you are fetching Patient objects, then you can create a predicate using the to-many relationship from Patient to List (assume it's named "lists" and the list's name in a string named "listName"):
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ANY lists.name == %#",listName];
Ten years later, some more info !
Just some more info on the fantastic #GarryPettet answer,
Say you have entity CD_Person and aPerson is one of those. IE:
var aPerson: CDPerson
Say you have an entity CD_Pet
CD_Person has a relationship .pets which is a one-to-many CD_Pet
So just to be clear,
aPerson.pets
is indeed a Set of CD_Pet entities.
Almost certainly you'll have an id field which comes from your data source.
(Aside, .id must be an Int64 in your core data entity, even if it's a smaller int in your source data)
Two ways to go!
BOTH this
let p = NSPredicate(format: "(ANY pets == %#)", aPerson )
AND this
let p = NSPredicate(format: "(ANY pets.id == %lld)", aPerson.id)
... work perfectly, both are possibilities.
So there's two ways to go!
(PS: Don't forget the lld .. # won't work for Int64!)
Both work fine in the common situation where you have a "many-to-many" relationship.

How to setup NSPredicate and NSEntityDescription in Coredata to do complicated SQL query

I know how to create NSPredicate to do sql like "SELECT * FROM DRINK". But how about this query:
"SELECT I.name, D_I.amount
FROM DRINK_INGREDIENT D_I, DRINK, INGREDIENT I
WHERE D_I.drinkID=1 AND DRINK.drinkID=1 AND I.ingredientID = D_I.ingredientID;"
How do I even set the NSEntityDescription, and NSPredicate for this kind of query?
Typically with Core Data you will design and use a data model that meets your needs. You should not think about it in terms of SQL--it does not even have to use SQL for storage--nor should you try to directly translate a SQL query into a fetch request with a predicate, etc.
That said, it can sometimes be helpful to think in terms of SQL WHERE clauses when building predicates, but really fetch requests comes down to what entity you need to get and how the collection should be filtered and/or sorted, etc.
Fetch requests are limited to a single entity, so I think you would need multiple fetch requests to simulate your query.
What does your Core Data data model look like? What are you trying to accomplish? What have you tried so far?
UPDATE
It sounds like your data model includes two entities: Drink and Ingredient with a many-to-many relationship between them:
Drink <<-->> Ingredient
Note that in Core Data, there is no DrinkIngredient entity unless you explicitly create it (there is an additional table for the many-to-many relationship, but it's abstracted away from you). Since you want an amount value associated with the rows in the additional table, I would recommend adding a DrinkIngredient entity in Core Data:
Drink <-->> DrinkIngredient <<--> Ingredient
Note: a DrinkIngredient has exactly one Drink and one Ingredient. Drinks can have many DrinkIngredients and Ingredients can be used by many DrinkIngredients.
It sounds like you want to get the name and amount for the list of ingredients for a particular drink. To do this, simply fetch DrinkIngredient objects with a filter predicate like this:
// assuming "aDrink" is a reference to a particular Drink object
// and "drink" is the relationship from DrinkIngredient to Drink
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"drink == %#",aDrink];
// if the fetch request result array is named "ingredientList"
// and the relationship from DrinkIngredient to Ingredient is "ingredient"
for (DrinkIngredient *di in ingredientList) {
NSString *ingredientName = di.ingredient.name;
NSUInteger amount = di.amount.integerValue;
// use "ingredientName" and "amount" here
}
Since you are using Core Data and not SQL, you do things differently. For example, if you wanted to display an ingredient list with name and amount for all drinks, you would simply fetch all Drink objects (no filter predicate) and you access the ingredients through the relationship from Drink to DrinkIngredient.
Again, you should think about what you are trying to accomplish and design and use your data model appropriately. You should not think about SQL or queries.