간혹 우리는 DB를 작성하고 DB에서 전체 검색을 하고 하고 싶을 때가 있습니다.
그런데 column들이 나눠져 있다보니 이를 합쳐서 검색하기도 쉽지 않고, 또 연관관계까지 고려해서 작성하기도 쉽지 않습니다.
Postgres 에서는 ts_vector로 데이터를 vector로 합치고 검색하기 쉽게 되어있습니다.
supabase에서도 이를 이용해서 검색을 용이하게 하기 위한 인터페이스를 제공 하고 있죠.
(supabse 의 textSearch는 postgres의 ts_query 를 기반으로 만들어져 있다.)
그러나 문제는 client api로 단어 검색을 하려다 보면, 완성된 단어는 잘 검색이 되는데, 중간 단어는 검색이 잘 안됩니다.
완성되지 않은 단어의 경우에는 (Partial search 참조) 따로 RPC 함수를 만들어 사용하라고 가이드 되어있습니다.
Partial search#
Partial search is particularly useful when you want to find matches on substrings within your data.
Implementing partial search#
You can use the :* syntax with to_tsquery(). Here's an example that searches for any book titles beginning with "Lit":
DB함수를 만들고 RPC로 호출 하는 것도 나쁘진 않습니다.
db 함수를 만들고.
select title from books where to_tsvector(title) @@ to_tsquery('Lit:*');
client에서 아래와 같이 사용한다.
const { data, error } = await supabase.rpc('search_books_by_title_prefix', { prefix: 'Lit' })
그러나 불편한 점도 있고,
아래 처럼 하나의 함수에서 다양한 쿼리 옵션들과 합쳐서 사용하기 힘들죠.
async getPaginatedOpt({columns, keyword, offset, range=10, landscapeOnly, orderBy, orderByDesc}:{columns?: string, keyword?: string, offset?: number, range?: number, landscapeOnly?: boolean, orderBy?: string, orderByDesc?: boolean}): Promise<{ v: Partial<MyItem>[], total: number }> {
let query = supabase.from("myitems_table").select(columns||'*', { count: 'exact' });
if(landscapeOnly){
query = query.eq('landscaping', landscapeOnly);
}
if(keyword){
// query = query.or(`s_name.ilike.%${keyword}%,kr_name.ilike.%${keyword}%,jp_name.ilike.%${keyword}%,eng_name.ilike.%${keyword}%,nk_name.ilike.%${keyword}%`);
query = query.textSearch('text_search',`${keyword}`,{type:'plain', config:'simple',});
}
if(offset!==undefined && range!==undefined){
query = query.range(offset, offset + (range<1?1:range) - 1);
}
if(orderBy){
query = query.order(orderBy, { ascending: orderByDesc||false });
}
const { data, count, error } = await query;
if (error) {
console.error('getPaginated error:', error);
return { v: [], total: 0 };
}
return { v: data as Partial<MyItem>[] ?? [], total: count ?? 0 };
},
우연찮게 textSearch 옵션에 'raw' 를 찾았습니다.
공식적으로는 plain, phrase, websearch만 명시되어있습니다만, "PostgREST의 FTS 연산자와 동일하게 작동" 한다고 되어있습니다.
FTS에는 raw라는 query가 있고, 이를 활용하면 접두사를 사용할 수 있습니다. " 오징:*" 와 같이 prefix 형태를 사용할 수 있죠.
// @ts-expect-error
query = query.textSearch('text_search',`${keyword}:*`,{type:'raw', config:'simple',});
물론 공식 API에는 제공되는것이 아니라서 typscript lint에서는 에러로 보입니다. ㅠ_ㅠ
공식 문서에서는 plain, phrase, websearch 3가지만 제공 됩니다. 그리고 이것은 완성된 단어들의 조합만 검색 가능합니다.
때로는 위와 같이 완성되지 않은 단어 검색도 필요하게 됩니다.
이때는 위와 같이 raw + keyword:* 가 되도록 해서 사용해보는 것도 좋을것 같네요.
#해피 코딩
'Supabase' 카테고리의 다른 글
[supabase] DB table UPDATE 시 Permission Denied? RLS와 GRANT로 해결하는 법 (0) | 2025.04.04 |
---|---|
[supabase] View에 RLS 적용 시키는 방법 (0) | 2025.03.08 |
[supabase] Auth Hooking 을 이용한 그룹별 DB access 권한 제한 (0) | 2025.01.24 |
[supabase] postgres plv8 활성화 (0) | 2025.01.24 |
[Supabase] Edge function 만들기 (0) | 2024.12.11 |