Early developmentNot everything described here has shipped yet.

Skip to content

Lua API ​

NeoWiki provides a Scribunto library at mw.neowiki for accessing structured data from Lua modules. Use Lua when you need to render multiple properties, iterate over collections, or build custom output. For simple inline values, the parser functions are usually enough.

If you want to...Use
Read one value from a propertynw.getValue
Read every value from a multi-valued propertynw.getAll
Get a page's Main Subject (label, schema, all properties)nw.getMainSubject
Get a Subject by its ID, regardless of which page it's onnw.getSubject
List every Subject on a pagenw.getSubjects
Run a read-only Cypher querynw.query
Run a read-only SPARQL querynw.sparqlQuery
Inspect a Schemanw.getSchema

For definitions of terms like Subject, Schema, and Statement, see the Glossary.

Loading the library ​

lua
local nw = require('mw.neowiki')

require fails on a wiki where the library is removed.

Functions ​

nw.getValue(propertyName, options) ​

Returns a single scalar value for a property. For multi-valued properties, returns the first value. Use nw.getAll() when you need every value.

ParameterTypeDescription
propertyNamestringRequired. The name of the property.
optionstableOptional. { page = '...' } or { subject = '...' }. If both are passed, subject takes precedence.

The current-page (no options) and { page = '...' } forms read the page's Main Subject; a property that lives only on another Subject is not found. Use { subject = '...' } to address a specific Subject (including one that is not the Main Subject) by ID.

Returns ​

The first value of the property, type-converted for Lua:

