Supabase のデータ取得の際に、任意のプロパティで複数の条件に合致するレコードを取得したい
Supabase の DB からデータを取得する際に、少し凝ったフィルタリングの実装に悩んだのでメモがてら書いておきます。
通常
supabase.from()
メソッドでデータを取得する際、通常は以下のように eq()
メソッドを繋ぎ、条件を指定します。この例では、posts
テーブルから、 user_id
が合致するレコードを取得しています。
const { data: posts } = await supabase .from("posts") .select('*') .eq('user_id', userId) // user_id の値が合致するレコードを抽出
任意のプロパティで、複数の条件に合致するレコードを取得したいケース
今回少し悩んだのが、任意のプロパティで、複数の条件に合致するレコードを取得したいケースです。
例えば、posts
テーブルに is_published
(公開されているか否か) というカラムがあると仮定して、is_published
の値が true
なレコードのみを抽出したい場合は以下のようにすればOKです。これは単純です。
const { data: posts } = await supabase .from("posts") .select('*') .eq('is_published', "true")
次に、URLのクエリで ?tab=all
、?tab=public
、?tab=private
のようにフィルタリングをかける例を考えてみます。以下のようなイメージです。
?tab=all
: 全件取得(is_publishedの値が
trueかつ
false` なレコードを取得)?tab=public
:is_published
の値がtrue
なレコードのみ取得?tab=private
:is_published
の値がfalse
なレコードのみ集約
2と3は上記の例のとおりに書けば取得できます。こんな感じで、クエリの値を見て条件を絞ればいいでしょう。
const specificValueFromQuery = () => { if (query.tab === 'public') return 'true' if (query.tab === 'private') return 'false' } const { data: posts } = await supabase .from("posts") .select('*') .eq('is_published', specificValueFromQuery())
しかし、問題となるのが1のパターンです。?tab=all
の場合は全件取得したいですが、eq()
メソッドで絞り込んでいる以上、 is_published
が true
または false
のレコードを取得することはできません。
Supabase のjavascript クライアントでの型定義を見ても、eq()
メソッドの第二引数は一意の値でないといけなさそうです(true | false
とかで指定できるとベターだった)。
/** * Finds all rows whose value on the stated `column` exactly matches the * specified `value`. * * @param column The column to filter on. * @param value The value to filter with. */ eq(column: keyof T, value: T[keyof T]): this { this.url.searchParams.append(`${column}`, `eq.${value}`) return this }
この場合、そもそも .eq()
メソッドが不要なので外したいが、ちょっとそれは難しそう。
でも、このために2回以上APIを叩くのは無駄だし、どうしたものか...。
解決策
苦肉の策ですが、全件取得した後のデータに対して、query
の値を条件にフィルタリングをかけ、新しい配列を生成するようにしました。
const { data: rowPosts } = await supabase .from("posts") .select('*') .eq('user_id', id) const posts = rowPosts?.filter((post) => { if (!query.tab) return post if (query.tab === 'public') return post.is_published if (query.tab === 'private') return !post.is_published })
Supabase 側の実装では実現できそうになかったので、苦し紛れの方法になってしまいましたが、なんとか条件を満たすフィルタリングができるようになりました。