anz blog

FluentでPostgreSQLにインデックス貼るときの罠

2018-02-10 #Vapor #Swift

VaporでDBさわるときのORMとして用意されているのがFluentですが... そのFluentを通してインデックスを貼ってみるとprepareが動かなかったのです。 ってことで

Fluentでインデックス貼るときの罠

です。

環境

  • Vapor Framework v2.4.0
  • Fluent v2.4.2

Fluentを使ってのインデックスの貼り方

何かしらのDBを扱っていると使いたくなるインデックス。

例えばItemというテーブルにseqというカラムがあってそこにインデックスを貼りたいですよと... (並びを自由に変更できるときなんかにやります。。。よね?(笑))

そういうときも、Fluentはちゃんとインデックスも対応しているので安心です。 使い方としては...

extension Item: Preparation {
    public static func prepare(_ database: Database) throws {
        // 定義のコード
        ...
        
        // add index
        try database.index("seq", for: Item.self)
    }
}

こんな感じです。お手軽でありがたいです。

問題

prepare自体はさくっと終わったので、いざコマンドを叩きましょう! いつものようにvapor run prepareタタターーン!!!!

posgresql Cannot create index: '_fluent_idx_seq' already exists

エラーどーん!! なぜなのか?🤔 実は他のテーブルに同じカラム名seqがあって、そこにも同じようにインデックスをはってあったのです。 それでインデックス名が重複エラーになっていると...

これにはPostgreSQLの仕様がおおきくて、スキーマ単位でインデックス名は一意じゃなければならないのです。 たとえ別テーブルであっても。。です (MySQLなんかはテーブル自体に差し込むので、テーブル内で一意だったらいいんですけどね...)

でもちょっとこれはこまりますよねぇ...例に上げた感じでテーブルは違えどカラム名は同じでインデックス貼るケースというのはそこまでレアなケースではないはず

すこし深掘りしてみる

ほんとかな?なにかあるのでは?とコードをおってみました。

public var name: String {
    let list = fields.joined(separator: "_")
    return "_fluent_idx_\(list)"
}

https://github.com/vapor/fluent/blob/master/Sources/Fluent/Schema/Index.swift#L9

うーむ...。どうやらやっぱりそうみたいだ。

対応するぞ

それでは、インデックス貼る時に被らないような名前をつける? try database.index("item_seq", for: TableName.self) こういう感じで... 駄目です🙅 item_seqなんぞというカラムはありません!というエラーにかわるだけです。

ではいったい....

諦めてカラム名を変える

です...僕が行き着いた答えは。 被らないように。。。Fluentの命名ルールに則ってもスキーマ内で一意になるように。。。。😇

※いやテーブルぐらい自分でSQL発行してやればええやんけ!っていうアレは無しで。。Fluentのお話をしているのです!(笑)

作業もある程度進んだ頃合いで、そいやインデックスはるかーってなって、「あ!?カラム名かえなきゃだと。。。!」ってなるとかなり凹むので、 Vapor/Fluent+PostgreSQLでやろうとしている人はテーブル設計はここらへんも考えて置くと幸せになれそうです。 インデックスをはるカラムにはテーブル名を含めておくとか(冗長になるけど)

  • item.item_seq
  • weapon.weapon_seq

みたいな?

まぁそのうち修正はされるはずです...。

参考

Fluent - Vapor Docs