Swift, CoreData, FetchRequest, 2 объекта, ManyToMany, с GroupBy, с подсчетом и сортировкой, как? - PullRequest
0 голосов
/ 30 мая 2020

У меня проблемы с запросами CoreData. Я бы обошелся с некоторыми SQL производными. Но здесь мне нужна помощь.

У меня есть две сущности с отношениями manyToMany. Это выглядит так:

Repeatable (name: String, type: Int16, entries: [Entry])
Entry      (id: UUID, createdAt: Date, repeatables: [Repeatable])

Я хочу получить список повторяющихся элементов, принадлежащих более чем 1 записи, упорядоченный по количеству ассоциаций

В SQL это будет выглядеть так :

SELECT rep.Z_PK, COUNT(rep.Z_PK) as count FROM ZREPEATABLE AS rep
INNER JOIN Z_1REPEATABLES as link
ON rep.Z_PK = link.Z_2REPEATABLES
INNER JOIN ZENTRY AS entry
ON entry.Z_PK = link.Z_1ENTRIES
GROUP BY rep.Z_PK
HAVING count > 1
ORDER BY count DESC

Могу ли я сделать это с Core Data быстро? Как выглядит код?

Изменить:

Я придумал (часть решения):

let somethingED = NSExpressionDescription()
somethingED.expression = NSExpression(forKeyPath: "entries.@count.id")
somethingED.name = "countSth"
somethingED.expressionResultType = .integer64AttributeType

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Repeatable")
fetchRequest.propertiesToFetch = ["name", somethingED]
fetchRequest.relationshipKeyPathsForPrefetching = ["entries"]
fetchRequest.resultType = .dictionaryResultType
fetchRequest.predicate = NSPredicate(format: "$countSth > 1")

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext

do {
    let result = try context.fetch(fetchRequest)
    print(result)
} catch{
    print("error fetching: \(error)")
}

Проблема с этим решением ... Я не знаю, как это отсортировать. Я всегда получаю ошибку. Также это не конструкция group-by + имеющая. Итак, я думаю, хорошо, хорошо, если это сработает, мне не нужна группировка. НО все же я хотел бы знать, как и возможно ли это с group-by + с предикатом?!


Edit 2:

Так что в комментариях @pbasdf связал это из wwd c 2019 (+1 за это). Он точно описывает мой вариант использования и предоставляет идеальное решение ... НО ... это для iOS 13, но я также хочу поддерживать iOS 12 (например, iPhone 6). И вот я наконец придумал это решение: иметь свойство entriesCount + при каждом сохранении обновлять это значение. Теперь сохранение происходит немного медленнее, но чтение будет очень быстрым, потому что в запросе не требуется объединения таблиц, группировки или подсчета.

    public override func willSave() {
        let newValue =  Int16(self.entries?.count ?? 0)
        if countEntries != newValue, changedValues()[#keyPath(Repeatable.countEntries)] == nil, !isDeleted {
            countEntries = newValue
        }
        super.willSave()
    }

1 Ответ

1 голос
/ 30 мая 2020

Да, ты можешь это сделать. Вы должны создать NSFetchRequest и настроить его свойства sortDescriptors и predicate, чтобы получить желаемый результат.

Если вы правильно настроили отношения между основными данными, нет необходимости вручную указать такие вещи, как «внутреннее соединение». Вы просто устанавливаете отношение «многие ко многим» в модели данных, а затем можете получить доступ к этим отношениям непосредственно в своем предикате.

Я не знаю, можно ли весь ваш запрос полностью перевести в одну выборку, но не стоит этим ограничиваться. После того, как вы закончили выборку, вы можете выполнить дальнейшее уточнение / обработку в контексте управляемого объекта. В этом вся суть CoreData ORM, вы просто работаете с объектами напрямую и не беспокоитесь о базовой БД.

...