1- type OperandType = string | number | bigint | string [ ] | bigint [ ] ;
2- type OperatorType =
3- | "eq"
4- | "gt"
5- | "gte"
6- | "lt"
7- | "lte"
8- | "ilike"
9- | "contains"
10- | "startsWith"
11- | "endsWith" ;
1+ import { sql , SqlBool } from "kysely" ;
2+ import {
3+ NumberSearchOptions ,
4+ StringSearchOptions ,
5+ StringArraySearchOptions ,
6+ NumberArraySearchOptions ,
7+ } from "../inputs/searchOptions.js" ;
8+
9+ export type OperandType = string | number | bigint | string [ ] | bigint [ ] ;
10+
11+ export type NumericOperatorType = "eq" | "gt" | "gte" | "lt" | "lte" ;
12+ export type StringOperatorType = "contains" | "startsWith" | "endsWith" ;
13+ export type ArrayOperatorType = "overlaps" | "contains" ;
14+ export type OperatorType =
15+ | NumericOperatorType
16+ | StringOperatorType
17+ | ArrayOperatorType ;
1218
1319enum OperatorSymbols {
1420 eq = "=" ,
1521 gt = ">" ,
1622 gte = ">=" ,
1723 lt = "<" ,
1824 lte = "<=" ,
19- ilike = "ilike" ,
25+ ilike = "~*" ,
26+ overlaps = "&&" ,
27+ contains = "@>" ,
2028}
2129
30+ // TODO: remove when data client is updated
2231export const generateFilterValues = (
2332 column : string ,
2433 operator : OperatorType ,
@@ -36,12 +45,192 @@ export const generateFilterValues = (
3645 case "lte" :
3746 return [ column , OperatorSymbols . lte , operand ] ;
3847 case "contains" :
39- return [ column , OperatorSymbols . ilike , operand ] ;
48+ return [ column , OperatorSymbols . ilike , `% ${ operand } %` ] ;
4049 case "startsWith" :
41- return [ column , OperatorSymbols . ilike , operand ] ;
50+ return [ column , OperatorSymbols . ilike , ` ${ operand } %` ] ;
4251 case "endsWith" :
43- return [ column , OperatorSymbols . ilike , operand ] ;
52+ return [ column , OperatorSymbols . ilike , `% ${ operand } ` ] ;
4453 }
4554
4655 return [ ] ;
4756} ;
57+
58+ export const getTablePrefix = ( column : string ) : string => {
59+ switch ( column ) {
60+ case "hypercerts" :
61+ return "claims" ;
62+ case "contract" :
63+ return "contracts" ;
64+ case "fractions" :
65+ return "fractions_view" ;
66+ case "metadata" :
67+ return "metadata" ;
68+ case "attestations" :
69+ return "attestations" ;
70+ default :
71+ return column ;
72+ }
73+ } ;
74+
75+ export const isFilterObject = ( obj : never ) : boolean => {
76+ const filterKeys = [
77+ "eq" ,
78+ "gt" ,
79+ "gte" ,
80+ "lt" ,
81+ "lte" ,
82+ "contains" ,
83+ "startsWith" ,
84+ "endsWith" ,
85+ "in" ,
86+ "overlaps" ,
87+ "contains" ,
88+ ] ;
89+ return Object . keys ( obj ) . some ( ( key ) => filterKeys . includes ( key ) ) ;
90+ } ;
91+
92+ // Helper functions for building conditions
93+ const buildEqualityCondition = (
94+ column : string ,
95+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
96+ value : any ,
97+ tableName : string ,
98+ ) : SqlBool => sql `${ sql . raw ( `"${ tableName } "."${ column } "` ) } =
99+ ${ value } `;
100+
101+ const buildComparisonCondition = (
102+ column : string ,
103+ operator : string ,
104+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105+ value : any ,
106+ tableName : string ,
107+ ) : SqlBool =>
108+ sql `${ sql . raw ( `"${ tableName } "."${ column } "` ) }
109+ ${ sql . raw ( operator ) }
110+ ${ value } ` ;
111+
112+ const buildLikeCondition = (
113+ column : string ,
114+ pattern : string ,
115+ tableName : string ,
116+ ) : SqlBool => sql `${ sql . raw ( `"${ tableName } "."${ column } "` ) } ILIKE
117+ ${ pattern } `;
118+
119+ const buildArrayCondition = (
120+ column : string ,
121+ operator : string ,
122+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
123+ values : any [ ] ,
124+ tableName : string ,
125+ ) : SqlBool =>
126+ sql `${ sql . raw ( `"${ tableName } "."${ column } "` ) }
127+ ${ sql . raw ( operator ) }
128+ ${ sql . raw ( `ARRAY[${ values . map ( ( v ) => `'${ v } '` ) . join ( ", " ) } ]` ) } ` ;
129+
130+ const conditionBuilders = {
131+ eq : buildEqualityCondition ,
132+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
133+ gt : ( column : string , value : any , tableName : string ) =>
134+ buildComparisonCondition ( column , ">" , value , tableName ) ,
135+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136+ gte : ( column : string , value : any , tableName : string ) =>
137+ buildComparisonCondition ( column , ">=" , value , tableName ) ,
138+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139+ lt : ( column : string , value : any , tableName : string ) =>
140+ buildComparisonCondition ( column , "<" , value , tableName ) ,
141+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142+ lte : ( column : string , value : any , tableName : string ) =>
143+ buildComparisonCondition ( column , "<=" , value , tableName ) ,
144+ contains : ( column : string , value : string , tableName : string ) =>
145+ buildLikeCondition ( column , `%${ value } %` , tableName ) ,
146+ startsWith : ( column : string , value : string , tableName : string ) =>
147+ buildLikeCondition ( column , `${ value } %` , tableName ) ,
148+ endsWith : ( column : string , value : string , tableName : string ) =>
149+ buildLikeCondition ( column , `%${ value } ` , tableName ) ,
150+ } ;
151+
152+ export const buildCondition = (
153+ column : string ,
154+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155+ value : any ,
156+ tableName : string ,
157+ ) : SqlBool => {
158+ const conditions : SqlBool [ ] = [ ] ;
159+
160+ if (
161+ value instanceof StringSearchOptions ||
162+ value instanceof NumberSearchOptions
163+ ) {
164+ Object . entries ( value ) . forEach ( ( [ key , val ] ) => {
165+ if ( key in conditionBuilders && val !== undefined ) {
166+ conditions . push ( conditionBuilders [ key ] ( column , val , tableName ) ) ;
167+ }
168+ } ) ;
169+ } else if (
170+ value instanceof StringArraySearchOptions ||
171+ value instanceof NumberArraySearchOptions
172+ ) {
173+ if ( value . contains && value . contains . length > 0 ) {
174+ conditions . push (
175+ buildArrayCondition ( column , "@>" , value . contains , tableName ) ,
176+ ) ;
177+ }
178+ if ( value . overlaps && value . overlaps . length > 0 ) {
179+ conditions . push (
180+ buildArrayCondition ( column , "&&" , value . overlaps , tableName ) ,
181+ ) ;
182+ }
183+ } else if ( typeof value === "object" && value !== null ) {
184+ Object . entries ( value ) . forEach ( ( [ key , val ] ) => {
185+ if ( key in conditionBuilders && val !== undefined ) {
186+ conditions . push ( conditionBuilders [ key ] ( column , val , tableName ) ) ;
187+ } else if ( key === "contains" && Array . isArray ( val ) ) {
188+ conditions . push ( buildArrayCondition ( column , "@>" , val , tableName ) ) ;
189+ } else if ( key === "overlaps" && Array . isArray ( val ) ) {
190+ conditions . push ( buildArrayCondition ( column , "&&" , val , tableName ) ) ;
191+ }
192+ } ) ;
193+ }
194+
195+ return sql . join ( conditions , sql ` AND ` ) ;
196+ } ;
197+
198+ export const buildWhereCondition = < T extends string > (
199+ column : string ,
200+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
201+ value : any ,
202+ tableName : T ,
203+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
204+ eb : any ,
205+ ) : SqlBool | null => {
206+ if ( ! column || value === undefined ) return null ;
207+
208+ if ( typeof value === "object" && value !== null ) {
209+ if ( isFilterObject ( value ) ) {
210+ return buildCondition ( column , value , tableName ) ;
211+ }
212+
213+ const relatedTable = getTablePrefix ( column ) ;
214+ const nestedConditions : SqlBool [ ] = [ ] ;
215+
216+ for ( const [ nestedColumn , nestedValue ] of Object . entries ( value ) ) {
217+ if ( ! nestedColumn || nestedValue === undefined ) continue ;
218+ const nestedCondition = buildWhereCondition (
219+ nestedColumn ,
220+ nestedValue ,
221+ relatedTable ,
222+ eb ,
223+ ) ;
224+ if ( nestedCondition ) {
225+ nestedConditions . push ( nestedCondition ) ;
226+ }
227+ }
228+
229+ return nestedConditions . length > 0
230+ ? sql . join ( nestedConditions , sql ` AND ` )
231+ : null ;
232+ }
233+
234+ return sql `${ sql . raw ( `"${ tableName } "."${ column } "` ) } =
235+ ${ value } ` ;
236+ } ;
0 commit comments