Skip to content

Commit 75d4ae7

Browse files
committed
fix: correct filters for the clickouse "Array(String)" datatype with IN filter
(for cases, if 'IN' filter stayed in filters after normalization) https://web.tracklify.com/project/2b7ZVgE5/AdminForth/1407/KQu076pa/image
1 parent eb4975a commit 75d4ae7

File tree

1 file changed

+36
-5
lines changed

1 file changed

+36
-5
lines changed

adminforth/dataConnectors/clickhouse.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,32 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
289289
}
290290
}
291291

292+
if ((filter.operator == AdminForthFilterOperators.IN || filter.operator == AdminForthFilterOperators.NIN)
293+
&& column.isArray?.enabled
294+
&& this.isArrayType(column._underlineType)) {
295+
const itemType = column._underlineType
296+
.replace(/^Nullable\(/, '')
297+
.match(/^Array\((.*)\)$/)?.[1];
298+
299+
if (!itemType) {
300+
throw new Error(`Unable to determine item type for array field '${column.name}' with type '${column._underlineType}'`);
301+
}
302+
303+
placeholder = `{f$?:Array(${itemType})}`;
304+
const arrayField = this.isNullableType(column._underlineType) ? `assumeNotNull(${field})` : field;
305+
const hasAnyExpression = `hasAny(${arrayField}, ${placeholder})`;
306+
307+
if (filter.operator == AdminForthFilterOperators.NIN) {
308+
return this.isNullableType(column._underlineType)
309+
? `(${field} IS NULL OR NOT ${hasAnyExpression})`
310+
: `NOT ${hasAnyExpression}`;
311+
}
312+
313+
return this.isNullableType(column._underlineType)
314+
? `${field} IS NOT NULL AND ${hasAnyExpression}`
315+
: hasAnyExpression;
316+
}
317+
292318
if (column._underlineType.startsWith('Decimal')) {
293319
field = `toDecimal64(${field}, 8)`;
294320
placeholder = `toDecimal64({f$?:String}, 8)`;
@@ -335,20 +361,24 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
335361
}).join(` ${this.OperatorsMap[filter.operator]} `);
336362
}
337363

338-
getFilterParams(filter: IAdminForthSingleFilter | IAdminForthAndOrFilter): any[] {
364+
getFilterParams(resource: AdminForthResource, filter: IAdminForthSingleFilter | IAdminForthAndOrFilter): any[] {
339365
if ((filter as IAdminForthSingleFilter).field) {
340366
if ((filter as IAdminForthSingleFilter).rightField) {
341367
// No params for field-to-field comparisons
342368
return [];
343369
}
344370
// filter is a Single filter
371+
const column = resource.dataSourceColumns.find((col) => col.name == (filter as IAdminForthSingleFilter).field);
345372

346373
// Handle IS_EMPTY and IS_NOT_EMPTY operators - no params needed
347374
if (filter.operator == AdminForthFilterOperators.IS_EMPTY || filter.operator == AdminForthFilterOperators.IS_NOT_EMPTY) {
348375
return [];
349376
} else if (filter.operator == AdminForthFilterOperators.LIKE || filter.operator == AdminForthFilterOperators.ILIKE) {
350377
return [{ 'f': `%${filter.value}%` }];
351378
} else if (filter.operator == AdminForthFilterOperators.IN || filter.operator == AdminForthFilterOperators.NIN) {
379+
if (column?.isArray?.enabled && this.isArrayType(column._underlineType)) {
380+
return [{ 'f': filter.value }];
381+
}
352382
return [{ 'p': filter.value }];
353383
} else if (filter.operator == AdminForthFilterOperators.EQ && filter.value === null) {
354384
// there is no param for IS NULL filter
@@ -368,15 +398,15 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
368398

369399
// filter is a AndOrFilter
370400
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
371-
return params.concat(this.getFilterParams(f));
401+
return params.concat(this.getFilterParams(resource, f));
372402
}, []);
373403
}
374404

375-
whereParams(filters: IAdminForthAndOrFilter): any {
405+
whereParams(resource: AdminForthResource, filters: IAdminForthAndOrFilter): any {
376406
if (filters.subFilters.length === 0) {
377407
return {};
378408
}
379-
const paramsArray = this.getFilterParams(filters);
409+
const paramsArray = this.getFilterParams(resource, filters);
380410
const params = paramsArray.reduce((acc, param, paramIndex) => {
381411
if (param.f !== undefined) {
382412
acc[`f${paramIndex}`] = param.f;
@@ -404,7 +434,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
404434
params: {},
405435
}
406436
}
407-
const params = this.whereParams(filters);
437+
const params = this.whereParams(resource, filters);
408438
const where = Object.keys(params).reduce((w, paramKey) => {
409439
// remove first char of string (will be "f" or "p") to leave only index
410440
const keyIndex = paramKey.substring(1);
@@ -430,6 +460,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
430460
}).join(', ');
431461
const tableName = resource.table;
432462

463+
console.log('getDataWithOriginalTypes called with filters', JSON.stringify(filters), 'and sort', JSON.stringify(sort));
433464
const { where, params } = this.whereClause(resource, filters);
434465

435466
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';

0 commit comments

Comments
 (0)