---
title: Counter
slug: v4-4/y7lt_FrNIXWP7LtY0rssI
docTags: 
createdAt: 2023-10-05T15:24:38.950Z
---

The `counter` CRDT is a special type intended for use cases where multiple peers need to increment (or decrement) field values at the same time while maintaining consistency; for example, in scenarios such as inventory management and voting. 

There are two methods of modifying an existing `counter` value:

:::hint{type="warning"}
The `counter` type is useful in very select scenarios. Consider using the `set` method to define new values on a `map` to track increments, while also storing additional metadata.&#x20;
:::

- Increment — Increases the `counter` value by the specific number you want to add to the `counter` value.

- Decrement — Decreases the `counter` value by the specific negative value that you want to subtract from the `counter`.&#x20;

# Common Use Case

A typical exampe for using a `counter`type: four flight attendants walk through an airlane and record passenger meal orders. For each seat, they count the number of cans ordered.

First, the flight attendant selects the flight they are on. They then see an interface with each seat in a list view. This might be retrieved by an external API, or it could be internal to the app based on some known information about each plane type.

In a production app, each seat would have a few values that the attendant can modify, but we will just focus on `name` and `orderedCount` in this example. As each flight attendant walks through the airplane, their devices synchronize together to make sure that each customer only receives one meal.

If in this case, you used a `register` type instead of a `counter`, the value alternates between the latest updated value. Instead, using a `counter` merges by taking the sum of each attendant's value. If attendant A has sold 100, attendent B has sold 33, and attendant C has sold 98, the value of the counter is 100 + 33 + 98 = 231.

In addition to incrementing counters, you can also decrement a `counter` .&#x20;

:::hint{type="warning"}
There is no mechanism preventing a counter from reaching a negative value. For example, if the initial count is 1 and two attendants decrement the count by 1 concurrently, the result is -1.
:::

# Creating a Counter&#x20;

To create a `counter`, use the Upsert method to ensure the document exists in Ditto, and then use the Update method to increment the `counter` and ensure that the correct and accurate `counter` value is maintained across all peers during replication:

:::hint{type="warning"}
Do not use the Upsert method to increment a `counter`. Only modify the `counter` field within an `update` clause, as follows:
:::

:::::WorkflowBlock
:::WorkflowBlockItem
Define the structure of the `counter` document.
:::

::::WorkflowBlockItem
Using the Upsert method, ensure that the document exists before interacting with it.&#x20;

:::hint{type="info"}
If it does not exist, Ditto automatically creates the document and initializes the `counter` with a set value of `0`.
:::
::::

:::WorkflowBlockItem
To increment the counter value, call the Update method and pass the value you want to increment the `counter` by.&#x20;
:::
:::::

:::CodeblockTabs
```swift
do {
    let docId = try ditto.store["people"].upsert([
        "name": "Frank",
        "ownedCars": DittoCounter() // here 0 is a number
    ])

    ditto.store["people"].findByID(docId).update({ mutableDoc in
        mutableDoc?["ownedCars"].counter?.increment(by: 1)
    })
} catch {
    //handle error
    print(error)
}
```

```kotlin
val docId = ditto.store["people"].upsert(mapOf(
    "name" to "Frank",
    "ownedCars" to DittoCounter()
))

ditto.store.collection("people").findById(docId).update { mutableDoc ->
    mutableDoc!!["ownedCars"].counter!!.increment(amount = 1.0)
}
```

```javascript
const frankId = await ditto.store.collection('people').upsert({
  name: 'Frank',
  ownedCars: 0, // here 0 is a number
})

await ditto.store
  .collection('people')
  .findByID(frankId)
  .update((mutableDoc) => {
    mutableDoc.at('ownedCars').set(new Counter())
    mutableDoc.at('ownedCars').counter.increment(1)
  })
```

```java
Map<String, Object> content = new HashMap<>();
content.put("name", "Frank");
content.put("ownedCars", new DittoCounter());
DittoDocumentId docId = ditto.store.collection("people").upsert(content);

ditto.store.collection("people").findById(docId).update(mutDoc -> {
    assertThat(mutDoc).isNotNull();
    DittoMutableCounter counter = mutDoc.get("ownedCars").getCounter();
    assertThat(counter).isNotNull();
    counter.increment(1);
});
```

```csharp
var counter = new DittoCounter();

var docId = coll.Upsert(new Dictionary<string, object> {
    { "make", "Honda" }, { "mileage", counter }
});

_ = coll.FindById(docId).Update(mutableDoc =>
{
    mutableDoc["mileage"].Counter.Increment(100);
});
```

```cpp
DocumentId docID = ditto.get_store().collection("people").upsert(
    {{"name", "Frank"}, {"ownedCars", Counter()}});

ditto.get_store().collection("people").find_by_id(docID).update(
    [](MutableDocument &doc) {
      auto counter = doc["ownedCars"].get_counter();
      counter->increment(1);
    });
```

```rust
let collection = ditto.store().collection("people").unwrap();
let doc_id = collection
    .upsert(json!({"name": "Frank", "owned_cars": DittoCounter::new()}))
    .unwrap();

collection
    .find_by_id(doc_id)
    .update(|x| {
        if let Some(doc) = x {
            doc.increment("owned_cars", 1.0).unwrap();
        }
    })
    .unwrap();
```
:::

