WOQL Explanation

WOQL fluent vs. functional style

WOQL supports both fluent and functional styles for writing queries. The fluent style is recommended for simplifying complex compound query expressions.

The fluent style

Many WOQL expressions accept a sub-query as an argument. WOQL enables appending sub-queries to the initial function as a new function. Queries in this style are easier to read and write. Visual parameter matching is easier to perform when checking for query correctness. A simple example below.

Code: Fluent style WOQL

select(a, b).triple(c, d, e)

The functional style

Sub-queries are contained within the initial function. The example below is the functional style equivalent of the fluent style example.

Code: Functional style WOQL

select(a, b, triple(c, d, e))

Conjunctions

Fluent queries are parsed left to right. Functions to the right of another function are considered sub-queries of the first function, with one important exception - conjunction.

Functional style conjunction

The functional style of expressing conjunction using the WOQL and() function is straightforward and is often more useful for clarity:

Code: Functional style conjunction

and(triple(a, b, c), triple(d, e, f))

Fluid style conjunction

Conjunction expressed in fluent style enables the use of any of the three variations shown below.

Code: Fluent style conjunction

// Fluent style 1
and(triple(a, b, c)).triple(d, e, f)

// Fluent style 2
triple(a, b, c).and().triple(d, e, f)

// Fluent style 3
triple(a, b, c).triple(d, e, f)

Implicit and()

In the example above, fluent style 3 is more concise and unambiguous where WOQL functions that are chained together do not take sub-clauses (or commands.) As conjunction is frequently used, this concise form, where the and() is implicit, is more convenient in many situations.

!> Use implicit and() with care.

?> if in doubt, use the explicit and() functional style as this clarifies which functions are sub-clauses of other functions.

The conjunction is always applied to the function immediately to the left of the period . in the chain, and not to any functions further up the chain. If used improperly with clauses that take sub-clauses, it will produce improperly specified queries, especially with negation (not) and optional functions (opt).

For example, consider the following three queries. The first two are equivalent. However, the first query is incorrect and easy to misinterpret when the intended expression is that shown in the third query.

Code: Fluent style implicit conjunction

triple(a, b, c).opt().triple(d, e, f).triple(g, h, i)

Code: Functional style explicit conjunction

and(
    triple(a, b, c),
    opt(
        and(
            triple(d, e, f),
            triple(g, h, i)
        )
    )
)

Code: Fluent style explicit conjunction

and(
    triple(a, b, c),
    opt().triple(d, e, f),
    triple(g, h, i)
)

WOQL and JSON-LD

WOQL uses JSON-LD and a formally specified ontology to define the language and transmit queries.

JSON-LD is sometimes tedious for us to read and write. Therefore, WOQL.js is designed to be as easy as possible for developers to write. All WOQL.js queries are translated into the equivalent JSON-LD format for transmission over networks.

The WOQLQuery object

The WOQL.js json() function translates any WOQL query to its JSON-LD format, and JSON-LD to its WOQL.js equivalent - a WOQLQuery() object.

As shown in the example below, if passed a JSON-LD (json_ld) argument, WOQL.js (wjs) will generate the equivalent WOQLQuery() object. If an argument is not provided, WOQL.js will return the JSON-LD equivalent of the WOQLQuery() object.

Code: Using WOQLQuery() and json()

let wjs = new WOQLQuery().json(json_ld)
json_ld == wjs.json()

Embedding JSON-LD in WOQL.js

It is possible to use JSON-LD interchangeably within WOQL.js. Wherever a WOQL function or argument can be accepted directly in WOQL.js, the JSON-LD equivalent can also be supplied. For example, the following two WOQL statements are identical.

There should never be a situation that necessitates using JSON-LD directly. WOQL.js expresses all queries that are expressible in the underlying JSON-LD. However, it can be convenient to embed JSON-LD in queries in some cases.

Code: Interchangeable WOQL and JSON-LD

triple(a, b, 1) == triple(a, b, {"@type": "xsd:integer", "@value": 1})

WOQL variables

WOQL allows variables or constants to be substituted for any argument to all its functions, except for the resource identifier functions: using, with, into, from. These functions are used for specifying the graphs against which operations such as queries are carried out.

Unification

WOQL uses the formal-logical approach to variables known as unification borrowed from the Prolog engine that implements WOQL within TerminusDB.

Unification in variables

Unification in variables means each valid value for a variable, as constrained by the totality of the query, will produce a new row in the results. For multiple variables, the rows returned are the cartesian product of all the possible combinations of variable values in the query.

Unification in functions

Unification in functions enables most WOQL functions to serve as both pattern matchers and pattern generators, depending on whether a variable or constant is provided as an argument. If a variable is provided, WOQL will generate all possible valid solutions which fill the variable value. If a constant is provided, WOQL will match only those solutions with exactly that value. Except for resource identifiers, WOQL functions accept either variables or constants in virtually all of their arguments.

Expressing variables

In WOQL.js, there are two distinct ways of expressing variables within queries. All are semantically equivalent. The first is generally preferred as it is easier to type and easier to distinguish variables from constants at a glance due to the lack of quotation marks around the variables

Code: WOQL variables using let

let [a, b, c] = vars('a', 'b', 'c')
triple(a, b, c)

Code: WOQL variables using prefix v:

triple('v:a', 'v:b', 'v:c')

WOQL prefixes

Internally, TerminusDB uses strict RDF rules to represent all data. This means all identifiers and properties are represented by IRIs (a superset of URLs.)

Shorthand prefixes

However, IRIs are difficult to remember and tedious to type. RDF generally solves this problem by allowing prefixed shorthand forms. For example, "http://obscure.w3c.url/with/embedded/dates#type" is shortened to "rdf:type".

Prefixes @base and @schema

TerminusDB also defines the two optional prefixes listed below. These enable users to write expressions such as "@base:X" or "@schema:X" and ensure expressions always resolve to valid IRIs in all databases.

  • The "@base" prefix for instance-data IRIs.
  • The "@schema" prefix for schema IRIs.

Automatic prefixes

WOQL goes a step beyond supporting prefixes by automatically applying prefixes where possible, enabling users to specify prefixes only when necessary. The default prefixes are applied as follows:

  • "@base" applies to woql:subject (first argument to triple) where instance data IRIs are normally required.
  • "@schema" applies to woql:predicate and other arguments (sub, type) where schema elements are normally required.
  • When standard predicates are used without a prefix, the standard correct prefixes are applied.
  • label
  • type
  • comment
  • subClassOf
  • domain
  • range
  • Otherwise, if no prefix is applied a string is assumed.

Further Reading

WOQL Reference

JavaScript and Python WOQL Reference guides

How-to guides

See the How-to Guides for further examples of using WOQL.

Documents

Documents in a knowledge graph and how to use them.