Создать столбец в fetchrequest на основе выражения

РЕЗЮМЕ

Я хочу иметь возможность создать новое значение на основе выражения в fetchRequest из сущности Core Data.

Наличие объекта с этими атрибутами (короткая версия)

  Name              Attributes
[Course] -> {licenseStart, name, id}

Я хочу сделать fetchResquest для этого объекта и получить словарь со всеми атрибутами плюс еще одну базу на этом выражении:

licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"

Итак, результат будет:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

POST

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

Позвольте мне объяснить вам ситуацию:

У меня есть модель CoreData с объектом, который имеет разные атрибуты, один из них - начало лицензии. Это номер временной метки unix, который используется для определения, когда начнется курс.

Итак, когда я хочу заполнить данные для создания UITableView с курсами, я без проблем извлекаю всю эту информацию из базы данных, я использую этот атрибут licenseStart для расчета разделов на основе формулы , это код для UITableView

UITableViewController

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

После этого мои результаты (небольшой пример) выглядят так

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

Что у меня будет в UITableView, будет:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Cooming Soon]
  [D]
  [E]

Итак, сейчас я использую licenseStart для создания двух разделов, как? Легко, просто используя атрибут моей модели под названием dateStatus и создавая два возможных результата с помощью лицензия начинается следующим образом:

ModelClass

var dateStatus : Int {
        get {
            return licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"
        }
 }

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

ПРОБЛЕМА

До сих пор все работает идеально. Но теперь я хочу добавить опцию сортировки, которая позволяет пользователям сортировать результаты по возрастанию или по убыванию, проблема в том, что когда я сортирую, то есть по возрастанию, используя другой вариант сортировки:

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

Из-за того, как я создаю разделы на основе licenseStart, результаты этой выборки будут точно такими же, как и раньше:

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

Что у меня будет в UITableView, будет:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Coming Soon]
  [D]
  [E]

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

i.e:

[Actually] 
  [C]
  [B]
  [B]
  [A]
[Coming Soon]
  [E]
  [D]

ВОЗМОЖНОЕ РЕШЕНИЕ

Я думал создать новый столбец во время запроса fetchrequest, если я смогу оценить это выражение licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? 0 : 1 в момент извлечения данных, в результате у меня будет этот новый столбец с 0 и 1, как это:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

И теперь я мог использовать этот статус для создания разделов в NSSortDescriptors [NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)] по возрастанию и [NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: false), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)] по убыванию.

Это дало бы мне поведение, которое я хочу ... но я не смог придумать красивый и чистый способ сделать это. Я пытался сделать это с помощью NSExpressions, но не могу использовать меньше с выборкой CoreData ..

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

Я спрашиваю, есть ли у кого-нибудь краткое представление или умное решение этой проблемы.

В MySQL или SQLite легко создать новый столбец на основе определенного значения или выражения, но в coredata я новичок, поэтому я не вижу четкого способа сделать это.

Большое спасибо.


person neteot    schedule 16.03.2017    source источник
comment
Это длинный вопрос. Не могли бы вы поместить где-нибудь резюме (возможно, вверху), которое показывает конечный результат, которого вы хотите достичь, и то, что вы на самом деле получаете. Это немного похоже на поток сознания - полезно для того, чтобы понять ваши мысли, но трудно понять, в чем заключается реальная проблема.   -  person Ashley Mills    schedule 16.03.2017
comment
Добавил резюме, спасибо за отзыв @AshleyMills :)   -  person neteot    schedule 16.03.2017


Ответы (2)


У меня была ситуация, когда я хотел разделить некоторые элементы, чтобы они были в верхней части списка, не на основе атрибута core-data. Я сделал выборку с помощью fetchedResultsController, а затем выполнил один прогон, чтобы поместить элементы в один из двух массивов (O (n)), сохраняя сортировку по их исходной сортировке. Когда элементы менялись, мне нужно было найти их в моем массиве, чтобы изменить их, но это оказалось не так сложно, как я ожидал.

Более простым решением может быть использование двух fetchedResultsController.

person Jon Rose    schedule 16.03.2017

Старайтесь не думать о CoreData как о обычной реляционной базе данных sql, в которой вы можете создавать столбцы в своих запросах.

Похоже, что вам нужно просто добавить еще один атрибут status к вашей сущности. Каждый раз, когда вы обновляете licenseStart, просто устанавливайте значение status.

Если вы используете кодогенератор, то либо

extension Course {
    func updateLicenseStart(date: Date) {
        licenseStart = date
        status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
    } 
}

или переименуйте базовый атрибут…

extension Course {
    var licenseStart {
        set {
            _licenseStart = newValue 
            status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
        }
        get { return _licenseStart }
    } 
}

Если вы поддерживаете свой собственный класс модели, вы не можете добавить наблюдателя свойств в @NSManaged var, но вы можете удалить @NSManaged и переопределить set и get

public var licenseStart: Date {
    set {
        willChangeValueForKey("licenseStart")
        setPrimitiveValue(newValue, forKey: "licenseStart")
        status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
        didChangeValueForKey("licenseStart")
    }
    get {
        willAccessValueForKey("licenseStart")
        let date = primitiveValueForKey("licenseStart") as? Date
        didAccessValueForKey("licenseStart")
        return date
    }
}
person Ashley Mills    schedule 16.03.2017
comment
Да, я впервые подумал об этом решении. Но дело в том, что когда вы извлекаете данные с сервера и сохраняете их в Core Data, статус будет основан на этом моменте, но мне нужно, чтобы статус был основан на текущей временной метке каждый раз, когда пользователь заполняет tableview и Я передаю fetchResultsController.fetchedObjects методам делегата tableview. - person neteot; 16.03.2017
comment
Думаю, я понимаю. О каком количестве строк идет речь? Насколько точным должно быть время? Не могли бы вы выполнить пакетное обновление при запуске приложения / на переднем плане / перед заполнением таблицы и установить статус для каждой строки? - person Ashley Mills; 16.03.2017
comment
Разве это не проблема количества, но приложение имеет всю эту автономную полную функциональность, которая позволяет пользователю даже пройти курс в автономном режиме (он загружает все содержимое курса), а затем, когда соединение снова становится активным, оно запускает функцию обновления. Так что, возможно, в течение нескольких дней будут несогласованные разделы (он может сказать скоро, когда это в настоящее время): S. Мне удалось создать выражение NSE с помощью propertiesToFetch и создать новое значение (но я не могу использовать сложную функцию, подобную приведенной выше licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? 0 : 1 - person neteot; 16.03.2017