Настраиваемая сортировка в NSFetchedResultsController

Проведя несколько часов, я обнаружил, что невозможно выполнить индивидуальную сортировку в NSFetchedResultsController при поддержке SQLite из следующей статьи.

Пользовательская сортировка NSFetchedResultsController не вызывается

Однако я не смог найти реального решения своей проблемы.

Вот что я пытаюсь сделать.

Предыстория:
у меня есть база данных англоязычных словарей (простой список слов - очень большой) в CoreData. Слова отображаются в UITableView с помощью NSFetchedResultsController.

UITableView имеет связанную панель поиска. Когда пользователь вводит строку в панель поиска, UITableView показывает список отфильтрованных слов.

Зачем мне настраиваемая сортировка:
Когда пользователь вводит строку, скажем, это bre, я меняю ее на регулярное выражение, b.*r.*e.* и использую как NSPredicate, а затем выполняю performFetch. Таким образом, все слова, такие как «голый» и «разрыв», будут отображаться в виде таблицы.

По умолчанию слова отображаются в алфавитном порядке. Следовательно, bare будет стоять перед break.

Я хочу, чтобы break находился перед bare в списке поиска, потому что первые три символа break точно соответствуют тому, что ввел пользователь.

Возможные идеи:

  1. Скопируйте результат NSFetchedResultsController в NSArray и выполните настраиваемую сортировку. Я не уверен, насколько быстро NSArray будет работать для большого массива, такого как английский словарь.
  2. Попробуйте выполнить PerformFetch несколько раз. Например, выше, попробуйте выполнитьFetch для bre.*, br.+e.*, b.+r.+e.* по порядку и объедините их.

Обе идеи выглядят не очень аккуратно.

Буду признателен, если вы можете предложить какое-либо известное аккуратное и типичное решение для такого рода проблем.


person Jaemin    schedule 16.10.2011    source источник


Ответы (2)


Другой подход, который вы могли бы рассмотреть, - использовать временное свойство для украшения результатов, чтобы указать тип соответствия, а затем использовать это временное свойство в качестве первого дескриптора сортировки.

Это требует перебора всех результатов, повторного сравнения строк и установки атрибутов. Если вы ожидаете длинного набора результатов, использование массива может быть таким же простым.

Если вам нужно эффективно обрабатывать большие наборы результатов, я бы предложил использовать два дескриптора сортировки, один из которых возвращает только точные совпадения, а другой - только неточные совпадения. Затем отобразите результаты первого, а затем результаты второго. С составным предикатом, который должен быть возможен.

person paulmelnikow    schedule 17.05.2012

Ух ты, эта проблема меня раздражала.

Моя установка выглядит следующим образом. У меня есть поиск, который вводит данные и ищет пользователей по имени пользователя или полному имени. Сервер уже возвращал соответствующий порядок, но поскольку я использую NSFetchedResultsController, мне нужен какой-то дескриптор сортировки. Вот то, что я сделал, похоже, работает хорошо. Я добавил новое свойство к своей пользовательской сущности под названием matchScore, и во время CRUD с сервера я получаю MIN() показатель расстояния Левенштейна между запросом ‹-> имя пользователя и запрос ‹-> полное имя

Теперь у меня есть дескриптор сортировки, который упорядочит результаты по наиболее близкому совпадению с сервера с запросом пользователя. Код - rubymotion, но должен быть читабельным.

sortDescriptors = []
sortDescriptors << NSSortDescriptor.sortDescriptorWithKey("matchScore", ascending:true)

С новым дескриптором сортировки я теперь могу получать «менее чем идеальные» результаты и по-прежнему сохранять в первую очередь самые близкие совпадения. Теперь я могу избежать некоторых потенциальных решений @Jaemin, которые включают сложную агрегацию результатов, чтобы обойти неработающие пользовательские сортировки.

request.predicate = NSPredicate.predicateWithFormat("(username MATCHES[cd] %@) OR (username BEGINSWITH[cd] %@) OR (name CONTAINS[cd] %@)", argumentArray:[searchString, searchString, searchString])

Счет матча теперь генерируется на сервере CRUD.

usersContext.performBlock(lambda{
  restUsers.each do |restUser|
    user = User.entityWithRestModel(restUser, usersContext)
    user.matchScore = [query.compareWithWord(user.username, matchGain:10, missingCost:1), query.compareWithWord(user.name, matchGain:10, missingCost:1].min
    puts "u:#{user.username} <-> q:#{query}   score:#{user.matchScore}"
  end
})

Вот категория NSString, которую я использую для получения расстояния Левенштейна. https://gist.github.com/iloveitaly/1515464

person Ryan Romanchuk    schedule 19.07.2014