Search code examples
iosswiftpostgresqlfluentvapor

Vapor/Fluent: Child object not saving when array of uuids in schema


I have a table containing an Coin entity, with a corresponding Fluent Model. It was refusing to save, until I removed one field, which was intended to contain an array of uuids. This was called sources, and its function was to provide a backwards trail to other coins from which a new coin was derived - for instance, if a user has two Coins (which can have an arbitrary quantity value), the goal was to be able to merge them into a single Coin with the combined value of the merged coins.

The schema (with commented field looks like this (it includes the migration, also with the corresponding field commented out):

final class Coin : Model, Content, ResponseEncodable {
    
    init() {}
    
    init(player: User, quantity: Int, type: CreatedFor, sources : [Coin] = []) {
        self.id = id
        self.$player.id = player.id!
        self.quantity = quantity
        self.type = type
//        self.sources = sources
    }
    
    static let schema: String = "coins"
    
    @ID(key: .id)                   var id: UUID?
    
    @Timestamp(key: "created_at", on: .create) var createdAt: Date?
    @Timestamp(key: "updated_at", on: .update) var updatedAt: Date?
    
    @Parent(key: "player")          var player: User
    
    @Field(key: "quantity")         var quantity: Int
    @Field(key: "type")             var type: CreatedFor
//    @Field(key: "sources")          var sources: [Coin]
    
    enum CreatedFor: String, Codable {
        case transfer
        case ante
        case purchase
        case winnings
        case gift
        case merge
    }
}

extension Coin {
    struct BuildCoin: Fluent.Migration {
        var name : String { "BuildCoin" }
        
        func prepare(on database: Database) -> EventLoopFuture<Void> {
            database.schema("coins")
                .id()
                .field("created_at", .datetime)
                .field("updated_at", .datetime)
                .field("player", .uuid, .required)
                .field("quantity", .int, .required, .custom("DEFAULT 0"))
                .field("type", .string, .required, .custom("DEFAULT 'gift'"))
//                .field("sources", .array(of: .uuid))
                .create()
        }
        
        func revert(on database: Database) -> EventLoopFuture<Void> {
            database.schema("coins").delete()
        }
        
    }
}

once I boiled it down to the place where the problem was arising, I was able to get this error, which seems to come from postgres:

{"error":true,"reason":"server: column \"sources\" is of type uuid[] but expression is of type jsonb[] (transformAssignedExpr)"}

I'd like to include this field (but I can proceed without it for now). What is happening here? In calling the init(), it was only ever called as an empty array, so I would have thought that this would cause no problems...

Thanks in advance...


Solution

  • If you want to save as array of uuids instead of

    @Field(key: "sources") var sources: [Coin]
    

    you should use

    @Field(key: "sources") var sources: [UUID]
    

    Other else you are trying to save array of Coins to DB, and this array will be serialised to jsonb - since it is object not UUID. Looking on your code, I think you should use 1-to-many or many-to-many relation.