Vapor join 和 alsoDecode 产生大量嵌套元组
Vapor join and alsoDecode produces lots of nested tuples
根据问题的回答:我能够通过多个连接为我的模型重现这个。
这导致了多个嵌套元组,我必须访问它们才能获得连接值。是否可以进行其他类型的连接或将此元组结构展平到一个级别?
struct MyTuple: Encodable, Content {
let title: String
let publicationDate: Date
let authorID: Author.ID
let authorName: String
let categoryID: Category.ID
let categoryName: String
let language: String
}
struct MyContext: Encodable {
let title: String
let books: [MyTuple]
}
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
return Book.query(on: req)
.join(\Category.id, to: \Book.categoryID)
.join(\Language.id, to: \Book.languageID)
.join(\ProgrammingLanguage.id, to: \Book.programmingLanguageID)
.join(\Author.id, to: \Book.authorID)
.alsoDecode(Category.self)
.alsoDecode(Language.self)
.alsoDecode(ProgrammingLanguage.self)
.alsoDecode(Author.self)
.sort(\Book.publicationDate, .descending).all().flatMap(to: [MyTuple].self) { tuples in
print(tuples)
var tempTuples: [MyTuple] = []
for tuple in tuples {
tempTuples.append(try MyTuple(
title: tuple.0.0.0.0.title,
publicationDate: tuple.0.0.0.0.publicationDate,
authorID: tuple.1.requireID(),
authorName: "\(tuple.1.name) \(tuple.1.surname)",
categoryID: tuple.0.0.0.1.requireID(),
categoryName: tuple.0.0.0.1.name, language: tuple.0.0.1.code ) )
}
return Future.map(on: req) {
tempTuples
}
}
}
原始查询是可能的,像这样
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
let rawSQL = """
SELECT
Book.title,
Book.publicationDate,
Book.authorID,
concat(Author.name, " ", Author.surname) as authorName,
Book.categoryID,
Category.name as categoryName,
Language.code as language
FROM Book
JOIN LEFT Category ON Category.id = Book.categoryID
JOIN LEFT Language ON Language.id = Book.languageID
JOIN LEFT ProgrammingLanguage ON ProgrammingLanguage.id = Book.programmingLanguageID
JOIN LEFT Author ON Author.id = Book.authorID
ORDER BY Book.publicationDate DESC
"""
return req.requestPooledConnection(to: .mysql).flatMap { conn in
return conn.raw(rawSQL).all(decoding: MyTuple.self).always {
try? req.releasePooledConnection(conn, to: .mysql)
}
}
}
或使用 SwifQL 库进行原始查询
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
return SwifQL
.select(\Book.title,
\Book.publicationDate,
\Book.authorID,
Fn.concat(\Author.name, " ", \Author.surname) => "authorName",
\Book.categoryID,
\Category.name => categoryName,
\Language.code => language)
.from(Book.table)
.join(.left, Category.table, on: \Category.id == \Book.categoryID)
.join(.left, Language.table, on: \ Language.id == \Book.languageID)
.join(.left, ProgrammingLanguage.table, on: \ ProgrammingLanguage.id == \Book.programmingLanguageID)
.join(.left, Author.table, on: \ Author.id == \Book. Book.authorID)
.orderBy(.desc(\Book.publicationDate))
.execute(on: req, as: .mysql)
.all(decoding: MyTuple.self)
}
同上,但有 select builder
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
let query = SwifQLSelectBuilder()
query.select(\Book.title)
query.select(\Book.publicationDate)
query.select(\Book.authorID)
query.select(Fn.concat(\Author.name, " ", \Author.surname) => "authorName")
query.select(\Book.categoryID,
query.select(\Category.name => categoryName)
query.select(\Language.code => language)
query.from(Book.table)
query.join(.left, Category.table, on: \Category.id == \Book.categoryID)
query.join(.left, Language.table, on: \Language.id == \Book.languageID)
query.join(.left, ProgrammingLanguage.table, on: \ProgrammingLanguage.id == \Book.programmingLanguageID)
query.join(.left, Author.table, on: \Author.id == \Book. Book.authorID)
query.orderBy(.desc(\Book.publicationDate))
return query.build()
.execute(on: req, as: .mysql)
.all(decoding: MyTuple.self)
}
根据问题的回答:
这导致了多个嵌套元组,我必须访问它们才能获得连接值。是否可以进行其他类型的连接或将此元组结构展平到一个级别?
struct MyTuple: Encodable, Content {
let title: String
let publicationDate: Date
let authorID: Author.ID
let authorName: String
let categoryID: Category.ID
let categoryName: String
let language: String
}
struct MyContext: Encodable {
let title: String
let books: [MyTuple]
}
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
return Book.query(on: req)
.join(\Category.id, to: \Book.categoryID)
.join(\Language.id, to: \Book.languageID)
.join(\ProgrammingLanguage.id, to: \Book.programmingLanguageID)
.join(\Author.id, to: \Book.authorID)
.alsoDecode(Category.self)
.alsoDecode(Language.self)
.alsoDecode(ProgrammingLanguage.self)
.alsoDecode(Author.self)
.sort(\Book.publicationDate, .descending).all().flatMap(to: [MyTuple].self) { tuples in
print(tuples)
var tempTuples: [MyTuple] = []
for tuple in tuples {
tempTuples.append(try MyTuple(
title: tuple.0.0.0.0.title,
publicationDate: tuple.0.0.0.0.publicationDate,
authorID: tuple.1.requireID(),
authorName: "\(tuple.1.name) \(tuple.1.surname)",
categoryID: tuple.0.0.0.1.requireID(),
categoryName: tuple.0.0.0.1.name, language: tuple.0.0.1.code ) )
}
return Future.map(on: req) {
tempTuples
}
}
}
原始查询是可能的,像这样
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
let rawSQL = """
SELECT
Book.title,
Book.publicationDate,
Book.authorID,
concat(Author.name, " ", Author.surname) as authorName,
Book.categoryID,
Category.name as categoryName,
Language.code as language
FROM Book
JOIN LEFT Category ON Category.id = Book.categoryID
JOIN LEFT Language ON Language.id = Book.languageID
JOIN LEFT ProgrammingLanguage ON ProgrammingLanguage.id = Book.programmingLanguageID
JOIN LEFT Author ON Author.id = Book.authorID
ORDER BY Book.publicationDate DESC
"""
return req.requestPooledConnection(to: .mysql).flatMap { conn in
return conn.raw(rawSQL).all(decoding: MyTuple.self).always {
try? req.releasePooledConnection(conn, to: .mysql)
}
}
}
或使用 SwifQL 库进行原始查询
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
return SwifQL
.select(\Book.title,
\Book.publicationDate,
\Book.authorID,
Fn.concat(\Author.name, " ", \Author.surname) => "authorName",
\Book.categoryID,
\Category.name => categoryName,
\Language.code => language)
.from(Book.table)
.join(.left, Category.table, on: \Category.id == \Book.categoryID)
.join(.left, Language.table, on: \ Language.id == \Book.languageID)
.join(.left, ProgrammingLanguage.table, on: \ ProgrammingLanguage.id == \Book.programmingLanguageID)
.join(.left, Author.table, on: \ Author.id == \Book. Book.authorID)
.orderBy(.desc(\Book.publicationDate))
.execute(on: req, as: .mysql)
.all(decoding: MyTuple.self)
}
同上,但有 select builder
func getBooks(_ req: Request) throws -> Future<[MyTuple]> {
let query = SwifQLSelectBuilder()
query.select(\Book.title)
query.select(\Book.publicationDate)
query.select(\Book.authorID)
query.select(Fn.concat(\Author.name, " ", \Author.surname) => "authorName")
query.select(\Book.categoryID,
query.select(\Category.name => categoryName)
query.select(\Language.code => language)
query.from(Book.table)
query.join(.left, Category.table, on: \Category.id == \Book.categoryID)
query.join(.left, Language.table, on: \Language.id == \Book.languageID)
query.join(.left, ProgrammingLanguage.table, on: \ProgrammingLanguage.id == \Book.programmingLanguageID)
query.join(.left, Author.table, on: \Author.id == \Book. Book.authorID)
query.orderBy(.desc(\Book.publicationDate))
return query.build()
.execute(on: req, as: .mysql)
.all(decoding: MyTuple.self)
}