Predicate for fetching children knowing grandchild - core-data

I have the following object model in Core Data:
Factory <-->> Division <-->> Department
Entities with relationships:
Factory.divisions_ -> Division.departments_
I'd like to fetch all divisions for factory knowing department but struggle to compose the predicate. The best I have not working:
let factoryPredicate = NSPredicate(format: "SUBQUERY(divisions_, $division, ANY $division.departments == %#).#count > 0", department)
let predicate = NSPredicate(format: "factory IN \(factoryPredicate)")
What I'm missing?
Thank you!

Related

NSFetchRequest: Query for distinct values and count other properties values

I try to set up an NSFetchRequest for an entity Location with properties like countryand city:
country | city
————————————————
Germany | Berlin
USA | San Francisco
USA | New York
Germany | Munich
Germany | Munich
USA | San Francisco
Germany | Stuttgart
The NSFetchRequest should return the country (or a Location object with the appropriate country) and the number of cities.
[
{ country: 'Germany', cityCount: 3 },
{ country: 'USA', cityCount: 2 }
]
I know that I can just fetch all entries and 'count it myself' but I am interested in how to set up an appropriate fetch request (or if it's possible to do so) and would like to see how you would do it! :)
The correct answer to this question to refactor the data model in order to avoid redundancy.
The country strings are repeated unnecessarily in the table. Additionally, you make a simple query gratuitously complicated. The model should reflect your data, and writing out "USA" for every American city is just not smart or efficient.
Your data model should look like this
Country <----->> City
Now you can just fetch all countries and get the cities with cities.count.
I had to resort to two separate fetches in order to achieve (I think) what you want. The first fetch gets objectIDs for one object for each distinct combination of country and city. The second fetch is filtered, using an IN predicate, to just these objects. It uses NSExpression and propertiesToGroupBy to get the counts for each country:
// Step 1, get the object IDs for one object for each distinct country and city
var objIDExp = NSExpression(expressionType: NSExpressionType.EvaluatedObjectExpressionType)
var objIDED = NSExpressionDescription()
objIDED.expression = objIDExp
objIDED.expressionResultType = .ObjectIDAttributeType
objIDED.name = "objID"
var fetch = NSFetchRequest(entityName: "Location")
fetch.propertiesToFetch = [objIDED]
fetch.propertiesToGroupBy = ["country", "city"]
fetch.resultType = .DictionaryResultType
let results = self.managedObjectContext!.executeFetchRequest(fetch, error: nil)
// extract the objectIDs into an array...
let objIDArray = (results! as NSArray).valueForKey("objID") as! [NSManagedObjectID];
// Step 2, count using GROUP BY
var countExp = NSExpression(format: "count:(SELF)")
var countED = NSExpressionDescription()
countED.expression = countExp
countED.expressionResultType = .ObjectIDAttributeType
countED.name = "count"
var newFetch = NSFetchRequest(entityName: "Location")
newFetch.predicate = NSPredicate(format: "SELF IN %#", objIDArray)
newFetch.propertiesToFetch = ["country", countED]
newFetch.propertiesToGroupBy = ["country"]
newFetch.resultType = .DictionaryResultType
let newResults = self.managedObjectContext!.executeFetchRequest(newFetch, error: nil)
println("\(newResults!)")
This will be inefficient: if you have a large number of distinct countries and cities, the IN predicate will slow things down. You might find it's more efficient to fetch everything and count them.

Core Data. Join 2 tables with many-tomany relationship?

I have 2 tables which are linked between themselves with many-to-many relationship.
SQL equivalent doesn't exist because it requires the third table to split a many-to-many relationship to two one-to-many relationships.
For example, I have two core data entities: category with a property (an array of items) and item with a property (an array of categories).
I need to get all the categories except of "empty" categories (when there is no item of this category).
My current temporary solution looks like an incorrect one. I use NSFetchRequest to get all the categories. Then I delete from this array all the categories with an empty item array manually using for-each.
In order to get all categories except the empty ones you could just use a predicate like this:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Category"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"items.#count > 0"];
NSArray *categories = [context executeFetchRequest:fetchRequest error:NULL];
My solution:
[NSPredicate predicateWithFormat:#"ANY items != nil"]

how to get eager loading in a many to many relationships?

I have a database with four tables. TableA and TableB are the main tables and the TableC is the table of the many to many relationships.
TableA(IDTableA, Name...)
TableB(IDTableB, Name...)
TableC(IDTableA, IDTableB)
This create three entities, The EntityA has an ICollection of Entity C and Entity C has a Collection of EntitiesB, so when I try to get the related entities I do this:
myContext.EntityA.Include(a=>a.EntityB.Select(b=>b.EntityC));
But this throw and exception that says that the collection is null.
So I would like to know if it is possible to do an eager loading when there are a table for the many to many relationship.
Thanks.
I think you need this:
var q = myContext.EntityC.Include("EntityA").Include("EntityB").ToList();
If you want Bs of an A:
var aId; // = something...;
var bs = from c in q
where c.EntityAId == aId
select c.EntityBId;
And simply vice versa if you need As of a B:
var bId; // = something...;
var eas = from c in q
where c.EntityBId == bId
select c.EntityAId;
With many to many association in Entity Framework you can choose between two implementations:
The junction table (C) is part of the conceptual model (class model) and the associations are A—C—B (1—n—1). A can't have a collection of Bs.
The junction table is not part of the conceptual model, but Entity Framework uses it transparently to sustain the association A—B (n—m). A has a collection of Bs and B has a collection of As. This is only possible when table C only contains the two FK columns to A and B.
So you can't have both.
You (apparently) chose the first option, so you will always have to query the other entites through C, like
from a in context.As
select new { a, Bs = a.Cs.Select(c => c.B) }
or
from a in As.Include(a1 => a1.Cs.Select(c => c.B))

Core Data: Fetching all entity objects having to-many-relationship object with certain value

I have the following relationship in Core Data:
What's the correct NSPredicate to get all courses having at least one student over the age of 25?
[NSPredicate predicateWithFormat:#"ANY students.age > %d", 25]
should work (as predicate for a fetch request for the "Course" entity).

How to create a Core Data predicate to test that a relation contains all given objects?

Setup:
I have a Core Data object A that has a to-many relation to B. Call the relation "items". So, a.items returns all B-s associated with A.
Now, I have a manually composed NSSet "itemSet" of B objects.
I want to do the following:
return all A objects whose "items" relation exactly matches itemSet
How do I construct a predicate for that? I’ve tried this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(ALL items in %#)", itemSet];
But that just gives me Unsupported predicate (null).
This:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(items in %#)", itemSet];
tells me unimplemented SQL generation for predicate. Interesting, but not helpful.
So what’s the right way to filter the relation with a set?
The following predicate could work:
[NSPredicate predicateWithFormat:#"(items.#count == %d) AND (SUBQUERY(items, $x, $x IN %#).#count == %d)",
itemSet.count, itemSet, itemSet.count];
The predicate checks first that the number of items is equal to the size of the given itemSet, and then checks that the number of items which are member of itemSet is also equal to the size of itemSet. If both are true, then items must be equal to itemSet.
Have you tried:
NSPredicate *predicate = [NSPredicate predicateWithFormate:#"items == %#", itemSet];
Alternatively, pull out a subset with a simpler predicate and filter them outside of the fetch request. i.e.
Set a predicate for the number of items in the relationship to the be the same as the number of items in your comparison set.
Fetch the results
Filter these results to show only the ones where the sets contain the same items.

Resources