Skip to content

Commit

Permalink
Merge branch 'master' into fixing-join-mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
tanner0101 committed Mar 20, 2018
2 parents 4a141be + 580a953 commit 8a5109b
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 37 deletions.
16 changes: 15 additions & 1 deletion Sources/Fluent/Query/Filter/QueryFilter.swift
Expand Up @@ -24,7 +24,7 @@ public struct QueryFilter<Database> where Database: QuerySupporting {

/// Supported filter comparison types.
public struct QueryFilterType<Database>: Equatable where Database: QuerySupporting {
enum QueryFilterTypeStorage: Int {
enum QueryFilterTypeStorage: Equatable {
case equals
case notEquals
case greaterThan
Expand All @@ -33,11 +33,20 @@ public struct QueryFilterType<Database>: Equatable where Database: QuerySupporti
case lessThanOrEquals
case `in`
case notIn
case custom(Database.QueryFilter)
}

/// Internal storage.
let storage: QueryFilterTypeStorage

/// Returns the custom query filter if it is set.
public func custom() -> Database.QueryFilter? {
switch storage {
case .custom(let filter): return filter
default: return nil
}
}

/// ==
public static var equals: QueryFilterType<Database> { return .init(storage: .equals) }
/// !=
Expand All @@ -54,6 +63,11 @@ public struct QueryFilterType<Database>: Equatable where Database: QuerySupporti
public static var `in`: QueryFilterType<Database> { return .init(storage: .`in`) }
/// not a part of
public static var notIn: QueryFilterType<Database> { return .init(storage: .notIn) }

/// Custom filter for this database type.
public static func custom(_ filter: Database.QueryFilter) -> QueryFilterType<Database> {
return .init(storage: .custom(filter))
}
}

/// Describes the values a subset can have. The subset can be either an array of encodable
Expand Down
3 changes: 3 additions & 0 deletions Sources/Fluent/Query/QuerySupporting.swift
Expand Up @@ -27,6 +27,9 @@ public protocol QuerySupporting: Database {

/// Parses this db's `QueryDataConvertible` into a native type.
static func queryDataParse<T>(_ type: T.Type, from data: QueryData) throws -> T?

/// This database's native filter types.
associatedtype QueryFilter: Equatable
}

public protocol FluentData {
Expand Down
2 changes: 1 addition & 1 deletion Sources/FluentSQL/DatabaseQuery.swift
@@ -1,7 +1,7 @@
import Fluent
import SQL

extension DatabaseQuery {
extension DatabaseQuery where Database.QueryFilter: DataPredicateComparisonConvertible {
/// Create a SQL query from this database query.
/// All Encodable values found while converting the query
/// will be returned in an array in the order that placeholders
Expand Down
115 changes: 82 additions & 33 deletions Sources/FluentSQL/QueryComparison.swift
@@ -1,37 +1,58 @@
import CodableKit
import Fluent
import SQL

extension QueryFilterType {
/// Convert query comparison to sql predicate comparison.
internal func makeDataPredicateComparison<D>(for value: QueryFilterValue<D>) -> DataPredicateComparison {
switch self {
case .greaterThan: return .greaterThan
case .greaterThanOrEquals: return .greaterThanOrEqual
case .lessThan: return .lessThan
case .lessThanOrEquals: return .lessThanOrEqual
case .equals:
if let _ = value.field() {
return .equal
} else if let data = value.data()?.first {
return data.isNull ? .isNull : .equal
} else {
return .none
}
case .notEquals:
if let _ = value.field() {
return .notEqual
} else if let data = value.data()?.first {
return data.isNull ? .isNotNull : .notEqual
} else {
return .none
internal func makeDataPredicateComparison<D>(for filter: QueryFilter<D>) -> DataPredicateComparison
where D.QueryFilter: DataPredicateComparisonConvertible
{
if let custom = filter.type.custom() {
return custom.convertToDataPredicateComparison()
} else {
switch self {
case .greaterThan: return .greaterThan
case .greaterThanOrEquals: return .greaterThanOrEqual
case .lessThan: return .lessThan
case .lessThanOrEquals: return .lessThanOrEqual
case .equals:
if let _ = filter.value.field() {
return .equal
} else if let data = filter.value.data()?.first {
return data.isNull ? .isNull : .equal
} else {
return .none
}
case .notEquals:
if let _ = filter.value.field() {
return .notEqual
} else if let data = filter.value.data()?.first {
return data.isNull ? .isNotNull : .notEqual
} else {
return .none
}
case .in: return .in
case .notIn: return .notIn
default: return .none
}
case .in: return .in
case .notIn: return .notIn
default: return .none
}
}
}

extension DataPredicateComparison: DataPredicateComparisonConvertible {
public func convertToDataPredicateComparison() -> DataPredicateComparison {
return self
}
public static func convertFromDataPredicateComparison(_ comparison: DataPredicateComparison) -> DataPredicateComparison {
return comparison
}
}

public protocol DataPredicateComparisonConvertible {
func convertToDataPredicateComparison() -> DataPredicateComparison
static func convertFromDataPredicateComparison(_ comparison: DataPredicateComparison) -> Self
}

extension QueryFilterValue {
/// Convert query comparison value to sql data predicate value.
internal func makeDataPredicateValue() -> DataPredicateValue {
Expand All @@ -42,14 +63,42 @@ extension QueryFilterValue {
} else {
return .none
}

/*
switch self {
case .array(let array): return (.placeholders(count: array.count), array)
case .subquery(let subquery):
let (dataQuery, values) = subquery.makeDataQuery()
return (.subquery(dataQuery), values)
}
*/
}
}


infix operator ~=
/// Has prefix
public func ~= <Model, Value>(lhs: KeyPath<Model, Value>, rhs: String) throws -> ModelFilter<Model>
where Value: KeyStringDecodable, Model.Database.QueryFilter: DataPredicateComparisonConvertible
{
return try _contains(lhs, value: "%\(rhs)")
}

infix operator =~
/// Has suffix.
public func =~ <Model, Value>(lhs: KeyPath<Model, Value>, rhs: String) throws -> ModelFilter<Model>
where Value: KeyStringDecodable, Model.Database.QueryFilter: DataPredicateComparisonConvertible
{
return try _contains(lhs, value: "\(rhs)%")
}

infix operator ~~
/// Contains.
public func ~~ <Model, Value>(lhs: KeyPath<Model, Value>, rhs: String) throws -> ModelFilter<Model>
where Value: KeyStringDecodable, Model.Database.QueryFilter: DataPredicateComparisonConvertible
{
return try _contains(lhs, value: "%\(rhs)%")
}

/// Operator helper func.
private func _contains<M, V>(_ key: KeyPath<M, V>, value: String) throws -> ModelFilter<M>
where V: KeyStringDecodable, M.Database.QueryFilter: DataPredicateComparisonConvertible
{
let filter = try QueryFilter<M.Database>(
field: key.makeQueryField(),
type: .custom(.convertFromDataPredicateComparison(.like)),
value: .data(value)
)
return ModelFilter<M>(filter: filter)
}
4 changes: 2 additions & 2 deletions Sources/FluentSQL/QueryFilter.swift
@@ -1,7 +1,7 @@
import Fluent
import SQL

extension QueryFilterItem {
extension QueryFilterItem where Database.QueryFilter: DataPredicateComparisonConvertible {
/// Convert query filter to sql data predicate and bind values.
internal func makeDataPredicateItem() -> (DataPredicateItem, [Database.QueryData]) {
let item: DataPredicateItem
Expand All @@ -11,7 +11,7 @@ extension QueryFilterItem {
case .single(let filter):
let predicate = DataPredicate(
column: filter.field.makeDataColumn(),
comparison: filter.type.makeDataPredicateComparison(for: filter.value),
comparison: filter.type.makeDataPredicateComparison(for: filter),
value: filter.value.makeDataPredicateValue()
)
if let array = filter.value.data() {
Expand Down

0 comments on commit 8a5109b

Please sign in to comment.