> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Types and Definitions

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

<CardGroup>
  <Card title="Data Types" icon="square-1" href="/dql/types-and-definitions#data-types" iconType="solid" horizontal />

  <Card title="Declaring Type Definitions" icon="square-2" href="/dql/types-and-definitions#declaring-type-definition" iconType="solid" horizontal />
</CardGroup>

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

| **Type**              | **CRDT Type**                  | **Payload** |
| --------------------- | ------------------------------ | ----------- |
| `REGISTER`            | Last-write-wins                | Any         |
| `MAP`                 | Add-wins                       | Object      |
| `ATTACHMENT`          | Last-write-wins                | Binary file |
| `COUNTER`             | Positive-negative & LWW on Set | Integer     |
| `PN_COUNTER` (legacy) | Positive-negative              | Integer     |

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

```sql DQL theme={null}
field1 = 1
```

### **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.

<CodeGroup>
  ```sql 4.11+ theme={null}
  field1.sub1.s_sub1 = 1
  ```

  ```sql <=4.10 theme={null}
  -- With STRICT_MODE=true, to perform `MAP` operations, use the arrow `->`
  -- operator followed by parentheses `()`, which contain one or more operations
  -- on child fields of the `MAP`.


  field1 -> (
    sub1 -> (
      s_sub1 = 1
    )
  )
  ```
</CodeGroup>

### **ATTACHMENT Operations**

To set the last-write-wins `ATTACHMENT` data type, provide an `ATTACHMENT` object:

```sql DQL theme={null}
field1 = :attachment
```

Read more about [attachments and large binary files](/sdk/latest/crud/working-with-attachments).

### Counter (Settable Counter)

<Warning>
  Counters are available on 4.13 and later.
</Warning>

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

<Note>
  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.
</Note>

#### Strict Mode and Type Declarations

