У меня проблемы с запросами 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()
}