【问题标题】:Vapor join and alsoDecode produces lots of nested tuplesVapor join 和 alsoDecode 产生大量嵌套元组
【发布时间】:2020-03-26 12:17:42
【问题描述】:

根据问题的回答:vapor - Obtaining data from mysql using alsodecode() 我能够通过多个连接为我的模型重现此问题。

这导致了多个嵌套元组,我必须访问这些元组才能获取连接值。是否可以进行其他类型的连接或将这个元组结构展平到一个级别?

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
            }
        }
}

【问题讨论】:

    标签: swift vapor vapor-fluent


    【解决方案1】:

    可以使用原始查询,像这样

    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)
    }
    

    与上面相同,但带有选择生成器

    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)
    }
    

    【讨论】:

    • 谢谢@imike。虽然我非常了解 SQL,但我觉得第二种解决方案是更快速的方式来实现它?
    • 是的,第二个解决方案更快捷,因为它支持重构,所以如果您更改某些模型,您不会忘记更改查询。
    • 这是使用第二个版本的好理由。再次感谢您的帮助……您的回答让我的 API 变得更好
    • 如果问题出在长 select 表达式中,那么您可以使用 SwifQLSelectBuilder() 拆分它,查看我更新的答案。但老实说,我从未遇到过 SwifQL 的这种错误。我有两个想法:要么上面的代码中某处只是一个错误,所以你可以通过注释所有行并逐行取消注释来找到问题,或者它也可能是 Xcode 11.2+ 中的错误。我仍在使用 11.1,因为它运行良好。
    • 函数.all(decoding: MyTuple.self)的最后一行出现错误,尝试SwiQLSelectBuilder,它可以工作。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-06
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多