mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-19 16:21:15 +01:00
keyPath utilities for Select queries
This commit is contained in:
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `SelectTerm`
|
||||
*/
|
||||
@objc
|
||||
public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
|
||||
public final class CSSelectTerm: NSObject {
|
||||
|
||||
/**
|
||||
Provides a `CSSelectTerm` to a `CSSelect` clause for querying an entity attribute.
|
||||
@@ -175,11 +175,11 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
|
||||
public let bridgeToSwift: SelectTerm
|
||||
public let bridgeToSwift: SelectTerm<NSManagedObject>
|
||||
|
||||
public init(_ swiftValue: SelectTerm) {
|
||||
public init<D: NSManagedObject>(_ swiftValue: SelectTerm<D>) {
|
||||
|
||||
self.bridgeToSwift = swiftValue
|
||||
self.bridgeToSwift = swiftValue.downcast()
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: - SelectTerm
|
||||
|
||||
extension SelectTerm: CoreStoreSwiftType {
|
||||
extension SelectTerm where D: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -195,6 +195,24 @@ extension SelectTerm: CoreStoreSwiftType {
|
||||
|
||||
return CSSelectTerm(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate func downcast() -> SelectTerm<NSManagedObject> {
|
||||
|
||||
switch self {
|
||||
|
||||
case ._attribute(let keyPath):
|
||||
return SelectTerm<NSManagedObject>._attribute(keyPath)
|
||||
|
||||
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||
return SelectTerm<NSManagedObject>._aggregate(function: function, keyPath: keyPath, alias: alias, nativeType: nativeType)
|
||||
|
||||
case ._identity(let alias, let nativeType):
|
||||
return SelectTerm<NSManagedObject>._identity(alias: alias, nativeType: nativeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +239,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(numberTerm: CSSelectTerm) {
|
||||
|
||||
self.init(Select<NSNumber>(numberTerm.bridgeToSwift))
|
||||
self.init(Select<NSManagedObject, NSNumber>(numberTerm.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +255,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(decimalTerm: CSSelectTerm) {
|
||||
|
||||
self.init(Select<NSDecimalNumber>(decimalTerm.bridgeToSwift))
|
||||
self.init(Select<NSManagedObject, NSDecimalNumber>(decimalTerm.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +271,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(stringTerm: CSSelectTerm) {
|
||||
|
||||
self.init(Select<NSString>(stringTerm.bridgeToSwift))
|
||||
self.init(Select<NSManagedObject, NSString>(stringTerm.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +287,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(dateTerm: CSSelectTerm) {
|
||||
|
||||
self.init(Select<Date>(dateTerm.bridgeToSwift))
|
||||
self.init(Select<NSManagedObject, Date>(dateTerm.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,7 +303,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(dataTerm: CSSelectTerm) {
|
||||
|
||||
self.init(Select<Data>(dataTerm.bridgeToSwift))
|
||||
self.init(Select<NSManagedObject, Data>(dataTerm.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +318,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public convenience init(objectIDTerm: ()) {
|
||||
|
||||
self.init(Select<NSManagedObjectID>(.objectID()))
|
||||
self.init(Select<NSManagedObject, NSManagedObjectID>(.objectID()))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +334,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public static func dictionaryForTerm(_ term: CSSelectTerm) -> CSSelect {
|
||||
|
||||
return self.init(Select<NSDictionary>(term.bridgeToSwift))
|
||||
return self.init(Select<NSManagedObject, NSDictionary>(term.bridgeToSwift))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,7 +353,7 @@ public final class CSSelect: NSObject {
|
||||
@objc
|
||||
public static func dictionaryForTerms(_ terms: [CSSelectTerm]) -> CSSelect {
|
||||
|
||||
return self.init(Select<NSDictionary>(terms.map { $0.bridgeToSwift }))
|
||||
return self.init(Select<NSManagedObject, NSDictionary>(terms.map { $0.bridgeToSwift }))
|
||||
}
|
||||
|
||||
|
||||
@@ -365,18 +383,18 @@ public final class CSSelect: NSObject {
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
|
||||
public init<T: QueryableAttributeType>(_ swiftValue: Select<T>) {
|
||||
public init<D: NSManagedObject, T: QueryableAttributeType>(_ swiftValue: Select<D, T>) {
|
||||
|
||||
self.attributeType = T.cs_rawAttributeType
|
||||
self.selectTerms = swiftValue.selectTerms
|
||||
self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
|
||||
self.bridgeToSwift = swiftValue
|
||||
super.init()
|
||||
}
|
||||
|
||||
public init<T>(_ swiftValue: Select<T>) {
|
||||
public init<D: NSManagedObject, T>(_ swiftValue: Select<D, T>) {
|
||||
|
||||
self.attributeType = .undefinedAttributeType
|
||||
self.selectTerms = swiftValue.selectTerms
|
||||
self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
|
||||
self.bridgeToSwift = swiftValue
|
||||
super.init()
|
||||
}
|
||||
@@ -385,7 +403,95 @@ public final class CSSelect: NSObject {
|
||||
// MARK: Internal
|
||||
|
||||
internal let attributeType: NSAttributeType
|
||||
internal let selectTerms: [SelectTerm]
|
||||
internal let selectTerms: [SelectTerm<NSManagedObject>]
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func applyToFetchRequest(_ fetchRequest: NSFetchRequest<NSFetchRequestResult>) {
|
||||
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.resultType = .dictionaryResultType
|
||||
|
||||
func attributeDescription(for keyPath: String, in entity: NSEntityDescription) -> NSAttributeDescription? {
|
||||
|
||||
let components = keyPath.components(separatedBy: ".")
|
||||
switch components.count {
|
||||
|
||||
case 0:
|
||||
return nil
|
||||
|
||||
case 1:
|
||||
return entity.attributesByName[components[0]]
|
||||
|
||||
default:
|
||||
guard let relationship = entity.relationshipsByName[components[0]],
|
||||
let destinationEntity = relationship.destinationEntity else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return attributeDescription(
|
||||
for: components.dropFirst().joined(separator: "."),
|
||||
in: destinationEntity
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var propertiesToFetch = [Any]()
|
||||
for term in self.selectTerms {
|
||||
|
||||
switch term {
|
||||
|
||||
case ._attribute(let keyPath):
|
||||
propertiesToFetch.append(keyPath)
|
||||
|
||||
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||
let entityDescription = fetchRequest.entity!
|
||||
if let attributeDescription = attributeDescription(for: keyPath, in: entityDescription) {
|
||||
|
||||
let expressionDescription = NSExpressionDescription()
|
||||
expressionDescription.name = alias
|
||||
if nativeType == .undefinedAttributeType {
|
||||
|
||||
expressionDescription.expressionResultType = attributeDescription.attributeType
|
||||
}
|
||||
else {
|
||||
|
||||
expressionDescription.expressionResultType = nativeType
|
||||
}
|
||||
expressionDescription.expression = NSExpression(
|
||||
forFunction: function,
|
||||
arguments: [NSExpression(forKeyPath: keyPath)]
|
||||
)
|
||||
propertiesToFetch.append(expressionDescription)
|
||||
}
|
||||
else {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(self)) query clause."
|
||||
)
|
||||
}
|
||||
|
||||
case ._identity(let alias, let nativeType):
|
||||
let expressionDescription = NSExpressionDescription()
|
||||
expressionDescription.name = alias
|
||||
if nativeType == .undefinedAttributeType {
|
||||
|
||||
expressionDescription.expressionResultType = .objectIDAttributeType
|
||||
}
|
||||
else {
|
||||
|
||||
expressionDescription.expressionResultType = nativeType
|
||||
}
|
||||
expressionDescription.expression = NSExpression.expressionForEvaluatedObject()
|
||||
|
||||
propertiesToFetch.append(expressionDescription)
|
||||
}
|
||||
}
|
||||
|
||||
fetchRequest.propertiesToFetch = propertiesToFetch
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -396,7 +502,7 @@ public final class CSSelect: NSObject {
|
||||
|
||||
// MARK: - Select
|
||||
|
||||
extension Select: CoreStoreSwiftType {
|
||||
extension Select where D: NSManagedObject {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -404,4 +510,12 @@ extension Select: CoreStoreSwiftType {
|
||||
|
||||
return CSSelect(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate func downcast() -> Select<NSManagedObject, T> {
|
||||
|
||||
return Select<NSManagedObject, T>(self.selectTerms.map({ $0.downcast() }))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user