Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.

Data Query Syntax

Chris Dollin edited this page Aug 22, 2017 · 18 revisions

A data query is a request for a projection of the data in the dataset. The response from such a query is described in data query response. Requests are name/value pairs encoded as a single JSON object in a POST; the values themselves may be objects.

Any name whose name starts with @ is a reserved parameter whose meaning is defined by this specification; other names are the names of aspects used as part of value filtering.

Reserved Parameters

  • "@sort":[{"PM": aspectName}, {"PM2": aspectName2}, ...] where PMi is @up or @down — the results of the query are sorted according to the values of the named aspects, leftmost first, in ascending order for @up and descending order for @down. If @sort is not specified or is empty, the results are in an arbitrary order which can change from response to response. A @sort must have at least one aspectName.

  • "@limit": A, A a non-negative integer — return at most A results, and as many as can be. (The results need not be sorted.)

  • "@offset": B, B a non-negative integer — the first B results are discarded. It is undefined behaviour if @offset is present without @sort.

  • "@itemLimit", "@itemOffset", "@itemSort", same as "@sort", "@limit", "@offset" but applied to the body of the sub-select rather than the entire query see generated SPARQL.

  • @count : true, causes the query to count the number of matches and just return that count, not the matches themselves. The count is returned as a single result object of the form { "@count" : 42 }. The value of the @count key is ignored, if the key is present then a count query is run. The count query can be combined with @limit and @offset parameters. In particular use of @limit sets an upper bound on the count.

  • "@json_mode": S, S is the string "complete" or the string "compact". Complete mode is equivalent having both "@suppress_types": true and "@compact_optionals": true. The default vale is "complete".

  • "@suppress_types": B, B true or false [default]; if true, non-XSD typed literals and URIs are represented by their string lexical form; otherwise literals are represnted as {"@value": lexicalForm, "@type": uriForType}.

  • "@compact_optionals": B, B true or false [default]; if true, absent optionals are omitted from result rows and present optionals are represented by themselves; if false, absent optionals are represented by [] and present ones by [theValue].

  • "@search": S, S a string — see below.

  • "@lang": L — if present, string values with a language that's name does not start with L are discarded. ((Hmm, not right. Rethink.))

  • "@childof": R — if the dataset is a codelist dataset then this will return just those items which are direct children of the given root resource. Use null as the root to list the top level children.

Value Filters

A filter is of the form "aspectName": range, requiring that the value of the named aspect satisfies the given range. A range is represented by a JSON object; keys of that object (typically one, sometimes two) are the operators of that range and the value of that key encodes the value anchor of the range.

  • "@eq": VV represents a value; the filter passes if the aspect value equals V. Similarly, @ne, @le, @lt, @ge, @gt.

  • "@oneof": [V1, V2 ...] — The Vi represent values; the filter passes if the aspect value equals any of the Vi.

  • "@contains": VV must be a string; the filter passes if V is a substring of the aspect value.

  • "@matches": XX must be a single string, a array of two strings, or an object.

    If it is a single string, it must be a legal SPARQL regex, and the filter passes if V matches it.

    If it is an array of two strings, the first must be a legal SPARQL regex and the second must be legal SPARQL regex flags. The filter passes if V matches the regex given the flags.

    If it is an object, it must have exactly one of the properties @value or @case-insensitive-value with a string which is a legal SPARQL regex, and may have a property @flags with a value which is legal SPARQL regex flags. The filter passes if V matches the given value with case-insensitivity enabled, ie, with the flag i present.

  • "@search": S — see Text Search below.

  • "@below": RR must be a resource naming a hierarchy root; this filter passes if the aspect value is at or below that root.

  • "@in": RR must be a resource naming a collection. ((More details of collections to be added later.)) This filter passes if the aspect value is a member of that collection. [Not yet implemented.]

Values

