iOS Core data predicate for one-to-many - ios

I have two entities as illustrate on the figure. I need to fetch a particular appointment and all the related attendees where attendee is not deleted (hasDeleted != 1). I have tried with different predicate formats. Following are two main predicate I have tried and not able to get the any of these predicates work. Could you please help me to overcome this issue?
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"objectName = %# AND objectId = %i AND SUBQUERY(attendees, $x, $x.hasDeleted != 1).#count != 0", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"objectName = %# AND objectId = %i AND (ANY attendees.hasDeleted != 1)", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
Appointment list tableview
Detail appointment view where the attendee details can be seen.
Fetch result controller code:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Attendee" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"uuid" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"appointment.objectName = %# AND appointment.objectId = %i AND hasDeleted != 1", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}

A fetch request always returns the objects from the managed object context. You cannot
fetch "modified objects", such as Appointment objects with a modified relationship to
"Attendee" which contains only the attendees with a certain property.
What you can do instead is to fetch the undeleted Attendee object instead
which are related to the given Appointment.
Create a fetch request for the Attendee entity and use the predicate
[NSPredicate predicateWithFormat:#"appointment.objectName = %# AND appointment.objectId = %i AND hasDeleted != 1", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]]
Update according to the new information: In your first view controller you use a
fetched results controller with a fetch request for the Appointment entity.
If you want to display only appointments that have any attendee which has not cancelled
then add the predicate
[NSPredicate predicateWithFormat:"ANY attendees.hasDeleted != 1"]
to this fetch request.
When the user taps on an appointment, you pass the select appointment to the next view
controllers. To display the attendees for this appointment, use a fetched results
controller with a fetch request for the Attendee entity, and add the predicate
[NSPredicate predicateWithFormat:#"appointment = %# AND hasDeleted != 1", selectedAppointment]

Related

NSPredicate to check property of first 5 objects in a Core Data Relationship

I am looking to create a predicate that can check the TypeID of the first 5 objects in a Core Data relationship.
Here is what I am trying, but it doesn't work:
int num = 5;
NSMutableArray *predicates = [[NSMutableArray alloc] init];
for (int i = 0; i < num; i++) {
[predicates addObject:[NSPredicate predicateWithFormat:#"SELF IN %# AND logs[%i].TypeID == 3", items, i]];
}
This gives the error:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null) CoreData:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null)
I realize I am probably doing this wrong, so is there a different way that I could be doing this using NSPredicate?
if you want only fetch 5. Set fetchLimit to 5. Maybe the following code can not run right away, but the principle is same. You can add a property or get function to assign the "logs.TypeID" like -(Int)myID {return logs[0].TypeID;} then "SELF IN %# AND myID == 3" would solve the problem.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND logs.TypeID == 3", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}
I’m not sure I understand the questions, but I don’t think it’s possible to do what you asked, at least not as worded. It doesn’t seem like it would be possible to inspect only five objects in the relationship and stop there, returning any number of matches or none at all, even if there are more in the database.
However, and I think this may be what you were getting at, it is possible to find five objects that are both in the items set and have at least one log with a typeID == 3. It can be done similar to what E.Coms proposed, except you need a subquery to handle the relationship. (I am assuming logs is a to-many relationship, either one-to-many or many-to-many).
Note that the following code has not been tested:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND SUBQUERY(logs, $log, $log.typeID == 3).#count != 0", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}

CoreData :NSFetchedResultsController one to many to many to many relationship

I working on app were I using core data and the core data object is is show below:
Giving theaterName and genreName I want to show the actorsName. Anyone know of a way to build a NSPredicate to do that?
You can use SUBQUERY to build NSPredicates that go deep into "to many" relationships:
// Set predicate
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(genres, $g, ANY $g.movies.name == %#).#count != 0", #"Batman vs Superman"];
[fetchRequest setPredicate:requestPredicate];
UPDATE
Based on your updated example, you'll need nested subqueries like this:
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(movies, $m, ANY $m.genres.name == %#).#count != 0 AND SUBQUERY(movies, $m, SUBQUERY($m.genres, $g, ANY $g.theaters.name == %#).#count != 0).#count != 0", #"Action", #"Harkins"];
Here is a sample Cast entity fetch:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Cast" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
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:nil];
// Set predicate
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(movies, $m, ANY $m.genres.name == %#).#count != 0 AND SUBQUERY(movies, $m, SUBQUERY($m.genres, $g, ANY $g.theaters.name == %#).#count != 0).#count != 0", #"Action", #"Harkins"];
[fetchRequest setPredicate:requestPredicate];
// Fetch
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// Check fetched objects
NSArray *fetchedObjects = [aFetchedResultsController fetchedObjects];
for (Cast *eachCast in fetchedObjects) {
NSLog(#"Each Cast name: %#", eachCast.name);
}
Just replace #"Action" with the desired Genre name and #"Harkins" with the Theater name.
Here is how I setup the core data model:

fetching specific data from core data database and displaying in UITableView

I currently have a table view that displays all the contents of the database Entity: ExData.
ExData has an attribute tag of string type.
What my problem is that i would like to display the contents of the ExData in the table view but only the entries that have a tag set of 2 for example.
The tag is to be sent from the previous view controller but this can be sorted out later as firstly i would just like to hard code only one tag value entries being displayed
ExDatasArray is a mutable Array.
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"ExData"];
self.ExdatasArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
Above is how the data from ExData is being fetched...
To fetch specific data, you add a predicate to the fetch request:
NSString *theTag = ...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"tag == %#", theTag];
[fetchRequest setPredicate:predicate];
Remark: If you are displaying the result set in a table view, you might also consider to
use a NSFetchedResultsController.
If I understand your question correctly, you are trying to limit the results of the fetchRequest. Use an NSPredicate to specify the query and if desired an NSSortDescriptor to sort it. Below I assume that your tag attribute is named tag, and that searchTagValue has been set appropriately.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"ExData"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"tag == %#", searchTagValue];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"tag" ascending:YES]];
NSError *error;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (!result) {
// handle error
}
else {
self.ExdatasArray = [results mutableCopy];
}

