在 Vapor 的 Fluent 中查询嵌套子项

Querying nested children in Vapor's Fluent

环境:Vapor/Fluent4.0.0

我有一个树状结构数据,其模型如下:

final class Ingredient: Model {
    static let schema = "ingredients"

    @ID(key: "id")
    var id: UUID?
    
    @Field(key: "name")
    var name: String

    @OptionalParent(key: "parent_id")
    var parent: Ingredient?

    @Children(for: \.$parent)
    var children: [Ingredient]
}

我想 return 整个树作为 JSON 中的一种 API 方法。

func index(req: Request) throws -> EventLoopFuture<[APIIngredient]> {
        // Gathering top-level nodes without parents
        let ingredients = try Ingredient.query(on: req.db)
            .filter(\.$parent.$id == nil)
            .sort(\.$name)
            .all()
            .wait()
    enter code here
        // Creating API models
        let apiIngredients = try ingredients.map {
            try APIIngredient(
                ingredient: [=11=],
                childrenGetter: {
                    try [=11=].$children.query(on: req.db).all().wait()
                }
            )
        }

        return req.eventLoop.future(apiIngredients)
    }

但我发现 .wait() 不允许在请求处理程序中使用。解决这个问题的正确方法是什么?

wait() 是不允许的,因为它会阻塞 EventLoop。所以你有几个选择:

  1. 按照 Nick 的建议使用预先加载。这将更加有效,因为它只有 2 个数据库查询而不是 N+1 个。

  2. 用async/await随心所欲地写

  3. 妥善处理期货。而不是使用 wait() 切换到处理期货:

func index(req: Request) throws -> EventLoopFuture<[APIIngredient]> {
    // Gathering top-level nodes without parents
    return Ingredient.query(on: req.db)
        .filter(\.$parent.$id == nil)
        .sort(\.$name)
        .all()
        .flatMap { ingredients in
            ingredients.map { ingredient -> EventLoopFuture<APIIngredient>
                let future = ingredient.$children.query(on: req.db).all().map { children in
                    APIIngredient(ingredient: ingredient, children: children)
                }
            }.flatten(on: req.eventLoop)
    }
}