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

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

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

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

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

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

Ответ 1

отредактировать на обычном компьютере...

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

  • Вам не нужно помещать кавычки вокруг замещающего заполнителя в строку формата. Когда метод строит предикат, он будет создавать абстрактное синтаксическое дерево объектов NSExpression и NSPredicate (в частности NSComparisonPredicate и NSCompoundPredicate). Ваша строка будет помещена в NSExpression типа NSConstantValueExpressionType, что означает, что она уже будет интерпретироваться как обычная строка. Размещение одиночных кавычек в строке формата фактически сделает ваш предикат нефункциональным.
  • Вы не ограничены одним сравнением в предикате. Из его звуков вы хотите иметь столько сравнений, сколько слов в строке поиска (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.