Property typeReturned type
text, url, date, dateTimestring
selectstring (the option's label, or its id when the option cannot be looked up)
numbernumber
booleanboolean
relationstring (the target Subject's display name, or its Subject ID when the target cannot be looked up)
monolingualTextstring (the text, without its language)

Returns nil when the Subject does not exist or its page is not readable (see Permissions), has no value for the property, or the value is empty.

Examples ​

lua
nw.getValue('Founded at')                              --> 2005
nw.getValue('Status')                                  --> "Active"
nw.getValue('Process owner')                           --> "Sarah Naumann"
nw.getValue('Status', { page = 'ACME Inc' })           --> "Active"
nw.getValue('City', { subject = 's1abc5def6ghi78' })   --> "Berlin"

nw.getAll(propertyName, options) ​

Returns every value for a property as a 1-indexed Lua table. Even single-valued properties are wrapped in a 1-element table.

Same parameters and resolution rules as nw.getValue().

Returns ​

A 1-indexed Lua table of values, in the order they are stored on the Subject, type-converted as in getValue. For relations, each entry is the target Subject's display name.

Returns nil under the same conditions as getValue.

Examples ​

lua
nw.getAll('Websites')
--> { [1] = "https://acme.com", [2] = "https://acme.org" }

nw.getAll('Products')
--> { [1] = "Foo", [2] = "Bar", [3] = "Baz" }

local websites = nw.getAll('Websites')
if websites then
    for _, url in ipairs(websites) do
        mw.log(url)
    end
end

nw.getMainSubject(pageName) ​

Returns the full data of a page's Main Subject as a Lua table.

ParameterTypeDescription
pageNamestringOptional. Defaults to the current page.

Returns ​

A Subject table (see Subject table format) or nil if the page does not exist, is not readable (see Permissions), or has no Main Subject.

Examples ​

lua
local subject = nw.getMainSubject()
if subject then
    mw.log(subject.label)         --> "ACME Inc."
    mw.log(subject.schema)        --> "Company"
end

local other = nw.getMainSubject('Berlin')

nw.getSubject(subjectId) ​

Returns the full data of any Subject by its ID, regardless of which page it lives on.

ParameterTypeDescription
subjectIdstringRequired. A Subject ID: 15 characters starting with s for a Subject of this wiki, or sourceKey:localId for one from another Source.

Returns ​

A Subject table (see Subject table format) or nil if no Subject exists with that ID, the ID is malformed, or its page is not readable (see Permissions).

Examples ​

lua
local subject = nw.getSubject('s1abc5def6ghi78')
local sourced = nw.getSubject('otherwiki:s1abc5def6ghi78')

nw.getSubjects(pageName) ​

Returns every Subject on a page, the Main Subject first where the page has one.

ParameterTypeDescription
pageNamestringOptional. Defaults to the current page.

Returns ​

A 1-indexed Lua table of Subject tables (see Subject table format). Returns an empty table {} (not nil) if the page holds no Subjects or is not readable (see Permissions), so it's safe to iterate the result directly with ipairs.

Examples ​

lua
for _, subject in ipairs(nw.getSubjects()) do
    if subject.isMainSubject then
        mw.log(subject.label .. ' (main)')
    else
        mw.log(subject.label)
    end
end

nw.query(cypher, params) ​

Runs a read-only Cypher query against the graph database and returns each row as a Lua table. It is available only when a Neo4j graph backend is configured; on a wiki without one, mw.neowiki.query is nil.

ParameterTypeDescription
cypherstringRequired. A Cypher query. Must be read-only (no CREATE, SET, DELETE, etc.).
paramstableOptional. Parameter name → value. Use $name in the query to reference them.

Returns ​

A 1-indexed Lua table of rows. Each row is a string-keyed table where the keys are the Cypher RETURN aliases. A query with no rows returns {}, and so does any query run without the neowiki-query right (see Permissions), even one that always returns a row, such as a count. Iterating with ipairs needs no nil check; reading rows[1] does.

Scalar values come back as strings, numbers, booleans, or nil. Nested Cypher lists become 1-indexed tables; Cypher maps become string-keyed tables. Graph types convert as follows:

Cypher typeLua shape
Node{ id, labels, properties }
Relationship{ id, type, startNodeId, endNodeId, properties }
Path{ nodes, relationships }
dateISO 8601 date string, e.g. "2023-10-01"
datetime / zoned timeISO 8601 string with offset, e.g. "2023-09-13T14:22:23+00:00" / "14:22:23+02:00"
localdatetime / localtimeISO 8601 string without offset, e.g. "2023-09-13T14:22:23" / "09:30:00"
duration{ months, days, seconds, nanoseconds }
point{ x, y, crs, srid } (plus z for 3D points)

Errors ​

With the neowiki-query right, always throws on failure; wrap in pcall if you need graceful degradation.

  • Empty or whitespace-only cypher.
  • Write or non-read-only queries.
  • Cypher syntax errors, missing parameters, or database errors.

Expensive ​

Every call counts as an expensive parser function.

Examples ​

lua
local rows = nw.query( 'MATCH (s:Subject) RETURN s.name LIMIT 5' )

for _, row in ipairs( rows ) do
    mw.log( row['s.name'] )
end
lua
-- Prefer parameterized queries over concatenating values into queries.
local rows = nw.query(
    'MATCH (s:Subject:`ISMS Document`) WHERE $valid IN s.`Valid` RETURN s.name AS name, s.`Expiry date`[0] AS expiry',
    { valid = 'Yes' }
)

Integer comparisons need an explicit cast in the query — e.g. WHERE s.year = toInteger($year).

nw.sparqlQuery(sparql) ​

Runs a read-only SPARQL query against the first configured SPARQL store and returns the results as a Lua table. The SPARQL counterpart of nw.query. It is available only when a SPARQL store is configured; on a wiki without one, mw.neowiki.sparqlQuery is nil.

ParameterTypeDescription
sparqlstringRequired. A SPARQL SELECT or ASK query. Read-only by protocol. CONSTRUCT and DESCRIBE are not supported.

Returns ​

The W3C application/sparql-results+json document as a Lua table, preserving its standard structure: head.vars and results.bindings for a SELECT, or boolean for an ASK. Every JSON array (head.vars, results.bindings) is a 1-indexed Lua sequence; each binding is a string-keyed table of RDF terms ({ type, value, datatype?, ['xml:lang']? }). Without the neowiki-query right (see Permissions), every query returns empty head.vars and results.bindings and no boolean.

Errors ​

With the neowiki-query right, always throws on failure; wrap in pcall if you need graceful degradation.

  • Empty or whitespace-only sparql.
  • A query the store rejects (e.g. a SPARQL syntax error), or the store being unavailable.

Expensive ​

Every call counts as an expensive parser function.

Named graphs ​

Pages are projected into named graphs, so which of them an unscoped query reaches depends on the store — see RDF Export.

Examples ​

lua
local results = nw.sparqlQuery(
    'SELECT ?label WHERE { ?s <http://www.w3.org/2000/01/rdf-schema#label> ?label } LIMIT 5'
)

for _, binding in ipairs( results.results.bindings ) do
    mw.log( binding.label.value )
end

nw.getSchema(name) ​

Returns a Schema as a Lua table so a module can inspect it at runtime.

ParameterTypeDescription
namestring or tableRequired. A Schema name (e.g. 'Company'), or a Subject's schema field, which is a table for a Schema from another Source.

Returns ​

A Schema table, or nil if no Schema with that name exists or its page is not readable (see Permissions). An empty or whitespace-only name, a table that is not a Schema reference, a reference to a Source this wiki does not have, and the reserved names page and subject all return nil. Guard with if schema then.

Top-level fields:

FieldTypeDescription
namestringThe Schema name.
descriptionstringThe Schema description. Omitted when empty.
propertiestable1-indexed list of properties, in Schema-defined order.

Every property entry has name, type, and required, plus description and default when those are set. Beyond that core, the fields depend on type:

Property typeAlways presentPresent only when set
text, monolingualTextmultiple, uniqueItemsminLength, maxLength
urlmultiple, uniqueItems—
number—precision, minimum, maximum
date, dateTime—minimum, maximum
selectmultiple, options (1-indexed list of { id, label } entries)—
relationmultiple, targetSchemas (1-indexed list, each entry like a Subject's schema)—
boolean——

Optional fields are omitted entirely when unset, so check with if prop.description then …. required is always present, and multiple/uniqueItems are present for the types the table marks under Always present; where present, these boolean flags are emitted even when false, so read them directly rather than testing truthiness. For a type where the table does not list multiple/uniqueItems, they are absent (nil), not false.

Errors ​

Raises a Lua error only when name is missing or not a string. Wrap a computed or possibly-nil name in pcall.

Expensive ​

Every call counts as an expensive parser function.

Examples ​

lua
-- Fetch a Schema and read its first property.
local schema = nw.getSchema( 'Company' )
if schema then
    mw.log( schema.name )                --> "Company"
    mw.log( schema.properties[1].name )  --> first property's name
end
lua
-- Render a property overview for the current page's Main Subject.
local subject = nw.getMainSubject()
local schema = subject and nw.getSchema( subject.schema )

if schema then
    for _, prop in ipairs( schema.properties ) do
        local tag = prop.required and ' (required)' or ''
        mw.log( prop.name .. ' — ' .. prop.type .. tag )
    end
end
lua
-- Read optional fields only after checking they're set.
for _, prop in ipairs( schema.properties ) do
    if prop.type == 'number' and prop.minimum then
        mw.log( prop.name .. ' min: ' .. prop.minimum )
    end
end

Subject table format ​

Subject tables returned by getMainSubject, getSubject, and getSubjects have this structure:

lua
{
    id = 's1abc5def6ghi78',
    label = 'ACME Inc.',
    storedLabel = 'ACME Inc.',
    schema = 'Company',
    isMainSubject = true,
    statements = {
        ['Headquarters'] = { propertyType = 'text',     values = { [1] = 'Berlin' } },
        ['Founded at']   = { propertyType = 'number',   values = { [1] = 2005 } },
        ['Status']       = { propertyType = 'select',   values = { [1] = 'Active' } },
        ['Websites']     = { propertyType = 'url',      values = { [1] = 'https://acme.com', [2] = 'https://acme.org' } },
        ['Active']       = { propertyType = 'boolean',  values = { [1] = true } },
        ['Motto']        = {
            propertyType = 'monolingualText',
            values = {
                [1] = { text = 'Onwards', language = 'en' },
                [2] = { text = 'Vorwärts', language = 'de' },
            },
        },
        ['Products']     = {
            propertyType = 'relation',
            values = {
                [1] = { id = 'r1...', target = 's1...', label = 'Foo' },
                [2] = { id = 'r1...', target = 's1...', label = 'Bar' },
            },
        },
    },
}

Notes:

  • label is the display name and is never nil: a Subject without a stored label takes its page name when it is the page's Main Subject, and its Schema name otherwise. storedLabel carries the stored value and is nil when the Subject has none. The REST API splits these the other way: label is the stored value, displayName the display name.
  • isMainSubject says whether the Subject is the Main Subject of the page it was read from.
  • A Subject fetched with nw.getSubject comes without its page: its isMainSubject key is absent rather than false, and a label-less Main Subject is named after its Schema there.
  • schema is a Schema name for a Schema of this wiki, and a table { source = ..., name = ... } for one from another Source. nw.getSchema takes either, so nw.getSchema( subject.schema ) works whichever it is.
  • statements is keyed by property name. values within each statement is 1-indexed.
  • propertyType is the property type at the time the Subject was last edited. If the Schema has changed since (e.g. a property was changed from text to select), older Subjects keep their original type until they are re-saved.
  • A relation's label is the target's display name, and falls back to the target Subject ID when the target cannot be looked up at all (e.g. a broken reference).
  • A select value is the option's label, and falls back to the option's id when the option cannot be looked up.
  • A monolingualText value keeps its languages here; nw.getValue and nw.getAll give the text alone.

Permissions ​

The parser function rules apply: every function reads as the user the page is parsed for. In Lua, Subjects and Schemas that user cannot read come back as nil or an empty table, a relation to such a Subject shows the Subject ID as its label, and nw.query and nw.sparqlQuery return no results without the neowiki-query right.

Performance ​

Each of these counts as an expensive parser function (against the page's expensive function limit): nw.query, nw.sparqlQuery, nw.getSchema, and nw.getSubject on every call; nw.getValue, nw.getAll, nw.getMainSubject, and nw.getSubjects only when passed a page/subject option or page name. Reads of the current page do not count.