The requirement to declare counter types in queries depends on your [DQL\_STRICT\_MODE](/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

<CodeGroup>
  ```sql STRICT_MODE=true theme={null}
  -- Must specify COUNTER type in all statements
  INSERT INTO COLLECTION products (stock_count COUNTER)
  VALUES ({ '_id': '123', 'stock_count': 100 })

  UPDATE COLLECTION products (stock_count COUNTER)
  APPLY stock_count INCREMENT BY 5
  WHERE _id = '123'

  SELECT * FROM COLLECTION products (stock_count COUNTER)
  WHERE _id = '123'
  ```

  ```sql STRICT_MODE=false theme={null}
  -- Must specify COUNTER type for INSERT
  INSERT INTO COLLECTION products (stock_count COUNTER)
  VALUES ({ '_id': '123', 'stock_count': 100 })

  -- No type declaration needed for UPDATE with APPLY
  UPDATE products
  APPLY stock_count INCREMENT BY 5
  WHERE _id = '123'

  -- No type declaration needed for SELECT
  SELECT * FROM products WHERE _id = '123'
  ```
</CodeGroup>

#### 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):

```sql DQL theme={null}
INSERT INTO COLLECTION products (stock_count COUNTER)
VALUES ({ '_id': '123', 'name': 'Widget', 'stock_count': 100 })
```

2. **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:

<Warning>
  Do not initialize counter fields by inserting an integer value without declaring the `COUNTER` type. This creates a register field, not a counter.
</Warning>

#### Incrementing Counters

<CodeGroup>
  ```swift Swift theme={null}
  // Create document without counter field
  let product = [
    "_id": "123",
    "name": "Widget"
  ]

  await ditto.store.execute(
    query: """
      INSERT INTO COLLECTION products
      INITIAL DOCUMENTS (:product)
      """,
    arguments: [ "product": product ])

  // Then increment the counter (creates it if doesn't exist)
  await ditto.store.execute(
    query: """
      UPDATE COLLECTION products (stock_count COUNTER)
      APPLY stock_count INCREMENT BY 5
      WHERE _id = '123'
      """)
  ```

  ```kotlin Kotlin theme={null}
  // Create document without counter field
  var product = mapOf(
    "_id" to "123",
    "name" to "Widget"
  )

  ditto.store.execute("""
    INSERT INTO COLLECTION products
    INITIAL DOCUMENTS (:product)
    """,
    mapOf("product" to product))

  // Then increment the counter (creates it if doesn't exist)
  ditto.store.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count INCREMENT BY 5
    WHERE _id = '123'
    """)
  ```

  ```javascript JS theme={null}
  // Create document without counter field
  const product = {
    _id: "123",
    name: "Widget"
  };

  await ditto.store.execute(`
    INSERT INTO COLLECTION products
    INITIAL DOCUMENTS (:product)`,
    { product });

  // Then increment the counter (creates it if doesn't exist)
  await ditto.store.execute(`
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count INCREMENT BY 5
    WHERE _id = '123'`);
  ```

  ```java Java theme={null}
  // Create document without counter field
  Map<String, Object> product = new HashMap<>();
  product.put("_id", "123");
  product.put("name", "Widget");

  DittoQueryResult result = (DittoQueryResult) ditto.store.execute(
      "INSERT INTO COLLECTION products INITIAL DOCUMENTS (:product)",
      Collections.singletonMap("product", product)
  );

  // Then increment the counter (creates it if doesn't exist)
  ditto.store.execute(
      "UPDATE COLLECTION products (stock_count COUNTER) " +
      "APPLY stock_count INCREMENT BY 5 WHERE _id = '123'"
  );
  ```

  ```csharp C# theme={null}
  // Create document without counter field
  var args = new Dictionary<string, object>();
  args.Add("product", new { _id = "123", name = "Widget" });

  await ditto.Store.ExecuteAsync(
    "INSERT INTO COLLECTION products INITIAL DOCUMENTS (:product)",
    args);

  // Then increment the counter (creates it if doesn't exist)
  await ditto.Store.ExecuteAsync(
    "UPDATE COLLECTION products (stock_count COUNTER) " +
    "APPLY stock_count INCREMENT BY 5 WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  // Create document without counter field
  std::map<std::string, std::any> product;
  product["_id"] = "123";
  product["name"] = "Widget";

  std::map<std::string, std::any> args;
  args["product"] = product;

  auto result = ditto.get_store().execute(
    "INSERT INTO COLLECTION products INITIAL DOCUMENTS (:product)",
    args).get();

  // Then increment the counter (creates it if doesn't exist)
  ditto.get_store().execute(
    "UPDATE COLLECTION products (stock_count COUNTER) "
    "APPLY stock_count INCREMENT BY 5 WHERE _id = '123'"
  ).get();
  ```

  ```rust Rust theme={null}
  // Create document without counter field
  let query_result = ditto
      .store()
      .execute_v2((
          "INSERT INTO COLLECTION products INITIAL DOCUMENTS (:product)",
          serde_json::json!({
              "product": {
                  "_id": "123",
                  "name": "Widget"
              }
          }),
      )).await?;

  // Then increment the counter (creates it if doesn't exist)
  ditto.store()
      .execute_v2((
          "UPDATE COLLECTION products (stock_count COUNTER) \
           APPLY stock_count INCREMENT BY 5 WHERE _id = '123'",
          serde_json::json!({}),
      )).await?;
  ```

  ```dart Dart theme={null}
  // Create document without counter field
  const product = {
    "_id": "123",
    "name": "Widget"
  };

  await ditto.execute("""
    INSERT INTO COLLECTION products
    INITIAL DOCUMENTS (:product)""",
    {"product": product},
  );

  // Then increment the counter (creates it if doesn't exist)
  await ditto.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count INCREMENT BY 5
    WHERE _id = '123'""",
  );
  ```
</CodeGroup>

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

```sql DQL theme={null}
UPDATE COLLECTION products (stock_count COUNTER)
APPLY stock_count INCREMENT BY -3
WHERE _id = '123'
```

#### 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:**

```sql DQL theme={null}
UPDATE COLLECTION products (stock_count COUNTER)
APPLY stock_count RESTART WITH 100
WHERE _id = '123'
```

**Reset counter to zero:**

```sql DQL theme={null}
UPDATE COLLECTION products (stock_count COUNTER)
APPLY stock_count RESTART
WHERE _id = '123'
```

<CodeGroup>
  ```swift Swift theme={null}
  // Set counter to specific value
  await ditto.store.execute(
    query: """
      UPDATE COLLECTION products (stock_count COUNTER)
      APPLY stock_count RESTART WITH 100
      WHERE _id = '123'
      """)

  // Reset counter to zero
  await ditto.store.execute(
    query: """
      UPDATE COLLECTION products (stock_count COUNTER)
      APPLY stock_count RESTART
      WHERE _id = '123'
      """)
  ```

  ```kotlin Kotlin theme={null}
  // Set counter to specific value
  ditto.store.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART WITH 100
    WHERE _id = '123'
    """)

  // Reset counter to zero
  ditto.store.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART
    WHERE _id = '123'
    """)
  ```

  ```javascript JS theme={null}
  // Set counter to specific value
  await ditto.store.execute(`
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART WITH 100
    WHERE _id = '123'`);

  // Reset counter to zero
  await ditto.store.execute(`
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART
    WHERE _id = '123'`);
  ```

  ```java Java theme={null}
  // Set counter to specific value
  ditto.store.execute(
      "UPDATE COLLECTION products (stock_count COUNTER) " +
      "APPLY stock_count RESTART WITH 100 WHERE _id = '123'"
  );

  // Reset counter to zero
  ditto.store.execute(
      "UPDATE COLLECTION products (stock_count COUNTER) " +
      "APPLY stock_count RESTART WHERE _id = '123'"
  );
  ```

  ```csharp C# theme={null}
  // Set counter to specific value
  await ditto.Store.ExecuteAsync(
    "UPDATE COLLECTION products (stock_count COUNTER) " +
    "APPLY stock_count RESTART WITH 100 WHERE _id = '123'");

  // Reset counter to zero
  await ditto.Store.ExecuteAsync(
    "UPDATE COLLECTION products (stock_count COUNTER) " +
    "APPLY stock_count RESTART WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  // Set counter to specific value
  ditto.get_store().execute(
    "UPDATE COLLECTION products (stock_count COUNTER) "
    "APPLY stock_count RESTART WITH 100 WHERE _id = '123'"
  ).get();

  // Reset counter to zero
  ditto.get_store().execute(
    "UPDATE COLLECTION products (stock_count COUNTER) "
    "APPLY stock_count RESTART WHERE _id = '123'"
  ).get();
  ```

  ```rust Rust theme={null}
  // Set counter to specific value
  ditto.store()
      .execute_v2((
          "UPDATE COLLECTION products (stock_count COUNTER) \
           APPLY stock_count RESTART WITH 100 WHERE _id = '123'",
          serde_json::json!({}),
      )).await?;

  // Reset counter to zero
  ditto.store()
      .execute_v2((
          "UPDATE COLLECTION products (stock_count COUNTER) \
           APPLY stock_count RESTART WHERE _id = '123'",
          serde_json::json!({}),
      )).await?;
  ```

  ```dart Dart theme={null}
  // Set counter to specific value
  await ditto.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART WITH 100
    WHERE _id = '123'""",
  );

  // Reset counter to zero
  await ditto.execute("""
    UPDATE COLLECTION products (stock_count COUNTER)
    APPLY stock_count RESTART
    WHERE _id = '123'""",
  );
  ```
</CodeGroup>

#### Combining Counter Operations with Other Updates

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

```sql DQL theme={null}
UPDATE COLLECTION products (stock_count COUNTER)
APPLY stock_count INCREMENT BY 10
SET lastUpdated = '2025-12-16', updatedBy = 'user123'
WHERE _id = '123'
```

#### Querying Counter Values

You can retrieve the current value of a counter using a `SELECT` statement:

```sql DQL theme={null}
SELECT * FROM COLLECTION products (stock_count COUNTER)
WHERE _id = '123'
```

The counter value will appear as a regular integer in the query results:

```json theme={null}
{
  "_id": "123",
  "name": "Widget",
  "stock_count": 105
}
```

#### Counter vs PN\_COUNTER

The key differences between `COUNTER` and `PN_COUNTER`:

| Feature               | COUNTER             | PN\_COUNTER |
| --------------------- | ------------------- | ----------- |
| Increment/Decrement   | ✓                   | ✓           |
| Set to specific value | ✓ (RESTART WITH)    | ✗           |
| Reset to zero         | ✓ (RESTART)         | ✗           |
| Conflict resolution   | PN + LWW on RESTART | PN only     |
| Available since       | 4.13+               | 4.11+       |

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)

