Ditto Query Language (DQL) offers a set of data types designed to accommodate any edge sync scenario.
A data type is different than a standard scalar type by declaring merge behaviors, operations, and the spectrum of scalar types accessible for individual fields:
In DQL, you’ll use three data types:REGISTER
, MAP
, and ATTACHMENT
type. By
default, fields in a DQL statement are assigned the REGISTER
type unless
otherwise specified by way of type definition.
Following are the key characteristics:
Type | REGISTER | MAP | ATTACHMENT | PN_COUNTER |
---|---|---|---|---|
CRDT type | Last-write-wins | Add-wins | Last-write-wins | Positive-negative |
Payload | Any | Object | Binary file | Double |
Data types have different operations available.
A Register supports scalar types, including primitive types, such as string
and boolean
, as well as a JSON blob, encapsulating multiple field‑value pairs
that function as a single object. The REGISTER
can only be set to a specific
field.
For example:
The MAP
type supports inserting and tombstoning of fields using the functional
operators. Inserting a field is an implicit operation performed by assigning a
value to a field or a child of the field.
To set the last-write-wins ATTACHMENT
data type, provide an ATTACHMENT
object:
Read more about attachments and large binary files.
Counters are available in 4.11 and later. Read more
A counter is a special type of field that can be incremented or decremented. A counter is a double-precision floating-point number. In 4.11 and above, ditto offers PN_COUNTER, or positive-negative counter, which is a CRDT type that can be incremented or decremented by any peer. Counters automatically resolve conflicting increments and decrements from different peers by tracking the operations and composing them to provide a final value.
Counters are useful for tracking counts that multiple peers might update simultaneously, such as:
You can first create a placeholder for the counter in a document by using the INSERT
statement to insert a double value of 0.0
:
To update a counter, use the APPLY keyword followed by the field name and then
the PN_INCREMENT
keyword followed by the value. To decrement a counter, use a
negative value.
You can then retrieve the latest value of a counter using a SELECT
statement:
Some data types can be set to a default value type using the default()
functional operator.
REGISTER
→ NULL
AWMAP
→ Empty Map {}
ATTACHMENT
→ NOT SUPPORTEDPN_COUNTER
→ NOT SUPPORTEDWith strict mode enabled, all fields are treated as a register by default. When enabled, every field in a document must match the collection definition exactly — including its CRDT type (e.g., map, register, counter).
Disabling strict mode enables new functionality: when set to false, collection definitions are no longer required. SELECT queries will return and display all fields by default.
A REGISTER
is a data type in Ditto that stores a single scalar value and
uses last-write-wins merge strategy for handling conflicts.
Key characteristics of REGISTER:
With DQL_STRICT_MODE=false
, if you want to force a JSON Object to use a
REGISTER data type instead of a MAP in DQL, it must be specified explicitly.
The results of the SELECT statement above would be:
If you need to remove a register map, you need to use the UNSET
statement at the top level. Because a register map is treated the same as a scalar value (such as string, int), you operate on the entire object as a whole, similar to a JSON blob.
You will receive an error if you attempt to SET
or UNSET
a nested key of a register using dot notation.
In 4.11+ and DQL_STRICT_MODE=false
, collection definitions for non-registers are no longer required.
With DQL_STRICT_MODE=true
, REGISTER
is the default type in DQL. That means
that you need to specify the type definition when overriding with type MAP
, PN_COUNTER
, or
ATTACHMENT
within your query.
DQL type definitions describe the schema of the documents within a specific collection — defining the field types within the collection and specifying the assigned data types for each field.
To explicitly declare the type definition as non-REGISTER
type, add a prefix
of COLLECTION
and the suffix of (field1 data_type, field2 data_type, ...)
to
list the fields within the collection and their associated data types:
In this syntax:
COLLECTION
declares that the collection has a type definitionyour_collection_name
is the name of the collection from which you want to set a definition.(field1 data_type, field2 data_type, ...)
specifies the data type of each field such as REGISTER
, MAP
, or ATTACHMENT
SELECT with Definition
UPDATE with Definition
INSERT with Definition
MAP Type Specifics
The MAP
(Add-Wins Map) contains fields with their own data type. Data types for these fields are defined using parentheses following the MAP
keyword. For example, MAP(sub1 data_type, sub2 data_type, ...)
:
Single MAP
The syntax for a single MAP
with all other fields type REGISTER
:
Single ATTACHMENT
The syntax for a single ATTACHMENT
with all other fields type REGISTER
:
MAP and ATTACHMENT
The syntax for a single MAP
and a single ATTACHMENT
with all other fields type REGISTER
:
Deeply Embedded MAP
The syntax for a document hierarchy of depth two — a single MAP
nested with another MAP
— with all other fields type REGISTER
:
The syntax for a document hierarchy of depth four with all other fields type REGISTER
: