diff --git a/packages/base-engine/src/expressions/evaluator.ts b/packages/base-engine/src/expressions/evaluator.ts index bcf00cf..2d98c5f 100644 --- a/packages/base-engine/src/expressions/evaluator.ts +++ b/packages/base-engine/src/expressions/evaluator.ts @@ -27,6 +27,7 @@ export type EvaluationContext = { byPath: Map; byBasename: Map; value?: unknown; + index?: number; acc?: unknown; values?: unknown[]; }; @@ -81,8 +82,10 @@ export function evaluateExpression( ); const predicate = expr.args[0]; return predicate - ? source.filter((value) => - truthy(evaluateExpression(predicate, { ...context, value })), + ? source.filter((value, index) => + truthy( + evaluateExpression(predicate, { ...context, value, index }), + ), ) : source; } @@ -176,6 +179,10 @@ function readIdentifier(name: string, context: EvaluationContext): unknown { return context.value ?? null; } + if (name === "index") { + return context.index ?? null; + } + if (name === "acc") { return context.acc ?? null; } diff --git a/packages/base-engine/test/query.test.ts b/packages/base-engine/test/query.test.ts index 48f8d2f..8160401 100644 --- a/packages/base-engine/test/query.test.ts +++ b/packages/base-engine/test/query.test.ts @@ -296,6 +296,28 @@ describe("Base query engine", () => { expect(b?.formulas.HasAll).toBe(false); }); + // Regression: filter must bind index as the 0-based position of the item. + it("binds index inside list filter", () => { + const base = parseBase( + "bases/FilterIndex.base", + [ + "formulas:", + ' Kept: "[1, 2, 3].filter(index > 0)"', + ' All: "[1, 2, 3].filter(index >= 0)"', + "views:", + " - name: FilterIndex", + " order:", + " - formula.Kept", + " - formula.All", + ].join("\n"), + ); + const result = queryBase(base, [file("A.md", "A", [])]); + + const a = result.rows.find((r) => r.file.path === "A.md"); + expect(a?.formulas.Kept).toEqual([2, 3]); + expect(a?.formulas.All).toEqual([1, 2, 3]); + }); + it("evaluates string matches and date format methods", () => { const base = parseBase( "bases/Fmt.base",