<Warning>
  PN Counters are available in 4.11 and later. [Read more](/dql/strict-mode)
  Users should use `Counter` which also provide the ability to set the counter value.
</Warning>

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

<Warning>
  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.
</Warning>

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:

<CodeGroup>
  ```swift Swift theme={null}
  // Create document without counter field
  let product = [
    "_id": "123",
    "updatedBy": "abc123"
  ]

  await ditto.store.execute(
    query: """
      INSERT INTO COLLECTION products
      INITIAL DOCUMENTS (:product)
      """,
    arguments: [ "product": product ])

  // Then increment the counter (creates it if doesn't exist)
  await ditto.store.execute(
    query: """
      UPDATE products
      APPLY in_stock PN_INCREMENT BY 5.0
      WHERE _id = '123'
      """)
  ```

  ```kotlin Kotlin theme={null}
  // Create document without counter field
  var product = mapOf(
    "_id" to "123",
    "updatedBy" to "abc123"
  )

  ditto.store.execute("""
    INSERT INTO products
    INITIAL DOCUMENTS (:product)
    """,
    mapOf("product", product))

  // Then increment the counter (creates it if doesn't exist)
  ditto.store.execute("""
    UPDATE products
    APPLY in_stock PN_INCREMENT BY 5.0
    WHERE _id = '123'
    """)
  ```

  ```javascript JS theme={null}
  // Create document without counter field
  const product = {
    _id: "123",
    updatedBy: "abc123"
  };

  await ditto.store.execute(`
    INSERT INTO products
    INITIAL DOCUMENTS (:product)`,
    { product });

  // Then increment the counter (creates it if doesn't exist)
  await ditto.store.execute(`
    UPDATE products
    APPLY in_stock PN_INCREMENT BY 5.0
    WHERE _id = '123'`);
  ```

  ```java Java theme={null}
  // Create document without counter field
  Map<String, Object> product = new HashMap<>();
  product.put("_id", "123");
  product.put("updatedBy", "abc123");

  DittoQueryResult result = (DittoQueryResult) ditto.store.execute(
      "INSERT INTO products INITIAL DOCUMENTS (:product)",
      Collections.singletonMap("product", product)
  );

  // Then increment the counter (creates it if doesn't exist)
  ditto.store.execute(
      "UPDATE products APPLY in_stock PN_INCREMENT BY 5.0 WHERE _id = '123'"
  );
  ```

  ```csharp C# theme={null}
  // Create document without counter field
  var args = new Dictionary<string, object>();
  args.Add("product", new { _id = "123", updatedBy = "abc123" });

  await ditto.Store.ExecuteAsync(
    "INSERT INTO products INITIAL DOCUMENTS (:product)",
    args);

  // Then increment the counter (creates it if doesn't exist)
  await ditto.Store.ExecuteAsync(
    "UPDATE products APPLY in_stock PN_INCREMENT BY 5.0 WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  // Create document without counter field
  std::map<std::string, std::any> product;
  product["_id"] = "123";
  product["updatedBy"] = "abc123";

  std::map<std::string, std::any> args;
  args["product"] = product;

  auto result = ditto.get_store().execute(
    "INSERT INTO products INITIAL DOCUMENTS (:product)",
    args).get();

  // Then increment the counter (creates it if doesn't exist)
  ditto.get_store().execute(
    "UPDATE products APPLY in_stock PN_INCREMENT BY 5.0 WHERE _id = '123'"
  ).get();
  ```

  ```rust Rust theme={null}
  // Create document without counter field
  let query_result = ditto
      .store()
      .execute_v2((
          "INSERT INTO products INITIAL DOCUMENTS (:product)",
          serde_json::json!({
              "product": {
                  "_id": "123",
                  "updatedBy": "abc123"
              }
          }),
      )).await?;

  // Then increment the counter (creates it if doesn't exist)
  ditto.store()
      .execute_v2((
          "UPDATE products APPLY in_stock PN_INCREMENT BY 5.0 WHERE _id = '123'",
          serde_json::json!({}),
      )).await?;
  ```

  ```dart Dart theme={null}
  // Create document without counter field
  const product = {
    "_id": "123",
    "updatedBy": "abc123"
  };

  await ditto.execute("""
    INSERT INTO products
    INITIAL DOCUMENTS (:product)""",
    {"product": product},
  );

  // Then increment the counter (creates it if doesn't exist)
  await ditto.execute("""
    UPDATE products
    APPLY in_stock PN_INCREMENT BY 5.0
    WHERE _id = '123'""",
  );
  ```
