简体   繁体   English

使用蒸汽流体来模拟模型

[英]Using vapor-fluent to upsert models

I am currently struggling with doing an upsert with vapor/fluent. 我目前正在努力做一个蒸气/流利的upsert。 I have a model something like this: 我有一个这样的模型:

struct DeviceToken: PostgreSQLModel {
    var id: Int?
    var token: String
    var updatedAt: Date = Date()

    init(id: Int? = nil, token: String, updatedAt: Date = Date()) {
        self.id = id
        self.token = token
        self.updatedAt = updatedAt
    }
}

struct Account: PostgreSQLModel {
    var id: Int?
    let username: String
    let service: String
    ...
    let deviceTokenId: DeviceToken.ID

    init(id: Int? = nil, service: String, username: String, ..., deviceTokenId: DeviceToken.ID) {
        self.id = id
        self.username = username
        ....
        self.deviceTokenId = deviceTokenId
    }
}

From the client something like 从客户端的东西

{
    "deviceToken": {
        "token": "ab123",
        "updatedAt": "01-01-2019 10:10:10"
    },
    "account": {
        "username": "user1",
        "service": "some service"
    }
}

is send. 发送。

What I'd like to do is to insert the new models if they do not exist else update them. 我想要做的是插入新模型,如果它们不存在,则更新它们。 I saw the create(orUpdate:) method however this will only update if the id is the same (in my understanding). 我看到了create(orUpdate:)方法但是只有在id相同时才会更新(在我的理解中)。 Since the client does not send the id i am not quite sure how to handle this. 由于客户端没有发送id,我不太清楚如何处理这个问题。

Also I can't decode the model since the account is send without the deviceTokenId and therefore the decoding will fail. 此外,我无法解码模型,因为帐户是在没有deviceTokenId情况下发送的,因此解码将失败。 I guess I can address the latter problem by overriding NodeCovertible or by using two different models (one for decoding the json without the id and the actual model from above). 我想我可以通过重写NodeCovertible或使用两个不同的模型来解决后一个问题(一个用于解码没有id的json和上面的实际模型)。 However the first problem still remains. 然而,第一个问题仍然存在。

What I exactly want to do is: 我真正想做的是:

  1. Update a DeviceToken if an entry with token already exists else create it 如果已存在具有令牌的条目,则更新DeviceToken,否则创建它

  2. If an account with the combination of username and service already exists update its username, service and deviceTokenId else create it. 如果已存在用户名和服务组合的帐户更新其用户名,则service和deviceTokenId会创建它。 DeviceTokenId is the id returned from 1. DeviceTokenId是从1返回的id。

Any chance you can help me out here ? 你有机会帮助我吗?

For everyone who is interested: I solved it by writing an extension on PostgreSQLModel to supply an upsert method. 对于每个感兴趣的人:我通过在PostgreSQLModel上编写扩展来解决它,以提供upsert方法。 I added a gist for you to have a look at: here . 我添加了一个要点,让你看看: 这里

Since these kind of links sometimes are broken when you need the information here a quick overview: 由于这些链接有时会在您需要此处的信息时被破坏,因此请快速浏览:

Actual upsert implementation: 实际upsert实现:

extension QueryBuilder
where Result: PostgreSQLModel, Result.Database == Database {

    /// Creates the model or updates it depending on whether a model
    /// with the same ID already exists.
    internal func upsert(_ model: Result,
                         columns: [PostgreSQLColumnIdentifier]) -> Future<Result> {

        let row = SQLQueryEncoder(PostgreSQLExpression.self).encode(model)

        /// remove id from row if not available
        /// otherwise the not-null constraint will break
        row = row.filter { (key, value) -> Bool in
            if key == "id" && value.isNull { return false }
            return true
        }

        let values = row
            .map { row -> (PostgreSQLIdentifier, PostgreSQLExpression) in
                return (.identifier(row.key), row.value)
        }

        self.query.upsert = .upsert(columns, values)
        return create(model)
    }

}

Convenience methods 方便的方法

extension PostgreSQLModel {

    /// Creates the model or updates it depending on whether a model
    /// with the same ID already exists.
    internal func upsert(on connection: DatabaseConnectable) -> Future<Self> {
        return Self
            .query(on: connection)
            .upsert(self, columns: [.keyPath(Self.idKey)])
    }

    internal func upsert<U>(on connection: DatabaseConnectable,
                        onConflict keyPath: KeyPath<Self, U>) -> Future<Self> {
        return Self
            .query(on: connection)
            .upsert(self, columns: [.keyPath(keyPath)])
    }

    ....
}

I solved the other problem I had that my database model could not be decoded since the id was not send from the client, by using a inner struct which would hold only the properties the client would send. 我解决了我的另一个问题,我的数据库模型无法解码,因为id不是从客户端发送的,通过使用一个内部结构,它只保存客户端将发送的属性。 The id and other database generated properties are in the outer struct. id和其他数据库生成的属性位于外部结构中。 Something like: 就像是:

struct DatabaseModel: PostgreSQLModel {

    var id: Int?
    var someProperty: String

    init(id: Int? = nil, form: DatabaseModelForm) {

        self.id = id
        self.someProperty = form.someProperty
    }

    struct DatabaseModelForm: Content {
        let someProperty: String
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM