Table.select() test (edge version)

I tried new table.select API in a query clause select and i notice this:

1- Both outputs are equivalent:

${query[[
  from p = index.tag "page"
  where p.name == editor.getCurrentPage()
  select table.select(p, "name", "lastModified")
]]}
${query[[
  from p = index.tag "page"
  where p.name == editor.getCurrentPage()
  select {p.name, p.lastModified}
]]}

2- But with table.unpack
${ table.unpack(query[[from p = index.tag "page" where p.name == editor.getCurrentPage() select {p.name, p.lastModified}]],1,2)} ==>

  • Accueil
  • 2026-01-31T08:12:08.840
${table.unpack(query[[from p = index.tag "page"   where p.name == editor.getCurrentPage() select table.select(p, "name", "lastModified")]],1,2)}

==> idem without table.unpack (return a table)

3- … or with table.unpack and table.concat

${table.concat(table.unpack(query[[  from p = index.tag "page"   where p.name == editor.getCurrentPage() select {p.name, p.lastModified}]]), " : ")}

==> Accueil : 2026-01-31T09:10:59.822

${table.concat(table.unpack(query[[  from p = index.tag "page"   where p.name == editor.getCurrentPage() select table.select(p, "name", "lastModified")]],1,2))}

==> empty (not nil)

the result is different !

Query[[…select table.select(…) ]] seems to provide a table incompatible with table.unpack or table.concat. I can’t figure out where the differences are.

If you look at the output of these you’ll see they’re not the same.

A query returns an array (Lua table) of results. Each result is whatever the expression in select evaluates to. In your first query this is a table with key, value mappings (this is what the table.select API returns). So for instance: a {name = "...", lastModified="..."} entry for each matching page, which accumulates to a table of tables (or in more common terms: an array of maps): such as:

{{name = "...", lastModified="..."}, {name = "...", lastModified="..."}}

In your second query, your select expression evaluates to something of a different shape, namely: {"pagename", "2026-02-02..."}

Technically also a Lua table (it’s a bit confusing that Lua uses tables both for array-like structures and maps). This accumulates to:

{{"page name", "date"}, {"page name", "date"}}

Let’s have a look at what table.unpack actually does (API docs):

The table.unpack() function returns the elements of a table as separate values, allowing you to easily use them in a function call or assign them to multiple variables.

Lua has this interesting feature that functions can return multiple values. Not all languages have this feature (Go does, JavaScript doesn’t for instance). This illustrates what this means in the context of table.unpack:

local one, two = table.unpack({1, 2})

This will assign 1 to the one variable and 2 to the two variable. Effectively it “unpacks” an array-like table into multiple return values. In JavaScript this would be similar:

let [one, two] = [1, 2];

Alright, to explain the behavior you’re seeing, let’s now see what happens when you use table.unpack in the context of a Lua directive:

${table.unpack({1, 2})

This will evaluate to simply 1. Why? Because when evaluation of the given expression results in multiple results, only the first one will be shown. Most expressions evaluate to a single value, so this should you usually don’t notice this.

This makes this:

${table.unpack(query[[from p = index.tag "page"   where p.name == editor.getCurrentPage() select table.select(p, "name", "lastModified")]],1,2)}

A rather peculiar thing to try to do, because it effectively means “give me the first result of this query”. Technically it returns all, but it will only render the first.

Now here you see another interesting Lua behavior that is that when you evaluate an expression in a function call, and that expressions results in multiple values (like table.unpack) it will “spread” those values into function arguments.

-- This means that this:
f(table.unpack({1, 2, 3}))
-- is the same as
f(1, 2, 3)

This means that table.unpack(query[[ from p = index.tag "page" where p.name == editor.getCurrentPage() select {p.name, p.lastModified}]]) results in a multi-result where each of the results in a page object. When you pass this to table.concat it will “spread” each of these objects to be function arguments to table.concat, effectively resulting in a call like

table.concat({name = "page", ...}, {name = "page2"}, ": ")

Now whatever the results of that may or should be, I’m pretty much sure this is not what you intend to do, especially considering the API of table.concat.

Hope this helps.

1 Like

Thank you very much for the clarification in your response.

I understood that the rendering of the two outputs in the editor are identical but the outputs themselves are structured differently.

With select table.select(, we get:

  "arrayPart": [
    {
      "metatable": null,
      "stringKeys": {
        "lastModified": "2026-01-31T16:20:08.260",
        "name": "Accueil"
      },

and without, we get:

  "arrayPart": [
    {
      "metatable": null,
      "stringKeys": {},
      "otherKeys": null,
      "arrayPart": [
        "2026-01-31T16:06:50.706",
        "Accueil"
      ],

For fun and to practice, I did some manipulations with js.tojs and concat, without table.select. For this:

${string.gsub(table.concat(js.tojs(query[[ from p = index.tag "page"   where p.name >= editor.getCurrentPage() select {'| '.. p.name .. ' |', " modifié le " ..p.lastModified} order by p.lastModified desc limit 5]]), "\n"), ",", "")}

i get:

| Accueil | modifié le 2026-01-31T15:39:44.639
| Library/mrmugame/Silversearch | modifié le 2026-01-31T14:20:08.905
| DOCS/test | modifié le 2026-01-31T12:02:16.449
| TECH/Base | modifié le 2026-01-31T11:05:32.317
| NOTE 08 - Query | modifié le 2026-01-31T10:43:46.324

result is identical to the simple query:

${query[[  from p = index.tag "page"   where p.name >= editor.getCurrentPage() select {Name=p.name, Modified=p.lastModified} order by p.lastModified desc limit 5]]}

but, the display is rendered as a string which fits better into the overall text of the page. Depending on the context and personal taste, this may be better suited.

Thank you again for taking the time to answer me!

1 Like