Query language

The body of POST /v1/query looks like so:

{
    "templateIds": [...template IDs...],
    "query": {...query elements...}
}

The elements of that query are defined here.

Fallback rule

Unless otherwise required by one of the other rules below or to follow, values are interpreted according to DAML-LF JSON Encoding, and compared for equality.

All types are supported by this simple equality comparison except:

  • lists
  • textmaps
  • genmaps

Simple equality

Match records having at least all the (potentially nested) keys expressed in the query. The result record may contain additional properties.

Example: { person: { name: "Bob" }, city: "London" }

  • Match: { person: { name: "Bob", dob: "1956-06-21" }, city: "London", createdAt: "2019-04-30T12:34:12Z" }
  • No match: { person: { name: "Bob" }, city: "Zurich" }
  • Typecheck failure: { person: { name: ["Bob", "Sue"] }, city: "London" }

A JSON object, when considered with a record type, is always interpreted as a field equality query. Its type context is thus mutually exclusive with comparison queries.

Comparison query

Match values on comparison operators for int64, numeric, text, date, and time values. Instead of a value, a key can be an object with one or more operators: { <op>: value } where <op> can be:

  • "%lt" for less than
  • "%gt" for greater than
  • "%lte" for less than or equal to
  • "%gte" for greater than or equal to

"%lt" and "%lte" may not be used at the same time, and likewise with "%gt" and "%gte", but all other combinations are allowed.

Example: { "person" { "dob": { "%lt": "2000-01-01", "%gte": "1980-01-01" } } }

  • Match: { person: { dob: "1986-06-21" } }
  • No match: { person: { dob: "1976-06-21" } }
  • No match: { person: { dob: "2006-06-21" } }

These operators cannot occur in objects interpreted in a record context, nor may other keys than these four operators occur where they are legal, so there is no ambiguity with field equality.

Appendix: Type-aware queries

This section is non-normative.

This is not a JSON query language, it is a DAML-LF query language. So, while we could theoretically treat queries (where not otherwise interpreted by the “may contain additional properties” rule above) without concern for what LF type (i.e. template) we’re considering, we will not do so.

Consider the subquery {"foo": "bar"}. This query conforms to types, among an unbounded number of others:

record A ↦ { foo : Text }
record B ↦ { foo : Optional Text }
variant C ↦ foo : Party | bar : Unit

// NB: LF does not require any particular case for VariantCon or Field;
// these are perfectly legal types in DAML-LF packages

In the cases of A and B, "foo" is part of the query language, and only "bar" is treated as an LF value; in the case of C, the whole query is treated as an LF value. The wide variety of ambiguous interpretations about what elements are interpreted, and what elements treated as literal, and how those elements are interpreted or compared, would preclude many techniques for efficient query compilation and LF value representation that we might otherwise consider.

Additionally, it would be extremely easy to overlook unintended meanings of queries when writing them, and impossible in many cases to suppress those unintended meanings within the query language. For example, there is no way that the above query could be written to match A but never C.

For these reasons, as with LF value input via JSON, queries written in JSON are also always interpreted with respect to some specified LF types (e.g. template IDs). For example:

{
    "templateIds": ["Foo:A", "Foo:B", "Foo:C"],
    "query": {"foo": "bar"}
}

will treat "foo" as a field equality query for A and B, and (supposing templates’ associated data types were permitted to be variants, which they are not, but for the sake of argument) as a whole value equality query for C.

The above “Typecheck failure” happens because there is no LF type to which both "Bob" and ["Bob", "Sue"] conform; this would be caught when interpreting the query, before considering any contracts.