21

I am trying to use a UISearchBar to query multiple properties of a NSManagedObject I have a NSManagedObject called Person, every person has a name and socialSecurity property. Right now my code can perform a search (fetch) for one of those properties or the other, but not both at the same time.

- (void) performFetch
{       
    [NSFetchedResultsController deleteCacheWithName:@"Master"];  

    // Init a fetch request
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"MainObject" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Apply an ascending sort for the color items
    //NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Term" ascending:YES selector:nil];
    NSSortDescriptor *sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fullName" ascending:YES selector:@selector(caseInsensitiveCompare:)];    

    NSArray *descriptors = [NSArray arrayWithObject:sortDescriptor];
    [fetchRequest setSortDescriptors:descriptors];

    // Recover query
    NSString *query = self.searchDisplayController.searchBar.text;
    //if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"Term contains[cd] %@", query];
    if(searchValue==1)
    {
        if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query];
    }
    else {
        if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"socialSecurity contains[cd] %@", query];
    }        

    // Init the fetched results controller
    NSError *error;
    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"pLLetter" cacheName:nil];

    self.fetchedResultsController.delegate = self;

    if (![[self fetchedResultsController] performFetch:&error]) NSLog(@"Error: %@", [error localizedDescription]);

    [self.tableView reloadData];
}

I don't know how to put both properties into this statement...

if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query];

Any help or ideas would be greatly appreciated.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
OscarTheGrouch
  • 2,374
  • 4
  • 28
  • 39

8 Answers8

34

You could use a NSCompoundPredicate.

Like this:

NSPredicate *predicateName = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query];
NSPredicate *predicateSSID = [NSPredicate predicateWithFormat:@"socialSecurity contains[cd] %@", query];
NSArray *subPredicates = [NSArray arrayWithObjects:predicateName, predicateSSID, nil];

NSPredicate *orPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

request.predicate = orPredicate;

there is a NSCompoundPredicate for AND too: andPredicateWithSubpredicates:

Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247
28

You can append multiple search terms in an NSPredicate using the usual boolean operands such as AND/OR.

Something like this should do the trick.

[NSPredicate predicateWithFormat:@"name contains[cd] %@ OR ssid contains[cd] %@", query, query];

Hope that helps :)

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
Zack Brown
  • 5,990
  • 2
  • 41
  • 54
  • 1
    if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(fullName contains[cd] %@) || (socialSecurity contains[cd] %@)",query, query]; – OscarTheGrouch May 16 '12 at 23:48
13

Addition to @Matthias's answer, you can also use NSCompoundPredicate for your AND operations like this.

Obj-C - AND

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[predicate1, predicate2]];

Swift - AND

let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate  = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2] )

Swift 3 - AND

    let predicate1 = NSPredicate(format: "X == 1")
    let predicate2 = NSPredicate(format: "Y == 2")
    let predicateCompound = NSCompoundPredicate(type: .and, subpredicates: [predicate1,predicate2])
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
Onur Var
  • 1,778
  • 1
  • 16
  • 16
6

Complete solution for Swift2

let request = NSFetchRequest(entityName: "Location")
let subPredicate1 = NSPredicate(format: "(name = %@)", searchString)
let subPredicate2 = NSPredicate(format: "(street = %@)", searchString)
let subPredicate3 = NSPredicate(format: "(city = %@)", searchString)

request.predicate = NSCompoundPredicate(type: .or, subpredicates: [subPredicate1, subPredicate2, subPredicate3])
HobbyMarks
  • 348
  • 2
  • 5
Vincent
  • 4,342
  • 1
  • 38
  • 37
4

To avoid the warning Incompatible pointer types initializing 'NSCompoundPredicate *_strong' with an expression of type 'NSPredicate *', replace the following:

NSCompoundPredicate * predicate = [NSCompoundPredicate orPredicateWithSubPredicates:subPredicates];

with this:

NSPredicate * predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

Source: NSCompoundPredicate

Community
  • 1
  • 1
MFarmer
  • 1,686
  • 13
  • 9
1

Fraser Hess over at CISMGF.com has a great search example. You can read the post at http://www.cimgf.com/2008/11/25/adding-itunes-style-search-to-your-core-data-application/

My code based off the post is:

NSArray *searchTerms = [searchText componentsSeparatedByString:@" "];
if ([searchTerms count] == 1) { // Search in name and description
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(name contains[cd] %@) OR (desc contains[cd] %@)", searchText, searchText];
    [self.searchFetchedResultsController.fetchRequest setPredicate:predicate];
} else { // Search in name and description for multiple words
    NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
    for (NSString *term in searchTerms) {
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"(name contains[cd] %@) OR (desc contains[cd] %@)", term, term];
        [subPredicates addObject:pred];
    }
    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
    [self.searchFetchedResultsController.fetchRequest setPredicate:predicate];
}
Bean
  • 21
  • 3
1

This is may be useful if you want to search multiple properties for anything that matches (eg UISearchControllerDelegate):

NSString *searchFor = @"foo";
NSArray *fields = @[@"Surname", @"FirstName", @"AKA", @"Nickname"]; // OR'd dictionary fields to search
NSMutableArray *predicates = NSMutableArray.new;
for (NSString *field in fields) {
    [predicates addObject:[NSPredicate predicateWithFormat:@"(%K BEGINSWITH[cd] %@)", field, searchFor]];
}
NSPredicate *search = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];

You could then use this, for example, to filter an array of dictionaries:

NSArray *results = [bigArray filteredArrayUsingPredicate:search];

(the BEGINSWITH[cd] is no magic, just means it'll match beginnings of strings and case-insensitive. Change as needed for your match criteria.)

tiritea
  • 1,229
  • 13
  • 18
0

For Swift:

var predicate = NSCompoundPredicate(
        type: .AndPredicateType,
        subpredicates: [predicate1, predicate2]
    )
Dhavaprathap
  • 188
  • 8