【DynamoDB】セカンダリインデックスをGoで扱う
はじめに
DynamoDBのセカンダリインデックスってよく分からないって人向けに、
実際にGoで扱ってみる記事を書いてみました。
これであなたもセカンダリインデックスが理解出来るはず!?
セカンダリインデックスとは
DynamoDBには2つのセカンダリインデックスがあります。
- Global Secondary Indexes(グローバルセカンダリインデックス)
- Local Secondary Indexes(ローカルセカンダリインデックス)
詳しくは書きませんが、DynamoDBはRDSと比べると、検索がかなりしんどいです。
ハッシュキーと呼ばれるキー + レンジキーと呼ばれるキー
に一致するものを取得というものだけしか取得出来ません。
※例 UserIDが1111で、Nameが太郎のレコードと一つ取得
err = userTable.Get("UserID", "1111").Range("Name", dynamo.Equal, "太郎").One(&user)
RDBみたいに、複雑な検索が出来ずに困る事が多いです。
それを少し補うのがセカンダリインデックスです。
セカンダリインデックスは、テーブルのハッシュキーとレンジキー以外の検索の方法で、
別のキーを設置して、その新しいキーで検索するというものです。
Global Secondary Indexes(グローバルセカンダリインデックス)
Global Secondary Indexesはそのテーブルのキーと全く違うキーで検索するインデックスを作成します。
※ 例
//構造体定義
type User struct {
UserID string `dynamo:"UserID,hash"`
Name string `dynamo:"Name,range"`
Age int `dynamo:"Age" index:"index-1,hash"`
TextID string `dynamo:"TextID" index:"index-1,range"`
}
//テーブル作成
err = db.CreateTable("UserTable", User{}).Run()
この例では、UserTableを作成しています。
このUserTableはUserIDがハッシュキー、Nameがレンジキーです。
なので、UserIDとNameが一致するものを取得出来ます。
しかし、別の検索方法で取得したい場合があります。
そこで、Global Secondary Indexesの出番です。
index-1という名前のindexを作成しました。
index-1はAgeがハッシュキーで、TextIDがレンジキーになっているので、Ageが20で、TextIDが3のものを取得という検索が出来ます。
詳しくは公式をご覧ください。
Local Secondary Indexes(ローカルセカンダリインデックス)
Local Secondary Indexes(ローカルセカンダリインデックス)は
ハッシュキーはテーブルと同じで、レンジキーだけ違うものです。
※ 例
//構造体定義
type User struct {
UserID string `dynamo:"UserID,hash"`
Name string `dynamo:"Name,range"`
Age int `dynamo:"Age" localIndex:"index-2,range"`
TextID string `dynamo:"TextID"`
}
//テーブル作成
err = db.CreateTable("UserTable", User{}).Run()
上記のように設定すると、UserIDが1234で、Ageが20の人というように検索出来るようになります。
Local Secondary Indexesはハッシュキーはテーブルと同じでUserIDになります。
Local Secondary Indexesを使う時は構造体のタグでlocalIndex
を使用します。
構造体のタグって凄いですね。
こちらも詳しくは公式をご覧ください。
前置きが長くなってしまったので、早速やっていきます。
環境
- go 1.15
- github.com/aws/aws-sdk-go v1.38.0
- github.com/guregu/dynamo v1.11.0
ソースコード
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/guregu/dynamo"
)
type User struct {
UserID string `dynamo:"UserID,hash" index:"index-1,range"`
Name string `dynamo:"Name,range"`
Age int `dynamo:"Age" localIndex:"index-2,range" index:"index-1,hash"`
TextID string `dynamo:"TextID" index:"index-3,hash"`
}
// 本来はenvから取得した方が良い
const AWS_REGION = "ap-northeast-1"
const DYNAMO_ENDPOINT = "http://localhost:8000"
func main() {
// クライアントの設定
sess, err := session.NewSession(&aws.Config{
Region: aws.String(AWS_REGION),
Endpoint: aws.String(DYNAMO_ENDPOINT),
Credentials: credentials.NewStaticCredentials("dummy", "dummy", "dummy"),
})
if err != nil {
panic(err)
}
db := dynamo.New(sess)
// テーブル作成をする為に、一度テーブルを削除します
db.Table("UserTable").DeleteTable().Run()
// テーブル作成
err = db.CreateTable("UserTable", User{}).Run()
if err != nil {
panic(err)
}
// テーブルの指定
userTable := db.Table("UserTable")
// User構造体をuser変数に定義
var user User
err = userTable.Put(&User{UserID: "1111", Name: "太郎", Age: 22, TextID: "1111abc"}).Run()
if err != nil {
panic(err)
}
err = userTable.Put(&User{UserID: "2222", Name: "花子", Age: 33, TextID: "2222abc"}).Run()
if err != nil {
panic(err)
}
err = userTable.Put(&User{UserID: "3333", Name: "かずや", Age: 30, TextID: "4444abc"}).Run()
if err != nil {
panic(err)
}
err = userTable.Put(&User{UserID: "4444", Name: "そら", Age: 20, TextID: "3333abc"}).Run()
if err != nil {
panic(err)
}
// 通常の取得
err = userTable.Get("UserID", "1111").Range("Name", dynamo.Equal, "太郎").One(&user)
if err != nil {
panic(err)
}
fmt.Printf("GetDB%+v\n", user)
// グローバルセカンダリーインデックスを利用して取得
err = userTable.Get("Age", 33).Range("UserID", dynamo.Equal, "2222").Index("index-1").One(&user)
if err != nil {
panic(err)
}
fmt.Printf("GetDB%+v\n", user)
err = userTable.Get("TextID", "4444abc").Index("index-3").One(&user)
if err != nil {
panic(err)
}
fmt.Printf("GetDB%+v\n", user)
// ローカルセカンダリーインデックスを利用して取得
err = userTable.Get("UserID", "4444").Range("Age", dynamo.Equal, 20).Index("index-2").One(&user)
if err != nil {
panic(err)
}
fmt.Printf("GetDB%+v\n", user)
}
出力結果
GetDB{UserID:1111 Name:太郎 Age:22 TextID:1111abc}
GetDB{UserID:2222 Name:花子 Age:33 TextID:2222abc}
GetDB{UserID:3333 Name:かずや Age:30 TextID:4444abc}
GetDB{UserID:4444 Name:そら Age:20 TextID:3333abc}
解説
少しだけ
この構造体でテーブルの定義をしています。
type User struct {
UserID string `dynamo:"UserID,hash" index:"index-1,range"`
Name string `dynamo:"Name,range"`
Age int `dynamo:"Age" localIndex:"index-2,range" index:"index-1,hash"`
TextID string `dynamo:"TextID" index:"index-3,hash"`
}
このテーブルはUserIDがハッシュキー、Nameがレンジキーです。
index-1がGlobal Secondary IndexesでハッシュキーがAge、レンジキーがUserID
index-2はLocal Secondary IndexesでハッシュキーがUserID、レンジキーがAge(index-1の逆ですね)
index-3はGlobal Secondary IndexesでハッシュキーがTextIDでレンジキーは無し
です。
このように一つのフィールドにGlobal Secondary IndexesとLocal Secondary Indexesどちらもタグで設定する事が可能です。
さいごに
これであなたもセカンダリインデックスマスターですね。
Discussion