1

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 and Attendee entities

Appointment list tableview

enter image description here

Detail appointment view where the attendee details can be seen. enter image description here

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;
}
Chinthaka
  • 966
  • 1
  • 13
  • 42
  • Do you have any errors logged to console? “objectId = %i” may be changed to “objectId = %@“ and pass there a NSNumber object (then there is no need to convert it to int). Subquery is not required for you, second one should work. – thom_ek Sep 02 '14 at 11:39
  • Did you try to use your predicates "one by one" ? I mean, divide all the and/or/subquery, and see if one by one it gets what you want. Then combine them little by little. – Larme Sep 02 '14 at 11:51
  • @thom_ek there is no errors. It just return all the attendees without filtering for second one. – Chinthaka Sep 02 '14 at 11:53
  • @thom_ek any more ideas ? – Chinthaka Sep 02 '14 at 13:04
  • @Larme are you saying try with AND, OR, SUBQUERY separately? – Chinthaka Sep 02 '14 at 13:05
  • Yes. I'd try first `objectName = %@` if it returns something. Then `objectId = %i`. Then `SUBQUERY(attendees, $x, $x.hasDeleted != 1).@count != 0`. Etc. Find the one that maybe don't return anything when it should. – Larme Sep 02 '14 at 13:07
  • @Larme, I can retrieve the data. Only issue is I cannot filter base on deleted attendees. – Chinthaka Sep 02 '14 at 13:17

1 Answers1

1

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]
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • are we modifying any object here? We just limit the "Attendee". Your solution works. But this return Attendee object with duplicate Appointment reference to each attendees. – Chinthaka Sep 03 '14 at 13:15
  • @Chinthaka: No object is modified. I do not fully understand your last remark. As I understand your question, all fetched Attendee objects should be related to the *same* Appointment (which is specified in the predicate). – Martin R Sep 03 '14 at 13:18
  • You are saying I have to first fetch Appointment and then do a separate fetch for Attendees? Is there a way we can do this in a one shot? Because I am using NSFetchedResultsController to populate these appointments to a tableview. – Chinthaka Sep 03 '14 at 14:12
  • @Chinthaka: No, you just fetch the attendees, and `attendee.appointment` is the Appointment object. – Martin R Sep 03 '14 at 16:25
  • There is an issue here. Do you have any idea a way to get the number of Appointment from `NSFetchedResultsController`. This is for `numberOfRowsInSection`? – Chinthaka Sep 03 '14 at 19:23
  • @Chinthaka: If you create a new project in Xcode with the "Master-Detail Application" template and select the "Use Core Data" checkbox then you get a sample Core Data app that shows how `numberOfRowsInSection` and all other table view methods are usually implemented in connection with a fetched results controller. – Martin R Sep 03 '14 at 19:30
  • I think you mean this `id sectionInfo = [self.fetchedResultsController sections][section]; return [sectionInfo numberOfObjects];`. Yes I know this. But this returns number Attendees after I implemented your method. Not number of appointments. I just need number of Appointments. Thanks – Chinthaka Sep 03 '14 at 19:36
  • @Chinthaka: In your question you talked about "one particular appointment". Now you have multiple appointments. Can you clarify *what exactly* should be displayed in the table view? – Martin R Sep 03 '14 at 19:46
  • Sorry, I didn't mean that much. I am displaying list of Appointments on a table view. – Chinthaka Sep 03 '14 at 19:48
  • @Chinthaka: And how are the attendees displayed? Can you show an example in your question? – Martin R Sep 03 '14 at 20:06
  • I have added two more images to the question. Once you tap on an appointment from the list, the details view will be loaded. Then if you tap on the attendee cell, then another view will load with selected attendees (there are list of name on this view and only related attendees are highlighted here). I hope you can understand. – Chinthaka Sep 03 '14 at 20:22
  • @Chinthaka: See updated answer. Hope that it works now. – Martin R Sep 03 '14 at 20:39
  • No, the number of Appointment is wrong. The number of appointment is equal to number attendees (this is not illustrate on the images). Because `id sectionInfo = [self.fetchedResultsController sections][section]; return [sectionInfo numberOfObjects]; ` returns number attendees base on the your predicate. – Chinthaka Sep 03 '14 at 20:42
  • @Chinthaka: Please show your code for the fetched results controller, fetch request and predicate, both for the first (Appointment) view controller and the last (Attendee) view controller. – Martin R Sep 03 '14 at 20:55
  • I have updated the question. I am passing selected appointment managed object to details view. So no fetching involve in second view. – Chinthaka Sep 03 '14 at 21:00
  • @Chinthaka: Did you read my updated answer? For the appointment list you need to fetch Appointment objects. For the attendee list you have to fetch the Attendee objects. You need two separate fetched results controllers. - Sorry, I do not know how to explain it better. – Martin R Sep 03 '14 at 21:13
  • I can understand. I was initially asking a way to filter attendees when I fetch appointment in one go using predicate. Because these got one-to-many relationship. Anyway thanks for you time so far. – Chinthaka Sep 03 '14 at 21:18