I'm currently working on an app that uses Core Data. The issue I'm dealing with is the fetch data with predicate, example shown below:
[[self managedObjectContext] fetchObjectsForEntityName:#"Employee" withPredicate:
#"(lastName LIKE[c] 'Worsley') AND (salary > %#)", minimumSalary];
What i want to do is fetch the number of different categories from a certain dataset like this:
Entity Person:
-Name (String)
-Location (String)
-Age (Int)
So if for example i have 3 samples in the dataset:
[Lisa, Sweden, 30] [Mike, USA, 24] [Jacob, England, 28]
I now want to fetch the number of different locations, is this possible?
Many Thanks
Robert
In the FetchResults controller method,add the following code.
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:[entityProperties objectForKey:#"value"]]];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
Related
My Core Data model has a Note entity with a to-many relationship to a Link entity, accessed through a links property on Note.
I'm trying to fetch the count of the links property for each note in Core Data. To do so, I followed the example at the link below, but I'm getting an unexpected result.
Chained expressions to perform calculations in Core Data
Here's how I'm configuring the fetch request and related expression descriptions:
// Create the expression description that should return the link count for each note.
NSExpression *linksExpression = [NSExpression expressionForKeyPath:#"links"];
NSExpression *countExpression = [NSExpression expressionForFunction:#"count:" arguments:#[linksExpression]];
NSExpressionDescription *linkCountExpressionDescription = [[NSExpressionDescription alloc] init];
linkCountExpressionDescription.name = #"linkCount";
linkCountExpressionDescription.expression = countExpression;
linkCountExpressionDescription.expressionResultType = NSInteger32AttributeType;
// Execute a fetch request that should return an array of dictionaries, each with a single "linkCount" key.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Note"];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = #[linkCountExpressionDescription];
NSArray *countsArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
The SQLite store I'm working with contains 1000 notes, so I expect to get an array of 1000 dictionaries, each containing a LinkCount key and the count of its links relationship. Instead, I get a single dictionary that gives me the total count of all links.
What should I do differently to get the link counts for each note?
I think I'm just missing something obvious here, but it's one of those frustrating things that's somehow eluding me.
I have a Core Data Entity called ProjectEntry. The ProjectEntry objects are displayed in uitableviews, using various attributes, arranged by date (attributes include things like "dateAsNSDate"[NSDate], "month"[NSString], "year"[NSString], "dayOfWeek"[NSString]).
I'm using an NSFetchedResultsController to populate the table views.
When I initially create and save the ProjectEntry object, the "dateAsNSDate" attribute is parsed and converted into various NSStrings. One string, also an attribute, is called "concatMonthAndYear". It takes the "month" and "year" strings and just joins them. So I get things such as "January 2014", "February 2015", etc.
I use the "concatMonthAndYear" as my cell.textLabel.text string to display in my tableview cells.
I use the NSDate attribute to sort the tableview rows (sortDescriptor), and the "year" attribute as my section headers (sectionNameKeyPath).
So right now, I'd have a tableview section called "2014", with tableview rows each representing a Core Data object, named things like "January 2014", February 2014", etc, in said section.
I can tap on one of those rows, segue to another tableview, and list all objects created in January 2014, for example, by using an NSPredicate on the second tableview.
However, on my first tableview, each Core Data object created is represented by its own tableview row. So I'll get multiple rows reading "January 2014" or "May 2015" or whatever. They're valid saved objects, and I want them, but I'd like to prevent a new row from being created if that "concatMonthAndYear" already exists. If a row titled "January 2014" already exists, I don't want a new row created. I want the new Core Data object stored, just not a new tableviewrow representing it. I only need one row with "January 2014", for example, to segue into a table listing ALL the entities from January 2014.
I know how to use an NSPredicate to get ALL the January 2014 objects into the second table, but how do I get JUST ONE object into the first table?
Is NSPredicate not the right device for that? Should I be somehow preventing a new cell from being created in the UITableView delegate methods? Each tableview row should be unique, and I'm stuck on whether it should be handled with the NSFetchedResults controller or in the tableview delegate methods?
Or some other way?
Can someone point in the right direction?
EDITED TO INCLUDE CODE:
- (void)setupFetchedResultsController
{
// 1 - Decide which Entity
NSString *entityName = #"ProjectEntry";
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
[request setReturnsDistinctResults:YES];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:#[#"monthYearTableSecHeader", #"year"]];
// 3 - Filter it
//request.predicate = [NSPredicate predicateWithFormat:#" "];
// 4 - Sort it
request.sortDescriptors = [NSArray arrayWithObjects:[NSSortDescriptor sortDescriptorWithKey:#"year"
ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:#"dateAsNSDate"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)], nil];
//5 - Fetch
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"year"
cacheName:nil];
[self performFetch];
}
You could use
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:#[#"concatMonthAndYear", #"year"]];
This will cause the fetch request to return distinct dictionary objects corresponding to "January 2014", etc. objects.
However, you cannot use a fetch request controller's delegate methods (to hear of updates to the data).
If you need to hear updates, I suggest you add a layer of indirection to your data, where MonthEntry is an object representing yearly months and have a one to many relationship with ProjectEntry, which is your normal entity. This way, you can set the fetch request entity to MonthEntry.
// Test listing all words with their sentence
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Word"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:10];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Word *info in fetchedObjects) {
NSLog(#"Word object: %#", info.word);
Sentence *details = info.relatedToSentence;
NSLog(#"Sentence object: %#", details.sentence);
}
I have two entities called Word and Sentence. In each entity there is an attribute called word and sentence, respectively. The relationship is inverse, not optional, and one-to-one.
I am able to extract records from both entities separately, but somehow I can't fetch related objects through one entity, what did I do wrong?
The code above works, it is printing out value only for word objects and (null) for sentence objects... I am using sqlite as source for this database. I filled the sqlite file which is created by Xcode after modeling up my model.
For your Core Data model the SQLite tables would look like this:
CREATE TABLE ZSENTENCE ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZRELATEDTOWORD INTEGER, ZSENTENCE VARCHAR );
CREATE TABLE ZWORD ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZRELATEDTOSENTENCE INTEGER, ZWORD VARCHAR );
where ZRELATEDTOWORD is the primary key (Z_PK) of the related word, and ZRELATEDTOSENTENCE
the primary key of the related sentence. Both have to be filled correctly.
BUT: Please note that modifying the Core Data SQLite file is not recommended and
error-prone. The format is not documented officially and might change in the future.
I still recommend to write a separate command-line tool (which can use the same Core Data
model as your main project) that uses Core Data methods to create and fill the
initial database.
Suppose a Manager has a to-many relationship with Employee objects. Given a reference to a Manager object (i.e. NSManagedObject *manager), how do I get a reference to "the Employee with the lowest salary among all of those whose salaries exceed 10000"?
I can think of two possible approaches:
Approach 1: constructing an NSFetchRequest and specifying the Manager wanted with the object ID of the Manager in question.
Approach 2: some kind of key-value coding expression on Manager, e.g. [manager valueForKeyPath:#"..."] (with some use of NSPredicate?)
I'm inclined towards Approach 2 if there's a plausible solution. Please enlighten me.
Of course, you can just apply a predicate and sort descriptor to the set returned by the relationship. Easy, and pretty quick if the set is relatively small (because it is done in memory, all the objects will have to be fetched). You may want to do that batch fetch up front to limit the number of times you do I/O.
Depending on the size of the database and the number of employees under the manager (and the indexing), you may want to do it all at the database level...
// We want "Employee" objects
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Employee"];
// Assuming the "inverse" relationship from employee-to-manager is "manager"...
// We want all employees that have "our" manager, and a salary > 10000
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"(manager == %#) AND (salary > 10000", manager];
// Sort all the matches by salary, little-to-big
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"salary" ascending:YES]];
// Limit the returned set to 1 object, which will be the smallest
fetchRequest.fetchLimit = 1;
This will perform the entire query at the database level, and only return 1 object.
As always, performance issues are usually highly dependent on your model layout, and the options used for specifying the model and its relationships.
You can filter your array of Employee relationship to get the one you want.
1) First, get all the Employee with salaries over 10000:
NSArray *filtered = [manager.employees filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(salary > 10000)"]];
2)Then sort it in descending order
NSSortDescriptor* sortOrder = [NSSortDescriptor sortDescriptorWithKey: #"salary" ascending: NO];
NSArray *sortedAndFiltered = [filtered sortedArrayUsingDescriptors: [NSArray arrayWithObject: sortOrder]];
3)Then just get your employee
[sortedAndFiltered lastObject];
I'm implementing search bar in iphone app. I have list of Workers, each have attributes: firstName, lastName, company. Sections of table view are set to company attribute.
I did set predicate when searching:
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"firstName contains[cd] %#", searchBar.text];
and i get error:
NSFetchedResultsController ERROR: The fetched object at index 3 has an out of order section name 'company2. Objects must be sorted by section name'
when I have sortDescriptor:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:YES selector:#selector(caseInsensitiveCompare:)];
I did notice that when I change it to
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName"` ascending:YES selector:#selector(caseInsensitiveCompare:)];
now search works correctly with no error. Does initWithKey param must match attribute name in predicate? I don't get it.
From the NSFetchedResultsController docs:
The fetch request must have at least
one sort descriptor. If the controller
generates sections, the first sort
descriptor in the array is used to
group the objects into sections; its
key must either be the same as
sectionNameKeyPath or the relative
ordering using its key must match that
using sectionNameKeyPath.
In your case, you need to use two sorts (in an array.) Element one should be a sort on the company name attribute and second element should sort on the lastName/firstName attribute.