Repository: nocodb/nocodb
Pull request #10904: Nc feat/field handler
Author: login: dstala
Created: 2025-03-19T06:55:31Z | Merged: 2025-03-25T09:56:12Z

## Change Summary

Provide summary of changes with issue number if any.

## Change type

- [ ] feat: (new feature for the user, not a new feature for build script)
- [ ] fix: (bug fix for the user, not a fix to a build script)
- [ ] docs: (changes to the documentation)
- [ ] style: (formatting, missing semi colons, etc; no production code change)
- [ ] refactor: (refactoring production code, eg. renaming a variable)
- [ ] test: (adding missing tests, refactoring tests; no production code change)
- [ ] chore: (updating grunt tasks etc; no production code change)

## Test/ Verification

Provide summary of changes.

## Additional information / screenshots (optional)

Anything for maintainers to be made aware of



Diff:
diff --git a/packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts b/packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
new file mode 100644
index 000000000000..08d88bb9e031
--- /dev/null
+++ b/packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
@@ -0,0 +1,20 @@
+import { ILexingError, IRecognitionException } from "chevrotain";
+import { NcSDKError } from "../errorUtils";
+export interface InvalidFilterErrorInfo {
+  lexingError?: ILexingError[];
+  parsingError?: IRecognitionException[],
+  message?: string;
+};
+export class InvalidFilterError extends NcSDKError {
+  constructor(public readonly info: InvalidFilterErrorInfo) {
+    let message = info.message ?? 'INVALID_FILTER';
+    if(info.lexingError) {
+      const lexingErrorMessage = info.lexingError.map(k => k.message).join(', ');
+      message = `${message} lexing_error: ${lexingErrorMessage}`;
+    } else if(info.parsingError) {
+      const parsingErrorMessage = info.parsingError.map(k => k.message);
+      message = `${message} parsing_error: ${parsingErrorMessage}`;
+    }
+    super(message)
+  }
+}
\ No newline at end of file
diff --git a/packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts b/packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
index d6acac94b1d9..a1b7858d07bd 100644
--- a/packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
+++ b/packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
@@ -1,5 +1,5 @@
 import { ColumnType, FilterType } from '~/lib/Api';
