Lua Queries: make object attributes variables

pull/1210/head
Zef Hemel 2025-01-13 21:17:08 +01:00
parent bf6a34f82c
commit cbf227fa49
5 changed files with 42 additions and 24 deletions

View File

@ -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) => {

View File

@ -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,

View File

@ -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<T> 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;
}

View File

@ -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
]]}

View File

@ -44,7 +44,7 @@ And the shorter:
from <<expression>>
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 <expression>`
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 <expression> [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
]]}