NSFetchedResultsController loads the whole data and bad performance

I have a large database with over 8000 entries in it. I want to load and list in tableview. I used NSFetchedResultsController. It's working fine except the bad performance. I found the tutorial here
And added my performFetch: method in the viewDidLoad: method. But it takes 2-3 seconds to load the view.
Here it is my code.
- (void)loadFetchResultsController {
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Error occurs while fetching the foods with fetch results controller %#, %#", error, [error userInfo]);
exit(-1);
}
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (isSearching)
{
[NSFetchedResultsController deleteCacheWithName:#"SearchResults"];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Food" inManagedObjectContext:_managedObjectContext];
NSPredicate *hiddenPredicate = [NSPredicate predicateWithFormat:#"hidden == %#",[NSNumber numberWithBool:NO]];
if (isSearching)
{
hiddenPredicate = [NSPredicate predicateWithFormat:#"(hidden == %#) AND (name contains[cd] %# OR foodDescription contains[cd] %#)",[NSNumber numberWithBool:NO], searchTerm,searchTerm];
}
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:hiddenPredicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_managedObjectContext sectionNameKeyPath:#"name.stringGroupByFirstInitial" cacheName:(isSearching) ? #"SearchResults" : #"Root"];
_fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I used the instruments and found out that calling performFetch: method loads the whole data for the first time when viewDidLoad: calls.
My food data model is like this:
Any idea to improve the performance and get rid of delay.. Any help would be appreciated.
I believe you need to set the fetch limit on your fetch request, not just the batch size. Try calling [fetchRequest setFetchLimit:20]; and see if that helps. This should cause the first 20 records to be fetched. Then as the user scrolls through your data, make additional fetch requests to get more data.

NSFetchedResultsController loses his rows after entering background

I've a strange problem in my app.
I instanciate a NSFetchedResultsController to populate my array.
It's declared this way (as a #propoerty (nonatomic, retain) )
- (NSFetchedResultsController *)fetchedContactsController {
if (self->fetchedContactsController != nil) return self->fetchedContactsController;
// Create the fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// I want every contact...
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:self->managedObjectContext];
[fetchRequest setEntity:entity];
// ... whose are not associated with my order item
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
[fetchRequest setPredicate:predicate];
// Edit the section name key path and cache name if appropriate
self.fetchedContactsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self->managedObjectContext sectionNameKeyPath:nil cacheName:nil];
[aFetchedResultsController release];
[firstnameSortDescriptor release];
[lastnameSortDescriptor release];
[sortDescriptors release];
[fetchRequest release];
// Execute the fetch request
[self->fetchedContactsController performFetch:nil];
return self->fetchedContactsController;
}
of course, contacts.items exists.
When my app goes background then foreground again, if I do another
NSLog(#"before >>> %d", [[fetchedContactsController fetchedObjects]count]); // >>> 3
[self->fetchedContactsController performFetch:nil];
[tableView reloadData];
NSLog(#"after >>> %d", [[fetchedContactsController fetchedObjects]count]); // >>> 0
every line in my array disappear !
EDIT :
If I comment these lines :
// ... whose are not associated with my order item
//NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
//[fetchRequest setPredicate:predicate];
The problem don't occur anymore ! But that's not what I want...
Ok I found it. Thanks to this question, I replaced this line :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
by
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT SELF IN %#", self.orderItem.recipients];
It's funny how the fact you ask a question can make you on the path to resolve.

Resources