🐥
Supabase SDKでJSONB配列の検索の実装方法
課題
SELECTは全てPostgresのViewで定義し、検索はSupabaseのJavascript SDKを使用する運用をしている。
この運用を行うと、Supabase側が自動的にデータベースのスキーマを読み取り、Type Fileを生成してくれるため、フロントエンド側ではTypeを定義する必要がなくなり、開発コストが若干下がる。
また、DBのスキーマを見れば、返しているレスポンスを確認できるため、オレオレレスポンスが行われることがなくなる。
今回は、下記のTypeをレスポンスとして受け取れるViewを作成し、like_idが一致するUserだけを抽出する検索を実装しようとしたが、公式ドキュメント等を参照して苦戦をしたのでメモ。
受け取りたいレスポンス
type user_likes = {
user_id: string
user_name: string
likes: {
like_id: string
like_name: string
}[]
}
▼ テストで使用したリポジトリ
DB構造
supabase/migrations/20241023103407_table.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
CREATE TABLE likes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);
CREATE TABLE user_likes (
user_id UUID REFERENCES users(id),
like_id UUID REFERENCES likes(id),
PRIMARY KEY (user_id, like_id)
);
CREATE VIEW user_likes_view AS
SELECT
users.id AS user_id,
users.name AS user_name,
jsonb_agg(
jsonb_build_object(
'like_id', likes.id,
'like_name', likes.name
)
) AS likes:jsonb
FROM
users
LEFT JOIN users_likes ON users.id = users_likes.user_id
LEFT JOIN likes ON users_likes.like_id = likes.id
GROUP BY
users.id;
検索してヒットするサイト
下記は同じような問題を抱えているが、解決するのか微妙な回答で解決に至らなかった。
解決策
.contains()を使用し、valueをJSON.stringify()
でJSON文字列にすることで検索の実装が出来た。
RedditでJSON.stringify
をしているコードを見つけてそんなバナナと思いながら実行をしてみたら検索が出来た。
上記の同じ課題を抱えていて、解決している例では1件もJSON.stringfiy()を使用している例が存在しなかったので、まさかの解決。
AND検索
AND検索はcontainsに配列で入れることで実装可能。
typescript
const supabase = createClient();
const payload = supabase
.from("user_likes_view")
.select()
.contains("likes", JSON.stringify([{ like_name: "1" }, {like_name: "2"}]));
OR検索
OR検索は.or()を活用することで実装可能。
typescript
const likeNames = ["1", "2"];
const orContains = likeNames.map((name) => `likes.cs.${JSON.stringify([{ like_name: name }])}`).join(",");
const supabase = createClient();
const payload = supabase
.from("user_likes_view")
.select()
.or(orContains)
さいごに
これを解決するのに1週間ぐらいテストしてたけど、みんな同じ壁にぶちあたって無いのかな。。。
Discussion