-import { BadRequest, NcSDKError } from '~/lib/errorUtils';
+import { BadRequest } from '~/lib/errorUtils';
 import {
   FilterClauseSubType,
   FilterGroupSubType,
@@ -11,6 +11,7 @@ import {
   FilterParseError,
   IS_WITHIN_COMPARISON_SUB_OPS,
 } from './filterHelpers';
+import { InvalidFilterError } from './error/invalid-filter.error';
 export {
   COMPARISON_OPS,
   COMPARISON_SUB_OPS,
@@ -103,7 +104,10 @@ function innerExtractFilterFromXwhere(
     (parseResult.lexErrors.length > 0 || parseResult.parseErrors.length > 0) &&
     throwErrorIfInvalid
   ) {
-    if (throwErrorIfInvalid) throw new NcSDKError('INVALID_FILTER');
+    if (throwErrorIfInvalid) throw new InvalidFilterError({
+      lexingError: parseResult.lexErrors,
+      parsingError: parseResult.parseErrors,
+    });
     else {
       errors.push({
         message: 'Invalid filter format.',
@@ -164,7 +168,9 @@ function mapFilterClauseSubType(
   const aliasCol = aliasColObjMap[filter.field];
   if (!aliasCol) {
     if (throwErrorIfInvalid) {
-      throw new NcSDKError('INVALID_FILTER');
+      throw new InvalidFilterError({
+        message: `Column '${filter.field}' not found.`
+      });
     } else {
       errors.push({
         message: `Column '${filter.field}' not found.`,
diff --git a/packages/nocodb/src/db/conditionV2.ts b/packages/nocodb/src/db/conditionV2.ts
index 8c9784bea77b..08b1722fa1f4 100644
--- a/packages/nocodb/src/db/conditionV2.ts
+++ b/packages/nocodb/src/db/conditionV2.ts
@@ -8,6 +8,7 @@ import {
   UITypes,
 } from 'nocodb-sdk';
 import dayjs from 'dayjs';
+import { FieldHandler } from './field-handler';
 import type { FilterType } from 'nocodb-sdk';
 // import customParseFormat from 'dayjs/plugin/customParseFormat.js';
 import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
@@ -42,6 +43,11 @@ export default async function conditionV2(
   if (!conditionObj || typeof conditionObj !== 'object') {
     return;
   }
+  await FieldHandler.fromBaseModel(baseModelSqlv2).verifyFilters(
+    Array.isArray(conditionObj)
+      ? (conditionObj as Filter[])
+      : ([conditionObj] as Filter[]),
+  );
   (
     await parseConditionV2(
       baseModelSqlv2,
@@ -190,6 +196,19 @@ const parseConditionV2 = async (
         NcError.fieldNotFound(filter.fk_column_id);
       }
     }
+    if ([UITypes.JSON].includes(column.uidt)) {
+      return FieldHandler.fromBaseModel(baseModelSqlv2).applyFilter(
+        filter,
+        column,
+        {
+          knex: baseModelSqlv2.dbDriver,
+          model: baseModelSqlv2.model,
+          alias,
+          context: baseModelSqlv2.context,
+          throwErrorIfInvalid,
+        },
+      );
+    }
     if (column.uidt === UITypes.LinkToAnotherRecord) {
       const colOptions = (await column.getColOptions(
         context,
diff --git a/packages/nocodb/src/db/field-handler/error/filter-verification.error.ts b/packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
new file mode 100644
index 000000000000..05cac5fa6e28
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
@@ -0,0 +1,5 @@
+export class FilterVerificationError extends Error {
+  constructor(readonly errors: string[]) {
+    super(`Filter verification failed, errors: ${errors.join(', ')}`);
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/field-handler.interface.ts b/packages/nocodb/src/db/field-handler/field-handler.interface.ts
new file mode 100644
index 000000000000..ddc510f2a9df
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/field-handler.interface.ts
@@ -0,0 +1,70 @@
+import type { MetaService } from 'src/meta/meta.service';
+import type { NcContext } from 'nocodb-sdk';
+import type { Knex } from 'knex';
+import type { Column, Filter, Model } from '~/models';
+
+export interface HandlerOptions {
+  alias?: string;
+  throwErrorIfInvalid?: boolean;
+  context?: NcContext;
+  model?: Model;
+  metaService?: MetaService;
+  knex?: Knex;
+  tnPath?: string;
+  fieldHandler?: IFieldHandler;
+}
+export interface FilterVerificationResult {
+  isValid: boolean;
+  errors?: string[];
+}
+export interface FieldHandlerInterface {
+  select(qb: Knex.QueryBuilder, column: Column, options: HandlerOptions): void;
+  filter(
+    knex: Knex,
+    filter: Filter,
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<(qb: Knex.QueryBuilder) => void>;
+  verifyFilter(
+    filter: Filter,
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<FilterVerificationResult>;
+}
+
+export interface IFieldHandler {
+  applyFilter(
+    filter: Filter,
+    column?: Column,
+    options?: HandlerOptions,
+  ): Promise<(qb: Knex.QueryBuilder) => void>;
+
+  applyFilters(
+    filters: Filter[],
+    options?: HandlerOptions,
+  ): Promise<(qb: Knex.QueryBuilder) => void>;
+
+  applySelect(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<void>;
+
+  verifyFilter(
+    filter: Filter,
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<FilterVerificationResult>;
+
+  verifyFiltersSafe(
+    filters: Filter[],
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<FilterVerificationResult>;
+
+  verifyFilters(
+    filters: Filter[],
+    column: Column,
+    options?: HandlerOptions,
+  ): Promise<boolean>;
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
new file mode 100644
index 000000000000..e1f40b924a9f
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
@@ -0,0 +1,41 @@
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class CheckboxGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(filter: Filter, column: Column) {
+    const supportedOperations = [
+      'eq',
+      'neq',
+      'blank',
+      'notblank',
+      'checked',
+      'notchecked',
+      'is',
+      'isnot',
+    ];
+    if (!supportedOperations.includes(filter.comparison_op)) {
+      return {
+        isValid: false,
+        errors: [
+          `Operation ${filter.comparison_op} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+    if (
+      ![null, true, false, 'true', 'false', '', 1, 0, '1', '0'].includes(
+        filter.value,
+      )
+    ) {
+      return {
+        isValid: false,
+        errors: [
+          `Value ${filter.value} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
new file mode 100644
index 000000000000..c34a95cf77a3
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
@@ -0,0 +1,61 @@
+import { parseDateTimeValue } from 'nocodb-sdk';
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class DateTimeGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(filter: Filter, column: Column) {
+    const supportedOperations = [
+      'eq',
+      'neq',
+      'not',
+      'empty',
+      'notempty',
+      'null',
+      'notnull',
+      'blank',
+      'notblank',
+      'gt',
+      'lt',
+      'gte',
+      'lte',
+      'isnot',
+      'is',
+      'isWithin',
+    ];
+    if (!supportedOperations.includes(filter.comparison_op)) {
+      return {
+        isValid: false,
+        errors: [
+          `Operation ${filter.comparison_op} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+    if (filter.comparison_sub_op === 'exactDate' || !filter.comparison_sub_op) {
+      // check if value is not null or empty
+      if (
+        !(
+          filter.value === null ||
+          typeof filter.value === 'undefined' ||
+          filter.value === '' ||
+          (filter.comparison_op === 'in' && Array.isArray(filter.value))
+        )
+      ) {
+        const dateTimeValue = await parseDateTimeValue(filter.value, {
+          col: column,
+        });
+        if (dateTimeValue === null || typeof dateTimeValue === 'undefined') {
+          return {
+            isValid: false,
+            errors: [
+              `Value ${filter.value} is not supported for type ${column.uidt} on column ${column.title}`,
+            ],
+          } as FilterVerificationResult;
+        }
+      }
+    }
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
new file mode 100644
index 000000000000..39032da24bcb
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
@@ -0,0 +1,3 @@
+import { DateTimeGeneralHandler } from '../date-time/date-time.general.handler';
+
+export class DateGeneralHandler extends DateTimeGeneralHandler {}
diff --git a/packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
new file mode 100644
index 000000000000..b8d363a29db8
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
@@ -0,0 +1,55 @@
+import { ncIsNumber } from 'nocodb-sdk';
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class DecimalGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(filter: Filter, column: Column) {
+    const supportedOperations = [
+      'eq',
+      'neq',
+      'blank',
+      'notblank',
+      'is',
+      'isnot',
+      'gt',
+      'lt',
+      'gte',
+      'lte',
+      'in',
+      'empty',
+      'notempty',
+      'null',
+      'notnull',
+      'gb_eq',
+    ];
+    if (!supportedOperations.includes(filter.comparison_op)) {
+      return {
+        isValid: false,
+        errors: [
+          `Operation ${filter.comparison_op} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+
+    if (
+      !(
+        typeof filter.value === 'undefined' ||
+        filter.value === null ||
+        typeof filter.value === 'number' ||
+        ncIsNumber(Number(filter.value)) ||
+        (filter.comparison_op === 'in' && Array.isArray(filter.value))
+      )
+    ) {
+      return {
+        isValid: false,
+        errors: [
+          `Value ${filter.value} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts
new file mode 100644
index 000000000000..57dfb764eaf8
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts
@@ -0,0 +1,56 @@
+import { FormulaDataTypes, parseProp } from 'nocodb-sdk';
+import { UITypes } from 'nocodb-sdk';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+} from '~/db/field-handler/field-handler.interface';
+import type { FormulaColumn } from '~/models';
+import { type Column, type Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class FormulaGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions = {},
+  ) {
+    const uidt = parseProp(column.meta).display_type;
+    if (uidt) {
+      column.uidt = uidt;
+      return options.fieldHandler.verifyFilter(filter, column, options);
+    } else {
+      const formulaCol = await column.getColOptions<FormulaColumn>(
+        options.context,
+      );
+      const parsedTree = await formulaCol.getParsedTree();
+      const dataType = parsedTree.dataType;
+      switch (dataType) {
+        case FormulaDataTypes.BOOLEAN: {
+          column.uidt = UITypes.Checkbox;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.DATE: {
+          column.uidt = UITypes.DateTime;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.INTERVAL: {
+          column.uidt = UITypes.Time;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.NUMERIC: {
+          column.uidt = UITypes.Decimal;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.STRING:
+        default: {
+          column.uidt = UITypes.SingleLineText;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+      }
+    }
+
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/generic.ts b/packages/nocodb/src/db/field-handler/handlers/generic.ts
new file mode 100644
index 000000000000..f024f55a6f0e
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/generic.ts
@@ -0,0 +1,128 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type {
+  FieldHandlerInterface,
+  FilterVerificationResult,
+  HandlerOptions,
+} from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { getColumnName } from '~/db/BaseModelSqlv2';
+import {
+  getAs,
+  ncIsStringHasValue,
+} from '~/db/field-handler/utils/handlerUtils';
+import { sanitize } from '~/helpers/sqlSanitize';
+
+export class GenericFieldHandler implements FieldHandlerInterface {
+  async select(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options: HandlerOptions,
+  ): Promise<void> {
+    const { alias, context, model, tnPath } = options;
+
+    const columnName = await getColumnName(
+      context,
+      column,
+      await model.getColumns(context),
+    );
+
+    const tableName = alias || tnPath || '';
+    const fullColumnName = tableName
+      ? `${tableName}.${columnName}`
+      : columnName;
+
+    const selectAlias = sanitize(getAs(column) || columnName);
+    const selectColumn = sanitize(fullColumnName);
+
+    qb.select({ [selectAlias]: selectColumn });
+  }
+
+  async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const val = filter.value;
+    const field = alias ? `${alias}.${column.column_name}` : column.column_name;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.where(knex.raw("?? = ''", [field])).orWhereNull(field);
+            });
+          } else {
+            qb.where(knex.raw('?? = ?', [field, val]));
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb
+                .where(knex.raw("?? != ''", [field]))
+                .orWhereNotNull(field);
+            });
+          } else {
+            qb.where((nestedQb) => {
+              nestedQb
+                .where(knex.raw('?? != ?', [field, val]))
+                .orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          if (!ncIsStringHasValue(val)) {
+            qb.whereNull(field);
+          } else {
+            qb.where(knex.raw('??::text ilike ?', [field, `%${val}%`]));
+          }
+          break;
+
+        case 'nlike':
+          if (!ncIsStringHasValue(val)) {
+            qb.whereNotNull(field);
+          } else {
+            qb.where((nestedQb) => {
+              nestedQb.where(
+                knex.raw('??::text not ilike ?', [field, `%${val}%`]),
+              );
+            });
+          }
+          break;
+
+        case 'blank':
+          qb.where((nestedQb) => {
+            nestedQb.whereNull(field).orWhere(knex.raw("?? = ''", [field]));
+          });
+          break;
+
+        case 'notblank':
+          qb.where((nestedQb) => {
+            nestedQb.whereNotNull(field).orWhere(knex.raw("?? != ''", [field]));
+          });
+          break;
+
+        default:
+          throw new Error(
+            `Unsupported comparison operator for JSON: ${filter.comparison_op}`,
+          );
+      }
+    };
+  }
+
+  async verifyFilter(
+    _filter: Filter,
+    _column: Column,
+  ): Promise<FilterVerificationResult> {
+    return {
+      isValid: true,
+    };
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts
new file mode 100644
index 000000000000..60b55c7b6504
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts
@@ -0,0 +1,130 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+export class JsonGeneralHandler extends GenericFieldHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('?? = ?', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('?? = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNotNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            qb.where((nestedQb) => {
+              if (isValidJson) {
+                nestedQb.where(knex.raw('?? != ?', [field, jsonVal]));
+              } else {
+                nestedQb.where(knex.raw('?? != ?', [field, jsonVal]));
+              }
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNotNull(field);
+          } else {
+            qb.where(knex.raw('?? ilike ?', [field, val]));
+          }
+          break;
+
+        case 'nlike':
+          if (!ncIsStringHasValue(val)) {
+            qb.whereNotNull(field);
+          } else {
+            val = `%${val}%`;
+            qb.where((nestedQb) => {
+              nestedQb.where(knex.raw('?? not like ?', [field, val]));
+              if (val !== '%%') {
+                nestedQb.orWhere(field, '').orWhereNull(field);
+              } else {
+                nestedQb.orWhereNull(field);
+              }
+            });
+          }
+          break;
+
+        case 'blank':
+          qb.where((nestedQb) => {
+            nestedQb
+              .whereNull(field)
+              .orWhere(knex.raw("?? = '{}'", [field]))
+              .orWhere(knex.raw("?? = '[]'", [field]));
+          });
+          break;
+
+        case 'notblank':
+          qb.whereNotNull(field)
+            .whereNot(knex.raw("?? = '{}'", [field]))
+            .whereNot(knex.raw("?? = '[]'", [field]));
+          break;
+
+        default:
+          throw new Error(
+            `Unsupported comparison operator for JSON: ${filter.comparison_op}`,
+          );
+      }
+    };
+  }
+
+  protected parseJsonValue(val: any): {
+    jsonVal: string;
+    isValidJson: boolean;
+  } {
+    let jsonVal = val;
+    let isValidJson = false;
+    if (typeof val === 'object' && val !== null) {
+      jsonVal = JSON.stringify(val);
+      isValidJson = true;
+    } else if (typeof val === 'string') {
+      try {
+        JSON.parse(val);
+        jsonVal = val;
+        isValidJson = true;
+      } catch {
+        jsonVal = val;
+      }
+    }
+    return { jsonVal, isValidJson };
+  }
+
+  override async verifyFilter(filter: Filter, column: Column) {
+    return super.verifyFilter(filter, column);
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts b/packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
new file mode 100644
index 000000000000..4edacb03b9cd
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
@@ -0,0 +1,136 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+export class JsonMySqlHandler extends GenericFieldHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (val === '') {
+            qb.where((nestedQb) => {
+              nestedQb.whereNotNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            qb.where((nestedQb) => {
+              if (isValidJson) {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              } else {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              }
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNotNull(field);
+          } else {
+            qb.where(knex.raw('JSON_UNQUOTE(??) like ?', [field, val]));
+          }
+          break;
+
+        case 'nlike':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNull(field);
+          } else {
+            qb.where((nestedQb) => {
+              nestedQb.where(
+                knex.raw('JSON_UNQUOTE(??) not like ?', [field, val]),
+              );
+              if (val !== '%%') {
+                nestedQb.orWhere(field, '').orWhereNull(field);
+              } else {
+                nestedQb.orWhereNull(field);
+              }
+            });
+          }
+          break;
+
+        case 'blank':
+          qb.where((nestedQb) => {
+            nestedQb
+              .whereNull(field)
+              .orWhere(knex.raw("JSON_UNQUOTE(??) = '{}'", [field]))
+              .orWhere(knex.raw("JSON_UNQUOTE(??) = '[]'", [field]));
+          });
+          break;
+
+        case 'notblank':
+          qb.whereNotNull(field)
+            .whereNot(knex.raw("JSON_UNQUOTE(??) = '{}'", [field]))
+            .whereNot(knex.raw("JSON_UNQUOTE(??) = '[]'", [field]));
+          break;
+
+        default:
+          throw new Error(
+            `Unsupported comparison operator for JSON: ${filter.comparison_op}`,
+          );
+      }
+    };
+  }
+
+  protected parseJsonValue(val: any): {
+    jsonVal: string;
+    isValidJson: boolean;
+  } {
+    let jsonVal = val;
+    let isValidJson = false;
+    if (typeof val === 'object' && val !== null) {
+      jsonVal = JSON.stringify(val);
+      isValidJson = true;
+    } else if (typeof val === 'string') {
+      try {
+        JSON.parse(val);
+        jsonVal = val;
+        isValidJson = true;
+      } catch {
+        jsonVal = val;
+      }
+    }
+    return { jsonVal, isValidJson };
+  }
+
+  override async verifyFilter(filter: Filter, column: Column) {
+    return super.verifyFilter(filter, column);
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts b/packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
new file mode 100644
index 000000000000..046b66435717
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
@@ -0,0 +1,127 @@
+import { JsonGeneralHandler } from './json.general.handler';
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+const appendIsNull = ({
+  qb,
+  field,
+  knex,
+}: {
+  qb: Knex.QueryBuilder;
+  knex: CustomKnex;
+  field: string | any;
+}) => {
+  qb.where((nestedQb) => {
+    nestedQb
+      .where(knex.raw("??::jsonb = '{}'::jsonb", [field]))
+      .orWhere(knex.raw("??::jsonb = '[]'::jsonb", [field]))
+      .orWhere(knex.raw("??::text = ''", [field]))
+      .orWhereNull(field);
+  });
+};
+const appendIsNotNull = ({
+  qb,
+  field,
+  knex,
+}: {
+  qb: Knex.QueryBuilder;
+  knex: CustomKnex;
+  field: string | any;
+}) => {
+  qb.where((nestedQb) => {
+    nestedQb
+      .whereNot(knex.raw("??::jsonb = '{}'::jsonb", [field]))
+      .whereNot(knex.raw("??::jsonb = '[]'::jsonb", [field]))
+      .whereNot(knex.raw("??::text = ''", [field]))
+      .whereNotNull(field);
+  });
+};
+
+export class JsonPgHandler extends JsonGeneralHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            appendIsNull({ qb, field, knex });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('??::jsonb = ?::jsonb', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('??::text = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (!ncIsStringHasValue(val)) {
+            appendIsNotNull({ qb, field, knex });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            qb.where((nestedQb) => {
+              if (isValidJson) {
+                nestedQb.where(
+                  knex.raw('??::jsonb != ?::jsonb', [field, jsonVal]),
+                );
+              } else {
+                nestedQb.where(knex.raw('??::text != ?', [field, jsonVal]));
+              }
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          if (!ncIsStringHasValue(val)) {
+            appendIsNull({ qb, knex, field });
+          } else {
+            val = `%${val}%`;
+            qb.where(knex.raw('??::text ilike ?', [field, val]));
+          }
+          break;
+
+        case 'nlike':
+          if (!ncIsStringHasValue(val)) {
+            appendIsNotNull({ qb, knex, field });
+          } else {
+            val = `%${val}%`;
+
+            qb.where((nestedQb) => {
+              nestedQb.where(knex.raw('??::text not ilike ?', [field, val]));
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'blank':
+          appendIsNull({ qb, knex, field });
+          break;
+
+        case 'notblank':
+          appendIsNotNull({ qb, knex, field });
+          break;
+
+        default:
+          throw new Error(
+            `Unsupported comparison operator for JSON: ${filter.comparison_op}`,
+          );
+      }
+    };
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
new file mode 100644
index 000000000000..5964c2158b25
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
@@ -0,0 +1,11 @@
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class MultiSelectGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(_filter: Filter, _column: Column) {
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
new file mode 100644
index 000000000000..59b1d31c7a40
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
@@ -0,0 +1,3 @@
+import { DecimalGeneralHandler } from '../decimal/decimal.general.handler';
+
+export class NumberGeneralHandler extends DecimalGeneralHandler {}
diff --git a/packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
new file mode 100644
index 000000000000..6889772913c5
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
@@ -0,0 +1,41 @@
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class SingleLineTextGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(filter: Filter, column: Column) {
+    const supportedOperations = [
+      'eq',
+      'neq',
+      'blank',
+      'notblank',
+      'is',
+      'isnot',
+      'gt',
+      'lt',
+      'gte',
+      'lte',
+      'ge',
+      'le',
+      'in',
+      'like',
+      'nlike',
+      'empty',
+      'notempty',
+      'null',
+      'notnull',
+      'gb_eq',
+    ];
+    if (!supportedOperations.includes(filter.comparison_op)) {
+      return {
+        isValid: false,
+        errors: [
+          `Operation ${filter.comparison_op} is not supported for type ${column.uidt} on column ${column.title}`,
+        ],
+      } as FilterVerificationResult;
+    }
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts b/packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
new file mode 100644
index 000000000000..bc27a235323d
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
@@ -0,0 +1,3 @@
+import { MultiSelectGeneralHandler } from '../multi-select/multi-select.general.handler';
+
+export class SingleSelectGeneralHandler extends MultiSelectGeneralHandler {}
diff --git a/packages/nocodb/src/db/field-handler/index.ts b/packages/nocodb/src/db/field-handler/index.ts
new file mode 100644
index 000000000000..4cd9890dbe96
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/index.ts
@@ -0,0 +1,342 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import { JsonMySqlHandler } from './handlers/json/json.mysql.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [ClientType.MYSQL]: JsonMySqlHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
+    return new HandlerClass();
+  }
+
+  /**
+   * Applies a filter to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param filter - Filter object
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applyFilter(
+    filter: Filter,
+    column?: Column,
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client.config.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    const useColumn =
+      column ?? this.info.model.columns.find((col) => col.id === filter.id);
+    return handler.filter(knex, filter, useColumn, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async applyFilterGroup(filter: Filter, options: HandlerOptions = {}) {
+    return this.applyFilters(filter.children, options);
+  }
+
+  async applyFilters(
+    filters: Filter[],
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const qbHandlers: {
+      handler: (qb: Knex.QueryBuilder) => void;
+      index: number;
+      logicalOps?: string;
+    }[] = [];
+    let index = 0;
+    for (const filter of filters) {
+      if (filter.is_group) {
+        qbHandlers.push({
+          handler: await this.applyFilterGroup(filter, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      } else {
+        let column = model.columns.find(
+          (col) => col.id === filter.fk_column_id,
+        );
+        if (!column) {
+          column = await this.getRelatedColumnById(
+            options.context,
+            filter.fk_column_id,
+          );
+        }
+        qbHandlers.push({
+          handler: await this.applyFilter(filter, column, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      }
+    }
+    return (qb: Knex.QueryBuilder) => {
+      for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
+        qb[getLogicalOpMethod(handler.logicalOps)](qb);
+      }
+    };
+  }
+
+  /**
+   * Applies a select operation to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applySelect(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options: HandlerOptions = {},
+  ): Promise<void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    return handler.select(qb, column, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async verifyFilter(
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions = {},
+  ) {
+    const knex = options.knex ?? this.info.knex;
+    return (
+      this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+    ).verifyFilter(filter, column, {
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async verifyFiltersSafe(filters: Filter[], options: HandlerOptions = {}) {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const traverseResult: FilterVerificationResult[] = [];
+    await this.traverseFilters(filters, async (filter: Filter) => {
+      let column = model.columns.find((col) => col.id === filter.fk_column_id);
+      if (!column) {
+        column = await this.getRelatedColumnById(
+          options.context,
+          filter.fk_column_id,
+        );
+      }
+      traverseResult.push(await this.verifyFilter(filter, column, options));
+    });
+    const invalidFilterResults = traverseResult
+      .filter((k) => !k.isValid)
+      .reduce(
+        (obj, res) => {
+          obj.errors = obj.errors.concat(res.errors);
+          return obj;
+        },
+        { isValid: false, errors: [] } as FilterVerificationResult,
+      );
+    if (invalidFilterResults.errors?.length > 0) {
+      return invalidFilterResults;
+    } else {
+      return { isValid: true } as FilterVerificationResult;
+    }
+  }
+
+  async verifyFilters(filters: Filter[], options: HandlerOptions = {}) {
+    const verificationResult = await this.verifyFiltersSafe(filters, options);
+    if (!verificationResult.isValid) {
+      throw new FilterVerificationError(verificationResult.errors!);
+    }
+    return true;
+  }
+
+  async traverseFilters(
+    filters: Filter[],
+    handler: <T>(filter: Filter) => Promise<void | T>,
+  ) {
+    return Promise.all(
+      (filters ?? []).map(async (filter) => {
+        // somehow filters is an array of array
+        if (Array.isArray(filter)) {
+          return this.traverseFilters(filter, handler);
+        } else if (filter.is_group) {
+          return this.traverseFilters(filter.children, handler);
+        } else {
+          return handler(filter);
+        }
+      }),
+    );
+  }
+
+  async getRelatedColumnById(context: NcContext, colId: string) {
+    // possibly to be enhanced into getting the model and basemodel too
+    const column = await Column.get(context, { colId });
+    return column;
+  }
+}
diff --git a/packages/nocodb/src/db/field-handler/utils/handlerUtils.ts b/packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
new file mode 100644
index 000000000000..3bafe302e3af
--- /dev/null
+++ b/packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
@@ -0,0 +1,9 @@
+import { ncIsNull, ncIsUndefined } from 'nocodb-sdk';
+import type { Column } from '~/models';
+
+export function getAs(column: Column) {
+  return column.asId || column.id;
+}
+export function ncIsStringHasValue(val: string | undefined | null) {
+  return val !== '' && !ncIsUndefined(val) && !ncIsNull(val);
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (25)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary><blockquote>

`1-3`: **Consider enriching the SingleSelectGeneralHandler with specific validation**

While extending `MultiSelectGeneralHandler` allows for code reuse, the current implementation always returns `{ isValid: true }` without any actual validation. Single-select fields typically have more restrictive validation requirements than multi-select fields (e.g., ensuring only one value is selected).

Consider overriding the `verifyFilter` method to add specific validation logic for single-select fields, such as checking that the filter value isn't an array of multiple values.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1)</summary><blockquote>

`5-10`: **Consider implementation necessity.**

This class currently overrides the `verifyFilter` method but implements the exact same behavior as the parent `GenericFieldHandler` class, returning `{ isValid: true }`. If there are no special validation rules for multi-select fields yet, this override might be unnecessary unless it's serving as a placeholder for future specialized validation logic.

</blockquote></details>
<details>
<summary>packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts (1)</summary><blockquote>

`1-258`: **Consider removing unreachable code.**

Line 180 (`return {};`) is unreachable code as it's placed after a conditional block where all paths either throw an error or return. This line should be removed to avoid confusion.


```diff
- return {};
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 180-180: This code is unreachable

... because either this statement will throw an exception ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

---

[error] 63-63: Avoid the use of spread (`...`) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in `.reduce`) because it causes a time complexity of `O(n^2)`.
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

---

[error] 69-69: Avoid the use of spread (`...`) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in `.reduce`) because it causes a time complexity of `O(n^2)`.
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (2)</summary><blockquote>

`44-45`: **Remove the redundant case clause.**

Lines 44-45 introduce a `case FormulaDataTypes.STRING:` directly followed by a `default:` block handling the same logic. The `STRING` case is thus redundant and can be removed to simplify the switch statement.



```diff
        switch (dataType) {
          ...
-        case FormulaDataTypes.STRING:
         default: {
           column.uidt = UITypes.SingleLineText;
           return options.fieldHandler.verifyFilter(filter, column, options);
         }
        }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

---

`52-54`: **Eliminate unreachable return statement.**

The return statements in each branch of the switch make lines 52-54 unreachable. Removing this final return block enhances clarity and avoids confusion.



```diff
     }
   }

-    return {
-      isValid: true,
-    } as FilterVerificationResult;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1)</summary><blockquote>

`35-50`: **Refine type checking for array values in 'in' operations.**

Your code permits an array for the `in` operation but does not confirm each element is numeric. It’s beneficial to ensure that all array items are valid decimal inputs.



```diff
-        (filter.comparison_op === 'in' && Array.isArray(filter.value))
+        (filter.comparison_op === 'in' && Array.isArray(filter.value)
+          && filter.value.every(
+            (v) => typeof v === 'number' || ncIsNumber(Number(v))
+          )
+        )
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (2)</summary><blockquote>

`7-16`: **Consider using an Enum or Set for supported operations.**

Defining your `supportedOperations` as an Enum or a Set can lead to improved code clarity and faster lookups:
```diff
-const supportedOperations = [
-  'eq', 'neq', 'blank', 'notblank', 'checked', 'notchecked', 'is', 'isnot',
-];
+const supportedOperations = new Set([
+  'eq',
+  'neq',
+  'blank',
+  'notblank',
+  'checked',
+  'notchecked',
+  'is',
+  'isnot',
+]);
```
Using a data structure that conveys the meaning of “unique set of allowed operators” can be more expressive than a plain array.

---

`17-24`: **Validate correctness of `checked`/`notchecked` usage without a filter value.**

Currently, the code checks for a valid filter value even for `checked`/`notchecked` comparison operators. Since these operators might not require a filter value, consider skipping this validation to avoid potential confusion. Otherwise, ensure that the user experience is clear if a value is supplied.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary><blockquote>

`44-62`: **Verify error handling for invalid date/time formats.**

Currently, the code returns `isValid: false` if `parseDateTimeValue` yields `null` or `undefined`. Confirm that all date/time parse errors are caught here, and no unhandled exceptions occur upstream. If necessary, incorporate more specific error handling, e.g., logging or throwing a typed error.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (4)</summary><blockquote>

`65-88`: **Potential performance caveat with ILIKE on large JSON.**

Using `??::text ilike ?` can be expensive on large JSON structures. Monitor for performance issues or consider storing a separate searchable text field if large-scale partial matches are frequent.

---

`105-109`: **Use a structured error class for unsupported operators.**

Instead of throwing a generic `Error`, consider throwing a typed error such as `InvalidFilterError` for consistency with other new classes. This practice makes error handling more uniform and clearer throughout the codebase.

---

`113-132`: **Validate string-literal JSON.**

`parseJsonValue` attempts to parse `val` when it’s a string, marking it as valid JSON if parsing succeeds. Ensure that your logic gracefully handles strings that contain valid JSON but semantically incorrect data (e.g., an empty array when a non-empty object is expected). If such scenarios are possible, consider adding specialized validation to produce more descriptive errors.

---

`134-136`: **Coordinate `verifyFilter` with deeper JSON checks.**

`verifyFilter` simply calls `super.verifyFilter`, which returns `isValid: true` by default. If you plan to enforce stricter JSON-specific filtering rules, consider overriding `verifyFilter` here with actual checks or at least referencing `parseJsonValue`.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (6)</summary><blockquote>

`13-36`: **Use consistent error-handling strategy in `select` method.**  
The `select` method reads cleanly and correctly uses `sanitize` and `getAs` to manage aliases. However, if `model.getColumns(context)` or `getColumnName` fail for any reason, the code might raise unhandled exceptions. You might consider wrapping external calls to ensure a consistent error-handling strategy across all field handlers.

---

`38-44`: **Return type matches the interface but check for improved readability.**  
This portion returns a function implementing the filter logic. It may be clearer to explicitly declare the function type or add inline documentation (e.g., via a JSDoc annotation) to help maintainers quickly identify how the filter function is used.

---

`49-75`: **Consolidate repeated null/empty string checks for `eq` and `neq`.**  
Both operators handle the cases for empty string, undefined, and null in slightly different ways. You could unify them by extracting a helper to reduce repetitive code and improve readability.

---

`77-95`: **Avoid potential duplication in `like`/`nlike` blocks.**  
Currently, `like` and `nlike` blocks each perform near-identical checks (e.g., checking for `val === ''`). Consider consolidating the shared logic into a helper function or a small inline utility.

---

`107-111`: **Clarify error message for unsupported comparison operators.**  
Throwing a generic message mentioning “JSON” might be confusing when used by non-JSON fields that still use this generic handler. Consider a more neutral or context-specific message to reduce ambiguity.

---

`116-124`: **Question the unconditional `isValid: true` in `verifyFilter`.**  
The `verifyFilter` method always returns `isValid: true`. If any future expansions require validation, this default may obscure real filter issues. Consider verifying the filter’s shape or data before returning success.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (1)</summary><blockquote>

`11-11`: **Consider grouping imports for clarity.**  
Importing `FieldHandler` among other references in a large file can become tedious to track. Although not a strict error, you might keep field handler imports together or in a dedicated block to highlight their significance.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2)</summary><blockquote>

`8-24`: **Ensure `appendIsNull` logic aligns with business needs.**  
Currently, this helper treats any empty string, `'{}'`, `'[]'`, or `NULL` as logically equivalent. If your domain sees `'{}'` differently than empty string, consider clarifying or splitting the checks.

---

`43-125`: **Parsing JSON data in `JsonPgHandler.filter`.**  
Carrying out JSON-specific comparisons under each operator case is tidy here, but consider error-handling for malformed JSON (if `this.parseJsonValue` throws). Logging or wrapping the error might help with debugging.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (2)</summary><blockquote>

`16-19`: **Include explanatory comments for error capture strategy.**

While `FilterVerificationResult` clearly indicates potential errors, consider adding brief JSDoc or inline comments to clarify when and how these errors might be populated. This fosters better maintainability and readability.

---

`35-70`: **Consider consolidating interface naming conventions.**

Having both `FieldHandlerInterface` and `IFieldHandler` can cause confusion, as they both serve as high-level contracts for field operations. If these interfaces are intended to represent distinct concepts, consider adding clarifying documentation or naming to highlight their difference.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary><blockquote>

`194-237`: **Potential improvement in logical operations chaining.**

When composing multiple filters in `applyFilters`, consider verifying that the correct method (`andWhere`, `orWhere`, etc.) is indeed called for each handler. Although you sort by index, ensuring the final query respects the intended logic for complex filter sets could benefit from additional tests or fail-safe checks.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between eebe67e142549dfdd44ac613ca8b867271e24cd8 and 604147ad492d031e4ec9291d20024bb147e4387f.

</details>

<details>
<summary>📒 Files selected for processing (19)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (12)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1) (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1) (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1) (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1) (1)</summary>

* `GenericFieldHandler` (13-124)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1) (1)</summary>

* `FilterVerificationResult` (16-19)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2) (2)</summary>

* `GenericFieldHandler` (13-124)
* `filter` (38-114)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1) (1)</summary>

* `FilterVerificationResult` (16-19)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts (1)</summary><blockquote>

<details>
<summary>packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts (1) (1)</summary>

* `InvalidFilterError` (8-20)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2) (2)</summary>

* `GenericFieldHandler` (13-124)
* `filter` (38-114)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1) (1)</summary>

* `FilterVerificationResult` (16-19)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2) (2)</summary>

* `GenericFieldHandler` (13-124)
* `filter` (38-114)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1) (1)</summary>

* `filter` (9-111)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1) (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1) (1)</summary>

* `FilterVerificationResult` (16-19)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1) (1)</summary>

* `FieldHandler` (127-340)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1) (1)</summary>

* `filter` (38-114)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (3)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1) (1)</summary>

* `filter` (38-114)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1) (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1) (1)</summary>

* `filter` (9-111)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (3) (3)</summary>

* `FieldHandlerInterface` (20-33)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1) (1)</summary>

* `getAs` (3-5)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (11)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4) (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1) (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1) (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1) (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1) (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1) (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1) (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1) (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1) (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2) (2)</summary>

* `JsonPgHandler` (43-126)
* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1) (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: pre-build-for-playwright / playwright
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests

</details>

<details>
<summary>🔇 Additional comments (22)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary>

`1-3`: **Clean implementation of the date handler**

The `DateGeneralHandler` extends the `DateTimeGeneralHandler`, which is a logical inheritance pattern since date fields are essentially a subset of date-time fields. This structure allows for code reuse without duplication.

The class doesn't override any methods from its parent, indicating that the same filter verification logic from `DateTimeGeneralHandler` will be used, which checks for supported operations and validates date values.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1)</summary>

`3-5`: **Good utility function for consistent column identification**

The `getAs` function follows the single responsibility principle by providing a simple way to extract a consistent identifier from a column object. It prioritizes the `asId` property with a fallback to the `id` property, which is a clean approach to handling column identifiers.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary>

`1-3`: **Appropriate inheritance for number handler**

The `NumberGeneralHandler` extends `DecimalGeneralHandler`, which is a logical inheritance since number fields can be considered a specific type of decimal fields. This allows for reuse of the decimal field filter verification logic without code duplication.

The parent class has a comprehensive `verifyFilter` method that handles various comparison operations and validates numeric values, which will be inherited by this handler.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1)</summary>

`1-5`: **Well-structured error class for filter verification.**

This new error class provides a clear way to aggregate multiple validation errors during filter verification. The implementation is clean and follows good practices by extending the standard Error class and using the `readonly` modifier for the errors array.

</details>
<details>
<summary>packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts (1)</summary>

`1-7`: **Well-defined interface for filter error information.**

The `InvalidFilterErrorInfo` interface properly defines the structure for filter error details, allowing for flexible error reporting with optional properties.

</details>
<details>
<summary>packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts (3)</summary>

`2-14`: **Good error handling refactoring.**

The update to imports properly replaces generic error handling with more specific error classes, improving the error reporting capabilities of the library.

---

`107-110`: **Improved error handling for invalid filters.**

Replacing generic errors with the specialized `InvalidFilterError` class enhances error reporting by providing detailed information about lexing and parsing errors. This will make debugging filter issues much easier.

---

`171-173`: **Better column validation error reporting.**

Using `InvalidFilterError` for column validation errors maintains consistency in error handling throughout the filter parsing process and provides clearer error messages to users.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts (1)</summary>

`5-41`: **Implementation looks good.**

The filter validation logic is straightforward and correctly flags unsupported comparison operators while approving valid ones. No immediate issues are observed.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (2)</summary>

`25-36`: **Confirm all allowable checkbox values.**

The list `[null, true, false, 'true', 'false', '', 1, 0, '1', '0']` is broad and may conflate multiple data types. If your system strictly interprets booleans, consider narrowing this array to ensure stricter data validation. If you intend this flexibility, ensure the rest of your code can handle each type consistently.

---

`37-39`: **Looks good overall.**

This successful return logic correctly indicates the filter is valid when all checks pass.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

`34-43`: **Ensure consistency for `exactDate` sub-operation.**

When `comparison_sub_op` is `'exactDate'`, the code checks for non-null and non-empty values. Confirm that your system never expects an empty date string for `'exactDate'`. Otherwise, consider handling that scenario or providing a more descriptive error message.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary>

`23-40`: **Clarify equality check handling for empty string.**

For `eq`:
```typescript
if (val === '' || typeof val === 'undefined' || val === null) {
  // ...
}
```
This logic treats empty strings, undefined, and null as equivalent to `'{}'`, `'[]'`, or `NULL` JSON objects. Confirm if this approach meets your functional requirements, as it could mask subtle differences (e.g., an empty string vs. empty object).

</details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary>

`46-50`: **Proactive verification of filters.**  
Using `FieldHandler.fromBaseModel(...).verifyFilters(...)` ensures early detection of invalid filters. This is good practice, but confirm that the calling code properly handles any resulting exceptions or logs them in your broader system flow.

---

`199-211`: **Leverage the specialized JSON filter logic.**  
When a column has `UITypes.JSON`, delegating to `FieldHandler.fromBaseModel(baseModelSqlv2).applyFilter(...)` is a clean design. Ensure other JSON usage paths (e.g., fallback to generic logic, if any) remain consistent or gracefully handled to avoid unexpected query behaviors.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

`25-41`: **Confirm consistency in `appendIsNotNull`.**  
`appendIsNotNull` inverts the conditions from `appendIsNull`, excluding `'{}'`, `'[]'`, or `''`. Confirm that excluding empty arrays or objects is the intended definition of “not null” for your business logic.  

Can you confirm these exclusions by checking specs or acceptance criteria? If they must differ, we can adjust them accordingly.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (2)</summary>

`6-15`: **Good use of optional properties in `HandlerOptions`.**

All properties are appropriately marked as optional. This design is flexible and promotes reusability across multiple scenarios.

---

`20-33`: **Methods return types appear consistent with the usage pattern.**

The promise-based approach for `filter` and `verifyFilter` methods is straightforward and easy to integrate. Great job ensuring the interface precisely captures the returned function type.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (4)</summary>

`28-113`: **Comprehensive handler registry definition.**

The `HANDLER_REGISTRY` covers a broad set of UI types with corresponding handler classes. This approach will simplify extensibility and keeps the mapping clean.

---

`115-125`: **Logical operator handling is concise and readable.**

The `getLogicalOpMethod` function is straightforward, properly handling `or`, `and`, and `not`. This switch statement is easy to maintain if new logical operators are introduced in the future.

---

`163-189`: **Separation of concerns is clear for `applyFilter`.**

Delegating filter logic to the handler instance is a clean approach, preventing the `FieldHandler` class from becoming overloaded with low-level filter operations. This method is well-structured for future maintainability.

---

`335-339`: **Good abstraction for column retrieval.**

`getRelatedColumnById` neatly encapsulates remote column lookups and keeps logic out of the primary filter application methods. This helps maintain a single responsibility per method.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T07:00:38Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary><blockquote>

`8-132`: **The implementation looks good but has unnecessary code**

The `JsonGeneralHandler` class correctly implements JSON-specific filtering with proper sanitization and consideration for different types of JSON values. However, there's an unnecessary override of `verifyFilter` that doesn't add any functionality beyond what the parent class provides.


Consider removing the redundant `verifyFilter` method since it simply calls the parent implementation:

```diff
export class JsonGeneralHandler extends GenericFieldHandler {
  // Rest of the implementation...

  protected parseJsonValue(val: any): {
    jsonVal: string;
    isValidJson: boolean;
  } {
    // Implementation remains the same...
    return { jsonVal, isValidJson };
  }

-  override async verifyFilter(filter: Filter, column: Column) {
-    return super.verifyFilter(filter, column);
-  }
}
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary><blockquote>

`38-114`: **Fix unreachable code comment and consider refactoring repeated conditionals**

The `filter` method contains a well-structured implementation for various comparison operators, but has a few issues:

1. Line 113 has an unreachable comment "// not implemented" that appears after the method has returned
2. There's significant repetition in the conditional checks for empty values (`val === '' || typeof val === 'undefined' || val === null`)


Apply these improvements:

```diff
  async filter(
    knex: CustomKnex,
    filter: Filter,
    column: Column,
    options: HandlerOptions,
  ) {
    const { alias } = options;
    const val = filter.value;
    const field = alias ? `${alias}.${column.column_name}` : column.column_name;

    return (qb: Knex.QueryBuilder) => {
      switch (filter.comparison_op) {
        // Implementation remains the same...
      }
    };
-   // not implemented
  }
```

Additionally, consider extracting the empty value check to a helper function to reduce repetition:

```typescript
const isEmpty = (val: any): boolean => val === '' || typeof val === 'undefined' || val === null;

// Then use it in the implementation
if (isEmpty(val)) {
  // handle empty case
} else {
  // handle non-empty case
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 604147ad492d031e4ec9291d20024bb147e4387f and 908d2abf9c423101bb8639afb5722da332919c8d.

</details>

<details>
<summary>📒 Files selected for processing (19)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (14)</summary>

* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
* packages/nocodb/src/db/field-handler/field-handler.interface.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (2)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (3) (3)</summary>

* `FieldHandlerInterface` (20-33)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1) (1)</summary>

* `getAs` (3-5)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1) (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1) (1)</summary>

* `filter` (9-111)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (11)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4) (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1) (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1) (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1) (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1) (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1) (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1) (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1) (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1) (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2) (2)</summary>

* `JsonPgHandler` (43-126)
* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1) (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary>

`13-37`: **Implementation of `select` method looks good**

The `select` method correctly builds the SQL select statement with proper sanitization of column names and aliases.

---

`116-123`: **Base implementation of `verifyFilter` is minimal but sufficient**

The `verifyFilter` method provides a simple default implementation that always returns `{ isValid: true }`. This is acceptable for a base class, as specific handlers can override it with their own validation logic.

</details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (3)</summary>

`11-11`: **Good addition of FieldHandler import**

Adding the import for the FieldHandler class is necessary for the upcoming implementation changes.

---

`46-50`: **Added filter verification using the FieldHandler**

This addition properly uses the FieldHandler to verify filters before applying them, which helps catch invalid filters early in the process.

---

`199-211`: **Good implementation of JSON filter handling**

The changes delegate JSON filter handling to the specialized field handler, which is a good design decision that improves modularity and maintainability.


The code now routes JSON field filtering through the FieldHandler system, which allows for specialized handling tailored to JSON data types, improving consistency and maintainability.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (2)</summary>

`319-319`: **: Replace `Promise<void | T>` with `Promise<T | undefined>` to clarify return types.**

As previously noted, mixing `void` in a union return type can be confusing. Replacing `void` with `undefined` clarifies the possible return values and improves type safety.

```diff
-    handler: <T>(filter: Filter) => Promise<void | T>,
+    handler: <T>(filter: Filter) => Promise<T | undefined>,
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

---

`263-275`: **Confirm fallback to `GenericFieldHandler` behavior.**

When no specialized handler is found, line 270 falls back to a `GenericFieldHandler`. This might produce unexpected filter behavior if the column’s UIDT actually requires special logic. Verify that this fallback is indeed the intended design.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T04:28:16Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (3)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary><blockquote>

`52-54`: _⚠️ Potential issue_

**Remove unreachable code.**

This return statement is unreachable as all code paths in the function return before reaching it. The switch statement handles all possible cases and returns from each case.

```diff
-    return {
-      isValid: true,
-    } as FilterVerificationResult;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (2)</summary><blockquote>

`157-159`: _⚠️ Potential issue_

**Handle the case when no handler is found.**

The `getHandler` method returns `undefined` if no handler is found, but this will cause a runtime error when calling methods on the undefined handler. Consider throwing an explicit error or falling back to a default handler.

```diff
if (!HandlerClass) {
-  return undefined;
+  return new GenericFieldHandler(); // Fall back to generic handler
}
```

---

`232-236`: _⚠️ Potential issue_

**Fix the query builder method usage in filter composition.**

The current implementation `qb[getLogicalOpMethod(handler.logicalOps)](qb)` passes the same query builder as a parameter to itself, which will lead to unexpected behavior. You need to call the handler function instead.

```diff
return (qb: Knex.QueryBuilder) => {
  for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
-    qb[getLogicalOpMethod(handler.logicalOps)](qb);
+    qb[getLogicalOpMethod(handler.logicalOps)]((subQb) => {
+      handler.handler(subQb);
+    });
  }
};
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary><blockquote>

`44-48`: **Remove the redundant case statement.**

The `FormulaDataTypes.STRING` case is redundant since it's followed by a default case that performs the same action. This creates unnecessary code.

```diff
-        case FormulaDataTypes.STRING:
        default: {
          column.uidt = UITypes.SingleLineText;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary><blockquote>

`317-320`: **Use `undefined` instead of `void` in the union type.**

Using `void` in a union type can be confusing as it doesn't clearly indicate that the function might not return a value.

```diff
async traverseFilters(
  filters: Filter[],
-  handler: <T>(filter: Filter) => Promise<void | T>,
+  handler: <T>(filter: Filter) => Promise<T | undefined>,
) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 908d2abf9c423101bb8639afb5722da332919c8d and 0c2f9b21ccdda8cab4ab04a1a84e60aaf8e1f77b.

</details>

<details>
<summary>📒 Files selected for processing (19)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (16)</summary>

* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
* packages/nocodb/src/db/field-handler/handlers/generic.ts
* packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
* packages/nocodb/src/db/field-handler/field-handler.interface.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (2)</summary>

<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

* `FieldHandler` (127-340)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (38-113)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: pre-build-for-playwright / playwright
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary>

`46-50`: **Good addition for verifying filters before processing.**

This is a solid improvement that verifies filters early in the process, helping to catch invalid filter conditions before they're applied to the query.

---

`199-211`: **Good implementation of JSON field handling.**

This code properly delegates JSON field filtering to the specialized handler in the FieldHandler class, which is a clean architectural approach to handle the complexities of JSON data filtering.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

`30-113`: **Good implementation of the handler registry.**

The registry structure is well-organized, mapping UI types to their corresponding handlers with support for database-specific implementations.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T06:19:19Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (4)</summary><blockquote>

`153-163`: _⚠️ Potential issue_

**Fix the handler retrieval to prevent potential runtime errors.**

The `getHandler` method currently returns `undefined` if no handler is found, which could lead to runtime exceptions when methods are called on the undefined handler.



Apply this fix to properly handle missing handlers:

```diff
private getHandler(
  uiType: UITypes,
  dbClient: ClientType,
): FieldHandlerInterface {
  const dbHandlers = HANDLER_REGISTRY[uiType];
  const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
  if (!HandlerClass) {
-    return undefined;
+    return new GenericFieldHandler();
  }
  return new HandlerClass();
}
```

---

`234-238`: _⚠️ Potential issue_

**Fix incorrect query builder usage in filter application.**

The current implementation passes the same query builder as a parameter to itself, which is likely not what was intended and may lead to unexpected query behavior.



Apply this fix to correctly apply the handler functions:

```diff
return (qb: Knex.QueryBuilder) => {
  for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
-    qb[getLogicalOpMethod(handler.logicalOps)](qb);
+    qb[getLogicalOpMethod(handler.logicalOps)](handler.handler);
  }
};
```

---

`248-262`: _⚠️ Potential issue_

**Fix incorrect access to database client type.**

The line `const dbClient = knex.client as ClientType;` attempts to access the client type incorrectly.



Apply this fix to ensure consistent client type access:

```diff
async applySelect(
  qb: Knex.QueryBuilder,
  column: Column,
  options: HandlerOptions = {},
): Promise<void> {
  const knex = options.knex ?? this.info.knex;
-  const dbClient = knex.client as ClientType;
+  const dbClient = knex.client.config.client as ClientType;
  const handler = this.getHandler(column.uidt, dbClient);
  return handler.select(qb, column, {
    knex,
    context: this.info.context,
    model: this.info.model,
    fieldHandler: this,
    ...options,
  });
}
```

---

`265-277`: _⚠️ Potential issue_

**Fix inconsistency in handler retrieval.**

The `verifyFilter` method accesses the client type differently than other methods.



Apply this fix to ensure consistent client type access:

```diff
async verifyFilter(
  filter: Filter,
  column: Column,
  options: HandlerOptions = {},
) {
  const knex = options.knex ?? this.info.knex;
  return (
-    this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+    this.getHandler(column.uidt, knex.client.config.client as ClientType) ?? new GenericFieldHandler()
  ).verifyFilter(filter, column, {
    fieldHandler: this,
    ...options,
  });
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (2)</summary><blockquote>

`29-35`: **Consolidate duplicated equality logic.**

`if (isValidJson) { ... } else { ... }` blocks repeat the same `JSON_UNQUOTE(??) = ?`. Consider merging them to reduce duplication.  

```diff
- if (isValidJson) {
-   qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
- } else {
-   qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
- }
+ qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
```

---

`48-55`: **Combine repetitive inequality blocks.**

Both `if (isValidJson)` and the `else` branch issue the same SQL expression for `'neq'`/`'not'`. Merging them simplifies maintenance.  

```diff
- if (isValidJson) {
-   nestedQb.where(
-     knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-   );
- } else {
-   nestedQb.where(
-     knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-   );
- }
+ nestedQb.where(
+   knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+ );
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary><blockquote>

`35-70`: **Comprehensive interface for field handling operations.**

The `IFieldHandler` interface extends the core functionality with a complete set of methods for applying and verifying filters in various scenarios. The promise-based design properly supports asynchronous database operations.

Consider adding more JSDoc comments to document the purpose and expected behavior of each method, especially for the more complex operations like `verifyFiltersSafe`.

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (2)</summary><blockquote>

`325-327`: **Clarify the comment about filter structure.**

The comment "somehow filters is an array of array" suggests uncertainty about the data structure. This should be clarified to improve code maintainability.


```diff
async traverseFilters(
  filters: Filter[],
  handler: <T>(filter: Filter) => Promise<void | T>,
) {
  return Promise.all(
    (filters ?? []).map(async (filter) => {
-      // somehow filters is an array of array
+      // Handle nested arrays of filters that can occur in complex query structures
      if (Array.isArray(filter)) {
        return this.traverseFilters(filter, handler);
      } else if (filter.is_group) {
```

---

`337-341`: **Consider enhancing `getRelatedColumnById` with additional context.**

As mentioned in the comment, this method could be enhanced to retrieve the model and basemodel as well for more comprehensive filter handling.


```diff
async getRelatedColumnById(context: NcContext, colId: string) {
  // possibly to be enhanced into getting the model and basemodel too
  const column = await Column.get(context, { colId });
+  // If needed, retrieve the associated model and basemodel
+  // const model = await column.getModel();
  return column;
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0c2f9b21ccdda8cab4ab04a1a84e60aaf8e1f77b and 0eb6c94c3e2583deb9772bb9352854ec0fc9f23a.

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (15)</summary>

* packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
* packages/nocodb/src/db/field-handler/handlers/generic.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (38-113)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary>

* `filter` (9-103)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (1)</summary>

* `filter` (9-109)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary>

* `GenericFieldHandler` (13-123)
* `filter` (38-113)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary>

* `filter` (9-103)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary>

* `HandlerOptions` (6-15)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

* `FieldHandler` (129-342)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (38-113)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (12)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2)</summary>

* `JsonPgHandler` (43-126)
* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (2)</summary>

* `JsonMySqlHandler` (8-135)
* `filter` (9-109)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests

</details>

<details>
<summary>🔇 Additional comments (14)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (2)</summary>

`44-44`: **Remove the redundant string case.**

The `case FormulaDataTypes.STRING:` block is unnecessary since the `default:` block handles the same logic.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

---

`52-54`: **Eliminate unreachable return statement.**

All code paths in `verifyFilter` return before reaching this statement; consider removing these lines.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (3)</summary>

`111-130`: **Validate potential string vs. JSON conflict.**

`parseJsonValue` treats any valid JSON string as JSON. If user input is a plain string containing valid JSON, it will be processed differently. Ensure that’s desired.

---

`132-134`: **Override usage is straightforward.**

Forwarding to `super.verifyFilter` is clear and appears consistent with the generic approach.

---

`61-86`: <details>
<summary>❓ Verification inconclusive</summary>

**Check consistency of case-sensitivity in LIKE operations.**

MySQL’s default behavior is case-insensitive for `LIKE`. Ensure the chosen behavior aligns with your requirements. If case sensitivity is needed, consider using `COLLATE` or `BINARY`.  

Please verify the desired string comparison behavior by reviewing functional tests or verifying user expectations in the documentation to confirm this is correct.

---

🏁 Script executed:

```shell
#!/bin/bash
# Searching for test coverage of MySQL's LIKE usage
rg --context 5 "like" | grep -i "mysql"
```

Length of output: 65574

---



</details>

**Action: Verify MySQL LIKE operation case-sensitivity behavior**  
The current implementation relies on MySQL’s default case-insensitive behavior in the LIKE and NOT LIKE operations. Please confirm—via your functional tests and/or documentation—that this behavior meets the intended requirements. If a case-sensitive comparison is needed, consider modifying the query to include an explicit collation (e.g., `COLLATE utf8mb4_bin`) or the `BINARY` keyword, ensuring consistent behavior between the `'like'` and `'nlike'` cases.

- Location: `packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts` (lines 61–86)

</details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary>

`46-50`: **Ensure robust error handling for filter verification.**

Verifying filters before parsing the condition is a good approach, but confirm that invalid conditions surface actionable errors if `throwErrorIfInvalid = true`.

---

`199-211`: **Seamlessly delegate JSON columns to FieldHandler.**

Routing filters to `applyFilter` for JSON columns centralizes logic and keeps the code clean. No immediate issues found.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (3)</summary>

`6-15`: **Well-structured options interface for handler configuration.**

The `HandlerOptions` interface provides a comprehensive set of optional properties that cover the necessary configuration scenarios for field handlers. This design allows for flexible usage across different contexts while maintaining type safety.

---

`16-19`: **Effective result structure for validation.**

The `FilterVerificationResult` interface provides a clean way to return both validation status and error messages, making error handling and reporting more robust.

---

`20-33`: **Good separation of core field handler responsibilities.**

The `FieldHandlerInterface` clearly defines the three primary responsibilities for field handlers: selecting data, filtering data, and verifying filters. The method signatures use appropriate parameter and return types that align with the Knex query builder pattern.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (4)</summary>

`31-115`: **Well-organized handler registry with client-specific implementations.**

The `HANDLER_REGISTRY` provides a clear mapping between UI types, database clients, and their corresponding handler implementations. This design enables flexibility and extensibility for supporting various field types across different database systems.

---

`138-144`: **Good factory method for creating handlers from base models.**

The `fromBaseModel` static method provides a convenient way to create field handlers from existing base models, simplifying integration with the existing codebase.

---

`173-190`: **Good implementation of filter application with proper context passing.**

The `applyFilter` method correctly retrieves the appropriate handler based on both UI type and database client, and passes all necessary context to the handler. The fallback to find the column by ID is also a useful feature.

---

`319-335`: **Address the confusing `void` union type.**

Per the static analysis hint, returning `Promise<void | T>` can be replaced by `Promise<T | undefined>` for clarity.



```diff
async traverseFilters(
  filters: Filter[],
-  handler: <T>(filter: Filter) => Promise<void | T>,
+  handler: <T>(filter: Filter) => Promise<T | undefined>,
) {
  return Promise.all(
    (filters ?? []).map(async (filter) => {
      // somehow filters is an array of array
      if (Array.isArray(filter)) {
        return this.traverseFilters(filter, handler);
      } else if (filter.is_group) {
        return this.traverseFilters(filter.children, handler);
      } else {
        return handler(filter);
      }
    }),
  );
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T06:46:59Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary><blockquote>

`52-54`: _⚠️ Potential issue_

**Remove unreachable code**

The return statement at lines 52-54 is unreachable since all code paths in the function return before reaching it.

```diff
  override async verifyFilter(
    filter: Filter,
    column: Column,
    options: HandlerOptions = {},
  ) {
    const uidt = parseProp(column.meta).display_type;
    if (uidt) {
      column.uidt = uidt;
      return options.fieldHandler.verifyFilter(filter, column, options);
    } else {
      const formulaCol = await column.getColOptions<FormulaColumn>(
        options.context,
      );
      const parsedTree = await formulaCol.getParsedTree();
      const dataType = parsedTree.dataType;
      switch (dataType) {
        // cases...
      }
    }

-   return {
-     isValid: true,
-   } as FilterVerificationResult;
  }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary><blockquote>

`44-49`: **Remove redundant case statement**

The `FormulaDataTypes.STRING` case at line 44 is redundant since it's followed by a default case that performs the same actions.

```diff
      switch (dataType) {
        case FormulaDataTypes.BOOLEAN: {
          column.uidt = UITypes.Checkbox;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.DATE: {
          column.uidt = UITypes.DateTime;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.INTERVAL: {
          column.uidt = UITypes.Time;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.NUMERIC: {
          column.uidt = UITypes.Decimal;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
-       case FormulaDataTypes.STRING:
        default: {
          column.uidt = UITypes.SingleLineText;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
      }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (3)</summary><blockquote>

`29-35`: **Simplify redundant code in JSON equality check**

The conditional branches for valid and invalid JSON both use the same raw SQL query. This redundancy can be simplified by removing the condition.

```diff
          } else {
-           const { jsonVal, isValidJson } = this.parseJsonValue(val);
-           if (isValidJson) {
-             qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
-           } else {
-             qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
-           }
+           const { jsonVal } = this.parseJsonValue(val);
+           qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
          }
```

---

`45-56`: **Simplify redundant code in JSON inequality check**

Similar to the equality case, the conditional branches for valid and invalid JSON both use the same raw SQL query. This redundancy can be simplified.

```diff
            const { jsonVal, isValidJson } = this.parseJsonValue(val);
            qb.where((nestedQb) => {
-             if (isValidJson) {
-               nestedQb.where(
-                 knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-               );
-             } else {
-               nestedQb.where(
-                 knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-               );
-             }
+             nestedQb.where(
+               knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+             );
              nestedQb.orWhereNull(field);
            });
```

---

`132-134`: **Remove unnecessary override that simply calls super**

The `verifyFilter` method is overridden just to call the parent's implementation without adding any functionality. This is unnecessary and can be removed.

```diff
-  override async verifyFilter(filter: Filter, column: Column) {
-    return super.verifyFilter(filter, column);
-  }
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary><blockquote>

`6-15`: **Consider documenting interface fields.**  
Providing brief JSDoc comments for each optional property in `HandlerOptions` would clarify their intended usage and improve maintainability.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0eb6c94c3e2583deb9772bb9352854ec0fc9f23a and d25a432dacc7695b78e7df65a5c9803511f3ca89.

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (14)</summary>

* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/generic.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary>

* `GenericFieldHandler` (13-123)
* `filter` (38-113)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary>

* `filter` (9-103)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary>

* `HandlerOptions` (6-15)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

* `FieldHandler` (129-342)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (38-113)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (38-113)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (1)</summary>

* `filter` (9-103)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (1)</summary>

* `filter` (9-109)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (13)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2)</summary>

* `JsonPgHandler` (43-126)
* `filter` (44-125)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (2)</summary>

* `JsonMySqlHandler` (8-135)
* `filter` (9-109)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (2)</summary>

* `JsonGeneralHandler` (8-129)
* `filter` (9-103)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: unit-tests
* GitHub Check: release-docker / buildx
* GitHub Check: unit-tests-pg

</details>

<details>
<summary>🔇 Additional comments (22)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

`8-25`: **Add 'in' to supportedOperations to align with its usage**

You check for `filter.comparison_op === 'in'` at lines 41-42, but 'in' is missing from the `supportedOperations` array. Add it to ensure consistency.


```diff
    const supportedOperations = [
      'eq',
      'neq',
      'not',
      'empty',
      'notempty',
      'null',
      'notnull',
      'blank',
      'notblank',
      'gt',
      'lt',
      'gte',
      'lte',
      'isnot',
      'is',
      'isWithin',
+     'in',
    ];
```

</details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary>

`46-50`: **Good implementation of filter verification**

The addition of filter verification using the `FieldHandler` class is a good practice to validate filters before processing them. This will help catch invalid filters early in the process.

---

`199-211`: **Well-structured JSON handler integration**

The integration of the JSON field handler through `FieldHandler.applyFilter` is well implemented. This approach provides a cleaner separation of concerns and makes the code more maintainable by delegating JSON field handling to specialized handlers.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

`1-5`: **Good organization of imports.**  
These imports reference external types from various modules. The structure is clear and avoids unnecessary clutter.

---

`16-19`: **Interface is straightforward and clear.**  
`FilterVerificationResult` neatly captures the validation outcome and possible error messages.

---

`20-33`: **Well-defined contract for implementers.**  
`FieldHandlerInterface` method signatures (`select`, `filter`, `verifyFilter`) are cohesive and intuitive.

---

`35-70`: **Interface `IFieldHandler` follows consistent design.**  
These extended methods (`applyFilter`, `applyFilters`, `applySelect`, `verifyFilter`, etc.) effectively form a higher-level abstraction for database operations. No immediate issues found.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (15)</summary>

`1-15`: **Imports are comprehensive and consistent.**  
All the required handler classes, error types, and type declarations are imported successfully for the registry and class definitions.

---

`16-22`: **Type and interface imports look properly structured.**  
Type imports for `FilterVerificationResult`, `HandlerOptions`, `IFieldHandler`, etc., ensure clear type usage.

---

`29-115`: **Handler registry is well-structured.**  
Associating each `UITypes` constant with a specific handler class or `_default` fallback is a clean approach for dynamic field handling.

---

`117-128`: **Logical operation method is neatly mapped.**  
Switching on `logical_op` is straightforward, covering common operation cases (`orWhere`, `andWhere`, etc.).

---

`129-136`: **Constructor aligns object references correctly.**  
Injecting `model`, `knex`, and `context` fosters reusability.

---

`138-144`: **Factory method `fromBaseModel` is clear.**  
This static factory yields a `FieldHandler` given a base model, simplifying instantiation.

---

`146-163`: **Handle undefined return value for no matching handler.**  
Line 160 simply returns `undefined` rather than throwing or providing a fallback. If the caller does not guard against `undefined`, this can lead to runtime errors.

---

`165-190`: **Logic for applying a filter is coherent.**  
The method merges `options` with context, model, etc., before calling the selected handler. Apart from the undefined handler concern mentioned above, everything looks accurate.

---

`192-239`: **Likely misuse of query builder in line 236.**  
Invoking `qb[getLogicalOpMethod(handler.logicalOps)](qb);` appears to pass the same query builder back to itself rather than the handler’s function. This might be a no-op or lead to confusion.

---

`241-263`: **Knex client type usage is inconsistent.**  
Line 254 uses `knex.client as ClientType` instead of `knex.client.config.client as ClientType`, potentially causing misidentification of the underlying DB client.

---

`265-277`: **Knex client mismatch in `verifyFilter`.**  
Line 272 references `knex.client` without `.config.client`, which can lead to inconsistent behavior versus other sections that access `knex.client.config.client`.

---

`279-309`: **Safe verification flow is thorough.**  
`verifyFiltersSafe` aggregates all validation results before returning. This robust approach helps gather multiple errors at once.

---

`311-317`: **Throwing `FilterVerificationError` upon invalid results is appropriate.**  
Ensures that calling code clearly distinguishes handled vs. unhandled filter validation issues.

---

`319-335`: **Union type with `void` can cause confusion.**  
Line 321 uses `Promise<void | T>`. This was flagged by static analysis to consider `Promise<T | undefined>` for clearer semantics.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

---

`337-341`: **Retrieving related column is straightforward.**  
Leverages `Column.get` with a `colId` param for consistent data lookups.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T06:58:37Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1)</summary><blockquote>

`7-9`: **Consider whether whitespace-only strings should count as value.**

Currently, `ncIsStringHasValue()` returns `true` for strings like `" "` because it only checks for an empty string, `null`, or `undefined`. If the intent is to treat whitespace-only strings as empty, enhance the function to trim the input.

```diff
-export function ncIsStringHasValue(val: string | undefined | null) {
-  return val !== '' && !ncIsUndefined(val) && !ncIsNull(val);
+export function ncIsStringHasValue(val: string | undefined | null) {
+  if (ncIsUndefined(val) || ncIsNull(val)) return false;
+  return val.trim() !== '';
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d25a432dacc7695b78e7df65a5c9803511f3ca89 and 0a4ecc617bbecda159ae800de7dd527f65e1dc15.

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (16)</summary>

* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/generic.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
* packages/nocodb/src/db/field-handler/field-handler.interface.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (2)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (13)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2)</summary>

* `JsonPgHandler` (44-127)
* `filter` (45-126)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (2)</summary>

* `JsonMySqlHandler` (9-136)
* `filter` (10-110)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (2)</summary>

* `JsonGeneralHandler` (9-130)
* `filter` (10-104)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

* `FieldHandler` (129-342)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (41-118)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: pre-build-for-playwright / playwright
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1)</summary>

`4-6`: **Looks good—well-defined fallback mechanism.**

The `getAs()` function cleanly returns `column.asId` if present or defaults to `column.id`. No further issues identified.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary>

`44-56`: **Remove the redundant `STRING` case and unreachable return statement.**  
This was already flagged in a past review comment. Please remove the `case FormulaDataTypes.STRING:` since it's identical to the `default` block, and remove the unreachable lines 52–54 where you return `{ isValid: true }`.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (3)</summary>

`11-11`: **No issues with the new import.**

Importing `FieldHandler` looks correct, and there's no conflict with existing imports.

---

`46-50`: **Validate no edge cases are missed.**

Verifying a single filter or an array of filters here is straightforward. However, if `conditionObj` is empty or invalid, the function silently returns in line 44. Confirm that this behavior is intended and covered by upstream logic.

Do you want an additional check or exception if `conditionObj` is unexpectedly empty?

---

`199-211`: **Good approach for JSON-type columns.**

Calling `applyFilter` on JSON columns centralizes the logic in `FieldHandler`. This remains consistent with the rest of the code. No further issues identified.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (3)</summary>

`153-163`: **Prevent runtime errors by handling undefined handler properly.**

This was previously flagged. If `HandlerClass` is not found, `getHandler()` returns `undefined`, which can cause a runtime error later in `handler.filter`. Consider throwing an error or providing a fallback handler instead of returning `undefined`.

---

`235-237`: **Fix misuse of the Knex query builder method.**

This was previously flagged. Passing `qb` to itself (e.g., `qb[getLogicalOpMethod(handler.logicalOps)](qb)`) can lead to a no-op or unintended behavior. Wrap the filter function in a callback, or apply it directly to avoid recursion or null effects.

---

`319-321`: **Use `undefined` instead of `void` in the union type.**

This was addressed in a previous comment. Changing `Promise<void | T>` to `Promise<T | undefined>` clarifies the return type and avoids confusion.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T08:26:55Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (7)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (2)</summary><blockquote>

`44-45`: _⚠️ Potential issue_

**Fix unnecessary case clause.**

The `FormulaDataTypes.STRING` case is redundant since it's followed by a default case that does the same thing.



```diff
-        case FormulaDataTypes.STRING:
        default: {
          column.uidt = UITypes.SingleLineText;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

---

`52-54`: _⚠️ Potential issue_

**Remove unreachable code.**

This return statement is unreachable since all code paths in the function return before reaching it.



```diff
-    return {
-      isValid: true,
-    } as FilterVerificationResult;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary><blockquote>

`8-25`: _⚠️ Potential issue_

**Add 'in' to supportedOperations list.**

You check for `(filter.comparison_op === 'in' && Array.isArray(filter.value))` at lines 41-42, yet `'in'` is absent from `supportedOperations`. Add it to maintain consistency.



```diff
 const supportedOperations = [
   'eq',
   'neq',
   'not',
   'empty',
   'notempty',
   'null',
   'notnull',
   'blank',
   'notblank',
   'gt',
   'lt',
   'gte',
   'lte',
   'isnot',
   'is',
   'isWithin',
+  'in',
 ];
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (4)</summary><blockquote>

`265-277`: _⚠️ Potential issue_

**Fix inconsistency in database client access in verifyFilter.**

Similar to the issue in `applySelect`, you're accessing `knex.client` directly in `verifyFilter` instead of `knex.client.config.client`.


```diff
async verifyFilter(
  filter: Filter,
  column: Column,
  options: HandlerOptions = {},
) {
  const knex = options.knex ?? this.info.knex;
  return (
-    this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+    this.getHandler(column.uidt, knex.client.config.client as ClientType) ?? new GenericFieldHandler()
  ).verifyFilter(filter, column, {
    fieldHandler: this,
    ...options,
  });
}
```

---

`248-263`: _⚠️ Potential issue_

**Fix inconsistency in database client access.**

In `applySelect`, you're accessing `knex.client` directly, but in `applyFilter` you use `knex.client.config.client`. This inconsistency could lead to errors.


```diff
async applySelect(
  qb: Knex.QueryBuilder,
  column: Column,
  options: HandlerOptions = {},
): Promise<void> {
  const knex = options.knex ?? this.info.knex;
-  const dbClient = knex.client as ClientType;
+  const dbClient = knex.client.config.client as ClientType;
  const handler = this.getHandler(column.uidt, dbClient);
  return handler.select(qb, column, {
    knex,
    context: this.info.context,
    model: this.info.model,
    fieldHandler: this,
    ...options,
  });
}
```

---

`234-238`: _⚠️ Potential issue_

**Fix incorrect query builder method usage in filter composition.**

Calling `qb[getLogicalOpMethod(handler.logicalOps)](qb)` passes the same query builder as a parameter to itself, which can lead to recursive or no-op conditions.


```diff
return (qb: Knex.QueryBuilder) => {
  for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
-    qb[getLogicalOpMethod(handler.logicalOps)](qb);
+    qb[getLogicalOpMethod(handler.logicalOps)](handler.handler);
  }
};
```

---

`153-163`: _⚠️ Potential issue_

**Handle potential undefined handler.**

The `getHandler` method currently returns `undefined` if no handler is found for a specific UI type and database client. This could lead to runtime errors if the handler is directly used without checking for undefined.


```diff
private getHandler(
  uiType: UITypes,
  dbClient: ClientType,
): FieldHandlerInterface {
  const dbHandlers = HANDLER_REGISTRY[uiType];
  const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
  if (!HandlerClass) {
-    return undefined;
+    return new GenericFieldHandler(); // Fall back to generic handler
  }
  return new HandlerClass();
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (3)</summary><blockquote>

`30-36`: **Simplify redundant conditional logic.**

The same operation is performed regardless of whether `isValidJson` is true or false, making the conditional check unnecessary.


```diff
-            const { jsonVal, isValidJson } = this.parseJsonValue(val);
-            if (isValidJson) {
-              qb.where(knex.raw('?? = ?', [field, jsonVal]));
-            } else {
-              qb.where(knex.raw('?? = ?', [field, jsonVal]));
-            }
+            const { jsonVal } = this.parseJsonValue(val);
+            qb.where(knex.raw('?? = ?', [field, jsonVal]));
```

---

`46-53`: **Simplify redundant conditional logic in neq/not case.**

The same operation is performed regardless of whether `isValidJson` is true or false, making the conditional check unnecessary.


```diff
-            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            const { jsonVal } = this.parseJsonValue(val);
             qb.where((nestedQb) => {
-              if (isValidJson) {
-                nestedQb.where(knex.raw('?? != ?', [field, jsonVal]));
-              } else {
-                nestedQb.where(knex.raw('?? != ?', [field, jsonVal]));
-              }
+              nestedQb.where(knex.raw('?? != ?', [field, jsonVal]));
               nestedQb.orWhereNull(field);
             });
```

---

`127-129`: **Remove unnecessary method override.**

The `verifyFilter` method simply calls the parent class's implementation without adding any functionality. This override is unnecessary and can be removed.


```diff
-  override async verifyFilter(filter: Filter, column: Column) {
-    return super.verifyFilter(filter, column);
-  }
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (3)</summary><blockquote>

`30-36`: **Simplify redundant conditional logic.**

The same operation is performed regardless of whether `isValidJson` is true or false, making the conditional check unnecessary.


```diff
-            const { jsonVal, isValidJson } = this.parseJsonValue(val);
-            if (isValidJson) {
-              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
-            } else {
-              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
-            }
+            const { jsonVal } = this.parseJsonValue(val);
+            qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
```

---

`46-57`: **Simplify redundant conditional logic in neq/not case.**

The same operation is performed regardless of whether `isValidJson` is true or false, making the conditional check unnecessary.


```diff
-            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            const { jsonVal } = this.parseJsonValue(val);
             qb.where((nestedQb) => {
-              if (isValidJson) {
-                nestedQb.where(
-                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-                );
-              } else {
-                nestedQb.where(
-                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
-                );
-              }
+              nestedQb.where(
+                knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+              );
               nestedQb.orWhereNull(field);
             });
```

---

`133-135`: **Remove unnecessary method override.**

The `verifyFilter` method simply calls the parent class's implementation without adding any functionality. This override is unnecessary and can be removed.


```diff
-  override async verifyFilter(filter: Filter, column: Column) {
-    return super.verifyFilter(filter, column);
-  }
```

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (2)</summary><blockquote>

`319-335`: **Address the confusing `void` union type.**

The static analysis tool indicates that using `void` in a union type can be confusing. Consider using `undefined` instead for better type clarity.


```diff
async traverseFilters(
  filters: Filter[],
-  handler: <T>(filter: Filter) => Promise<void | T>,
+  handler: <T>(filter: Filter) => Promise<undefined | T>,
) {
  return Promise.all(
    (filters ?? []).map(async (filter) => {
      // somehow filters is an array of array
      if (Array.isArray(filter)) {
        return this.traverseFilters(filter, handler);
      } else if (filter.is_group) {
        return this.traverseFilters(filter.children, handler);
      } else {
        return handler(filter);
      }
    }),
  );
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

---

`325-327`: **Clarify comment about filter array structure.**

The comment "somehow filters is an array of array" suggests uncertainty about the data structure. It would be helpful to clarify this comment to better explain the expected data structure and why this check is necessary.


```diff
-      // somehow filters is an array of array
+      // Handle case where filters might be nested arrays from complex query structures
       if (Array.isArray(filter)) {
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0a4ecc617bbecda159ae800de7dd527f65e1dc15 and acd031410b7c33e315e15c2512d1545f246f43e9.

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts` (1 hunks)
* `packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` (4 hunks)
* `packages/nocodb/src/db/conditionV2.ts` (3 hunks)
* `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/field-handler.interface.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/generic.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/index.ts` (1 hunks)
* `packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (14)</summary>

* packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts
* packages/nocodb/src/db/field-handler/error/filter-verification.error.ts
* packages/nocodb/src/db/field-handler/utils/handlerUtils.ts
* packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts
* packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
* packages/nocodb/src/db/field-handler/handlers/generic.ts
* packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/single-line-text/single-line-text.general.handler.ts
* packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts
* packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts
* packages/nocodb/src/db/field-handler/field-handler.interface.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (4)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary>

* `GenericFieldHandler` (16-128)
* `filter` (41-118)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (1)</summary>

* `filter` (45-126)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary>

* `HandlerOptions` (6-15)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1)</summary>

* `ncIsStringHasValue` (7-9)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (3)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (2)</summary>

* `GenericFieldHandler` (16-128)
* `filter` (41-118)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (1)</summary>

* `HandlerOptions` (6-15)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/utils/handlerUtils.ts (1)</summary>

* `ncIsStringHasValue` (7-9)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (1)</summary>

* `FieldHandler` (129-342)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/generic.ts (1)</summary>

* `filter` (41-118)

</details>

</blockquote></details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (13)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/field-handler/field-handler.interface.ts (4)</summary>

* `FieldHandlerInterface` (20-33)
* `IFieldHandler` (35-70)
* `HandlerOptions` (6-15)
* `FilterVerificationResult` (16-19)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/checkbox/checkbox.general.handler.ts (1)</summary>

* `CheckboxGeneralHandler` (5-41)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/multi-select/multi-select.general.handler.ts (1)</summary>

* `MultiSelectGeneralHandler` (5-11)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/single-select/single-select.general.handler.ts (1)</summary>

* `SingleSelectGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date/date.general.handler.ts (1)</summary>

* `DateGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/number/number.general.handler.ts (1)</summary>

* `NumberGeneralHandler` (3-3)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/decimal/decimal.general.handler.ts (1)</summary>

* `DecimalGeneralHandler` (6-55)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts (1)</summary>

* `FormulaGeneralHandler` (11-56)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts (1)</summary>

* `DateTimeGeneralHandler` (6-61)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.pg.handler.ts (2)</summary>

* `JsonPgHandler` (44-127)
* `filter` (45-126)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts (2)</summary>

* `JsonMySqlHandler` (9-136)
* `filter` (10-110)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/handlers/json/json.general.handler.ts (2)</summary>

* `JsonGeneralHandler` (9-130)
* `filter` (10-104)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/error/filter-verification.error.ts (1)</summary>

* `FilterVerificationError` (1-5)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts</summary>

[error] 321-321: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: release-docker / buildx
* GitHub Check: pre-build-for-playwright / playwright
* GitHub Check: unit-tests-pg
* GitHub Check: unit-tests
* GitHub Check: jest-unit-test

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>packages/nocodb/src/db/conditionV2.ts (2)</summary>

`46-50`: **Good addition of filter verification before processing.**

This is an excellent addition that ensures filters are validated before being processed, which can help catch issues early and prevent potential errors in query execution.

---

`199-211`: **Well-implemented JSON field handling using the new FieldHandler system.**

The special handling for JSON fields is a good approach, replacing the previous generic implementation with a more specialized one. This ensures that JSON filters are properly processed based on the database client type.

</details>
<details>
<summary>packages/nocodb/src/db/field-handler/index.ts (5)</summary>

`31-115`: **Good implementation of the handler registry with client-specific handlers.**

The `HANDLER_REGISTRY` is well-structured and provides a comprehensive mapping of UI types to their corresponding field handlers. The use of a default handler (`CLIENT_DEFAULT`) for each UI type is a good fallback strategy.

---

`117-128`: **LGTM: Well-structured logical operator mapping.**

The `getLogicalOpMethod` function provides a clear and concise mapping between logical operators and Knex query builder methods.

---

`129-144`: **LGTM: Good factory method pattern for creating FieldHandler instances.**

The `fromBaseModel` static method is a nice, clean factory approach for creating FieldHandler instances from BaseModelSqlV2 objects.

---

`173-190`: **LGTM: Comprehensive filter application method.**

The `applyFilter` method provides a robust way to apply filters to query builders, correctly handling columns and options.

---

`337-341`: **LGTM: Simple and effective column retrieval method.**

The `getRelatedColumnById` method provides a straightforward way to retrieve columns by ID.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T09:06:10Z
user: login: fendy3002
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-25T09:55:57Z

Review comments:
diff_hunk: @@ -0,0 +1,20 @@
+import { ILexingError, IRecognitionException } from "chevrotain";
+import { NcSDKError } from "../errorUtils";
+export interface InvalidFilterErrorInfo {
+  lexingError?: ILexingError[];
+  parsingError?: IRecognitionException[],
+  message?: string;
+};
+export class InvalidFilterError extends NcSDKError {
+  constructor(public readonly info: InvalidFilterErrorInfo) {
+    let message = info.message ?? 'INVALID_FILTER';
+    if(info.lexingError) {
+      const lexingErrorMessage = info.lexingError.map(k => k.message).join(', ');
+      message = `${message} lexing_error: ${lexingErrorMessage}`;
+    } else if(info.parsingError) {
+      const parsingErrorMessage = info.parsingError.map(k => k.message);
+      message = `${message} parsing_error: ${parsingErrorMessage}`;
+    }
+    super(message)
+  }
path: packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix parsing error message formatting.**

The error construction logic has inconsistent handling between lexing and parsing errors. While lexing errors are properly joined with commas, parsing errors are not joined and will be implicitly converted to a string, which might result in unexpected formatting.


Apply this fix:

```diff
- const parsingErrorMessage = info.parsingError.map(k => k.message);
- message = `${message} parsing_error: ${parsingErrorMessage}`;
+ const parsingErrorMessage = info.parsingError.map(k => k.message).join(', ');
+ message = `${message} parsing_error: ${parsingErrorMessage}`;
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export class InvalidFilterError extends NcSDKError {
  constructor(public readonly info: InvalidFilterErrorInfo) {
    let message = info.message ?? 'INVALID_FILTER';
    if (info.lexingError) {
      const lexingErrorMessage = info.lexingError.map(k => k.message).join(', ');
      message = `${message} lexing_error: ${lexingErrorMessage}`;
    } else if (info.parsingError) {
      const parsingErrorMessage = info.parsingError.map(k => k.message).join(', ');
      message = `${message} parsing_error: ${parsingErrorMessage}`;
    }
    super(message)
  }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-19T07:00:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+import { parseDateTimeValue } from 'nocodb-sdk';
+import type { FilterVerificationResult } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class DateTimeGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(filter: Filter, column: Column) {
+    const supportedOperations = [
+      'eq',
+      'neq',
+      'not',
+      'empty',
+      'notempty',
+      'null',
+      'notnull',
+      'blank',
+      'notblank',
+      'gt',
+      'lt',
+      'gte',
+      'lte',
+      'isnot',
+      'is',
+      'isWithin',
+    ];
path: packages/nocodb/src/db/field-handler/handlers/date-time/date-time.general.handler.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Consider aligning `in` checks with supported operations.**

You check for `(filter.comparison_op === 'in' && Array.isArray(filter.value))` at lines 41–42, yet `'in'` is absent from `supportedOperations`. If `'in'` should be a valid operator, add it to `supportedOperations`. Otherwise, remove that check to prevent confusion.

```diff
 const supportedOperations = [
-  'eq', 'neq', 'not', 'empty', 'notempty', 
-  'null', 'notnull', 'blank', 'notblank',
-  'gt', 'lt', 'gte', 'lte', 'isnot', 'is', 'isWithin'
+  // If 'in' is truly supported, add it:
+  'eq', 'neq', 'not', 'empty', 'notempty',
+  'null', 'notnull', 'blank', 'notblank',
+  'gt', 'lt', 'gte', 'lte', 'isnot', 'is', 'isWithin',
+  'in'
 ];
```
Alternatively, remove the `in` check if it is not supported.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  override async verifyFilter(filter: Filter, column: Column) {
    const supportedOperations = [
      // If 'in' is truly supported, add it:
      'eq',
      'neq',
      'not',
      'empty',
      'notempty',
      'null',
      'notnull',
      'blank',
      'notblank',
      'gt',
      'lt',
      'gte',
      'lte',
      'isnot',
      'is',
      'isWithin',
      'in'
    ];
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits cd210ac to 908d2ab
created_at: 2025-03-19T07:00:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,340 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
+    return new HandlerClass();
+  }
+
+  /**
+   * Applies a filter to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param filter - Filter object
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applyFilter(
+    filter: Filter,
+    column?: Column,
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client.config.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    const useColumn =
+      column ?? this.info.model.columns.find((col) => col.id === filter.id);
+    return handler.filter(knex, filter, useColumn, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async applyFilterGroup(filter: Filter, options: HandlerOptions = {}) {
+    return this.applyFilters(filter.children, options);
+  }
+
+  async applyFilters(
+    filters: Filter[],
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const qbHandlers: {
+      handler: (qb: Knex.QueryBuilder) => void;
+      index: number;
+      logicalOps?: string;
+    }[] = [];
+    let index = 0;
+    for (const filter of filters) {
+      if (filter.is_group) {
+        qbHandlers.push({
+          handler: await this.applyFilterGroup(filter, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      } else {
+        let column = model.columns.find(
+          (col) => col.id === filter.fk_column_id,
+        );
+        if (!column) {
+          column = await this.getRelatedColumnById(
+            options.context,
+            filter.fk_column_id,
+          );
+        }
+        qbHandlers.push({
+          handler: await this.applyFilter(filter, column, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      }
+    }
+    return (qb: Knex.QueryBuilder) => {
+      for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
+        qb[getLogicalOpMethod(handler.logicalOps)](qb);
+      }
+    };
+  }
+
+  /**
+   * Applies a select operation to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applySelect(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options: HandlerOptions = {},
+  ): Promise<void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    return handler.select(qb, column, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async verifyFilter(
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions = {},
+  ) {
+    const knex = options.knex ?? this.info.knex;
+    return (
+      this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+    ).verifyFilter(filter, column, {
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async verifyFiltersSafe(filters: Filter[], options: HandlerOptions = {}) {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const traverseResult: FilterVerificationResult[] = [];
+    await this.traverseFilters(filters, async (filter: Filter) => {
+      let column = model.columns.find((col) => col.id === filter.fk_column_id);
+      if (!column) {
+        column = await this.getRelatedColumnById(
+          options.context,
+          filter.fk_column_id,
+        );
+      }
+      traverseResult.push(await this.verifyFilter(filter, column, options));
+    });
+    const invalidFilterResults = traverseResult
+      .filter((k) => !k.isValid)
+      .reduce(
+        (obj, res) => {
+          obj.errors = obj.errors.concat(res.errors);
+          return obj;
+        },
+        { isValid: false, errors: [] } as FilterVerificationResult,
+      );
+    if (invalidFilterResults.errors?.length > 0) {
+      return invalidFilterResults;
+    } else {
+      return { isValid: true } as FilterVerificationResult;
+    }
+  }
+
+  async verifyFilters(filters: Filter[], options: HandlerOptions = {}) {
+    const verificationResult = await this.verifyFiltersSafe(filters, options);
+    if (!verificationResult.isValid) {
+      throw new FilterVerificationError(verificationResult.errors!);
+    }
+    return true;
+  }
+
+  async traverseFilters(
+    filters: Filter[],
+    handler: <T>(filter: Filter) => Promise<void | T>,
+  ) {
+    return Promise.all(
+      (filters ?? []).map(async (filter) => {
+        // somehow filters is an array of array
+        if (Array.isArray(filter)) {
+          return this.traverseFilters(filter, handler);
+        } else if (filter.is_group) {
+          return this.traverseFilters(filter.children, handler);
+        } else {
+          return handler(filter);
+        }
+      }),
+    );
+  }
path: packages/nocodb/src/db/field-handler/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Address the confusing `void` union type.**

Per the static analysis hint, returning `Promise<void | T>` can be replaced by `Promise<T | undefined>` for clarity. This improves type-safety and eliminates ambiguity around potential return values.  

```diff
- handler: <T>(filter: Filter) => Promise<void | T>,
+ handler: <T>(filter: Filter) => Promise<T | undefined>,
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  async traverseFilters(
    filters: Filter[],
    handler: <T>(filter: Filter) => Promise<T | undefined>,
  ) {
    return Promise.all(
      (filters ?? []).map(async (filter) => {
        // somehow filters is an array of array
        if (Array.isArray(filter)) {
          return this.traverseFilters(filter, handler);
        } else if (filter.is_group) {
          return this.traverseFilters(filter.children, handler);
        } else {
          return handler(filter);
        }
      }),
    );
  }
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 319-319: void is confusing inside a union type.

Unsafe fix: Use undefined instead.


(lint/suspicious/noConfusingVoidType)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits cd210ac to 908d2ab
created_at: 2025-03-19T07:00:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,56 @@
+import { FormulaDataTypes, parseProp } from 'nocodb-sdk';
+import { UITypes } from 'nocodb-sdk';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+} from '~/db/field-handler/field-handler.interface';
+import type { FormulaColumn } from '~/models';
+import { type Column, type Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+
+export class FormulaGeneralHandler extends GenericFieldHandler {
+  override async verifyFilter(
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions = {},
+  ) {
+    const uidt = parseProp(column.meta).display_type;
+    if (uidt) {
+      column.uidt = uidt;
+      return options.fieldHandler.verifyFilter(filter, column, options);
+    } else {
+      const formulaCol = await column.getColOptions<FormulaColumn>(
+        options.context,
+      );
+      const parsedTree = await formulaCol.getParsedTree();
+      const dataType = parsedTree.dataType;
+      switch (dataType) {
+        case FormulaDataTypes.BOOLEAN: {
+          column.uidt = UITypes.Checkbox;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.DATE: {
+          column.uidt = UITypes.DateTime;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.INTERVAL: {
+          column.uidt = UITypes.Time;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.NUMERIC: {
+          column.uidt = UITypes.Decimal;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+        case FormulaDataTypes.STRING:
+        default: {
+          column.uidt = UITypes.SingleLineText;
+          return options.fieldHandler.verifyFilter(filter, column, options);
+        }
+      }
+    }
+
+    return {
+      isValid: true,
+    } as FilterVerificationResult;
+  }
+}
path: packages/nocodb/src/db/field-handler/handlers/formula/formula.general.handler.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix unreachable code and simplify case statement**

This implementation of `FormulaGeneralHandler` has two important issues:

1. The return statement at lines 52-54 is unreachable as all code paths in the function return before reaching it
2. The `FormulaDataTypes.STRING` case at line 44 is redundant since it's followed by a default case that does the same thing


Apply this fix to remove the unreachable code and simplify the switch statement:

```diff
export class FormulaGeneralHandler extends GenericFieldHandler {
  override async verifyFilter(
    filter: Filter,
    column: Column,
    options: HandlerOptions = {},
  ) {
    const uidt = parseProp(column.meta).display_type;
    if (uidt) {
      column.uidt = uidt;
      return options.fieldHandler.verifyFilter(filter, column, options);
    } else {
      const formulaCol = await column.getColOptions<FormulaColumn>(
        options.context,
      );
      const parsedTree = await formulaCol.getParsedTree();
      const dataType = parsedTree.dataType;
      switch (dataType) {
        case FormulaDataTypes.BOOLEAN: {
          column.uidt = UITypes.Checkbox;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.DATE: {
          column.uidt = UITypes.DateTime;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.INTERVAL: {
          column.uidt = UITypes.Time;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.NUMERIC: {
          column.uidt = UITypes.Decimal;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
-       case FormulaDataTypes.STRING:
        default: {
          column.uidt = UITypes.SingleLineText;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
      }
    }

-   return {
-     isValid: true,
-   } as FilterVerificationResult;
  }
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export class FormulaGeneralHandler extends GenericFieldHandler {
  override async verifyFilter(
    filter: Filter,
    column: Column,
    options: HandlerOptions = {},
  ) {
    const uidt = parseProp(column.meta).display_type;
    if (uidt) {
      column.uidt = uidt;
      return options.fieldHandler.verifyFilter(filter, column, options);
    } else {
      const formulaCol = await column.getColOptions<FormulaColumn>(
        options.context,
      );
      const parsedTree = await formulaCol.getParsedTree();
      const dataType = parsedTree.dataType;
      switch (dataType) {
        case FormulaDataTypes.BOOLEAN: {
          column.uidt = UITypes.Checkbox;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.DATE: {
          column.uidt = UITypes.DateTime;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.INTERVAL: {
          column.uidt = UITypes.Time;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        case FormulaDataTypes.NUMERIC: {
          column.uidt = UITypes.Decimal;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
        default: {
          column.uidt = UITypes.SingleLineText;
          return options.fieldHandler.verifyFilter(filter, column, options);
        }
      }
    }
  }
}
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 44-44: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

---

[error] 52-54: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-20T04:28:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,340 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
+    return new HandlerClass();
+  }
+
+  /**
+   * Applies a filter to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param filter - Filter object
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applyFilter(
+    filter: Filter,
+    column?: Column,
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client.config.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    const useColumn =
+      column ?? this.info.model.columns.find((col) => col.id === filter.id);
+    return handler.filter(knex, filter, useColumn, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async applyFilterGroup(filter: Filter, options: HandlerOptions = {}) {
+    return this.applyFilters(filter.children, options);
+  }
+
+  async applyFilters(
+    filters: Filter[],
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const qbHandlers: {
+      handler: (qb: Knex.QueryBuilder) => void;
+      index: number;
+      logicalOps?: string;
+    }[] = [];
+    let index = 0;
+    for (const filter of filters) {
+      if (filter.is_group) {
+        qbHandlers.push({
+          handler: await this.applyFilterGroup(filter, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      } else {
+        let column = model.columns.find(
+          (col) => col.id === filter.fk_column_id,
+        );
+        if (!column) {
+          column = await this.getRelatedColumnById(
+            options.context,
+            filter.fk_column_id,
+          );
+        }
+        qbHandlers.push({
+          handler: await this.applyFilter(filter, column, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      }
+    }
+    return (qb: Knex.QueryBuilder) => {
+      for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
+        qb[getLogicalOpMethod(handler.logicalOps)](qb);
+      }
+    };
path: packages/nocodb/src/db/field-handler/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix likely misuse of query builder method usage in filter composition.**

Calling `qb[getLogicalOpMethod(handler.logicalOps)](qb)` passes the same query builder as a parameter to itself and never invokes the filter function. This can lead to recursive or no-op conditions. Typically, you’d want something like:
```js
qb.orWhere((subQb) => handler.handler(subQb));
```
or directly call `handler.handler(qb)` if no grouping is needed.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-20T04:28:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,340 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
path: packages/nocodb/src/db/field-handler/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Handle `undefined` return value from `getHandler` to prevent runtime failures.**

Currently, the `getHandler` method returns `undefined` if no matching handler is found. However, at line 178, the returned handler is always invoked with `.filter(...)`. If `handler` is `undefined`, this causes a runtime error. Consider either throwing an explicit error if no handler is found or falling back to a fully supported default handler.

```diff
 if (!HandlerClass) {
-  return undefined;
+  throw new Error(`No handler found for UI type ${uiType} and client ${dbClient}`);
 }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
if (!HandlerClass) {
-      return undefined;
+      throw new Error(`No handler found for UI type ${uiType} and client ${dbClient}`);
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 3250e08 to 0c2f9b2
created_at: 2025-03-20T04:28:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,340 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
+    return new HandlerClass();
+  }
+
+  /**
+   * Applies a filter to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param filter - Filter object
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applyFilter(
+    filter: Filter,
+    column?: Column,
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client.config.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    const useColumn =
+      column ?? this.info.model.columns.find((col) => col.id === filter.id);
+    return handler.filter(knex, filter, useColumn, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async applyFilterGroup(filter: Filter, options: HandlerOptions = {}) {
+    return this.applyFilters(filter.children, options);
+  }
+
+  async applyFilters(
+    filters: Filter[],
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const qbHandlers: {
+      handler: (qb: Knex.QueryBuilder) => void;
+      index: number;
+      logicalOps?: string;
+    }[] = [];
+    let index = 0;
+    for (const filter of filters) {
+      if (filter.is_group) {
+        qbHandlers.push({
+          handler: await this.applyFilterGroup(filter, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      } else {
+        let column = model.columns.find(
+          (col) => col.id === filter.fk_column_id,
+        );
+        if (!column) {
+          column = await this.getRelatedColumnById(
+            options.context,
+            filter.fk_column_id,
+          );
+        }
+        qbHandlers.push({
+          handler: await this.applyFilter(filter, column, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      }
+    }
+    return (qb: Knex.QueryBuilder) => {
+      for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
+        qb[getLogicalOpMethod(handler.logicalOps)](qb);
+      }
+    };
+  }
+
+  /**
+   * Applies a select operation to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applySelect(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options: HandlerOptions = {},
+  ): Promise<void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
path: packages/nocodb/src/db/field-handler/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix incorrect access to Knex client type.**

The line `const dbClient = knex.client as ClientType;` attempts to access the client type incorrectly. It should be `knex.client.config.client` as used in `applyFilter` method.

```diff
const knex = options.knex ?? this.info.knex;
- const dbClient = knex.client as ClientType;
+ const dbClient = knex.client.config.client as ClientType;
const handler = this.getHandler(column.uidt, dbClient);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const knex = options.knex ?? this.info.knex;
    const dbClient = knex.client.config.client as ClientType;
    const handler = this.getHandler(column.uidt, dbClient);
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 8be6efc to 0eb6c94
created_at: 2025-03-25T06:19:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,340 @@
+import { UITypes } from 'nocodb-sdk';
+import { ClientType } from 'nocodb-sdk';
+import { JsonGeneralHandler } from './handlers/json/json.general.handler';
+import { GenericFieldHandler } from './handlers/generic';
+import { CheckboxGeneralHandler } from './handlers/checkbox/checkbox.general.handler';
+import { DateTimeGeneralHandler } from './handlers/date-time/date-time.general.handler';
+import { DateGeneralHandler } from './handlers/date/date.general.handler';
+import { MultiSelectGeneralHandler } from './handlers/multi-select/multi-select.general.handler';
+import { SingleSelectGeneralHandler } from './handlers/single-select/single-select.general.handler';
+import { DecimalGeneralHandler } from './handlers/decimal/decimal.general.handler';
+import { NumberGeneralHandler } from './handlers/number/number.general.handler';
+import { FilterVerificationError } from './error/filter-verification.error';
+import { FormulaGeneralHandler } from './handlers/formula/formula.general.handler';
+import type CustomKnex from '../CustomKnex';
+import type { NcContext } from 'nocodb-sdk';
+import type { IBaseModelSqlV2 } from '../IBaseModelSqlV2';
+import type {
+  FilterVerificationResult,
+  HandlerOptions,
+  IFieldHandler,
+} from './field-handler.interface';
+import type { Knex } from 'knex';
+import type { Filter, Model } from '~/models';
+import type { FieldHandlerInterface } from '~/db/field-handler/field-handler.interface';
+import { Column } from '~/models';
+import { JsonPgHandler } from '~/db/field-handler/handlers/json/json.pg.handler';
+
+const CLIENT_DEFAULT = '_default';
+
+const HANDLER_REGISTRY: Partial<
+  Record<
+    UITypes,
+    Partial<
+      Record<
+        ClientType | typeof CLIENT_DEFAULT,
+        new () => FieldHandlerInterface
+      >
+    >
+  >
+> = {
+  [UITypes.ID]: {},
+  [UITypes.LinkToAnotherRecord]: {},
+  [UITypes.ForeignKey]: {},
+  [UITypes.Lookup]: {},
+  [UITypes.SingleLineText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.LongText]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Attachment]: {},
+  [UITypes.Checkbox]: {
+    [CLIENT_DEFAULT]: CheckboxGeneralHandler,
+  },
+  [UITypes.MultiSelect]: {
+    [CLIENT_DEFAULT]: MultiSelectGeneralHandler,
+  },
+  [UITypes.SingleSelect]: {
+    [CLIENT_DEFAULT]: SingleSelectGeneralHandler,
+  },
+  [UITypes.Date]: {
+    [CLIENT_DEFAULT]: DateGeneralHandler,
+  },
+  [UITypes.Year]: {},
+  [UITypes.Time]: {},
+  [UITypes.PhoneNumber]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.GeoData]: {},
+  [UITypes.Email]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.URL]: {
+    [CLIENT_DEFAULT]: GenericFieldHandler,
+  },
+  [UITypes.Number]: {
+    [CLIENT_DEFAULT]: NumberGeneralHandler,
+  },
+  [UITypes.Decimal]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Currency]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Percent]: {
+    [CLIENT_DEFAULT]: DecimalGeneralHandler,
+  },
+  [UITypes.Duration]: {},
+  [UITypes.Rating]: {},
+  [UITypes.Formula]: {
+    [CLIENT_DEFAULT]: FormulaGeneralHandler,
+  },
+  [UITypes.Rollup]: {},
+  [UITypes.DateTime]: {
+    [CLIENT_DEFAULT]: DateTimeGeneralHandler,
+  },
+  [UITypes.CreatedTime]: {},
+  [UITypes.LastModifiedTime]: {},
+  [UITypes.AutoNumber]: {},
+  [UITypes.Geometry]: {},
+  [UITypes.JSON]: {
+    [ClientType.PG]: JsonPgHandler,
+    [CLIENT_DEFAULT]: JsonGeneralHandler,
+  },
+  [UITypes.SpecificDBType]: {},
+  [UITypes.Barcode]: {},
+  [UITypes.QrCode]: {},
+  [UITypes.Button]: {},
+  [UITypes.Links]: {},
+  [UITypes.User]: {},
+  [UITypes.CreatedBy]: {},
+  [UITypes.LastModifiedBy]: {},
+};
+
+function getLogicalOpMethod(logical_op?: string) {
+  switch (logical_op?.toLowerCase()) {
+    case 'or':
+      return 'orWhere';
+    case 'and':
+      return 'andWhere';
+    case 'not':
+      return 'whereNot';
+    default:
+      return 'where';
+  }
+}
+export class FieldHandler implements IFieldHandler {
+  constructor(
+    public readonly info: {
+      model: Model;
+      knex: CustomKnex;
+      context: NcContext;
+    },
+  ) {}
+
+  static fromBaseModel(baseModel: IBaseModelSqlV2) {
+    return new FieldHandler({
+      context: baseModel.context,
+      model: baseModel.model,
+      knex: baseModel.dbDriver,
+    });
+  }
+
+  /**
+   * Retrieves the appropriate FieldHandler instance based on UI type and database client.
+   * @param uiType - The UI type of the column (e.g., UITypes.Date).
+   * @param dbClient - The database client type (e.g., 'pg').
+   * @returns An instance of the corresponding FieldHandler.
+   * @throws Error if no handler is registered.
+   */
+  private getHandler(
+    uiType: UITypes,
+    dbClient: ClientType,
+  ): FieldHandlerInterface {
+    const dbHandlers = HANDLER_REGISTRY[uiType];
+    const HandlerClass = dbHandlers?.[dbClient] ?? dbHandlers?.[CLIENT_DEFAULT];
+    if (!HandlerClass) {
+      return undefined;
+    }
+    return new HandlerClass();
+  }
+
+  /**
+   * Applies a filter to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param filter - Filter object
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applyFilter(
+    filter: Filter,
+    column?: Column,
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client.config.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    const useColumn =
+      column ?? this.info.model.columns.find((col) => col.id === filter.id);
+    return handler.filter(knex, filter, useColumn, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async applyFilterGroup(filter: Filter, options: HandlerOptions = {}) {
+    return this.applyFilters(filter.children, options);
+  }
+
+  async applyFilters(
+    filters: Filter[],
+    options: HandlerOptions = {},
+  ): Promise<(qb: Knex.QueryBuilder) => void> {
+    const model = options.model ?? this.info.model;
+    if (!model.columns) {
+      await model.getColumns(options.context ?? this.info.context);
+    }
+    const qbHandlers: {
+      handler: (qb: Knex.QueryBuilder) => void;
+      index: number;
+      logicalOps?: string;
+    }[] = [];
+    let index = 0;
+    for (const filter of filters) {
+      if (filter.is_group) {
+        qbHandlers.push({
+          handler: await this.applyFilterGroup(filter, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      } else {
+        let column = model.columns.find(
+          (col) => col.id === filter.fk_column_id,
+        );
+        if (!column) {
+          column = await this.getRelatedColumnById(
+            options.context,
+            filter.fk_column_id,
+          );
+        }
+        qbHandlers.push({
+          handler: await this.applyFilter(filter, column, options),
+          index: index++,
+          logicalOps: filter.logical_op,
+        });
+      }
+    }
+    return (qb: Knex.QueryBuilder) => {
+      for (const handler of qbHandlers.sort((a, b) => a.index - b.index)) {
+        qb[getLogicalOpMethod(handler.logicalOps)](qb);
+      }
+    };
+  }
+
+  /**
+   * Applies a select operation to the query builder using the appropriate handler.
+   * @param qb - Knex query builder instance.
+   * @param column - Column object
+   * @param knex - Knex instance to determine the database client.
+   * @param options - Additional options like alias.
+   */
+  async applySelect(
+    qb: Knex.QueryBuilder,
+    column: Column,
+    options: HandlerOptions = {},
+  ): Promise<void> {
+    const knex = options.knex ?? this.info.knex;
+    const dbClient = knex.client as ClientType;
+    const handler = this.getHandler(column.uidt, dbClient);
+    return handler.select(qb, column, {
+      knex,
+      context: this.info.context,
+      model: this.info.model,
+      fieldHandler: this,
+      ...options,
+    });
+  }
+
+  async verifyFilter(
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions = {},
+  ) {
+    const knex = options.knex ?? this.info.knex;
+    return (
+      this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+    ).verifyFilter(filter, column, {
path: packages/nocodb/src/db/field-handler/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix inconsistency in handler retrieval.**

In `verifyFilter`, you access `knex.client` directly, but in other methods, you use `knex.client.config.client`. This inconsistency could lead to errors.

```diff
const knex = options.knex ?? this.info.knex;
return (
-  this.getHandler(column.uidt, knex.client) ?? new GenericFieldHandler()
+  this.getHandler(column.uidt, knex.client.config.client as ClientType) ?? new GenericFieldHandler()
).verifyFilter(filter, column, {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const knex = options.knex ?? this.info.knex;
    return (
      this.getHandler(column.uidt, knex.client.config.client as ClientType) ?? new GenericFieldHandler()
    ).verifyFilter(filter, column, {
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 8be6efc to 0eb6c94
created_at: 2025-03-25T06:19:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+export class JsonMySqlHandler extends GenericFieldHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (val === '') {
+            qb.where((nestedQb) => {
path: packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use consistent empty value checking.**

In the `eq` case you use `!ncIsStringHasValue(val)` to check for empty values, but here you're using `val === ''`. Maintain consistency by using the same approach throughout.


```diff
-          if (val === '') {
+          if (!ncIsStringHasValue(val)) {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
          if (!ncIsStringHasValue(val)) {
            qb.where((nestedQb) => {
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-25T09:06:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+export class JsonMySqlHandler extends GenericFieldHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (val === '') {
+            qb.where((nestedQb) => {
+              nestedQb.whereNotNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            qb.where((nestedQb) => {
+              if (isValidJson) {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              } else {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              }
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNotNull(field);
+          } else {
+            qb.where(knex.raw('JSON_UNQUOTE(??) like ?', [field, val]));
+          }
+          break;
path: packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Standardize empty value checking for 'like' operator.**

The check `!val` is used here, but elsewhere you use `!ncIsStringHasValue(val)`. Consider using the same approach for consistency.

Additionally, the behavior here is inconsistent with the generic JSON handler - for empty 'like' values, this handler calls `whereNotNull(field)` while the generic handler calls `whereNull(field)`.


```diff
          val = val ? `%${val}%` : val;
-          if (!val) {
-            qb.whereNotNull(field);
+          if (!ncIsStringHasValue(val)) {
+            qb.whereNull(field);
           } else {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        case 'like':
          val = val ? `%${val}%` : val;
          if (!ncIsStringHasValue(val)) {
            qb.whereNull(field);
          } else {
            qb.where(knex.raw('JSON_UNQUOTE(??) like ?', [field, val]));
          }
          break;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-25T09:06:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+import type { Knex } from 'knex';
+import type CustomKnex from '~/db/CustomKnex';
+import type { HandlerOptions } from '~/db/field-handler/field-handler.interface';
+import type { Column, Filter } from '~/models';
+import { GenericFieldHandler } from '~/db/field-handler/handlers/generic';
+import { sanitize } from '~/helpers/sqlSanitize';
+import { ncIsStringHasValue } from '~/db/field-handler/utils/handlerUtils';
+
+export class JsonMySqlHandler extends GenericFieldHandler {
+  override async filter(
+    knex: CustomKnex,
+    filter: Filter,
+    column: Column,
+    options: HandlerOptions,
+  ) {
+    const { alias } = options;
+    const field = sanitize(
+      alias ? `${alias}.${column.column_name}` : column.column_name,
+    );
+    let val = filter.value;
+
+    return (qb: Knex.QueryBuilder) => {
+      switch (filter.comparison_op) {
+        case 'eq':
+          if (!ncIsStringHasValue(val)) {
+            qb.where((nestedQb) => {
+              nestedQb.whereNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            if (isValidJson) {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            } else {
+              qb.where(knex.raw('JSON_UNQUOTE(??) = ?', [field, jsonVal]));
+            }
+          }
+          break;
+
+        case 'neq':
+        case 'not':
+          if (val === '') {
+            qb.where((nestedQb) => {
+              nestedQb.whereNotNull(field);
+            });
+          } else {
+            const { jsonVal, isValidJson } = this.parseJsonValue(val);
+            qb.where((nestedQb) => {
+              if (isValidJson) {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              } else {
+                nestedQb.where(
+                  knex.raw('JSON_UNQUOTE(??) != ?', [field, jsonVal]),
+                );
+              }
+              nestedQb.orWhereNull(field);
+            });
+          }
+          break;
+
+        case 'like':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNotNull(field);
+          } else {
+            qb.where(knex.raw('JSON_UNQUOTE(??) like ?', [field, val]));
+          }
+          break;
+
+        case 'nlike':
+          val = val ? `%${val}%` : val;
+          if (!val) {
+            qb.whereNull(field);
+          } else {
+            qb.where((nestedQb) => {
+              nestedQb.where(
+                knex.raw('JSON_UNQUOTE(??) not like ?', [field, val]),
+              );
+              if (val !== '%%') {
+                nestedQb.orWhere(field, '').orWhereNull(field);
+              } else {
+                nestedQb.orWhereNull(field);
+              }
+            });
+          }
+          break;
path: packages/nocodb/src/db/field-handler/handlers/json/json.mysql.handler.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Fix inconsistent handling of 'nlike' operator.**

The behavior for 'nlike' with empty values is inconsistent with the generic JSON handler. In addition, the condition checking logic for empty values is different from the 'like' operator.


```diff
          val = val ? `%${val}%` : val;
-          if (!val) {
+          if (!ncIsStringHasValue(val)) {
-            qb.whereNull(field);
+            qb.whereNotNull(field);
           } else {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        case 'nlike':
          val = val ? `%${val}%` : val;
          if (!ncIsStringHasValue(val)) {
            qb.whereNotNull(field);
          } else {
            qb.where((nestedQb) => {
              nestedQb.where(
                knex.raw('JSON_UNQUOTE(??) not like ?', [field, val]),
              );
              if (val !== '%%') {
                nestedQb.orWhere(field, '').orWhereNull(field);
              } else {
                nestedQb.orWhereNull(field);
              }
            });
          }
          break;
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-25T09:06:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-19T06:55:39Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough

This pull request introduces a new error class, `InvalidFilterError`, to provide detailed error reporting during filter parsing in the NocoDB SDK. The update replaces generic error handling in functions like `innerExtractFilterFromXwhere` and `mapFilterClauseSubType`. Additionally, several new field handler classes and interfaces are added to enhance filter verification and application, including JSON, checkbox, date/time, decimal, formula, multi-select, number, single-line text, and single-select handlers. A new utility method (`getAs`) and a comprehensive `FieldHandler` class with a registry for managing handlers have also been introduced.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `packages/nocodb-sdk/src/lib/error/invalid-filter.error.ts`<br>`packages/nocodb-sdk/src/lib/filterHelpers_withparser.ts` | Added `InvalidFilterError` for detailed invalid filter error reporting; updated error handling in filter parsing functions (`innerExtractFilterFromXwhere`, `mapFilterClauseSubType`). |
| `packages/nocodb/src/db/conditionV2.ts`<br>`packages/nocodb/src/db/field-handler/field-handler.interface.ts`<br>`packages/nocodb/src/db/field-handler/index.ts`<br>`packages/nocodb/src/db/field-handler/utils/handlerUtils.ts` | Enhanced filter verification and JSON filter handling by integrating `FieldHandler`; introduced new interfaces and the `getAs` utility function for field management. |
| `packages/nocodb/src/db/field-handler/error/filter-verification.error.ts` | Introduced `FilterVerificationError` to encapsulate and report multiple filter verification failures. |
| `packages/nocodb/src/db/field-handler/handlers/...` | Added multiple new field handler classes (for checkbox, date/time, date, decimal, formula, generic, JSON, multi-select, number, single-line text, and single-select) to validate and apply filters based on field type. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant ConditionV2
    participant FieldHandler
    participant Registry
    participant Handler

    Client->>ConditionV2: Submit filter conditions
    ConditionV2->>FieldHandler: Verify filters
    FieldHandler->>Registry: Retrieve handler for field type
    Registry-->>FieldHandler: Return appropriate handler
    FieldHandler->>Handler: Call verifyFilter(filter, column)
    alt Filter is valid
        Handler-->>FieldHandler: Valid filter result
        FieldHandler-->>ConditionV2: Proceed with filter application
    else Filter is invalid
        Handler-->>FieldHandler: Error details
        FieldHandler-->>ConditionV2: Throw InvalidFilterError
    end
```

## Possibly related PRs

- **nocodb/nocodb#10723:** The changes in the main PR, which introduce the `InvalidFilterError` class for handling invalid filter errors, are related to the modifications in the retrieved PR that enhance JSON filtering logic, as both involve error handling and validation in the context of filter operations.
- **nocodb/nocodb#9447:** The changes in the main PR, which introduce the `InvalidFilterError` class and its usage in error handling, are directly related to the modifications in the retrieved PR that involve error handling logic, specifically in how errors are managed and reported in the context of filter operations.
- **nocodb/nocodb#9530:** The changes in the main PR, which introduce the `InvalidFilterError` class and its usage in error handling, are directly related to the modifications in the retrieved PR that involve error handling in the `innerExtractFilterFromXwhere` and `mapFilterClauseSubType` functions, as they both utilize the `InvalidFilterError` class for improved error reporting.

## Suggested labels

`🔦 Type: Feature`, `size:L`, `lgtm`

## Suggested reviewers

- pranavxc
- mertmit

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=nocodb/nocodb&utm_content=10904):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-19T07:03:29Z
body: Uffizzi Preview `deployment-62197` was deleted.
author_association: CONTRIBUTOR