support value querying in Objective C

This commit is contained in:
John Rommel Estropia
2016-04-02 21:39:05 +09:00
parent b95aaf151d
commit aa32f5e158
9 changed files with 680 additions and 131 deletions
@@ -32,7 +32,7 @@ import CoreData
/**
The `SelectResultType` protocol is implemented by return types supported by the `Select` clause.
*/
public protocol SelectResultType { }
public protocol SelectResultType {}
// MARK: - SelectValueResultType
@@ -42,6 +42,8 @@ public protocol SelectResultType { }
*/
public protocol SelectValueResultType: SelectResultType {
static var attributeType: NSAttributeType { get }
static func fromResultObject(result: AnyObject) -> Self?
}
@@ -62,7 +64,7 @@ public protocol SelectAttributesResultType: SelectResultType {
/**
The `SelectTerm` is passed to the `Select` clause to indicate the attributes/aggregate keys to be queried.
*/
public enum SelectTerm: StringLiteralConvertible {
public enum SelectTerm: StringLiteralConvertible, Hashable {
/**
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. A shorter way to do the same is to assign from the string keypath directly:
@@ -218,6 +220,21 @@ public enum SelectTerm: StringLiteralConvertible {
}
// MARK: Hashable
public var hashValue: Int {
switch self {
case ._Attribute(let keyPath):
return 0 ^ keyPath.hashValue
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
return 1 ^ function.hashValue ^ keyPath.hashValue ^ alias.hashValue ^ nativeType.hashValue
}
}
// MARK: Internal
case _Attribute(KeyPath)
@@ -225,6 +242,29 @@ public enum SelectTerm: StringLiteralConvertible {
}
// MARK: - SelectTerm: Equatable
@warn_unused_result
public func == (lhs: SelectTerm, rhs: SelectTerm) -> Bool {
switch (lhs, rhs) {
case (._Attribute(let keyPath1), ._Attribute(let keyPath2)):
return keyPath1 == keyPath2
case (._Aggregate(let function1, let keyPath1, let alias1, let nativeType1),
._Aggregate(let function2, let keyPath2, let alias2, let nativeType2)):
return function1 == function2
&& keyPath1 == keyPath2
&& alias1 == alias2
&& nativeType1 == nativeType2
default:
return false
}
}
// MARK: - Select
/**
@@ -267,7 +307,7 @@ public enum SelectTerm: StringLiteralConvertible {
- parameter sortDescriptors: a series of `NSSortDescriptor`s
*/
public struct Select<T: SelectResultType> {
public struct Select<T: SelectResultType>: Hashable {
/**
The `SelectResultType` type for the query's return value
@@ -285,93 +325,37 @@ public struct Select<T: SelectResultType> {
self.selectTerms = [selectTerm] + selectTerms
}
/**
Initializes a `Select` clause with a list of `SelectTerm`s
- parameter selectTerms: a series of `SelectTerm`s
*/
public init(_ selectTerms: [SelectTerm]) {
self.selectTerms = selectTerms
}
// MARK: Hashable
public var hashValue: Int {
return self.selectTerms.map { $0.hashValue }.reduce(0, combine: ^)
}
// MARK: Internal
internal func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.propertiesToFetch != nil {
CoreStore.log(
.Warning,
message: "An existing \"propertiesToFetch\" for the \(typeName(NSFetchRequest)) was overwritten by \(typeName(self)) query clause."
)
}
fetchRequest.includesPendingChanges = false
fetchRequest.resultType = .DictionaryResultType
let entityDescription = fetchRequest.entity!
let propertiesByName = entityDescription.propertiesByName
let attributesByName = entityDescription.attributesByName
var propertiesToFetch = [AnyObject]()
for term in self.selectTerms {
switch term {
case ._Attribute(let keyPath):
if let propertyDescription = propertiesByName[keyPath] {
propertiesToFetch.append(propertyDescription)
}
else {
CoreStore.log(
.Warning,
message: "The property \"\(keyPath)\" does not exist in entity \(typeName(entityDescription.managedObjectClassName)) and will be ignored by \(typeName(self)) query clause."
)
}
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
if let attributeDescription = attributesByName[keyPath] {
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 attribute \"\(keyPath)\" does not exist in entity \(typeName(entityDescription.managedObjectClassName)) and will be ignored by \(typeName(self)) query clause."
)
}
}
}
fetchRequest.propertiesToFetch = propertiesToFetch
}
internal let selectTerms: [SelectTerm]
}
// MARK: - Select: Equatable
@warn_unused_result
public func == <T: SelectResultType, U: SelectResultType>(lhs: Select<T>, rhs: Select<U>) -> Bool {
internal func keyPathForFirstSelectTerm() -> KeyPath {
switch self.selectTerms.first! {
case ._Attribute(let keyPath):
return keyPath
case ._Aggregate(_, _, let alias, _):
return alias
}
}
// MARK: Private
private let selectTerms: [SelectTerm]
return lhs.selectTerms == rhs.selectTerms
}
@@ -666,3 +650,81 @@ extension NSDictionary: SelectAttributesResultType {
return result as! [[NSString: AnyObject]]
}
}
// MARK: - Internal
internal extension CollectionType where Generator.Element == SelectTerm {
internal func applyToFetchRequest<T>(fetchRequest: NSFetchRequest, owner: T) {
fetchRequest.includesPendingChanges = false
fetchRequest.resultType = .DictionaryResultType
let entityDescription = fetchRequest.entity!
let propertiesByName = entityDescription.propertiesByName
let attributesByName = entityDescription.attributesByName
var propertiesToFetch = [AnyObject]()
for term in self {
switch term {
case ._Attribute(let keyPath):
if let propertyDescription = propertiesByName[keyPath] {
propertiesToFetch.append(propertyDescription)
}
else {
CoreStore.log(
.Warning,
message: "The property \"\(keyPath)\" does not exist in entity \(typeName(entityDescription.managedObjectClassName)) and will be ignored by \(typeName(owner)) query clause."
)
}
case ._Aggregate(let function, let keyPath, let alias, let nativeType):
if let attributeDescription = attributesByName[keyPath] {
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 attribute \"\(keyPath)\" does not exist in entity \(typeName(entityDescription.managedObjectClassName)) and will be ignored by \(typeName(owner)) query clause."
)
}
}
}
fetchRequest.propertiesToFetch = propertiesToFetch
}
internal func keyPathForFirstSelectTerm() -> KeyPath {
switch self.first! {
case ._Attribute(let keyPath):
return keyPath
case ._Aggregate(_, _, let alias, _):
return alias
}
}
}