···58585959Available in both queries and procedures:
60606161-| Function | Returns | Description |
6262-| -------------- | ------- | ------------------------------------------------------------------- |
6363-| `now()` | string | Current UTC timestamp in ISO 8601 format |
6464-| `log(message)` | — | Log a message (appears in server logs at debug level) |
6565-| `TID()` | string | Generate a fresh AT Protocol TID (13-character sortable identifier) |
6161+| Function | Returns | Description |
6262+| ---------------- | ------- | ------------------------------------------------------------------- |
6363+| `now()` | string | Current UTC timestamp in ISO 8601 format |
6464+| `log(message)` | — | Log a message (appears in server logs at debug level) |
6565+| `TID()` | string | Generate a fresh AT Protocol TID (13-character sortable identifier) |
6666+| `toarray(table)` | table | Mark a table as a JSON array for serialization (see [below](#toarray)) |
6767+6868+### toarray
6969+7070+Lua tables don't distinguish between arrays and objects. When a table is serialized to JSON, an empty table `{}` becomes a JSON object `{}` instead of an array `[]`. The `toarray()` function marks a table so it always serializes as a JSON array — even when empty.
7171+7272+```lua
7373+return { items = toarray(results) }
7474+-- With results: [{"name": "a"}, {"name": "b"}]
7575+-- Without results: {"items": []} (not {"items": {}})
7676+```
7777+7878+You don't need `toarray()` on results from `db.query`, `db.search`, `db.backlinks`, or `db.raw` — those already return properly marked arrays. Use it when you build a table yourself with `table.insert()`.
66796780## Record API
6881···175188-- The returned table includes a "uri" field
176189```
177190191191+### db.search
192192+193193+```lua
194194+local result = db.search({
195195+ collection = "xyz.statusphere.status", -- required
196196+ field = "displayName", -- required: record field to search
197197+ query = "alice", -- required: search term
198198+ limit = 10, -- optional: max 100, default 10
199199+})
200200+201201+-- result.records — array of matching records, ranked by relevance:
202202+-- exact match > prefix match > contains match, then alphabetical
203203+```
204204+205205+### db.backlinks
206206+207207+Find records that reference a given AT URI anywhere in their data. Useful for finding likes on a post, replies to a thread, or any record that links to another.
208208+209209+```lua
210210+local result = db.backlinks({
211211+ collection = "xyz.statusphere.status", -- required
212212+ uri = "at://did:plc:abc/xyz.statusphere.status/foo", -- required: the URI to find references to
213213+ did = "did:plc:abc", -- optional: filter by DID
214214+ limit = 20, -- optional: max 100, default 20
215215+ offset = 0, -- optional: for pagination
216216+})
217217+218218+-- result.records — array of records whose data contains the given URI
219219+-- result.cursor — present when more records exist
220220+```
221221+222222+The search checks the full record data, so it works regardless of which field holds the reference (`subject`, `parent`, `reply.root`, etc.).
223223+178224### db.count
179225180226```lua
181227local n = db.count("xyz.statusphere.status")
182228local n = db.count("xyz.statusphere.status", "did:plc:abc") -- filter by DID
183229```
230230+231231+### db.raw
232232+233233+Run a raw SQL query against the database. Only `SELECT` statements are allowed.
234234+235235+```lua
236236+local rows = db.raw(
237237+ "SELECT uri, did, record FROM records WHERE collection = $1 AND did = $2 LIMIT $3",
238238+ { "xyz.statusphere.status", "did:plc:abc", 10 }
239239+)
240240+241241+for _, row in ipairs(rows) do
242242+ -- row.uri, row.did, row.record (JSONB is returned as a Lua table)
243243+end
244244+```
245245+246246+Parameters are passed as an array and bound to `$1`, `$2`, etc. Supported parameter types: strings, integers, numbers, booleans, and nil.
247247+248248+Column types are mapped automatically:
249249+250250+| Postgres type | Lua type |
251251+| ---------------------- | -------- |
252252+| `TEXT`, `VARCHAR` | string |
253253+| `INT4`, `INT8` | integer |
254254+| `FLOAT4`, `FLOAT8` | number |
255255+| `BOOL` | boolean |
256256+| `JSON`, `JSONB` | table |
257257+| `TIMESTAMPTZ` | string (ISO 8601) |
258258+| Other | string (fallback) |
184259185260## Standard libraries
186261