---
title: MAP
slug: v4-6/document-model/data-types/map
docTags: 
createdAt: 2023-10-05T15:11:08.674Z
---

An alternative approach to using a JSON object as a `REGISTER` to represent and organize data in a hierarchical structure within a document is to use a `MAP`.

The `MAP` type uses the add-wins approach to resolving concurrency conflicts.&#x20;

So instead of choosing a single definitive value to merge and distribute across peers like the last-write-wins strategy utilized by the `REGISTER` and `ATTACHMENT` data types, choose them all  — even if the data is already contained within the `MAP` object.&#x20;

# Comparing a JSON Object

The `MAP` data type in DQL forms a tree-like, parent-child relationship within a document's dataset. This is similar to a JSON object, which functions as a single unit. However, a `MAP` allows:

- Nesting of various DQL data types:`REGISTER`, `MAP`, and `ATTACHMENT`as values for its keys.``

- Each key-value pair within a `MAP` can be modified independently without affecting other pairs.

- Utilization of an *add-wins* merge strategy for conflict resolution. When conflicting changes occur during sync, all conflicting changes are retained rather than converging on only one. This ensures that no data is lost.

# MAP Data Structure

The response structure of a `MAP` is a JSON-like object; however, each field in a `MAP` has a corresponding data type. This means that each field within a `MAP` has an independent merge definition the same as fields on the top-level document.

The `MAP` type is useful when creating a list of non-conflicting items and updating items over time within a document.&#x20;

The following snippet demonstrates a Ditto document with an embedded `MAP` `my_map`:

:::CodeblockTabs
Ditto Document

```json
{
  "_id": "123"
  "my_map": {
    "sub_field": "value",
  }
}
```
:::

# Declaring a MAP

The `REGISTER` is the default data type in Ditto; therefore, there is no need for explicit declaration — it's implicitly assumed. &#x20;

However, to use a `MAP` (and `ATTACHMENT`) data type, you must explicitly declare it within your DQL statement using t*ype definition&#x20;*&#x73;yntax.&#x20;

A type definition is a description informing Ditto that the given set of key-value pairs embedded in the document as a nested structure is distinct from the default `REGISTER` type. This definition specifies how Ditto should handle the dataset within that nested structure.

To declare a data type a `MAP`, you use the `COLLECTION` identifier prefix, followed by the collection name enclosed in parentheses alongside a type definition. All done inline as follows:&#x20;

:::CodeblockTabs
DQL

```sql
... COLLECTION your_collection_name (my_map_1 MAP, my_map_2 MAP, ...) ...
```
:::

For more information and how-to instructions, see *Ditto Query Language&#x20;*> [Types and Definitions](docId\:I1cPg-wxtOtz6EBNw9Z9e)

# Merge Strategy with MAP

The `MAP` type has an add-wins merge behavior. This means that the adding or updating of a field always wins over the removal (`tombstone`) of the same field when performing a merge across two peers.&#x20;

## Conflicts with Multiple Field Entries

If multiple fields are added across Small Peer devices, the result will be a `MAP` with all added fields. This is because each field within a `MAP` is associated with a distinct data type; that is, each can be represented as a `REGISTER`, `MAP`, or an `ATTACHMENT`.&#x20;

That means, unlike a JSON blob object, which acts as a `REGISTER` functioning as a single unit, the merge strategy for each field within a `MAP` object depends on each of their data type representation.&#x20;

In the following example, USER 1 inserts `item1` and USER 2 inserts `item2` to an `items` `MAP`:

:::CodeblockTabs
DQL

```sql
# USER 1 adds item1
UPDATE COLLECTION my_collection (items MAP)
SET
  items -> (item1 = 'red')
WHERE
  _id = '123' 
```
:::

:::CodeblockTabs
DQL

```sql
# USER 2 adds item2
UPDATE COLLECTION my_collection (items MAP)
SET
  items -> (item2 = 'blue')
WHERE
  _id = '123' 
```
:::

The resulting document contains an `items` `MAP` with both `item1` and `item2:`

:::CodeblockTabs
Ditto Document

```json
{
  "_id": "123"
  "items": {
    "item1": "red",
    "item2": "blue",
  }
}
```
:::

## Comparing MAP With REGISTER Types

The nested JSON object functions as a `REGISTER `and closely resembles a nested `MAP` structure within a document. However, their merge strategies and supported types for nested values are significantly different.

Understanding these distinctions is crucial when selecting the appropriate data type for expressing your data structure.

### Example Scenario

For example, consider a document with a `properties` field of the `REGISTER` data type storing information about a car in an embedded JSON object.&#x20;

