|
1 | | -import { Data, System } from '@objectstack/spec'; |
| 1 | +import { Data, Driver as DriverSpec } from '@objectstack/spec'; |
2 | 2 | type QueryAST = Data.QueryAST; |
3 | | -type FilterNode = Data.FilterNode; |
4 | 3 | type SortNode = Data.SortNode; |
5 | | -type DriverInterface = System.DriverInterface; |
| 4 | +type DriverInterface = DriverSpec.DriverInterface; |
6 | 5 | /** |
7 | 6 | * ObjectQL |
8 | 7 | * Copyright (c) 2026-present ObjectStack Inc. |
@@ -620,12 +619,13 @@ export class FileSystemDriver implements Driver { |
620 | 619 | const objectName = ast.object || ''; |
621 | 620 |
|
622 | 621 | // Convert QueryAST to legacy query format |
| 622 | + // Note: Convert FilterCondition (MongoDB-like) to array format for fs driver |
623 | 623 | const legacyQuery: any = { |
624 | 624 | fields: ast.fields, |
625 | | - filters: this.convertFilterNodeToLegacy(ast.filters), |
626 | | - sort: ast.sort?.map((s: SortNode) => [s.field, s.order]), |
627 | | - limit: ast.top, |
628 | | - skip: ast.skip, |
| 625 | + filters: this.convertFilterConditionToArray(ast.where), |
| 626 | + sort: ast.orderBy?.map((s: SortNode) => [s.field, s.order]), |
| 627 | + limit: ast.limit, |
| 628 | + skip: ast.offset, |
629 | 629 | }; |
630 | 630 |
|
631 | 631 | // Use existing find method |
@@ -754,60 +754,80 @@ export class FileSystemDriver implements Driver { |
754 | 754 | // ========== Helper Methods ========== |
755 | 755 |
|
756 | 756 | /** |
757 | | - * Convert FilterNode from QueryAST to legacy filter format. |
| 757 | + * Convert FilterCondition (MongoDB-like format) to legacy array format. |
| 758 | + * This allows the fs driver to use its existing filter evaluation logic. |
758 | 759 | * |
759 | | - * @param node - The FilterNode to convert |
| 760 | + * @param condition - FilterCondition object or legacy array |
760 | 761 | * @returns Legacy filter array format |
761 | 762 | */ |
762 | | - private convertFilterNodeToLegacy(node?: FilterNode): any { |
763 | | - if (!node) return undefined; |
| 763 | + private convertFilterConditionToArray(condition?: any): any[] | undefined { |
| 764 | + if (!condition) return undefined; |
764 | 765 |
|
765 | | - switch (node.type) { |
766 | | - case 'comparison': |
767 | | - // Convert comparison node to [field, operator, value] format |
768 | | - const operator = node.operator || '='; |
769 | | - return [[node.field, operator, node.value]]; |
770 | | - |
771 | | - case 'and': |
772 | | - // Convert AND node to array with 'and' separator |
773 | | - if (!node.children || node.children.length === 0) return undefined; |
774 | | - const andResults: any[] = []; |
775 | | - for (const child of node.children) { |
776 | | - const converted = this.convertFilterNodeToLegacy(child); |
777 | | - if (converted) { |
778 | | - if (andResults.length > 0) { |
779 | | - andResults.push('and'); |
| 766 | + // If already an array, return as-is |
| 767 | + if (Array.isArray(condition)) { |
| 768 | + return condition; |
| 769 | + } |
| 770 | + |
| 771 | + // If it's an object (FilterCondition), convert to array format |
| 772 | + // This is a simplified conversion - a full implementation would need to handle all operators |
| 773 | + const result: any[] = []; |
| 774 | + |
| 775 | + for (const [key, value] of Object.entries(condition)) { |
| 776 | + if (key === '$and' && Array.isArray(value)) { |
| 777 | + // Handle $and: [cond1, cond2, ...] |
| 778 | + for (let i = 0; i < value.length; i++) { |
| 779 | + const converted = this.convertFilterConditionToArray(value[i]); |
| 780 | + if (converted && converted.length > 0) { |
| 781 | + if (result.length > 0) { |
| 782 | + result.push('and'); |
780 | 783 | } |
781 | | - andResults.push(...(Array.isArray(converted) ? converted : [converted])); |
| 784 | + result.push(...converted); |
782 | 785 | } |
783 | 786 | } |
784 | | - return andResults.length > 0 ? andResults : undefined; |
785 | | - |
786 | | - case 'or': |
787 | | - // Convert OR node to array with 'or' separator |
788 | | - if (!node.children || node.children.length === 0) return undefined; |
789 | | - const orResults: any[] = []; |
790 | | - for (const child of node.children) { |
791 | | - const converted = this.convertFilterNodeToLegacy(child); |
792 | | - if (converted) { |
793 | | - if (orResults.length > 0) { |
794 | | - orResults.push('or'); |
| 787 | + } else if (key === '$or' && Array.isArray(value)) { |
| 788 | + // Handle $or: [cond1, cond2, ...] |
| 789 | + for (let i = 0; i < value.length; i++) { |
| 790 | + const converted = this.convertFilterConditionToArray(value[i]); |
| 791 | + if (converted && converted.length > 0) { |
| 792 | + if (result.length > 0) { |
| 793 | + result.push('or'); |
795 | 794 | } |
796 | | - orResults.push(...(Array.isArray(converted) ? converted : [converted])); |
| 795 | + result.push(...converted); |
797 | 796 | } |
798 | 797 | } |
799 | | - return orResults.length > 0 ? orResults : undefined; |
800 | | - |
801 | | - case 'not': |
802 | | - // NOT is complex - we'll just process the first child for now |
803 | | - if (node.children && node.children.length > 0) { |
804 | | - return this.convertFilterNodeToLegacy(node.children[0]); |
| 798 | + } else if (key === '$not' && typeof value === 'object') { |
| 799 | + // Handle $not: { condition } |
| 800 | + // Note: NOT is complex to represent in array format, so we skip it for now |
| 801 | + const converted = this.convertFilterConditionToArray(value); |
| 802 | + if (converted) { |
| 803 | + result.push(...converted); |
805 | 804 | } |
806 | | - return undefined; |
807 | | - |
808 | | - default: |
809 | | - return undefined; |
| 805 | + } else if (typeof value === 'object' && value !== null) { |
| 806 | + // Handle field-level conditions like { field: { $eq: value } } |
| 807 | + const field = key; |
| 808 | + for (const [operator, operandValue] of Object.entries(value)) { |
| 809 | + let op: string; |
| 810 | + switch (operator) { |
| 811 | + case '$eq': op = '='; break; |
| 812 | + case '$ne': op = '!='; break; |
| 813 | + case '$gt': op = '>'; break; |
| 814 | + case '$gte': op = '>='; break; |
| 815 | + case '$lt': op = '<'; break; |
| 816 | + case '$lte': op = '<='; break; |
| 817 | + case '$in': op = 'in'; break; |
| 818 | + case '$nin': op = 'nin'; break; |
| 819 | + case '$regex': op = 'like'; break; |
| 820 | + default: op = '='; |
| 821 | + } |
| 822 | + result.push([field, op, operandValue]); |
| 823 | + } |
| 824 | + } else { |
| 825 | + // Handle simple equality: { field: value } |
| 826 | + result.push([key, '=', value]); |
| 827 | + } |
810 | 828 | } |
| 829 | + |
| 830 | + return result.length > 0 ? result : undefined; |
811 | 831 | } |
812 | 832 |
|
813 | 833 | /** |
|
0 commit comments