diff --git a/common/space_lua/eval.ts b/common/space_lua/eval.ts index b1fc3a8a..1dea3d5c 100644 --- a/common/space_lua/eval.ts +++ b/common/space_lua/eval.ts @@ -257,7 +257,7 @@ export function evalExpression( if (!findFromClause) { throw new LuaRuntimeError("No from clause found", sf.withCtx(e.ctx)); } - const objectVariable = findFromClause.name || "_"; + const objectVariable = findFromClause.name; const objectExpression = findFromClause.expression; return Promise.resolve(evalExpression(objectExpression, env, sf)).then( async (collection: LuaValue) => { diff --git a/common/space_lua/query_collection.test.ts b/common/space_lua/query_collection.test.ts index 9fe896ee..1e125d35 100644 --- a/common/space_lua/query_collection.test.ts +++ b/common/space_lua/query_collection.test.ts @@ -124,11 +124,10 @@ Deno.test("ArrayQueryCollection", async () => { assertEquals(result8[2], "Jane Doe"); assertEquals(result8[3], "Bob Johnson"); - // Test select with native function + // Test select with native function and implicit object variable const result9 = await collection2.query( { - objectVariable: "p", - select: parseExpressionString("build_name(p.firstName, p.lastName)"), + select: parseExpressionString("build_name(firstName, lastName)"), }, rootEnv, LuaStackFrame.lostFrame, diff --git a/common/space_lua/query_collection.ts b/common/space_lua/query_collection.ts index 0a967dd8..52e0e5dc 100644 --- a/common/space_lua/query_collection.ts +++ b/common/space_lua/query_collection.ts @@ -1,12 +1,31 @@ import type { LuaExpression } from "$common/space_lua/ast.ts"; -import { LuaEnv, type LuaStackFrame } from "$common/space_lua/runtime.ts"; +import { + LuaEnv, + luaGet, + luaKeys, + type LuaStackFrame, +} from "$common/space_lua/runtime.ts"; import { evalExpression } from "$common/space_lua/eval.ts"; import { asyncQuickSort } from "$common/space_lua/util.ts"; import type { DataStore } from "$lib/data/datastore.ts"; -function buildItemEnv(objectVariable: string, item: any, env: LuaEnv): LuaEnv { +function buildItemEnv( + objectVariable: string | undefined, + item: any, + env: LuaEnv, + sf: LuaStackFrame, +): LuaEnv { const itemEnv = new LuaEnv(env); - itemEnv.setLocal(objectVariable, item); + if (!objectVariable) { + // Inject all item keys as variables + for (const key of luaKeys(item)) { + itemEnv.setLocal(key, luaGet(item, key, sf)); + } + // As well as _ + itemEnv.setLocal("_", item); + } else { + itemEnv.setLocal(objectVariable, item); + } return itemEnv; } @@ -19,7 +38,7 @@ export type LuaOrderBy = { * Represents a query for a collection */ export type LuaCollectionQuery = { - objectVariable: string; + objectVariable?: string; // The filter expression evaluated with Lua where?: LuaExpression; // The order by expression evaluated with Lua @@ -55,7 +74,7 @@ export class ArrayQueryCollection implements LuaQueryCollection { // Filter the array for (const item of this.array) { - const itemEnv = buildItemEnv(query.objectVariable, item, env); + const itemEnv = buildItemEnv(query.objectVariable, item, env, sf); if (query.where && !await evalExpression(query.where, itemEnv, sf)) { continue; } @@ -77,8 +96,8 @@ async function applyTransforms( result = await asyncQuickSort(result, async (a, b) => { // Compare each orderBy clause until we find a difference for (const { expr, desc } of query.orderBy!) { - const aEnv = buildItemEnv(query.objectVariable, a, env); - const bEnv = buildItemEnv(query.objectVariable, b, env); + const aEnv = buildItemEnv(query.objectVariable, a, env, sf); + const bEnv = buildItemEnv(query.objectVariable, b, env, sf); const aVal = await evalExpression(expr, aEnv, sf); const bVal = await evalExpression(expr, bEnv, sf); @@ -99,7 +118,7 @@ async function applyTransforms( if (query.select) { const newResult = []; for (const item of result) { - const itemEnv = buildItemEnv(query.objectVariable, item, env); + const itemEnv = buildItemEnv(query.objectVariable, item, env, sf); newResult.push(await evalExpression(query.select, itemEnv, sf)); } result = newResult; @@ -134,7 +153,7 @@ export class DataStoreQueryCollection implements LuaQueryCollection { ) { // Enrich this.dataStore.enrichObject(value); - const itemEnv = buildItemEnv(query.objectVariable, value, env); + const itemEnv = buildItemEnv(query.objectVariable, value, env, sf); if (query.where && !await evalExpression(query.where, itemEnv, sf)) { continue; } diff --git a/website/Space Lua.md b/website/Space Lua.md index 965607f4..fa75357e 100644 --- a/website/Space Lua.md +++ b/website/Space Lua.md @@ -61,8 +61,8 @@ Space Lua has a feature called [[Space Lua/Lua Integrated Query]], which integra ${query[[ from tag "page" - order by _.lastModified desc - select _.name + order by lastModified desc + select name limit 3 ]]} diff --git a/website/Space Lua/Lua Integrated Query.md b/website/Space Lua/Lua Integrated Query.md index 9a152896..8ff16298 100644 --- a/website/Space Lua/Lua Integrated Query.md +++ b/website/Space Lua/Lua Integrated Query.md @@ -44,7 +44,7 @@ And the shorter: from <> -implicitly binding each item to the variable `_`. +implicitly binding each item to the variable `_` as well as making all attributes directly available as variables. Example without variable binding: ${query[[from {1, 2, 3} select _]]} @@ -52,8 +52,8 @@ ${query[[from {1, 2, 3} select _]]} With variable binding: ${query[[from n = {1, 2, 3} select n]]} -A more realist example using `tag`: -${query[[from t = tag "page" limit 3 select t.name]]} +A more realistic example using `tag`: +${query[[from tag "page" order by lastModified select name limit 3]]} ## `where ` The `where` clause allows you to filter data. When the expression evaluated to a truthy value, the item is included in the result. @@ -64,7 +64,7 @@ ${query[[from {1, 2, 3, 4, 5} where _ > 2]]} Or to select all pages tagged with `#meta`: -${query[[from tag "page" where table.includes(_.tags, "meta")]]} +${query[[from tag "page" where table.includes(tags, "meta")]]} ## `order by [desc]` The `order by` clause allows you to sort data, when `desc` is specified it reverts the sort order. @@ -72,8 +72,8 @@ The `order by` clause allows you to sort data, when `desc` is specified it rever As an example, the last 3 modified pages: ${query[[ from tag "page" - order by _.lastModified desc - select _.name + order by lastModified desc + select name limit 3 ]]} @@ -101,10 +101,10 @@ ${query[[from tag "page" select _.name limit 3]]} You can also return tables or other complex values: ${query[[ - from tag "page" + from p = tag "page" select { - name = _.name, - modified = _.lastModified + name = p.name, + modified = p.lastModified } limit 3 ]]}