FluentでPostgreSQLにインデックス貼るときの罠
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
みたいな?
まぁそのうち修正はされるはずです...。