You want to change only the `color` field for a car identified by document ID `123` to `blue`.

Since a `REGISTER` acts as a single object, to update just the `color`, you'd need to update the entire object by providing the entire set of nested key-value pairs, including the modified `color` , as follows:

:::CodeblockTabs
```swift
let newProperties = [
  "color": "blue",
  "sunroof": true,
  "door_count": 4
]
  
await ditto.store.execute(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",
  ["newProperties": newProperties]);
```

```kotlin
var result = ditto.store.execute(
  "INSERT INTO cars DOCUMENTS (:newCar)",
  mapOf("newProperties"
    to mapOf("color" to "blue", "sunroof" to true, "door_count" to 4)))
```

```javascript
const newProperties = {
  color: 'blue',
  sunroof: true,
  door_count: 4
}
  
await ditto.store.execute(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",     
  {newProperties});
```

```java
Map<String, String> newProperties = new HashMap<>();
newCar.put("color", "blue");
newCar.put("sunroof", true);
newCar.put("door_count", 4);

DittoQueryResult result = (DittoQueryResult) ditto.store.execute(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",
  Collections.singletonMap("newProperties", newProperties),
  new Continuation<>() {
    @NonNull
    @Override
    public CoroutineContext getContext() {
      return EmptyCoroutineContext.INSTANCE;
    }

    @Override
    public void resumeWith(@NonNull Object o) {
      if (o instanceof Result.Failure) {
        // Handle failure
      }
    }
  }
);

```

```csharp
var args = new Dictionary<string, object>();
args.Add("newProperties", new { color="blue", sunroof=true, doorCount=4});

await ditto.Store.ExecuteAsync(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",
  args);
```

```cpp
struct Properties {
  std::string color;
  bool sunroof;
  int door_count;
};

// ...

std::map<std::string, Properties> args;
args["newProperties"] = {"blue", true, 4};

ditto.get_store().execute(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",
  args);
```

```rust
struct Properties {
  color: String,
  sunroof: bool,
  door_count: i32
}

struct Args {
  newProperties: Properties,
}

// ...

let args = Args {
  newProperties: Properties {
    color: "blue".to_string(),
    sunroof: true,
    door_count: 4
  }
};

await ditto.store().execute(
  "UPDATE cars SET properties = :newProperties WHERE _id = '123'",
  args);
```
:::

However, if the `properties` field is of the `MAP` data type instead, to update the color without altering the other nested key-value pairs in the set, assign the `properties.color` field to `blue` as follows:

:::CodeblockTabs
DQL

```sql
UPDATE COLLECTION cars (properties MAP)
SET properties -> (color = blue)
WHERE _id = '123'
```
:::

## Conflicts with Tombstone and Modify

If a tombstone (deletion flag) and a modification (`INSERT`) operation occur across two peers simultaneously, the resulting state depends on the *causal order* of these changes.

Causal order refers to the logical sequence in which events occur, considering the cause-and-effect relationship between them.&#x20;

:::hint{type="warning"}
Modifications to the value of fields within a `MAP` do not count as modifications to the `MAP` itself.&#x20;
:::

Following is a truth table showing "if a `MAP` is deleted" based on "if the peers are connected" and "when the tombstone operation occurs relative to the modification."&#x20;

| **PEER Status** | **Tombstone First** | **Tombstone Last** |
| --------------- | ------------------- | ------------------ |
| Connected       | FALSE               | TRUE               |
| Disconnected    | FALSE               | FALSE              |

## Warnings and Cautions

:::hint{type="danger"}
Syncing large documents can significantly impact sync performance.&#x20;

Consider using an `ATTACHMENT` instead of a regular document when handling a deeply embedded or large document object.&#x20;
:::

:::hint{type="warning"}
When internet connection is limited or using only Bluetooth Low Energy (LE) to sync across connected peers, syncing documents containing large amounts of data is slow.&#x20;

For instance, when using only Bluetooth LE to sync a document of a typical size, the rate of replication is 20 KB per second maximum. Given this, a document of 250 KB or larger may require 10 seconds or more to sync for the first time between Small Peer devices.

A slow replication rate results in a loading spinner displayed to your end users until the sync process fully completes. This is due to the callback being unable to render the returned data; the end-to-end sync process requires that documents be broken down into smaller parts before being synced across the mesh, and then, until the client receives all of the smaller parts and reconstructs them, the document is not returned.

Instead of using a single document to encode large datasets, use a series of smaller documents.

- If a document exceeds 250 KB, a `stdout` warning prints. &#x20;
- Any documents exceeding 5 MB do not sync to other peers.
:::

