Skip to main content
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:

Data Types

Declaring Type Definitions

Data Types

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:

Data Type Operations

Data types have different operations available.

REGISTER Operations

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:
DQL

MAP Operations

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.

ATTACHMENT Operations

To set the last-write-wins ATTACHMENT data type, provide an ATTACHMENT object:
DQL
Read more about attachments and large binary files.

Counter (Settable Counter)

Counters are available on 4.13 and later.
A COUNTER is an enhanced version of the PN_COUNTER that combines positive-negative counter semantics with the ability to explicitly set the counter to a specific value. Like PN_COUNTER, it’s a CRDT type that can be incremented or decremented by any peer, but it also supports a RESTART operation that uses last-write-wins semantics. The counter is an integer value that automatically resolves conflicting increments and decrements from different peers by tracking operations and composing them to provide a final value. When peers perform RESTART operations concurrently, the last write wins. Counters are useful for tracking counts that multiple peers might update simultaneously, such as:
  • Like/vote counts with the ability to reset
  • Inventory counts that need periodic recalibration
  • Session counts that can be initialized to specific values
  • Metrics that require both incremental updates and explicit resets
Counter operations (INCREMENT BY, RESTART WITH, RESTART) are specified using the APPLY clause, not the SET clause. The APPLY clause is specifically designed for CRDT operations on special field types like counters.

Strict Mode and Type Declarations

The requirement to declare counter types in queries depends on your DQL_STRICT_MODE setting: When DQL_STRICT_MODE=true (default):
  • You must declare counter types in COLLECTION definitions for INSERT, UPDATE, and SELECT statements
  • Counter fields are only visible in queries when the type declaration is included
When DQL_STRICT_MODE=false:
  • Counter type declarations are not required for SELECT and UPDATE statements using APPLY
  • Counter type declarations are still required for INSERT statements
  • Counter fields are automatically visible in queries without type declarations

Creating Counters

There are two ways to create a counter field:
  1. Using INSERT with type declaration: You can declare a field as a COUNTER in the COLLECTION definition when inserting a document. This allows you to initialize the counter with a specific value (type declaration is required for INSERT regardless of strict mode):
DQL
  1. Using APPLY operations: Counter fields are automatically created when you first use INCREMENT or RESTART operations on them. This is useful when you want to create documents without counter fields initially:
Do not initialize counter fields by inserting an integer value without declaring the COUNTER type. This creates a register field, not a counter.

Incrementing Counters

To decrement a counter, use a negative value with INCREMENT:
DQL

Setting Counter Values with RESTART

The RESTART operation allows you to explicitly set a counter to a specific value or reset it to zero. This uses last-write-wins semantics, so if multiple peers restart a counter concurrently, the last write will win. Set counter to a specific value:
DQL
Reset counter to zero:
DQL

Combining Counter Operations with Other Updates

You can combine counter operations with other field updates in a single statement:
DQL

Querying Counter Values

You can retrieve the current value of a counter using a SELECT statement:
DQL
The counter value will appear as a regular integer in the query results:

Counter vs PN_COUNTER

The key differences between COUNTER and PN_COUNTER: Use COUNTER when you need the ability to explicitly set or reset counter values. Use PN_COUNTER only for backward compatibility with older Ditto versions.

PN Counter Operations (Legacy)

PN Counters are available in 4.11 and later. Read more Users should use Counter which also provide the ability to set the counter value.
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:
  • Like/vote counts
  • Number of views or interactions
Do not initialize counter fields by inserting a double value (e.g., 0.0). This creates a register field, not a counter. Counter fields are automatically created when you first use PN_INCREMENT on them.
To use a counter, apply the PN_INCREMENT operation directly on the field. If the field doesn’t exist, it will be created as a counter with the increment value. If you need to create a document first, insert it without the counter field or with other fields only:
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:

Default Value Operation

Some data types can be set to a default value type using the default() functional operator.
  • REGISTER β†’ NULL
  • AWMAP β†’ Empty Map {}
  • ATTACHMENT β†’ NOT SUPPORTED
  • PN_COUNTER β†’ NOT SUPPORTED
  • COUNTER β†’ NOT SUPPORTED

Declaring Type Definition

With 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. ​ Type Definition Syntax Diagram

Registers

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:
  • Stores primitive types (string, boolean) or JSON objects
  • Last-write-wins conflict resolution ensures consistent values across peers
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.

Non-Registers

In 4.11+ and DQL_STRICT_MODE=false, collection definitions for non-registers are no longer required.Read more
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.

Document ID Constraints

Document ID Requirements

Every document in Ditto must have a unique _id field that serves as the document’s identifier. The following constraints apply:
  • Required: Every document must have an _id field
  • Type: The _id can be a string, number, or other scalar type
  • Uniqueness: Each _id must be unique within its collection
  • Null Restriction: null cannot be used as a document _id (enforced in SDK 5.0+)
Example - Valid Document IDs:
DQL
Example - Invalid Document ID:
DQL
Starting in SDK version 5.0, attempting to use null as a document _id will result in an error. Earlier versions may have allowed this, but it should be avoided for forward compatibility.
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:
DQL
In this syntax:
DQL
  • COLLECTION declares that the collection has a type definition
  • your_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
DQL
UPDATE with Definition
DQL
INSERT with Definition
DQL
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, ...):
DQL
Single MAP The syntax for a single MAP with all other fields type REGISTER:
DQL
Single ATTACHMENT The syntax for a single ATTACHMENT with all other fields type REGISTER:
DQL
MAP and ATTACHMENT The syntax for a single MAP and a single ATTACHMENT with all other fields type REGISTER:
DQL
Deeply Embedded MAP

Disable Strict Mode

In 4.11+ and DQL_STRICT_MODE=false, collection definitions are no longer required.Read more
The syntax for a document hierarchy of depth two β€” a single MAP nested with another MAP β€” with all other fields type REGISTER:
DQL
The syntax for a document hierarchy of depth four with all other fields type REGISTER:
DQL