I have a one to many relationship as shown in the attached image. I want to list down all the course titles a particular lecturer teaches and the solution i have come up so far is as follows.
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Lecturer"];
NSMutableSet *courseList = [[NSMutableSet alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == 'smith'"];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *lecturers = [self.managedObjectContext executeFetchRequest:request
error:&error];
for (Lecturer *lecturer in lecturers)
{
NSSet *set = lecturer.courses;
for (Course *course in set)
{
[courseList addObject:course.title];
}
}
The courseList object contains all the courses a given lecturer teaches.
IS there a better way to do this using nspredicates alone and not by using 2 for loops ?
Thanks in advance.
You can just fetch the Course objects directly, but specifying a different predicate that requires their authorname.name to match:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Course"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"authorname.name == 'smith'"];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *courseList = [self.managedObjectContext executeFetchRequest:request error:&error];
If you want an array with just the titles, you can then use:
NSArray *courseTitles = [courseList valueForKeyPath:#"title"];
Related
I have an entity that has title title and amount. I would like to display the total value I have for each title in my entity.
You can pretty much dispense with fetch requests by leveraging KVC (Key-Value-Coding).
First fetch (or filter) with this predicate:
[NSPredicate predicateWithFormat:#"category = %#", #"Food"];
Then just sum up the result in one line:
NSNumber *sum = [result valueForKeyPath:#"#sum.amount"];
There is no need to fetch only certain attributes using NSDictionaryResultType - just fetch the normal NSManagedObjects (Core Data will optimize for you).
You use NSPredicate to filter your objects by category and then you can fetch only the values of a certain property e.g.:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Entity
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Incoming" inManagedObjectContext:context];
[request setEntity:ent];
// Predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"category MATCHES[cd] %#", #"FOOD"];
[request setPredicate:pred];
// Property fetch
[request setResultType:NSDictionaryResultType];
[request setReturnsDistinctResults:NO];
[request setPropertiesToFetch:#[#"Amount"]];
// Execute the fetch.
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
int total = 0;
if (objects) {
for(NSDictionary *dict in objects) {
NSString *key = dict.allKeys.lastObject;
total += [dict[key] intValue];
}
} else {
NSLog(#"Fetch error: %#", error.localizedDescription);
}
NSLog(#"Total: %i", total);
The following will get the categories and the sum in one fetch operation:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Incoming"];
fetchRequest.resultType = NSDictionaryResultType;
NSExpression *sumExp = [NSExpression expressionWithFormat:#"sum:(amount)"];
NSExpressionDescription *sumED = [[NSExpressionDescription alloc] init];
sumED.name = #"sum";
sumED.expression = sumExp;
sumED.expressionResultType = NSDoubleAttributeType;
fetchRequest.propertiesToFetch = [#"title", sumED];
fetchRequest.propertiesToGroupBy = [#"title"];
NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
See the NSExpression documentation here.
I have two Entity models "Content" and "LessonsContent" with no relationship between them. Both entities have common attribute "contentID" of NSNumber type. I am fetching all contents objects from "Content" entity for given predicate and storing these objects in contents array. I was successful in doing so.
Now, I want to fetch all lessonsContent objects from "LessonsContent" entity whose contentIds are present in contents array.
I am using following code:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"(contentType == %#)", #"Games"];
NSError * error;
NSFetchRequest * request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Content class])];
[request setPredicate:predicate];
[request setReturnsObjectsAsFaults:NO];
NSArray *contents = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
if(error){ NSLog(#"Error in fatching data");}
//predicate = [NSPredicate predicateWithFormat:#"Content.contentID IN %#", contents];
predicate = [NSPredicate predicateWithFormat:#"(SUBQUERY(contents, $x, $x.Content.contentID IN %#).#count != 0)",contents];
request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([LessonsContent class])];
[request setPredicate:predicate];
NSArray * mappingArray = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
NSLog(#"mappingArray %#", mappingArray);
But app is crashing. Please suggest appropriate solution.
Thanks in advance.
You have to get an array of contentID's first. Then use IN.
NSArray *contents = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
NSArray * allContentIDs = [contents valueForKey:#"contentID"];
then use the second predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"contentID IN %#", allContentIDs];
[request setPredicate:predicate];
Hope this helps.
I have a application that show the amount sold by a store all the time.
NSNumber *amount = [self valueForKeyPath:#"sales.#sum.value"];
Now i have to filter and show the amount by period. I have the field "period" (string) that holds month and year, like "2012-11".
How can i make the same query using the where clause?
You can filter the sales before computing the sum.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"period == %#", #"2012-11"];
NSSet *sales = [self valueForKeyPath:#"sales"];
NSSet *filteredSales = [sales filteredSetUsingPredicate:predicate];
NSNumber *amount = [filteredSales valueForKeyPath:#"#sum.value"];
But when you have many dozens or more sales, this method is not really efficient because it requires to load each sale into memory.
Moreover, if the sales are in fault, each sale will fire a fault and generate a SQL request. You may prevent this by batch faulting the sales before filtering them.
We will assume that self is a managed object.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"period == %#", #"2012-11"];
NSSet *sales = [self valueForKeyPath:#"sales"];
// Begin batch-faulting
NSManagedObjectContext *moc = [self managedObjectContext];
NSFetchRequest *request = [NSFetchRequest new];
[request setEntity:[NSEntityDescription entityForName:#"Sale" inManagedObjectContext:moc]];
[request setPredicate:[NSPredicate predicateWithFormat:#"self IN %#", sales]];
[request setReturnsObjectsAsFaults:NO];
[moc executeFetchRequest:request error:NULL];
// Batch-faulting done
NSSet *filteredSales = [sales filteredSetUsingPredicate:predicate];
NSNumber *amount = [filteredSales valueForKeyPath:#"#sum.value"];
However, you still get several allocations for each sale (the managed object, its cache, its attributes). The best solution is to use a fetch request to compute the sum in the SQLite database.
NSString *reverseRelationship = #"store"; // name of the relationship from the sale to self
NSExpressionDescription *description = [NSExpressionDescription new];
[description setName:#"amount"];
[description setExpressionResultType:NSDoubleAttributeType];
[description setExpression:[NSExpression expressionWithFormat:#"#sum.value"]];
NSManagedObjectContext *moc = [self managedObjectContext];
NSFetchRequest *request = [NSFetchRequest new];
[request setEntity:[NSEntityDescription entityForName:#"Sale" inManagedObjectContext:moc]];
[request setResultType:NSDictionaryResultType];
[request setPredicate:[NSPredicate predicateWithFormat:
#"%K == %# AND period == %#", reverseRelationship, self, #"2012-11"]];
[request setPropertiesToFetch:#[description]];
NSDictionary *result = [[moc executeFetchRequest:request error:NULL] lastObject];
NSNumber *amount = [result objectForKey:#"amount"];
I want to make NSPredicate over different entities. Here is my core database.
To make it simple, this is the query I should do.
Select c.c_name From Company c, Person p Where c.c_id = p.cu_company_id
Now I was wondering how my predicate should look like if I want to achieve the result like the query above.
Assuming that c_id and cu_company_id are integers, you can try
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"cu_company_id == data.company.c_id"];
[fr setPredicate:predicate];
NSArray *persons = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have persons array, you can then loop through it and get the person's name. To get it the other way around
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Company"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"c_id == data.person.cu_company_id"];
[fr setPredicate:predicate];
NSArray *companies = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have companies array, you can then loop through it and get the company's name.
I have two entities News and Tag.
News have to-many relationship to Tag and Tag has to-many relationship to News (inverse relationships).
Tag:
-tagcontent
News:
-title
-newscontent
How to get the list of all News with the tagcontent=#"Cars"
You can use somehting like this - ANY should help you
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSString *tagName = #"Cars";
request.entity = [NSEntityDescription entityForName:#"News" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:#"ANY tag.tagcontent = %#",tagName];
NSError *error = nil;
NSrray *results = [context executeFetchRequest:request error:&error];