Gorm で PostgreSQL の JSONB 型の key, value で絞り込む
この記事では、Golang の ORM である Gorm を使って、 PostgreSQL の JSONB 型の中身の key や value による絞り込みをかける方法を紹介しています。
動作環境
- Go 1.10
- PostgreSQL 9.6
Gorm で PostgreSQL の JSONB を絞り込む
モデル
例えば、以下のような、JSON 型の任意のタグを持てるデータ型について考えます。
type TaggedData struct { ID uint32 `gorm:"NOT NULL;primary_key" sql:"TYPE:serial"` Name string `json:"name" gorm:"NOT NULL"` Tags postgres.Jsonb `json:"tags"` }
ここに、3件のデータを登録します。
host := "localhost" user := "postgres" password := "" db := "tagged" client, err := gorm.Open("postgres", fmt.Sprintf( "host=%s user=%s password=%s dbname=%s sslmode=disable", host, user, password, db, )) if err != nil { fmt.Printf("%v\n", err) return } // テーブルを作成 client.AutoMigrate(&TaggedData{}) // データを登録 client.Create(&TaggedData{Name: "data1", Tags: postgres.Jsonb{[]byte(`{"key1": "val1"}`)}}) client.Create(&TaggedData{Name: "data2", Tags: postgres.Jsonb{[]byte(`{"key1": "val1", "key2": "val2"}`)}}) client.Create(&TaggedData{Name: "data3", Tags: postgres.Jsonb{[]byte(`{"key1": "val3", "key2": "val2", "key3": {"key4": "val4"}}`)}})
JSONB の値で検索
例えば Tags
が "key1" == "val1"
のデータを検索してみます。
data := []TaggedData{} // JSONB の値で検索 res := client.Where("tags ->> 'key1' = ?", "val1").Find(&data) if res.Error != nil { fmt.Printf("%v\n", res.Error) return } // 結果を表示 for _, d := range data { fmt.Println(d.Name) }
結果は以下のようになります。
data1 data2
続いて、 "key2" == "val2"
のデータを検索してみます。
res := client.Where("tags ->> 'key2' = ?", "val2").Find(&data)
結果は以下の通りで、data1 の Tags
は "key2"
というフィールドを持っていませんが、エラーにはなりません。
data2 data3
入れ子になっているフィールドを検索することもできます。
// 入れ子のフィールドを検索 res := client.Where("tags #>> '{key3, key4}' = ?", "val4").Find(&data)
こちらも想定通り動きました。結果は以下。
data3
プログラム全体
package main import ( "fmt" "github.com/jinzhu/gorm" "github.com/jinzhu/gorm/dialects/postgres" ) type TaggedData struct { ID uint32 `gorm:"NOT NULL;primary_key" sql:"TYPE:serial"` Name string `json:"name" gorm:"NOT NULL"` Tags postgres.Jsonb `json:"tags"` } func main() { host := "localhost" user := "postgres" password := "" db := "tagged" client, err := gorm.Open("postgres", fmt.Sprintf( "host=%s user=%s password=%s dbname=%s sslmode=disable", host, user, password, db, )) if err != nil { fmt.Printf("%v\n", err) return } client.AutoMigrate(&TaggedData{}) client.Create(&TaggedData{Name: "data1", Tags: postgres.Jsonb{[]byte(`{"key1": "val1"}`)}}) client.Create(&TaggedData{Name: "data2", Tags: postgres.Jsonb{[]byte(`{"key1": "val1", "key2": "val2"}`)}}) client.Create(&TaggedData{Name: "data3", Tags: postgres.Jsonb{[]byte(`{"key1": "val3", "key2": "val2", "key3": {"key4": "val4"}}`)}}) data := []TaggedData{} res := client.Where("tags ->> 'key1' = ?", "val1").Find(&data) if res.Error != nil { fmt.Printf("%v\n", res.Error) return } for _, d := range data { fmt.Println(d.Name) } data = []TaggedData{} res = client.Where("tags ->> 'key2' = ?", "val2").Find(&data) if res.Error != nil { fmt.Printf("%v\n", res.Error) return } for _, d := range data { fmt.Println(d.Name) } res = client.Where("tags #>> '{key3, key4}' = ?", "val4").Find(&data) if res.Error != nil { fmt.Printf("%v\n", res.Error) return } for _, d := range data { fmt.Println(d.Name) } if err := client.DropTable(&TaggedData{}); err != nil { fmt.Printf("%v\n", err) return } }