Skip to content

Commit

Permalink
Lua Queries: make object attributes variables
Browse files Browse the repository at this point in the history
  • Loading branch information
zefhemel committed Jan 13, 2025
1 parent bf6a34f commit cbf227f
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 24 deletions.
2 changes: 1 addition & 1 deletion common/space_lua/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
5 changes: 2 additions & 3 deletions common/space_lua/query_collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
37 changes: 28 additions & 9 deletions common/space_lua/query_collection.ts
Original file line number Diff line number Diff line change
@@ -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;
}

Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions website/Space Lua.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
]]}

Expand Down
18 changes: 9 additions & 9 deletions website/Space Lua/Lua Integrated Query.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ 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 _]]}

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.
Expand All @@ -64,16 +64,16 @@ ${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.

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

Expand Down Expand Up @@ -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
]]}

0 comments on commit cbf227f

Please sign in to comment.