</CodeGroup>

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.

<CodeGroup>
  ```sql STRICT_MODE=false theme={null}
  UPDATE products
  APPLY in_stock PN_INCREMENT BY 1.0
  SET updatedBy = 'def456'
  WHERE _id = '123'
  ```

  ```sql STRICT_MODE=true theme={null}
  UPDATE COLLECTION products (in_stock PN_COUNTER)
  APPLY in_stock PN_INCREMENT BY 1.0
  SET updatedBy = 'def456'
  WHERE _id = '123'
  ```
</CodeGroup>

You can then retrieve the latest value of a counter using a `SELECT` statement:

<CodeGroup>
  ```sql STRICT_MODE=false theme={null}
  SELECT * FROM products
  WHERE _id = '123'
  ```

  ```sql STRICT_MODE=true theme={null}
  SELECT * FROM COLLECTION products (in_stock PN_COUNTER)
  WHERE _id = '123'
  ```
</CodeGroup>

### **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*

<CodeGroup>
  ```sql 4.11+ theme={null}
  field1 = default()
  ```

  ```sql <4.10 theme={null}
  field1 -> default()
  ```
</CodeGroup>

## Declaring Type Definition

With [strict mode](/dql/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.
​

<img src="https://mintcdn.com/ditto-248bc0d1/pf3IlRElveI85RoM/images/dql/TypeDefinition.svg?fit=max&auto=format&n=pf3IlRElveI85RoM&q=85&s=9f7a40e1c8feb3f90da083299c77df37" alt="Type Definition Syntax Diagram" width="451" height="241" data-path="images/dql/TypeDefinition.svg" />

### 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.

```sql theme={null}
UPDATE COLLECTION orders (updatedAt REGISTER)
SET updatedAt = {
	"datetime": "2025-02-28",
	"authorId": "67c0faa40054d13a000c614a"
}
WHERE _id = 'my-id'

SELECT * FROM COLLECTION orders (updatedAt REGISTER) WHERE _id = 'my-id'
```

The results of the SELECT statement above would be:

```json theme={null}
{
  "_id": "my-id"
  "regionId": "01234",
  "items": {
    "shake": {...},
    "fries": {...},
    "burger": {...}
  },
  "updatedAt": {
	  "datetime": "2025-02-28",
	  "updatedBy": "67c0faa40054d13a000c614a"
  }
}
```

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.

```sql theme={null}
UPDATE COLLECTION orders (updatedAt REGISTER)
UNSET updatedAt
WHERE _id = 'my-id'
```

You will receive an error if you attempt to `SET` or `UNSET` a nested key of a register using dot notation.

```sql theme={null}
UPDATE COLLECTION orders (updatedAt REGISTER)
SET updatedAt.datetime = "2025-04-28"
WHERE _id = 'my-id'
-- Unsupported DML operation on REGISTER field "updatedAt"
```

### Non-Registers

<Warning>
  In 4.11+ and `DQL_STRICT_MODE=false`, collection definitions for non-registers are no longer required.

  [Read more](/dql/strict-mode)
</Warning>

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

```sql DQL theme={null}
-- String ID
INSERT INTO cars VALUES ({"_id": "car-123", "color": "blue"})

-- Numeric ID
INSERT INTO cars VALUES ({"_id": 42, "color": "red"})
```

**Example - Invalid Document ID:**

```sql DQL theme={null}
-- This will fail in SDK 5.0+
INSERT INTO cars VALUES ({"_id": null, "color": "green"})
```

<Warning>
  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.
</Warning>

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:

```sql DQL theme={null}
SELECT *
FROM COLLECTION your_collection_name (field1 MAP, field2 ATTACHMENT)
WHERE field1.rating > 100
```

In this syntax:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 data_type, field2 data_type, ...) ...
```

* `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**

