NSPredicate для поиска по нескольким словам


В моем приложении iOS у меня есть очень простой предикат для моего контроллера выборки.

NSString *format = [NSString stringWithFormat:@"name like[c] '%@'", nameVar];
NSPredicate *predicate = [NSPredicate predicateWithFormat:format];
[fetchController setPredicate:predicate];

Он выполняет основной поиск имени без учета регистра. Теперь я хотел бы изменить его, чтобы я мог поместить несколько слов в поле поиска (nameVar имеет значение из поля поиска), разделенное пробелами, и иметь предикат фильтровать результаты, соответствующие всем этим ключевым словам.

Итак, если у меня есть два имени: "Джон Смит" и "Мэри Смит", и я ищу: "Смит м" я хотел бы иметь только один результат, но поиск вроде этого: "см-го I-го" должна вернуть оба значения.

Есть ли у кого-нибудь идея, как это должно быть реализовано?
1 10

1 ответ:

Edit обратно на обычный компьютер...

Итак, есть пара вещей, о которых нужно знать:

  1. Вам не нужно ставить кавычки вокруг замещающего заполнителя в строке формата. Когда метод строит предикат, он будет создавать абстрактное синтаксическое дерево NSExpression и NSPredicate (в частности NSComparisonPredicate и NSCompoundPredicate) объектов. Ваша строка будет помещена в NSExpression типа NSConstantValueExpressionType, что означает, что она уже будет интерпретирована как обычная строка. Размещение одинарных кавычек в строке формата фактически сделает ваш предикат нефункциональным.
  2. Вы не ограничены одним сравнением в предикате. Судя по звукам, вы хотите иметь столько же сравнений, сколько "слов" в строке поиска (nameVar). В этом случае мы разбьем nameVar на составляющие его слова и создадим сравнение для каждого слова. Как только мы это сделаем, мы AND их объединим, чтобы создать единый всеобъемлющий предикат. То код ниже делает именно это.

Оригинальный ответ

Вы можете сделать это, построив свой собственный NSCompoundPredicate:

NSString *nameVar = ...; //ex: smith m
NSArray *names = ...; //ex: John Smith, Mary Smith

NSArray *terms = [nameVar componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSMutableArray *subpredicates = [NSMutableArray array];

for(NSString *term in terms) {
  if([term length] == 0) { continue; }
  NSPredicate *p = [NSPredicate predicateWithFormat:@"name contains[cd] %@", term];
  [subpredicates addObject:p];
}

NSPredicate *filter = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
[fetchController setPredicate:filter];

Предупреждение: введено в браузере на моем iPhone.