Core Data retrieving relationship data - core-data

I am really having trouble retrieving items that have been created through the Menu entity. This is the code I used to add an item to a specific Menu object
-(void)additem:(NSString *)entity :(NSDictionary *)aDictionary :(NSString *)menu
{
NSLog(#"additem");
NSError *error = nil;
Menu *menuItem = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Menu" inManagedObjectContext:managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"mname=%# and msection =%#",#"Parents",#"Keydates"]];
menuItem = [[managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (error) {
//Handle any errors
}
if (!menuItem) {
//Nothing there to update
NSLog(#"This class doesn't exist");
}
Items *anitem = (Items *)[NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:managedObjectContext];;
anitem.type = [aDictionary objectForKey:#"type"];
anitem.title = [aDictionary objectForKey:#"title"];
anitem.image = [aDictionary objectForKey:#"image"];
anitem.subtitle = [aDictionary objectForKey:#"subtitle"];
[menuItem addItemsObject:anitem];
[managedObjectContext save:&error];
}
I want to use a predicate to retrieve all the items that were created on a specific Menu object. Here is the code I am trying to retrieve it with.
- (void) readItems: (NSString *)section {
NSLog(#"readItems");
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Menu"
inManagedObjectContext:managedObjectContext];
fetchRequest.resultType = NSDictionaryResultType;
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"mname=%# and msection = %#",#"Parents",#"Keydates"]];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Items *item in fetchedObjects) {
NSLog(#">>>>>>%#",item);
}
}
Can anyone point me in the right direction.I know I pass section and don't use it. I have place the actual values in.

Your readItems: method specifies the entity for your fetch as Menu yet when you execute the fetch you are expecting Item (since you start iterating through fetchedObjects and casting them as Item).
Instead what you want to do is et your entity to Item and change the predicate to
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"items.mname=%# and items.msection = %#",#"Parents",#"Keydates"]];
Furthermore you should consider renaming your relationships. The relationship field specifies exactly what you would expect to see when you follow the arrow. Thus when I look at Items and follow its arrow to Menu I would expect the name of this relationship to be menu yet you call it menuItems. You have done this correctly for the relationship from Menu to Item.
That way we would end up with a more readable predicate looking like:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"menu.mname=%# and menu.msection = %#",#"Parents",#"Keydates"]];
Happy Coding

Related

search core data without fetch all rows

I have Core Data with +2 millions of rows and i want to search for two specific fields: name & phone (for example). I insert scopes for each field in the Search Bar. Everything go fine if I haven't large data set. That's why I want to search in my core data without load all rows in memory before go search controller. Just need a result when my search text length > 3 or when the Search Button Clicked.
I have just one Table View with Search Bar
I populate when AppDidFinish with Call history plist
When the search bar isActive my App frezes until 2 millions rows loads. I need Avoid this step and move forward to step 4
Enter the search text. Then the result filteredArray has shown in the Table View
...
If have any idea I will appreciated.
Here you have some codes:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Guia" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:50];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"GuiaCache"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
abort();
}
return _fetchedResultsController;
}
Maybe in this method I need to improve with some Predicate
- (NSFetchRequest *)searchFetchRequest
{
if (_searchFetchRequest != nil) {
return _searchFetchRequest;
}
_searchFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Guia" inManagedObjectContext:self.managedObjectContext];
[_searchFetchRequest setEntity:entity];
[_searchFetchRequest setFetchBatchSize:50];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[_searchFetchRequest setSortDescriptors:sortDescriptors];
return _searchFetchRequest;
}
Finaly the two functions to search in Core Data
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
if ([searchText length] > 3)
[self searchForText:searchText scope:_scopeKeyIndex];
}
- (void)searchForText:(NSString *)searchText scope:(NSUInteger)scopeOption
{
if (self.managedObjectContext)
{
NSString *predicateFormat = #"%K BEGINSWITH[cd] %#";
NSString *searchAttribute = #"telephone";
if (scopeOption == 1)
{
searchAttribute = #"name";
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute, searchText];
[self.searchFetchRequest setPredicate:predicate];
NSError *error = nil;
_filteredList = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
}
}
If you need some other piece of code, just comment.
First, run Instruments against your application, specifically Time Profiler. Where does it say the time is being spent?
Do you have a predicate in your search? What does the predicate look like? You need to show some code for people to help.
Doing a fetch against against 2 million records will take a bit of time but I suspect it is not loading 2 million rows into memory as that would most likely cause memory problems as well as speed problems.
Post the results from Time Profiler and lets see where the time is being spent.
Update
First your predicate is very expensive. BEGINSWITH should be avoided if at all possible. Making it both case and diacritic insensitive increases that cost dramatically.
Did you run Instruments against your code? Did you run the time profiler? Without running that you are just guessing. You NEED to run the time profiler and at least show the results if not link to the entire profile.

Core Data input fetches into labels

I want to store data from a text field. And then use this data to populate the text in a label inside a view controller. Is this possible? Ive messed around with it, but nothing seems to work. Any thoughts? Here are my two methods...
- (IBAction)saveButton:(id)sender {
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSManagedObject *noteEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Notes" inManagedObjectContext:coreDataStack.managedObjectContext];
[noteEntry setValue:_notesField.text forKey:#"notes"];
NSError *error = nil;
// Save the object to persistent store
if (![coreDataStack.managedObjectContext save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[coreDataStack saveContext];
}
Here is my view did load method:
- (void)viewDidLoad
{
[super viewDidLoad];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Notes"];
Notes *entry = [NSEntityDescription insertNewObjectForEntityForName:#"Notes" inManagedObjectContext:coreDataStack.managedObjectContext];
_outputLabel.text = entry.notes;
}
Your code in the saveButton: method seems OK. But your code in the viewDidLoad method seems wrong. Are you trying to do the following?
- (void)viewDidLoad {
[super viewDidLoad];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
//First way to init your fetchRequest:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Notes"];
//Second way to init your fetchRequest:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Notes" inManagedObjectContext:coreDataStack];
[fetchRequest setEntity:entity];
//Having a NSEntityDescription object can be usefull if you need to group your data, for example
//Set some predicates, if necessary
//Set some sortdescriptors, if necessary
NSError *error;
NSArray *resultsArray = [coreDataStack executeFetchRequest:request error:&error];
NSLog(#"%#", resultsArray);
_outputLabel.text = [[resultsArray firstObject] notes]; //if your array can only have one object
}
The NSLog will give you the structure of your fetch array (I can't guess it). You will then be able to set _outputLabel.text.
Answer by #user1966109 is exactly correct answer what you are looking for. Since you need to show the text in a label and for that you need to set predicate to get a single row. Set the predicate as below
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"value == abc"];//considerin abc is the word you are looking for.
request.predicate = predicate;
//then execute the query.

Core Data Fetch Save relationship while inserting entity (simultaneously)

First question here and I've tried a bunch of stuff and can't figure it out.
Core Data with 2 entities with to-many relationship both ways
A<<---->>B
A entity has name as an attribute, and a relationship Bs
First Controller lists all A entities and i have a second controller to add A entities and I want to have it save a default B in its relationship.
In the prepare for segue I have this code:
if ([[segue identifier] isEqualToString:#"addAEntitySegue"]) {
AddAEntityViewController *addAEntityViewController = [segue destinationViewController];
addAEntityViewController.delegate = self;
addAEntityViewController.managedObjectContext = self.managedObjectContext;
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
addAEntityViewController.A = [NSEntityDescription insertNewObjectForEntityForName:#"A" inManagedObjectContext:addingContext];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:addAEntityViewController
action:#selector(save:)];
addAEntityViewController.navigationItem.rightBarButtonItem = saveButton;
}
In addAEntityViewController i have this to save
-(IBAction)save:(id)sender
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"B" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#",[defaults objectForKey:#"BDefault"]];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
//Set the batch size to a suitable number
[fetchRequest setFetchBatchSize:20];
NSError *error;
self.A.name = textFieldVal;
[self.A addBObject:[[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] objectAtIndex:0]];
NSLog(#"A = %#", self.A.Bs);
[self.delegate addAEntityViewController:self didFinishWithSave:YES];
}
In the addAEntityViewController everything saves correctly even the NSLog(#"A = %#", self.A.Bs); statement shows the B. But when the delegate saves in the First Controller (AEntityViewController) it only saves the A.name but not the relationship A.Bs, I can't figure out what's wrong.
Here's the delegate method:
-(void) addAEntityViewController:self didFinishWithSave:YES{
if (save) {
NSLog(#"saveworkouts");
NSError *error;
if (![addingManagedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
// release the adding managed object context
self.addingManagedObjectContext = nil;
}
Like I said it saves the A entity but not the relationship to B even though the relationship to B saved correctly in the addAEntityViewController (the second View). An NSLOg of A.Bs is null.
First I believe that this line:
addAEntityViewController = self.managedObjectContext;
should be:
addAEntityViewController.managedObjectContext = self.managedObjectContext;
but that would also be wrong.
it should be getting the addingContext you created afterwards:
addAEntityViewController.managedObjectContext = addingContext;
I'm a bit surprised that your app didn't crash, as you are mixing managed objects from 2 different contexts.

fetch request for entity.attribute == #"somevalue"

How do I setup a fetch request to only pull the data from an entity's attribute with one particular value? This is the basic code I've used before.
-(void)fetchResults
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:self.entityDescription.name];
NSString *cacheName = [self.entityDescription.name stringByAppendingString:#"Cache"];
// predicate code
if (self.predicate != nil) {
[fetchRequest setPredicate:self.predicate];
}
// end of predicate code
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];
BOOL success;
NSError *error;
success = [self.fetchedResultsController performFetch:&error];
if (!success)
{
NSLog(#"%#", [error localizedDescription]);
}
}
I've been looking at this page: http://bit.ly/KevYwR is this the right direction?
Do I need to use NSPredicate or can I do without?
Thanks for any help, point in the right direction, etc.
Setting up a NSFetchRequest is equivalent to a SELECT statetement in SQL language.
Here a simple example:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:moc]];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
// error handling code
The array results contains all the managed objects contained within the sqlite file. If you want to grab a specific object (or more objects) you need to use a predicate with that request. For example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"attribute == %#", #"Some Value"];
[request setPredicate:predicate];
In this case results contains the objects where attribute is equal to Some Value. Setting a predicate is equal to put the WHERE clause in a SQL statement.
Note
I suppose that the name of the entity is EntityName and its property is called attribute which is of type string.
For further info I suggest you to read the Core Data programming guide and NSFecthRequest class reference.
http://developer.apple.com/library/iOS/#documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSFetchRequest_Class/NSFetchRequest.html
Hope it helps.

(Core Data )Fetch an specific entity with a max property

I have a Entity wich has a toMany relationship to another one. In this second one I have an attribute called "versionNumber" so. I have an object on entity type A, and I want to get the related entity B which has the biggest (max) versionNumber.
I have the following but that returns me a result obtained over all records on entity B, not over the specific entities related to the object of type A.
NSInteger vNumber = 0;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:DPA_VERSION_KEY inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:VERSION_NUMBER_KEY];
NSExpression *maxNumberExpression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxNumber"];
[expressionDescription setExpression:maxNumberExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [[self managedObjectContext] executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}
else {
if ([objects count] > 0) {
vNumber = [[[objects objectAtIndex:0] valueForKey:#"maxNumber"] integerValue] +1;
}
}
[expressionDescription release];
[request release];
return vNumber;
I have an idea but I hadn't been able to materialize it. I must ask SELF which is my object A to do that fetch over its relationship toVersions (Entity B).
Thanks for the help.
G.
Set a predicate to limit the request to only those B objects who have a relationship with A.
[request setPredicate:[NSPredicate predicateWithFormat:#"myA == %#", myA];

Resources