-
Notifications
You must be signed in to change notification settings - Fork 6
Fix filter AST array format incompatibility with query engine #800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -335,6 +335,124 @@ export const NormalizedFilterSchema: z.ZodType<any> = z.lazy(() => | |||||||||||||||
|
|
||||||||||||||||
| export type NormalizedFilter = z.infer<typeof NormalizedFilterSchema>; | ||||||||||||||||
|
|
||||||||||||||||
| // ============================================================================ | ||||||||||||||||
| // AST Array → FilterCondition Conversion | ||||||||||||||||
| // ============================================================================ | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Operator mapping from AST infix operators to FilterCondition `$`-prefixed operators. | ||||||||||||||||
| */ | ||||||||||||||||
| const AST_OPERATOR_MAP: Record<string, string> = { | ||||||||||||||||
| '=': '$eq', | ||||||||||||||||
| '==': '$eq', | ||||||||||||||||
| '!=': '$ne', | ||||||||||||||||
| '<>': '$ne', | ||||||||||||||||
| '>': '$gt', | ||||||||||||||||
| '>=': '$gte', | ||||||||||||||||
| '<': '$lt', | ||||||||||||||||
| '<=': '$lte', | ||||||||||||||||
| 'in': '$in', | ||||||||||||||||
| 'nin': '$nin', | ||||||||||||||||
| 'not_in': '$nin', | ||||||||||||||||
| 'contains': '$contains', | ||||||||||||||||
| 'like': '$contains', | ||||||||||||||||
| 'startswith': '$startsWith', | ||||||||||||||||
| 'starts_with': '$startsWith', | ||||||||||||||||
| 'endswith': '$endsWith', | ||||||||||||||||
| 'ends_with': '$endsWith', | ||||||||||||||||
| 'between': '$between', | ||||||||||||||||
| 'is_null': '$null', | ||||||||||||||||
| 'is_not_null': '$null', | ||||||||||||||||
|
Comment on lines
+364
to
+365
|
||||||||||||||||
| 'is_null': '$null', | |
| 'is_not_null': '$null', |
Copilot
AI
Feb 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseFilterAST currently “passes through” any non-array input by casting to FilterCondition. That includes primitives (e.g., a string/number/boolean) which would silently become an invalid FilterCondition at runtime. Consider only passing through when typeof filter === 'object' (and not null), otherwise return undefined (or throw) to avoid propagating invalid filters.
| if (!Array.isArray(filter)) return filter as FilterCondition; | |
| if (!Array.isArray(filter)) { | |
| if (typeof filter === 'object') { | |
| return filter as FilterCondition; | |
| } | |
| return undefined; | |
| } |
Copilot
AI
Feb 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using .filter(Boolean) for narrowing works here but is a bit opaque and relies on truthiness. A typed predicate (e.g., filtering only undefined results from parseFilterAST) is clearer and keeps TypeScript inference safer if the return type later changes.
Copilot
AI
Feb 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch will treat any array with at least 2 elements as a comparison node, which can produce { field: { $op: undefined } } when the value is missing (e.g. ["age", ">"]) and also doesn’t ensure the operator is a string. Tighten the shape check (e.g., require filter.length === 3 and typeof filter[1] === 'string'), while still allowing unary operators like is_null / is_not_null if you intend to support [field, op] forms.
| if (filter.length >= 2 && typeof first === 'string') { | |
| if (filter.length === 3 && typeof first === 'string' && typeof filter[1] === 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filtersnow holds either the entirequeryobject or the legacyfiltersvalue, so the name is misleading (it’s no longer necessarily just filters). Renaming to something likequeryParams(and then extractingfilters/filterfrom it explicitly) would make the subsequent logic easier to follow.