# Resetting Counter Values

A `counter` is capable of recalling its previous increment and decrement calls; however, with a `counter` there is no concept of time. This is true even if you change the type of a counter to another field, such as number, and then back into a counter again.

Given this constraint, avoid resetting `counters`. Instead, create new documents or collections that have meaning and encapsulate a period of time.

:::hint{type="warning"}
If it is essential that you reset the `counter` to zero (0), make sure to remove the `counter` from the document before attempting to reset the `counter`.&#x20;

Note that if another peer increments the `counter` at the same time that another peer removes the `counter`, you may encounter unexpected behavior.
:::

# Code Example

The following snippet demonstrates code for the typical example for a `counter` described in [Common Use Case](./#common-use-case), previous.

:::::WorkflowBlock
:::WorkflowBlockItem
Ensure the document exists before interacting with it by calling Upsert.
:::

::::WorkflowBlockItem
Increment the `counter` by calling Update.

:::hint{type="warning"}
Do not use Upsert to increment a counter. Incrementing a `counter` using Upsert results in the `counter` resetting to its initial value.

This is because when you upsert, you're effectively replacing the entire document with a new one. That is, each field updates, even the document fields that remain unchanged. For more information, see [Upserting and Updating](docId\:L2hFjvzUYyNu5ahZXHwAJ).
:::

:::hint{type="info"}
If you need to perform an upsert at a later time, ensure that your counter value remains consistent by excluding it in your upsert operation.
:::
::::

:::WorkflowBlockItem
When the app loads, call `populateSeats()`, and then `renderSeatList()`.
:::

:::WorkflowBlockItem
Since flight attendants keep track of orders, locally populate all known flights with default data.&#x20;

That way, flight attendants can start collecting information from te beginning, even without an internet connection or any connection with other peers.&#x20;
:::

::::WorkflowBlockItem
Initialize the `counter` `orderedCount`  by calling `DittoCounter()`.

:::hint{type="info"}
The `totalAvailable`  variable is stored in a `register`; therefore, the value remains constant and does *not* change unless you explicitly make an update to it at a later time.
:::
::::

::::WorkflowBlockItem
Render the seat list by incrementing the `counter` when a user selects the `Plus` button on the interface.

:::CodeblockTabs
```swift
do {
    let docID = try ditto.store["people"].upsert([
        "name": "Frank",
        "age": 31,
        "ownedCars": DittoCounter()
    ])

    ditto.store["people"].findByID(docID).update { mutableDoc in
        mutableDoc?["age"] = 32
        mutableDoc?["ownedCars"].counter?.increment(by: 1)
    }
} catch {
    //handle error
    print(error)
}
```

```kotlin
ditto.store.collection("people").findById(frankId).update { mutableDoc ->
    mutableDoc?.let {
        it["age"].set(32)
        it["ownedCars"].counter!!.increment(amount = 1.0)
    }
}
```

```javascript
const docID = await ditto.store.collection('people').upsert({
  name: 'Frank',
  age: 31,
  ownedCars: 0,
})

await ditto.store
  .collection('people')
  .findByID(docID)
  .update((mutableDoc) => {
    mutableDoc.at('age').set(32)

    mutableDoc.at('ownedCars').set(new Counter())
    mutableDoc.at('ownedCars').counter.increment(1)
  })
```

```java
// not supported in Java
```

```csharp
var content = new Dictionary<string, object>
{
    { "name", "Bob" },
    { "age", 40 },
    { "ownedCars",  new DittoCounter() }
};

var docId = Ditto.Store.Collection("people").Upsert(content);
Ditto.Store.Collection("people").FindById(docId).Update(mutableDoc =>
{
    mutableDoc["age"].Set(32);
    mutableDoc["ownedCars"].Counter.Increment(1);
});
```

```cpp
DocumentId doc_id = ditto.get_store().collection("people").upsert(
    {{"name", "Frank"}, {"age", 31}, {"ownedCars", Counter()}});

ditto.get_store().collection("people").find_by_id(doc_id).update(
    [](MutableDocument &doc) {
      doc["age"].set(32);
      auto counter = doc["ownedCars"].get_counter();
      counter->increment(1);
    });
```

```rust
let collection = ditto.store().collection("people").unwrap();
let doc_id = collection
    .upsert(json!({"name": "Frank", "owned_cars": DittoCounter::new()}))
    .unwrap();

collection
    .find_by_id(doc_id)
    .update(|opt_doc| {
        if let Some(doc) = opt_doc {
            doc.set("age", 32).unwrap();
            doc.increment("owned_cars", 1.0).unwrap();
        }
    })
    .unwrap();
```
:::
::::

:::WorkflowBlockItem
If you want to notify flight attendants when an item is out of stock and no longer available, compare the number of orders made (`orderCount`) with the total quantity available (`totalAvailable`).&#x20;

If the number of orders equals or exceeds the total available quantity, the app signals that the item is no longer in stock.&#x20;
:::
:::::

#

