@@ -24,17 +24,44 @@ interface DBSong extends Song {
2424
2525function applyExactFilter ( baseQuery : any , type : string , searchText : string ) {
2626 if ( type === 'all' ) {
27- return baseQuery . or ( `title.ilike.${ searchText } ,artist.ilike.${ searchText } ` ) ;
27+ return baseQuery . or (
28+ `title.ilike.${ searchText } ,title_ko.ilike.${ searchText } ,artist.ilike.${ searchText } ,artist_ko.ilike.${ searchText } ` ,
29+ ) ;
30+ }
31+ if ( type === 'title' ) {
32+ return baseQuery . or ( `title.ilike.${ searchText } ,title_ko.ilike.${ searchText } ` ) ;
33+ }
34+ if ( type === 'artist' ) {
35+ return baseQuery . or ( `artist.ilike.${ searchText } ,artist_ko.ilike.${ searchText } ` ) ;
36+ }
37+ if ( type === 'number' ) {
38+ return baseQuery . or ( `num_tj.eq.${ searchText } ,num_ky.eq.${ searchText } ` ) ;
2839 }
2940 return baseQuery . ilike ( type , searchText ) ;
3041}
3142
3243function applyPartialFilter ( baseQuery : any , type : string , searchText : string ) {
3344 if ( type === 'all' ) {
3445 return baseQuery
35- . or ( `title.ilike.%${ searchText } %,artist.ilike.%${ searchText } %` )
46+ . or (
47+ `title.ilike.%${ searchText } %,title_ko.ilike.%${ searchText } %,artist.ilike.%${ searchText } %,artist_ko.ilike.%${ searchText } %` ,
48+ )
3649 . not ( 'title' , 'ilike' , searchText )
37- . not ( 'artist' , 'ilike' , searchText ) ;
50+ . not ( 'title_ko' , 'ilike' , searchText )
51+ . not ( 'artist' , 'ilike' , searchText )
52+ . not ( 'artist_ko' , 'ilike' , searchText ) ;
53+ }
54+ if ( type === 'title' ) {
55+ return baseQuery
56+ . or ( `title.ilike.%${ searchText } %,title_ko.ilike.%${ searchText } %` )
57+ . not ( 'title' , 'ilike' , searchText )
58+ . not ( 'title_ko' , 'ilike' , searchText ) ;
59+ }
60+ if ( type === 'artist' ) {
61+ return baseQuery
62+ . or ( `artist.ilike.%${ searchText } %,artist_ko.ilike.%${ searchText } %` )
63+ . not ( 'artist' , 'ilike' , searchText )
64+ . not ( 'artist_ko' , 'ilike' , searchText ) ;
3865 }
3966 return baseQuery . ilike ( type , `%${ searchText } %` ) . not ( type , 'ilike' , searchText ) ;
4067}
@@ -50,6 +77,26 @@ async function executeSearchQueries(
5077) : Promise < { data : DBSong [ ] ; hasNext : boolean } | { error : string } > {
5178 const size = to - from + 1 ;
5279
80+ // 번호 검색은 정확 매칭만 지원 (부분 일치 단계 스킵)
81+ if ( type === 'number' ) {
82+ const exactCountResult = await applyExactFilter (
83+ supabase . from ( 'songs' ) . select ( selectClause , { count : 'exact' , head : true } ) ,
84+ type ,
85+ query ,
86+ ) ;
87+ if ( exactCountResult . error ) return { error : exactCountResult . error . message } ;
88+ const exactTotal = exactCountResult . count ?? 0 ;
89+
90+ const exactQuery = applyExactFilter ( supabase . from ( 'songs' ) . select ( selectClause ) , type , query ) ;
91+ const { data, error } = await exactQuery . order ( order ) . range ( from , to ) ;
92+ if ( error ) return { error : error . message } ;
93+
94+ return {
95+ data : ( data as DBSong [ ] ) ?? [ ] ,
96+ hasNext : exactTotal > to + 1 ,
97+ } ;
98+ }
99+
53100 // 1. 정확 일치 / 부분 일치 각각의 총 개수를 병렬로 조회
54101 const exactCountQuery = applyExactFilter (
55102 supabase . from ( 'songs' ) . select ( selectClause , { count : 'exact' , head : true } ) ,
@@ -134,7 +181,7 @@ export async function GET(request: Request): Promise<NextResponse<ApiResponse<Se
134181 const { searchParams } = new URL ( request . url ) ;
135182 const query = searchParams . get ( 'q' ) ;
136183 const type = searchParams . get ( 'type' ) || 'title' ;
137- const order = type === 'all' ? 'title' : type ;
184+ const order = type === 'all' ? 'title' : type === 'number' ? 'num_tj' : type ;
138185 const authenticated = searchParams . get ( 'authenticated' ) === 'true' ;
139186
140187 const page = parseInt ( searchParams . get ( 'page' ) || '0' , 10 ) ;
0 commit comments