🍥

Meilisearch でマルチテナント

2022/11/05に公開

Meilisearch でマルチテナントを実現する方法を Meilisearch Go を利用してサンプルを作りました。

前提として検索対象のデータ自体は TimescaleDB にため込んで、検索部分を Meilisearch で処理するという仕組みです。

複数のクライアントが利用する場合はユーザー単位で検索サーバーを立てるわけには行かないので検索サーバーのマルチテナントが必要になります。

Meilisearch のマルチテナント戦略はとてもシンプルで、検索サーバーを利用するクライアントに渡す JWT にフィルター追加して特定のインデックスや特定のフィルターにマッチした中での検索しか行えないようにするというものです。

検索サーバーへのアクセスはアプリサーバー経由ではなく、クライアントが直接検索サーバーへアクセスするというのがポイントです。

マルチテナント向け JWT 生成サンプル

クライアントに渡す JWT を作成するサンプルを Meilisearch Go で書いてみました。

package main

import (
	"fmt"

	"github.com/meilisearch/meilisearch-go"
)

// ローカルで立てている Meilisearch の key なので隠していない

// API Key は masterKey を指定
// $ meilisearch --master-key=masterKey

func main() {
	client := meilisearch.NewClient(meilisearch.ClientConfig{
		Host:   "http://127.0.0.1:7700",
		APIKey: "masterKey",
	})

	token, err := client.GenerateTenantToken(
		// ここには親となる API Key の uid を指定する
		// API key の一覧は uid は keys で見れる
		// index はすべて利用できるが、actions は search のみに限定されている
		// curl -X GET 'http://localhost:7700/keys?limit=3' -H 'Authorization: Bearer masterKey' | jq
		// {
		//   "name": "Default Search API Key",
		//   "description": "Use it to search from the frontend",
		//   "key": "d74a98765192c721273d6abf0e460a0526398e4db0bd3612adec0c3fc33e3a08",
		//   "uid": "712d3ba6-2c31-4f51-a507-4c6c6c1998e1",
		//   "actions": [
		//     "search"
		//   ],
		//   "indexes": [
		//     "*"
		//   ],
		//   "expiresAt": null,
		//   "createdAt": "2022-11-04T12:56:23.490397Z",
		//   "updatedAt": "2022-11-04T12:56:23.490397Z"
		// },
		"712d3ba6-2c31-4f51-a507-4c6c6c1998e1",
		map[string]interface{}{
			// ここで index 名を指定して制限をかける
			"spam-logs": map[string]interface{}{
				// フィルターのリストを作りたいときは []string を使う
				// フィルターを強制した token を生成することでマルチテナントとして利用できる
				"filter": []string{
					// ham_id が abc かつ egg_id が xyz にマッチしている中での検索が行われる
					"ham_id = abc",
					"egg_id = xyz",
				},
			},
		},
		&meilisearch.TenantTokenOptions{},
	)
	if err != nil {
		panic(err)
	}
	
	// トークン出力
	fmt.Println(token)
}

資料

Discussion