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

# Removing Documents

> This article provides information on removing documents within Ditto.

<Info>
  When using `DELETE` ensure that devices running Ditto Edge SDK are on version **4.10.1 and later**.

  For more information on how to use delete and manage data reach out to [Ditto's Customer Support](https://support.ditto.com)
</Info>

<Note>
  When using `DELETE` on Edge SDKs-Only deployments reach out to [Ditto's Customer Support](https://support.ditto.com) to ensure the
  best design patterns are being used to avoid data loss and/or performance related issues.
</Note>

<Warning>
  Deletes in Ditto requires all devices to connect and share information within the TTL window. If a device goes offline then appears later after all other
  devices have evicted the deleted document the connecting device will not know the document was deleted and share it with other peers.

  Deletes in Ditto require all devices to connect and share updates within the configured TTL (time-to-live) window. Default 7 days (TOMBSTONE\_TTL\_HOURS) for
  Edge SDK, 30 days for Ditto Cloud.

  If a device goes offline and returns after all other devices have already evicted the deleted document, that device will not be aware of the deletion.
  As a result, it may reintroduce the document to the sync network, causing it to reappear on other devices.

  To learn more about how to best use DELETE in your application reach out to [Ditto's Customer Support](https://support.ditto.com)
</Warning>

In Ditto, there are two key concepts for removing documents:

1. Document Deletions using the `DELETE` keyword in DQL
2. Document Evictions using the `EVICT` keyword in DQL

Deletions and evictions are designed to work together to give you flexibility in managing data within your Ditto application.

* Use `DELETE` when you want to permanently remove a document from a collection. Once a document is deleted with `DELETE`,
  it is considered gone from the user’s perspective and cannot be recovered.
* Use `EVICT` when you want to remove data only from the local device. Evictions are useful for scenarios where an Edge SDK
  only needs to store and sync part of the database. Evicting a document doesn’t delete it from the system — it just frees up local storage.

In general, for applications with both Ditto Servers and Edge SDKs, the typical approach is:

* Use `DELETE` to manage permanent deletion on Ditto Server.
* Use `EVICT` to manage data on a Ditto Edge SDK.

***Here’s an example to show how these might work together:***

Imagine a system where Devices with Ditto Edge SDK only need to keep documents for 3 days due to storage limits, while Ditto Server store documents for 90
days to meet data retention requirements. In this case, the Ditto Edge SDK would evict any document older than 3 days, removing them
from local storage without deleting them from the system. After 90 days, the user would use the Ditto Server HTTP API to permanently delete the
documents.

## Deleting Data

<Info>
  Auto-eviction of expired tombstones is enabled on SDK version **4.10.1 and later**.
</Info>

The `DELETE` keyword in DQL permanently removes one or more specified documents from the Ditto system. Once deleted these
document are non-recoverable.

```sql DQL theme={null}
DELETE FROM cars WHERE _id = '123'
```

<Tip>For complete DQL syntax for deleting, see [DELETE](/dql/delete).</Tip>

### Example `DELETE`

<CodeGroup>
  ```swift Swift theme={null}
  await ditto.store.execute("DELETE FROM cars WHERE _id = '123'");
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("DELETE FROM cars WHERE _id = '123'")
  ```

  ```javascript JS theme={null}
  await ditto.store.execute("DELETE FROM cars WHERE _id = '123'");
  ```

  ```typescript TypeScript theme={null}
  await ditto.store.execute("DELETE FROM cars WHERE _id = '123'");
  ```

  ```java Java theme={null}
  ditto.getStore().execute("DELETE FROM cars WHERE _id = '123'")
      .close(); // DittoQueryResult from execute() MUST always be explicitly closed
  ```

  ```csharp C# theme={null}
  await ditto.Store.ExecuteAsync("DELETE FROM cars WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  ditto.get_store().execute("DELETE FROM cars WHERE _id = '123'");
  ```

  ```rust Rust theme={null}
  ditto.store().execute("DELETE FROM cars WHERE _id = '123'").await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("DELETE FROM cars WHERE _id = '123'");
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute("DELETE FROM cars WHERE _id = '123'", nil)
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup
  ```
</CodeGroup>

### Deleting Multiple Documents in a Collection

All documents specified in the `DELETE` condition will be permanently deleted. The ids of deleted document can be referenced
using the `mutatedDocumentIDs` method on the `result`.

The following example permanently deletes all `blue` cars stored in the `cars` collection:

<CodeGroup>
  ```swift Swift theme={null}
  let result = await ditto.store.execute(
    "DELETE FROM cars WHERE color = 'blue'");

  result.mutatedDocumentIDs.forEach() { print($0) }
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("DELETE FROM cars WHERE color = 'blue'").use { result ->
      result.mutatedDocumentIDs().forEach { id ->
          println(id)
      }
  }
  ```

  ```javascript JS theme={null}
  const result = await ditto.store.execute(
    "DELETE FROM cars WHERE color = 'blue'"
  );

  console.log(result.mutatedDocumentIDs());
  ```

  ```typescript TypeScript theme={null}
  import { QueryResult } from '@dittolive/ditto';

  const result: QueryResult = await ditto.store.execute(
    "DELETE FROM cars WHERE color = 'blue'"
  );

  console.log(result.mutatedDocumentIDs());
  ```

  ```java Java theme={null}
  // This try-with-resources block auto-closes the DittoQueryResult
  try (DittoQueryResult result = ditto.getStore().execute(
      "DELETE FROM cars WHERE color = 'blue'").toCompletableFuture().join()) {
      result.mutatedDocumentIDs().forEach(System.out::println);
  }
  ```

  ```csharp C# theme={null}
  using var result = await ditto.Store.ExecuteAsync(
    "DELETE FROM cars WHERE color = 'blue'");

  result.MutatedDocumentIds.ForEach(id => Console.WriteLine(id));
  ```

  ```cpp C++ theme={null}
  auto result = ditto.get_store().execute(
    "DELETE FROM cars WHERE color = 'blue'");

  for (DocumentId id : result) {
      std::cout << id.to_string() << std::endl;
  }
  ```

  ```rust Rust theme={null}
  let result = ditto.store().execute(
      "DELETE FROM cars WHERE color = 'blue'",
  ).await?;

  for id in result.mutated_document_ids() {
      println!("{}", id);
  }
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("DELETE FROM cars WHERE color = 'blue'");
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute("DELETE FROM cars WHERE color = 'blue'")
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup

  for _, id := range result.MutatedDocumentIDs() {
      fmt.Printf("%s\n", id)
  }
  ```
</CodeGroup>

### Concurrent Deletes and Updates

<Info>
  The Ditto team is actively working on improved solutions to better handle concurrent delete and update scenarios in future releases.
</Info>

<Warning>
  When concurrent delete and update operations occur on the same document, Ditto's CRDT merge behavior can result in unexpected "husked documents" that contain only partially updated fields with other fields set to null.
</Warning>

When a document is deleted and updated concurrently across different devices, Ditto's add-wins map CRDT merges these operations by resolving conflicts on a
per-field basis. During a delete operation, each field in the document has its value set to null. When merging with concurrent updates, the CRDT preserves
any fields that were explicitly updated with new values, while all other fields retain the null values from the delete operation. This field-level conflict
resolution results in a document containing the updated field values alongside null values for all remaining fields. We call these documents Husked Documents.

#### How Husked Documents Occur

This happens when two operations occur concurrently:

1. **DELETE operation** - Removes all data from the document fields, creating a tombstone
2. **UPDATE operation** - Adds or modifies specific fields in the document

When these operations are merged using Ditto's add-wins CRDT behavior, the result is a document where:

* Fields that were updated contain their new values
* All other fields are set to null
* The document is not actually deleted as expected

#### Example Scenario

```mermaid theme={null}
graph LR
    subgraph "Initial State"
        A["{<br/>  'color': 'red',<br/>  'make': 'Toyota',<br/>  'model': 'Camry',<br/>  'year': 2020<br/>}"]
    end

    subgraph "Concurrent Operations"
        B["<b>Device A</b> <br/> DELETE"]
        C["<b>Device B</b> <br/> UPDATE color = blue"]
    end

    subgraph "Operation Results"
        D["{<br/>  'color': null,<br/>  'make': null,<br/>  'model': null,<br/>  'year': null<br/>}"]
        E["{<br/>  'color': 'blue',<br/>  'make': 'Toyota',<br/>  'model': 'Camry',<br/>  'year': 2020<br/>}"]
    end

    subgraph "CRDT Merge"
        F[Per-field conflict resolution]
    end

    subgraph "Final Result"
        G["{<br/>  'color': 'blue', ✓<br/>  'make': null, ✗<br/>  'model': null, ✗<br/>  'year': null ✗<br/>}"]
    end

    A --> B
    A --> C
    B --> D
    C --> E
    D --> F
    E --> F
    F --> G

    style A fill:#e1f5fe,text-align:left
    style B fill:#ffebee,text-align:left
    style C fill:#e8f5e8,text-align:left
    style D fill:#f5f5f5,text-align:left
    style E fill:#f5f5f5,text-align:left
    style F fill:#f3e5f5,text-align:left
    style G fill:#fff3e0,text-align:left
```

Consider a document in the `cars` collection:

```json Initial Document theme={null}
{
  "_id": "abc123",
  "color": "red",
  "make": "Toyota",
  "model": "Camry",
  "year": 2020,
  "mileage": 15000
}
```

If these operations happen concurrently on different devices:

<CodeGroup>
  ```sql DELETE Operation theme={null}
  DELETE FROM cars WHERE _id = 'abc123'
  ```

  ```sql UPDATE Operation theme={null}
  UPDATE cars SET color = 'blue' WHERE _id = 'abc123'
  ```
</CodeGroup>

The resulting merged document becomes:

```json Husk Document Result theme={null}
{
  "_id": "abc123",
  "color": "blue",
  "make": null,
  "model": null,
  "year": null,
  "mileage": null
}
```

#### Mitigation Strategies for Avoiding Husked Documents

To avoid husk documents in your application:

1. **Use Soft Delete Patterns** - Instead of hard deletes, use status flags (see [Soft-Delete Pattern](#advanced-design-pattern%3A-soft-delete-pattern) below).
2. **Use Eviction for Edge SDK Data Management** - Use Eviction for managing local data on an Edge SDK and Deletes for permanently removing data from the system
   via the Ditto Cloud HTTP API.
3. **Coordinate Operations** - Ensure delete and update operations don't happen simultaneously on the same document

### Design Considerations When Using `DELETE`

* `DELETE` will permanently remove the selected documents from the system.
* Document tombstones are an internal system concept and are not accessible to the user.
* Document tombstones are only shared with devices that have seen the document before its deletion event. Peers will
  not receive tombstones for documents that they have never known about.
* Document tombstones consist of the document id and fields of the document at the time of deletion. All values are removed.
* Tombstone reaping and removal can cause performance issues for large data sets. Customers with large numbers of deleted
  documents should specifying a `reaper_preferred_hour` and enable `enable_reaper_preferred_hour_scheduling` to ensure
  minimal business impact.
* If Ditto is not running on a Edge SDK device during the `reaper_preferred_hour` expired tombstones will not be evicted.

### Deleting Data Through the Ditto Server

Deleting data on the cloud server can be performed by executing a DQL `DELETE` query through the HTTP API.

For information on how to execute a DQL query through the HTTP API see [`HTTP POST API > Execute a DQL query`](/cloud/http-api/api/post-storeexecute)

**Considerations when Deleting through Ditto Server**

* Every `DELETE` statement performed on the Cloud/Server through the HTTP API are executed immediately as a single atomic operation. Removing a larger number
  of documents (approx. 50,000 or more) at once has the risk of causing performance impact to the larger system. Performance impacts includes
  slow sync times for all connected SDK Edge devices.  To minimize potential impact it's recommended to delete documents in batches of 30,000 or less.
  This can be done using the `LIMIT` keyword.

```sql theme={null}
DELETE FROM <collection_name> WHERE <condition> LIMIT 30000
```

* Deleted documents will be permanently removed from the Cloud/Server after 30 days. To configure this contact [Ditto's Customer Support](https://support.ditto.com)
* Tombstone reaping (removal) is run every hour on the Ditto Cloud/Server. To configure this contact [Ditto's Customer Support](https://support.ditto.com)

For more information on how to best design data removal policy contact [Ditto's Customer Support](https://support.ditto.com)

### Advanced: Configuring Tombstone Removal

<Info>
  Auto-eviction of expired tombstones is enabled on SDK version **4.10.0 and later**.
</Info>

To ensure all peers in the system are aware of a deleted document the Ditto system keeps a compressed version of the
document, called a document tombstone. A document tombstone indicates the document is deleted and is used to
shared with other peers in the mesh that a document is deleted.

When a document is deleted all of the mutable document information is removed. The remaining document information includes
the document's ID (`_id`) and metadata about the document including the time the document was deleted (deletion timestamp).

Document tombstones have a deletion timestamp. Once the timestamp goes beyond the set time to live (TTL) the document
tombstone is considered expired and will be removed in a process called the tombstone reaping. Tombstone
reaping is where the Ditto system scans for and removes expired tombstones. Tombstone reaping runs once daily
by default.

Document tombstones by default are retained on Edge SDK devices for **7 days** before being automatically removed.
The Ditto system keeps document tombstones around ensures all peers are aware of the deleted document. Once the document
tombstone is removed from the system there will be no history of the document's existence.

**Configuring Document Tombstone Retention on the Edge SDK**

Ditto offers a set of 5 system properties for configuring tombstone retention on a Edge SDK. All properties can be
enabled using the `ALTER SYSTEM` command on a Edge SDK device. For help configuring a retention policy that's best
for your use case reach out to [Ditto's Customer Support](https://support.ditto.com)

*Default configuration:*

* Tombstone removal is `enabled` by default.
* Scanning for expired tombstones, also known as tombstone reaping, happens shortly after the instance is created.
* Tombstone reaping occurs once a day from the initial reaping.
* Tombstones are marked as expired 7 days from the time of their deletion event. An expired tombstone will be removed
  in the following reaping.
* Tombstone reaping

<ResponseField name="TOMBSTONE_TTL_ENABLED" default="true" type="bool">
  <Expandable title="Details">
    |                  |        |
    | ---------------- | ------ |
    | *Type:*          | `bool` |
    | *Default value:* | `true` |

    Enables the automatic eviction of tombstones that have expired their time to live (TTL).

    **Setting the system parameter**

    ```sql theme={null}
    -- Setting TOMBSTONE_TTL_ENABLED to true
    ALTER SYSTEM SET TOMBSTONE_TTL_ENABLED = true
    ```

    For details on setting system parameters, see [`Advanced>Customizing System Settings`](/sdk/latest/sync/using-alter-system)

    <Note>
      Enabling this parameter on a running Ditto instance will **not** cause the peer to immediately check for expired tombstones; the peer
      will wait until the next time the tombstone reaping is schedule to run.

      *Example:* If the peer is configured to reap tombstones once a day around 9am, and `TOMBSTONE_TTL_ENABLED` is set to `true` at
      1pm, the peer will wait until the next morning at 9am to run the tombstone reaping.
    </Note>
  </Expandable>
</ResponseField>

<ResponseField name="TOMBSTONE_TTL_HOURS" default="`168` (7 days)" type="u64">
  <Expandable title="Details">
    |                  |                 |
    | ---------------- | --------------- |
    | *Type:*          | `u64`           |
    | *Default value:* | `168` (7 days)  |
    | *Bounds:*        | 1-5124095576029 |

    The threshold age in hours after which a document tombstone will be considered expired on the local peer. Once a document tombstone
    is expired it will be removed in the next tombstone reaping.

    **Setting the system parameter**

    ```sql theme={null}
    -- Setting TOMBSTONE_TTL_HOURS to 5 days
    ALTER SYSTEM SET TOMBSTONE_TTL_HOURS = 120
    ```

    For details on setting system parameters, see [`Advanced>Customizing System Settings`](/sdk/latest/sync/using-alter-system)

    <Note>
      The timestamp used to determine the tombstone's age is written by the device that deleted the document. If a peer with an
      inaccurate clock deletes document, that document may be removed from the local peer earlier or later than expected.
    </Note>

    <Note>
      Setting the TTL to a very low number may cause the deletion action to fail. This is because the device must share the document
      tombstone with other peers in the mesh so they know the document has been deleted.
    </Note>

    <Note>
      Setting the TTL to a very high number can cause performance issues due to the increased storage space requirement. Tombstones
      are quite small and don't take up much storage space, so the default of one week shouldn't pose any problems for typical use cases.

      If Edge SDK devices are expected to be offline for longer than a week then a higher TTL value may be needed to ensure the deletion
      event is being propagated through the system.
    </Note>

    <Warning>
      Never set the Edge SDK TTL to a number larger than the Ditto Server TTL. By default Edge SDK TTL is 7 days and Ditto Server TTL is 30 days.

      Because Ditto Server will always sync documents, setting this incorrectly will cause the tombstones to be sent back to Ditto Server
      after they've been removed, leading to increased resource consumption as tombstones are repeatedly synced back to Ditto Server and
      removed again.
    </Warning>
  </Expandable>
</ResponseField>

<ResponseField name="DAYS_BETWEEN_REAPING" default="1" type="u64">
  <Expandable title="Details">
    |                  |                  |
    | ---------------- | ---------------- |
    | *Type:*          | `u64`            |
    | *Default value:* | `1` (1 day)      |
    | *Bounds:*        | Minimum of 1 day |

    This parameter specifies the number of days between sweeps of the database by the tombstone reaping process.

    **Setting the system parameter**

    ```sql theme={null}
    -- Setting DAYS_BETWEEN_REAPING to 2 days
    ALTER SYSTEM SET DAYS_BETWEEN_REAPING = 2
    ```

    For details on setting system parameters, see [`Advanced>Customizing System Settings`](/sdk/latest/sync/using-alter-system)

    <Note>
      By default we only check for expired tombstones once a day, but this parameter allows us to check less frequently. This could provide
      small performance benefits on resource constrained devices.

      If you want to have tombstones expire more slowly, use the `TOMBSTONE_TTL_HOURS` parameter.
    </Note>
  </Expandable>
</ResponseField>

<ResponseField name="ENABLE_REAPER_PREFERRED_HOUR_SCHEDULING" default="false" type="bool">
  <Expandable title="Details">
    |                  |         |
    | ---------------- | ------- |
    | *Type:*          | `bool`  |
    | *Default value:* | `false` |

    When enabled, Ditto will attempt to schedule the tombstone reaping process to occur during the hour specified in the
    `REAPER_PREFERRED_HOUR` parameter.

    **Setting the system parameter**

    Due to a known issue, changes to this parameter at runtime are not currently respected. The desired value must be set before starting Ditto.
    This will be resolved in an upcoming release.

    This can be done by setting an system environment variable with the same name as the system parameter `ENABLE_REAPER_PREFERRED_HOUR_SCHEDULING`.
    For assistance reach out to [Ditto's Customer Support](https://support.ditto.com).

    <Tip>
      Because the reaping event can have performance impact for large databases, this setting, in use with `REAPER_PREFERRED_HOUR`,
      can be used to schedule the reaping to occur during off-hours to minimize business impact.
    </Tip>

    <Note>
      This is disabled by default. When disabled, we default to running tombstone reaping shortly after startup, and then repeat
      every `DAYS_BETWEEN_REAPING` days, regardless of the time of day.
    </Note>

    <Note>
      Enabling this setting does not specify that tombstone reaping will be triggered at a particular point during that hour. It indicates that we will
      attempt to reap tombstones at some point during that hour, assuming Ditto is running at that time.
    </Note>

    <Note>
      If this parameter is enable, `DAYS_BETWEEN_REAPING` will continue to be used to determine tombstone reaping cycles.
    </Note>

    <Warning>
      If this parameter is enable, and Ditto is not running during the specified hour in `REAPER_PREFERRED_HOUR`, the tombstone reaping process will **not** run that day.
      If this happens for many days in a row it could lead to performance issues due to increased storage space usage.
    </Warning>

    <Warning>
      WASM-based platform currently don't support this setting. This includes JavaScript Web and Flutter Web SDKs.
    </Warning>
  </Expandable>
</ResponseField>

<ResponseField name="REAPER_PREFERRED_HOUR" default="0" type="u64">
  <Expandable title="Details">
    |                  |                                       |
    | ---------------- | ------------------------------------- |
    | *Type:*          | `u64`                                 |
    | *Default value:* | `0`                                   |
    | *Bounds:*        | 0-23 (represent the hours of the day) |

    When `ENABLE_REAPER_PREFERRED_HOUR_SCHEDULING` is `true`, Ditto will try to schedule the tombstone reaping process to occur during the hour
    specified in the `REAPER_PREFERRED_HOUR` parameter.

    **Setting the system parameter**

    Due to a known issue, changes to this parameter at runtime are not currently respected. The desired value must be set before starting Ditto.
    This will be resolved in an upcoming release.

    This can be done by setting an system environment variable with the same name as the system parameter `REAPER_PREFERRED_HOUR`.
    For assistance reach out to [Ditto's Customer Support](https://support.ditto.com).

    <Check>
      This setting is only active if `ENABLE_REAPER_PREFERRED_HOUR_SCHEDULING` is set to `true`.
    </Check>

    <Warning>
      Due to a known issue, changes to this parameter at runtime are not currently respected. The desired value must be set before starting Ditto.
      This will be resolved in an upcoming release.

      This can be done by setting an system environment variable with the same name as the system parameter `REAPER_PREFERRED_HOUR`.
      For assistance reach out to [Ditto's Customer Support](https://support.ditto.com).
    </Warning>

    <Tip>
      Because the reaping event can have performance impact for large databases, this setting, in use with `REAPER_PREFERRED_HOUR`,
      can be used to schedule the reaping to occur during off-hours to minimize business impact.
    </Tip>

    <Note>
      This setting does not specify that tombstone reaping will be triggered at a particular point during that hour; only that we will attempt
      to reap tombstones at some point during that hour, assuming Ditto is running
    </Note>

    <Warning>
      If Ditto is not running during the specified hour, the tombstone reaping process will **not** run that day. If this happens for many days
      in a row it could lead to performance issues due to increased storage space usage.
    </Warning>
  </Expandable>
</ResponseField>

## Evicting Data

The `EVICT` method, once invoked, immediately removes the specified document(s) from the local Ditto store, making it inaccessible by local queries.

For complete DQL syntax, see [EVICT](/dql/evict).

Although the document you evicted is removed from the **local** Ditto store, the document stored within **remote** Ditto stores persists.

To prevent the evicted data from immediately reappearing on the screen, make sure to stop subscriptions **before** you call EVICT; otherwise, the subscription remains active and even if you reset the data in your end-user environment, the evicted data reappears as soon as the subscription sees it missing.

The `EVICT` keyword in DQL immediately removes one or more specified documents from the local Ditto store, making it inaccessible by local queries.

```sql DQL theme={null}
EVICT FROM cars WHERE _id = '123'
```

<Tip>For complete DQL syntax on evicting, see [EVICT](/dql/evict).</Tip>

### Example `EVICT`

<CodeGroup>
  ```swift Swift theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("EVICT FROM cars WHERE _id = '123'")
  ```

  ```javascript JS theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```typescript TypeScript theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```java Java theme={null}
  ditto.getStore().execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```csharp C# theme={null}
  await ditto.Store.ExecuteAsync("EVICT FROM cars WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  ditto.get_store().execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```rust Rust theme={null}
  ditto.store().execute("EVICT FROM cars WHERE _id = '123'").await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE _id = '123'");
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute(
  	"EVICT FROM cars WHERE _id = :id",
  	ditto.QueryArguments{"id": "123"},
  )
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup
  ```
</CodeGroup>

### Evicting Multiple Documents in a Collection

All documents specified in the `EVICT` condition, will be evicted. The ids of evicted document can be referenced using the `mutatedDocumentIDs` method on the `result`.

The following example evicts all `blue` cars stored in the `cars` collection from the local Ditto store:

<CodeGroup>
  ```swift Swift theme={null}

  let result = await ditto.store.execute(
    "EVICT FROM cars WHERE color = 'blue'");

  result.mutatedDocumentIDs.forEach() { print($0) }
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("EVICT FROM cars WHERE color = 'blue'").use { result ->
      result.mutatedDocumentIDs().forEach { id ->
          println(id)
      }
  }
  ```

  ```javascript JS theme={null}
  const result = await ditto.store.execute(
    "EVICT FROM cars WHERE color = 'blue'"
  );

  console.log(result.mutatedDocumentIDs());
  ```

  ```typescript TypeScript theme={null}
  import { QueryResult } from '@dittolive/ditto';

  const result: QueryResult = await ditto.store.execute(
    "EVICT FROM cars WHERE color = 'blue'"
  );

  console.log(result.mutatedDocumentIDs());
  ```

  ```java Java theme={null}
  // This try-with-resources block auto-closes the DittoQueryResult
  try (DittoQueryResult result = ditto.getStore().execute(
      "EVICT FROM cars WHERE color = 'blue'").toCompletableFuture().join()) {
      result.mutatedDocumentIDs().forEach(System.out::println);
  }
  ```

  ```csharp C# theme={null}
  using var result = await ditto.Store.ExecuteAsync(
    "EVICT FROM cars WHERE color = 'blue'");

  result.MutatedDocumentIds.ForEach(id => Console.WriteLine(id));
  ```

  ```cpp C++ theme={null}
  auto result = ditto.get_store().execute(
    "EVICT FROM cars WHERE color = 'blue'");

  for (DocumentId id : result) {
      std::cout << id.to_string();
  }
  ```

  ```rust Rust theme={null}
  let result = ditto.store().execute(
    "EVICT FROM cars WHERE color = 'blue'",
  ).await?;

  for id in result.mutated_document_ids() {
      println!("{}", id);
  }
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE color = 'blue'");
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute(
  	"EVICT FROM cars WHERE color = :color",
  	ditto.QueryArguments{"color": "blue"},
  )
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup

  for _, id := range result.MutatedDocumentIDs() {
      log.Printf("%v", id)
  }
  ```
</CodeGroup>

### Using Evict with Sync Subscriptions

Because evicting a document only removes it from the local device but not the large system other peers, including Ditto Server, will still have the document and if
not managed properly the document will return with the result of a sync subscription.

For example, if you have an active subscription for fetching `'blue'` cars and you subsequently evict a document with the ID `'123456'` that matches the replication
query, connected peers will notice that you are missing document `'123456'` that matches your subscription for `'blue'` cars and send it back to you. To prevent this
from happening you need to ensure that any active sync subscriptions don't contain documents you plan on evicting from the device.

### Coordinating Evictions

If you want to indicate that a batch of documents are irrelevant and, although they are to be retained, should *not* sync across peers, add the isSafeToEvict field to the document property tree. Then, use a method to alert clients to flag any documents they consider irrelevant.

```ditto Ditto Document theme={null}


{ "\_id": "abc123", "color": "red", "mileage": 40000, "isSafeToEvict": true, "createdAt": "2023-05-22T22:24:24.217Z" }
```

To ensure that peers continue replicating documents that are considered relevant, incorporate isSafeToEvict == false into their sync subscription query.

This approach restricts replication only to documents that peers mark as 'true' for isSafeToEvict. Once flagged, the peers clear irrelevant documents from their caches, all the while normal transactional operations continues without interruption.

<CodeGroup>
  ```swift Swift theme={null}

  await ditto.store.execute("EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("EVICT FROM cars WHERE isSafeToEvict = true")
  ```

  ```javascript JS theme={null}
  ditto.store.execute("EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```typescript TypeScript theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```java Java theme={null}
  ditto.getStore().execute("EVICT FROM cars WHERE isSafeToEvict = true")
      .close(); // DittoQueryResult from execute() MUST always be explicitly closed
  ```

  ```csharp C# theme={null}
  await ditto.Store.ExecuteAsync(
    "EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```cpp C++ theme={null}
  ditto.get_store().execute(
    "EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```rust Rust theme={null}
  ditto.store().execute("EVICT FROM cars WHERE isSafeToEvict = true").await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("EVICT FROM cars WHERE isSafeToEvict = true");
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute(
  	"EVICT FROM cars WHERE isSafeToEvict = true",
  	nil,
  )
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup
  ```
</CodeGroup>

## Advanced Design Pattern: Soft-Delete Pattern

If you need a data recovery option, instead of permanently removing the data from the local Ditto store like EVICT, opt for a *soft-delete pattern.*

A soft-delete pattern is a way to flag data as inactive while retaining it for various requirements, such as archival evidence, reference integrity, prevention of potential data loss due to end-user error, and so on.

### Adding a Soft-Delete Flag

To add a soft-delete pattern, set the `isArchived` field value to `true`:

<CodeGroup>
  ```json Ditto Document theme={null}
  {
    "_id": "123",
    "color": "blue",
    "isArchived": true // add this field
  }
  ```
</CodeGroup>

### Querying Non-Archived Documents

To query to monitor documents that are `NOT`\* \*archived, establish a live query where `isArchived` is set to `false`, and then construct your live query callback.

It's likely that the `isArchived` field is set lazily (i.e. has no value until it is `true`), so you can use the `coalesce()` function to automatically return `false` if the value is unset.
The following code demonstrates searching for documents that are unarchived:

<CodeGroup>
  ```swift Swift theme={null}

  let result = await ditto.store.execute("""
    SELECT *
    FROM cars
    WHERE coalesce(isArchived, false) = false
    """)
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("""
    SELECT *
    FROM cars
    WHERE coalesce(isArchived, false) = false
    """).use { result ->
      // Process results
  }
  ```

  ```javascript JS theme={null}
  const result = await ditto.store.execute(`
    SELECT *
    FROM cars
    WHERE coalesce(isArchived, false) = false`
  );
  ```

  ```typescript TypeScript theme={null}
  import { QueryResult } from '@dittolive/ditto';

  const result: QueryResult = await ditto.store.execute(`
    SELECT *
    FROM cars
    WHERE coalesce(isArchived, false) = false`
  );
  ```

  ```java Java theme={null}
  // This try-with-resources block auto-closes the DittoQueryResult
  try (DittoQueryResult result = ditto.getStore().execute(
      "SELECT * FROM cars WHERE coalesce(isArchived, false) = false")) {
      // Process results
  }
  ```

  ```csharp C# theme={null}
  using var result = await ditto.Store.ExecuteAsync(
    "SELECT * FROM cars WHERE coalesce(isArchived, false) = false");
  ```

  ```cpp C++ theme={null}
  auto result = ditto.get_store().execute(
    "SELECT * FROM cars WHERE coalesce(isArchived, false) = false");
  ```

  ```rust Rust theme={null}
  let result = ditto.store().execute(
    "SELECT * FROM cars WHERE coalesce(isArchived, false) = false",
  ).await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("""
    SELECT *
    FROM cars
    WHERE coalesce(isArchived, false) = false
    """);
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute(
      "SELECT * FROM cars WHERE coalesce(isArchived, false) = false")
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup
  ```
</CodeGroup>

### Removing Soft-Delete Flag

To remove the flag and reactivate the document, set the `isArchived` field to `false`:

<CodeGroup>
  ```swift Swift theme={null}

  await ditto.store.execute("""
    UPDATE cars
    SET isArchived = false
    WHERE _id = '123'
    """)
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute("""
    UPDATE cars
    SET isArchived = false
    WHERE _id = '123'
    """)
  ```

  ```javascript JS theme={null}
  await ditto.store.execute(`
    UPDATE cars
    SET isArchived = false
    WHERE _id = '123'`
  );
  ```

  ```typescript TypeScript theme={null}
  await ditto.store.execute(`
    UPDATE cars
    SET isArchived = false
    WHERE _id = '123'`
  );
  ```

  ```java Java theme={null}
  ditto.getStore().execute("UPDATE cars SET isArchived = false WHERE _id = '123'")
      .close(); // DittoQueryResult from execute() MUST always be explicitly closed
  ```

  ```csharp C# theme={null}
  await ditto.Store.ExecuteAsync(
    "UPDATE cars SET isArchived = false WHERE _id = '123'");
  ```

  ```cpp C++ theme={null}
  ditto.get_store().execute(
    "UPDATE cars SET isArchived = false WHERE _id = '123'");
  ```

  ```rust Rust theme={null}
  ditto.store().execute(
    "UPDATE cars SET isArchived = false WHERE _id = '123'",
  ).await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute("""
    UPDATE cars
    SET isArchived = false
    WHERE _id = '123'
    """)
  ```

  ```go Go theme={null}
  result, err := dit.Store().Execute(
      "UPDATE cars SET isArchived = false WHERE _id = '123'"
  )
  if err != nil {
      return err
  }
  defer result.Close()  // cleanup
  ```
</CodeGroup>