```sql DQL theme={null}
SELECT *
FROM COLLECTION your_collection_name (field1 MAP, field2 ATTACHMENT)
```

**UPDATE with Definition**

```sql DQL theme={null}
UPDATE COLLECTION your_collection_name (field1 MAP, field2 ATTACHMENT)
SET ...
```

**INSERT with Definition**

```sql DQL theme={null}
INSERT INTO COLLECTION your_collection_name (field1 MAP, field2 ATTACHMENT)
DOCUMENTS (...)
```

**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, ...)`:

```sql DQL theme={null}
... COLLECTION your_collection_name (map_name MAP(sub1 ATTACHMENT, sub2 MAP))
```

**Single MAP**

The syntax for a single `MAP` with all other fields type `REGISTER`:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 MAP)
```

**Single ATTACHMENT**

The syntax for a single `ATTACHMENT` with all other fields type `REGISTER`:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 ATTACHMENT)
```

**MAP and ATTACHMENT**

The syntax for a single `MAP` and a single `ATTACHMENT` with all other fields type `REGISTER`:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 MAP, field2 ATTACHMENT)
```

**Deeply Embedded MAP**

<Warning>
  ### Disable Strict Mode

  In 4.11+ and `DQL_STRICT_MODE=false`, collection definitions are no longer required.

  [Read more](/dql/strict-mode)
</Warning>

The syntax for a document hierarchy of depth two — a single `MAP` nested with another `MAP` — with all other fields type `REGISTER`:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 MAP(sub1 MAP))
```

The syntax for a document hierarchy of depth four with all other fields type `REGISTER`:

```sql DQL theme={null}
... COLLECTION your_collection_name (field1 MAP(sub1 MAP(s_sub1 MAP(s_s_sub1 MAP))))
```
