From a62b33cb365754062ead5f23b8d59a9afed45a98 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Tue, 20 Mar 2018 19:26:23 +0000 Subject: [PATCH 1/5] fixing join mapping - take 1 --- .../Fluent/Query/Builder/QueryBuilder.swift | 15 ++++----- .../Query/Codable/QueryDataDecoder.swift | 31 ++++++++++--------- Sources/Fluent/Query/QueryField.swift | 17 ++++++++-- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Sources/Fluent/Query/Builder/QueryBuilder.swift b/Sources/Fluent/Query/Builder/QueryBuilder.swift index c04baa27..85d88805 100644 --- a/Sources/Fluent/Query/Builder/QueryBuilder.swift +++ b/Sources/Fluent/Query/Builder/QueryBuilder.swift @@ -19,7 +19,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. query: DatabaseQuery, on connection: Future, resultTransformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) { + ) { self.query = query self.connection = connection self.resultTransformer = resultTransformer @@ -69,7 +69,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. static func make( on connection: Future, with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: DatabaseQuery(entity: Model.entity), on: connection, resultTransformer: { row, conn in return transformer(row, conn) }) @@ -78,7 +78,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Replaces the query result handler with the supplied closure. func changeResult( with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: self.query, on: self.connection, resultTransformer: { row, conn in return transformer(row, conn) }) @@ -87,7 +87,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Transforms the previous query result to a new result using the supplied closure. func transformResult( with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection, Result) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: self.query, on: self.connection, resultTransformer: { row, conn in return self.resultTransformer(row, conn).flatMap(to: NewResult.self) { result in return transformer(row, conn, result) @@ -97,7 +97,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Sets the query to decode type `D` when run. public func decode(_ type: D.Type, entity: String = Model.entity) -> QueryBuilder where D: Decodable { - let decoder = QueryDataDecoder(Model.Database.self) + let decoder = QueryDataDecoder(Model.self) return changeResult { row, conn in let row = row.onlyValues(forEntity: entity) return Future.map(on: conn) { @@ -109,7 +109,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Adds an additional type `D` to be decoded when run. /// The new result for this query will be a tuple containing the previous result and this new result. public func alsoDecode(_ type: D.Type, entity: String) -> QueryBuilder where D: Decodable { - let decoder = QueryDataDecoder(Model.Database.self) + let decoder = QueryDataDecoder(Model.self) return transformResult { row, conn, result in let row = row.onlyValues(forEntity: entity) return Future.map(on: conn) { @@ -131,7 +131,7 @@ extension Model where Database: QuerySupporting { static func query(decoding type: D.Type, on connection: Future) -> QueryBuilder where D: Decodable { return QueryBuilder.make(on: connection) { row, conn in return Future.map(on: conn) { - let decoder = QueryDataDecoder(Self.Database.self) + let decoder = QueryDataDecoder(Self.self) return try decoder.decode(D.self, from: row) } } @@ -146,3 +146,4 @@ extension Model where Database: QuerySupporting { } } } + diff --git a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift index 83a4a997..b82813cf 100644 --- a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift +++ b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift @@ -1,23 +1,23 @@ -final class QueryDataDecoder where Database: QuerySupporting { - init(_ database: Database.Type) { } - func decode(_ type: D.Type, from data: [QueryField: Database.QueryData]) throws -> D where D: Decodable { - let decoder = _QueryDataDecoder(data: data) +final class QueryDataDecoder where M: Model, M.Database: QuerySupporting { + init(_ model: M.Type) { } + func decode(_ type: D.Type, from data: [QueryField: M.Database.QueryData]) throws -> D where D: Decodable { + let decoder = _QueryDataDecoder(data: data) return try D.init(from: decoder) } } /// MARK: Private -fileprivate final class _QueryDataDecoder: Decoder where Database: QuerySupporting { +fileprivate final class _QueryDataDecoder: Decoder where M: Model, M.Database: QuerySupporting { var codingPath: [CodingKey] { return [] } var userInfo: [CodingUserInfoKey: Any] { return [:] } - var data: [QueryField: Database.QueryData] - init(data: [QueryField: Database.QueryData]) { + var data: [QueryField: M.Database.QueryData] + init(data: [QueryField: M.Database.QueryData]) { self.data = data } func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { - return KeyedDecodingContainer(_QueryDataKeyedDecoder(decoder: self)) + return KeyedDecodingContainer(_QueryDataKeyedDecoder(decoder: self)) } func unkeyedContainer() throws -> UnkeyedDecodingContainer { throw unsupported() } @@ -36,28 +36,28 @@ private func unsupported() -> FluentError { } -fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerProtocol - where K: CodingKey, Database: QuerySupporting +fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerProtocol + where K: CodingKey, M: Model, M.Database: QuerySupporting { var allKeys: [K] { return decoder.data.keys.compactMap { K(stringValue: $0.name) } } var codingPath: [CodingKey] { return [] } - let decoder: _QueryDataDecoder - init(decoder: _QueryDataDecoder) { + let decoder: _QueryDataDecoder + init(decoder: _QueryDataDecoder) { self.decoder = decoder } func _parse(_ type: T.Type, forKey key: K) throws -> T? { - guard let data = decoder.data.firstValue(forField: key.stringValue) else { + guard let data = decoder.data.firstValue(forField: key.stringValue, on: M.entity) else { return nil } - return try Database.queryDataParse(T.self, from: data) + return try M.Database.queryDataParse(T.self, from: data) } func contains(_ key: K) -> Bool { return decoder.data.keys.contains { $0.name == key.stringValue } } - func decodeNil(forKey key: K) throws -> Bool { return decoder.data.firstValue(forField: key.stringValue) == nil } + func decodeNil(forKey key: K) throws -> Bool { return decoder.data.firstValue(forField: key.stringValue, on: M.entity) == nil } func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? { return try _parse(Int.self, forKey: key) } func decodeIfPresent(_ type: Int8.Type, forKey key: K) throws -> Int8? { return try _parse(Int8.self, forKey: key) } func decodeIfPresent(_ type: Int16.Type, forKey key: K) throws -> Int16? { return try _parse(Int16.self, forKey: key) } @@ -86,3 +86,4 @@ fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerPr func superDecoder() throws -> Decoder { return decoder } func superDecoder(forKey key: K) throws -> Decoder { return decoder } } + diff --git a/Sources/Fluent/Query/QueryField.swift b/Sources/Fluent/Query/QueryField.swift index db877ac3..04fab6e7 100644 --- a/Sources/Fluent/Query/QueryField.swift +++ b/Sources/Fluent/Query/QueryField.swift @@ -43,12 +43,24 @@ extension QueryField: ExpressibleByStringLiteral { extension Dictionary where Key == QueryField { /// Accesses the _first_ value from this dictionary with a matching field name. - public func firstValue(forField fieldName: String) -> Value? { + public func firstValue(forField fieldName: String, on entity: String) -> Value? { + print("\n\n\nLooking for: \(entity).\(fieldName) in:") + for (field, _) in self { + print("\t\(field.entity ?? "unknown").\(field.name)") + } + for (field, value) in self { + if field.name == fieldName && field.entity == entity { + print("\n\tFound exact match: \(field.entity ?? "unknown").\(field.name)\n\n\n") + return value + } + } for (field, value) in self { if field.name == fieldName { + print("\n\tFound match: \(field.entity ?? "unknown").\(field.name)\n\n\n") return value } } + print("\tNot found\n\n\n") return nil } @@ -140,7 +152,7 @@ extension Model where ID: KeyStringDecodable { public struct QueryFieldDecodingContainer where Model: Fluent.Model { /// The underlying container. public var container: KeyedDecodingContainer - + /// Decodes a model key path to a type. public func decode(key: KeyPath) throws -> T where T: KeyStringDecodable { let field = try key.makeQueryField() @@ -163,3 +175,4 @@ public struct QueryFieldEncodingContainer { try container.encode(value, forKey: field) } } + From ea2a78b353f2ce65ba5c602c0fc462dd194eb8d0 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Tue, 20 Mar 2018 21:17:02 +0000 Subject: [PATCH 2/5] fixing join mapping - take 2 --- .../Fluent/Query/Builder/QueryBuilder.swift | 14 +++---- .../Query/Codable/QueryDataDecoder.swift | 42 ++++++++++++------- Sources/Fluent/Query/QueryField.swift | 14 +------ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Sources/Fluent/Query/Builder/QueryBuilder.swift b/Sources/Fluent/Query/Builder/QueryBuilder.swift index 85d88805..9d81bc8e 100644 --- a/Sources/Fluent/Query/Builder/QueryBuilder.swift +++ b/Sources/Fluent/Query/Builder/QueryBuilder.swift @@ -19,7 +19,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. query: DatabaseQuery, on connection: Future, resultTransformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) { + ) { self.query = query self.connection = connection self.resultTransformer = resultTransformer @@ -69,7 +69,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. static func make( on connection: Future, with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: DatabaseQuery(entity: Model.entity), on: connection, resultTransformer: { row, conn in return transformer(row, conn) }) @@ -78,7 +78,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Replaces the query result handler with the supplied closure. func changeResult( with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: self.query, on: self.connection, resultTransformer: { row, conn in return transformer(row, conn) }) @@ -87,7 +87,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Transforms the previous query result to a new result using the supplied closure. func transformResult( with transformer: @escaping ([QueryField: Model.Database.QueryData], Model.Database.Connection, Result) -> Future - ) -> QueryBuilder { + ) -> QueryBuilder { return QueryBuilder(query: self.query, on: self.connection, resultTransformer: { row, conn in return self.resultTransformer(row, conn).flatMap(to: NewResult.self) { result in return transformer(row, conn, result) @@ -97,7 +97,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Sets the query to decode type `D` when run. public func decode(_ type: D.Type, entity: String = Model.entity) -> QueryBuilder where D: Decodable { - let decoder = QueryDataDecoder(Model.self) + let decoder = QueryDataDecoder(Model.Database.self, entity: entity) return changeResult { row, conn in let row = row.onlyValues(forEntity: entity) return Future.map(on: conn) { @@ -109,7 +109,7 @@ public final class QueryBuilder where Model: Fluent.Model, Model. /// Adds an additional type `D` to be decoded when run. /// The new result for this query will be a tuple containing the previous result and this new result. public func alsoDecode(_ type: D.Type, entity: String) -> QueryBuilder where D: Decodable { - let decoder = QueryDataDecoder(Model.self) + let decoder = QueryDataDecoder(Model.Database.self, entity: entity) return transformResult { row, conn, result in let row = row.onlyValues(forEntity: entity) return Future.map(on: conn) { @@ -131,7 +131,7 @@ extension Model where Database: QuerySupporting { static func query(decoding type: D.Type, on connection: Future) -> QueryBuilder where D: Decodable { return QueryBuilder.make(on: connection) { row, conn in return Future.map(on: conn) { - let decoder = QueryDataDecoder(Self.self) + let decoder = QueryDataDecoder(Self.Database.self, entity: Self.entity) return try decoder.decode(D.self, from: row) } } diff --git a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift index b82813cf..63fc069b 100644 --- a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift +++ b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift @@ -1,23 +1,26 @@ -final class QueryDataDecoder where M: Model, M.Database: QuerySupporting { - init(_ model: M.Type) { } - func decode(_ type: D.Type, from data: [QueryField: M.Database.QueryData]) throws -> D where D: Decodable { - let decoder = _QueryDataDecoder(data: data) +final class QueryDataDecoder where Database: QuerySupporting { + var entity: String? + init(_ database: Database.Type, entity: String? = nil) { } + func decode(_ type: D.Type, from data: [QueryField: Database.QueryData]) throws -> D where D: Decodable { + let decoder = _QueryDataDecoder(data: data, entity: entity) return try D.init(from: decoder) } } /// MARK: Private -fileprivate final class _QueryDataDecoder: Decoder where M: Model, M.Database: QuerySupporting { +fileprivate final class _QueryDataDecoder: Decoder where Database: QuerySupporting { var codingPath: [CodingKey] { return [] } var userInfo: [CodingUserInfoKey: Any] { return [:] } - var data: [QueryField: M.Database.QueryData] - init(data: [QueryField: M.Database.QueryData]) { + var data: [QueryField: Database.QueryData] + var entity: String? + init(data: [QueryField: Database.QueryData], entity: String?) { self.data = data + self.entity = entity } func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { - return KeyedDecodingContainer(_QueryDataKeyedDecoder(decoder: self)) + return KeyedDecodingContainer(_QueryDataKeyedDecoder(decoder: self, entity: entity)) } func unkeyedContainer() throws -> UnkeyedDecodingContainer { throw unsupported() } @@ -36,28 +39,37 @@ private func unsupported() -> FluentError { } -fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerProtocol - where K: CodingKey, M: Model, M.Database: QuerySupporting +fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerProtocol + where K: CodingKey, Database: QuerySupporting { var allKeys: [K] { return decoder.data.keys.compactMap { K(stringValue: $0.name) } } var codingPath: [CodingKey] { return [] } - let decoder: _QueryDataDecoder - init(decoder: _QueryDataDecoder) { + let decoder: _QueryDataDecoder + var entity: String? + init(decoder: _QueryDataDecoder, entity: String?) { self.decoder = decoder + self.entity = entity + } + + func _value(forEntity entity: String?, atField field: String) -> Database.QueryData? { + guard let entity = entity else { + return decoder.data.firstValue(forField: field) + } + return decoder.data.value(forEntity: entity, atField: field) } func _parse(_ type: T.Type, forKey key: K) throws -> T? { - guard let data = decoder.data.firstValue(forField: key.stringValue, on: M.entity) else { + guard let data = _value(forEntity: entity, atField: key.stringValue) else { return nil } - return try M.Database.queryDataParse(T.self, from: data) + return try Database.queryDataParse(T.self, from: data) } func contains(_ key: K) -> Bool { return decoder.data.keys.contains { $0.name == key.stringValue } } - func decodeNil(forKey key: K) throws -> Bool { return decoder.data.firstValue(forField: key.stringValue, on: M.entity) == nil } + func decodeNil(forKey key: K) throws -> Bool { return _value(forEntity: entity, atField: key.stringValue) == nil } func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? { return try _parse(Int.self, forKey: key) } func decodeIfPresent(_ type: Int8.Type, forKey key: K) throws -> Int8? { return try _parse(Int8.self, forKey: key) } func decodeIfPresent(_ type: Int16.Type, forKey key: K) throws -> Int16? { return try _parse(Int16.self, forKey: key) } diff --git a/Sources/Fluent/Query/QueryField.swift b/Sources/Fluent/Query/QueryField.swift index 04fab6e7..4d92d7ca 100644 --- a/Sources/Fluent/Query/QueryField.swift +++ b/Sources/Fluent/Query/QueryField.swift @@ -43,24 +43,12 @@ extension QueryField: ExpressibleByStringLiteral { extension Dictionary where Key == QueryField { /// Accesses the _first_ value from this dictionary with a matching field name. - public func firstValue(forField fieldName: String, on entity: String) -> Value? { - print("\n\n\nLooking for: \(entity).\(fieldName) in:") - for (field, _) in self { - print("\t\(field.entity ?? "unknown").\(field.name)") - } - for (field, value) in self { - if field.name == fieldName && field.entity == entity { - print("\n\tFound exact match: \(field.entity ?? "unknown").\(field.name)\n\n\n") - return value - } - } + public func firstValue(forField fieldName: String) -> Value? { for (field, value) in self { if field.name == fieldName { - print("\n\tFound match: \(field.entity ?? "unknown").\(field.name)\n\n\n") return value } } - print("\tNot found\n\n\n") return nil } From f26e521db0ce8cba0243dd7a7f33819823234988 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Tue, 20 Mar 2018 21:53:01 +0000 Subject: [PATCH 3/5] Update QueryField.swift --- Sources/Fluent/Query/QueryField.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Fluent/Query/QueryField.swift b/Sources/Fluent/Query/QueryField.swift index 4d92d7ca..7302429d 100644 --- a/Sources/Fluent/Query/QueryField.swift +++ b/Sources/Fluent/Query/QueryField.swift @@ -163,4 +163,3 @@ public struct QueryFieldEncodingContainer { try container.encode(value, forKey: field) } } - From 5de135ceb949bbcf93478c52247db2b2000b5982 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Tue, 20 Mar 2018 21:53:59 +0000 Subject: [PATCH 4/5] Update QueryField.swift --- Sources/Fluent/Query/QueryField.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Fluent/Query/QueryField.swift b/Sources/Fluent/Query/QueryField.swift index 7302429d..db877ac3 100644 --- a/Sources/Fluent/Query/QueryField.swift +++ b/Sources/Fluent/Query/QueryField.swift @@ -140,7 +140,7 @@ extension Model where ID: KeyStringDecodable { public struct QueryFieldDecodingContainer where Model: Fluent.Model { /// The underlying container. public var container: KeyedDecodingContainer - + /// Decodes a model key path to a type. public func decode(key: KeyPath) throws -> T where T: KeyStringDecodable { let field = try key.makeQueryField() From 4a141be120dfee037f4165bdd0bc7c78fb30e310 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Tue, 20 Mar 2018 22:22:45 +0000 Subject: [PATCH 5/5] fixi ng agregators --- Sources/Fluent/Query/Codable/QueryDataDecoder.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift index 63fc069b..7aae5318 100644 --- a/Sources/Fluent/Query/Codable/QueryDataDecoder.swift +++ b/Sources/Fluent/Query/Codable/QueryDataDecoder.swift @@ -1,6 +1,8 @@ final class QueryDataDecoder where Database: QuerySupporting { var entity: String? - init(_ database: Database.Type, entity: String? = nil) { } + init(_ database: Database.Type, entity: String? = nil) { + self.entity = entity + } func decode(_ type: D.Type, from data: [QueryField: Database.QueryData]) throws -> D where D: Decodable { let decoder = _QueryDataDecoder(data: data, entity: entity) return try D.init(from: decoder) @@ -54,10 +56,11 @@ fileprivate struct _QueryDataKeyedDecoder: KeyedDecodingContainerPr } func _value(forEntity entity: String?, atField field: String) -> Database.QueryData? { + print("Entity is: \(entity ?? "unknown")") guard let entity = entity else { return decoder.data.firstValue(forField: field) } - return decoder.data.value(forEntity: entity, atField: field) + return decoder.data.value(forEntity: entity, atField: field) ?? decoder.data.firstValue(forField: field) } func _parse(_ type: T.Type, forKey key: K) throws -> T? {