The data query language allows representation of arbitrary RDF literals for the values of aspects.

  • number — plain JSON numeric value, making no special distinction between int/decimal/float; 17, 10.66.

  • boolean — plain JSON true or false.

  • plain literal — plain JSON string value, "hello", "world".

  • language-tagged literal — a JSON object with an @value member bound to a string for the literal's lexical form and an @language member bound to a string for the literal's language code; {"@value: "chat", "@language": "fr"}.

  • typed literals — a JSON object with an @value member bound to a string for the literal's lexical form and an @type member bound to a string for the URI of the literal's dadatype; {"@value": "false", "@type": "xsd:boolean"}.

  • a resource — a JSON object with an @id member bound to a string for the resource's URI and an optional @label member bound to a string for the resource's rdfs:label; `{"@id": "http://epimorphics.com/public/vocabulary/games.ttl#players", "@label": "players"}.

  • multiple values (such as the operands of @or or @oneof) — a JSON array with the individual values as its elements; [17, "shortcake"].

Text Search

Text search only works if the SPARQL endpoint for this data API supports the text query interface and a suitable index has been constructed.

// TODO put in a link to details of how to do that construction

An @search may appear outside an aspect (general) or within it (local). A general search specifies text to be search for for any literal of an item; a local search is restricted to the values of an aspect. Within the search, values can optionally have an associated indexing property.

  • "@search: "pattern" — search for items whose literal values match pattern, which is a Lucene text pattern (details TOBESPECIFIED).

  • "@search": {"@value": "pattern", "@property": "property"} — Search for items whose literal values for property match pattern.

  • "@search": {"@value": "pattern", "@limit": N} — Search for items whose literal values match pattern. Limit the number of results to less-than-or-equal N. This allows the default Lucene limit to be over-ridden.

  • "@search": {"@value": "pattern", "@limit": N, "@property": "property"} — Search for items whose literal values for property match pattern, limiting the number of results to N.

General Search

A general search will generate a SPARQL query using the Fuseki-supported text:query property. With no @property it generates the SPARQL fragment:

?item text:query "pattern"

With an @property:

?item text:query (property "pattern")

Aspect-Local Search

The kinds of query generated for an aspect-local @search depend on the kind of value that the aspect has: literal (datatype) values vs resource (object) values. If the aspect's range type is literal, then it is those literal values that are searched. If the aspect's range type is a class, then it is the values of the properties of those values that are searched.

An aspect with no range type is assumed to have non-literal values.

For a simple search, "aspectName": {"@search": "pattern"}, if the aspect is literal-valued then the SPARQL fragment is:

?item text:query (aspectName "pattern")

ie, the search only considers items that have literal values indexed under aspectName. If the aspect is resource-valued then the generated query fragment is:

?aspectVar text:query "pattern"

where ?aspectVar is the variable bound to the values of the aspect, ie, the search is over the literals of the aspect values.

A compound @search that specifies an @property is allowed only when the aspect has a non-literal range:

"aspectName": {"@search": {"@value": "pattern", "@property": "name"}}

generates the query fragment:

?aspectVar text:query (name "pattern")

Boolean Compositions

All the aspect filters of a query are implicitly ANDed together to select the returned subset of the dataset queried over. It's possible to use @not, @or, and @and to produce composite queries.

Below, A, B, ... must each be aspect filters as described above, or further (nested) boolean compositions.

Not

If a query has a member "@not": [A, B ...], the query will reject any results which make A and B and ... all true.

{"pf:A": {"@lt": 17}, "@not": [{"pf:B": {"@ge": 22}}]}

pf:A must have a value less than 17, and pf:B must have a value less than 22. (In this case it's also possible to change @ge to @lt and pull the pf:B constraint to the top level assuming pf:B isn't optional. An optional aspect can satisfy (not A=V) by having no value rather than by having a value not equal to V.)

Or

If a query has a member "@or": [A, B, ...], the query will accept any results which make any of A, B, ... true, even if the implicitly ANDed filters would reject them.

{"pf:A": {"@lt": 17}, "@or": [{"pf:B": {"@ge": 22}}]}

pf:A must have value less than 17 or pf:B must have value at least 22.

And

If a query has a member "@and": [A, B, ...], then the query will reject any results for which any of A, B, ... is false. Normally you don't need to write an @and, because everything is ANDed together anyway, but it's useful when nesting composites.

 "pf:postcode": {"@eq": "BC4"}, 
 "@and:" ["@or": 
     [ {"pf:class": {"@eq": "ClassA"}}
     , {"pf:class": {"@eq": "ClassB"}}
 ]]

The pf:postcode must be "EC4" and the pf:class must be "ClassA" or "ClassB".

(If the @and is dropped, then the query would mean "postcode muse be EC4 or class is ClassA or class is ClassB", a quite different query.)

(@oneof is the idiomatic tool to use for this query.)

Clone this wiki locally