---
title: CRUD Fundamentals
slug: v4-4/crud-fundamentals
docTags: 
createdAt: 2023-08-03T16:12:29.513Z
---

Ditto includes a robust query engine that allows you to perform various filter operations to carry out traditional `create`, `read`, `update`, and `delete` (CRUD) operations.

The Ditto SDK provides a comprehensive set of methods and functions to facilitate data interaction and perform a wide range of operations in your app:

- For read operations, use the `find` and `observeLocal` methods.

- For modifications, use the `update`, `upsert`, `evict`, and `delete` methods.

# Overview

The following tables provides a high-level overview of the different ways you can perform CRUD in Ditto:

| **Operation**       | **Description**                                                                                                                                                                        |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Create](./#create) | Using the `upsert` method, either insert a new document or updating an existing document for a given document ID.                                                                      |
| [Read](./#read)     | Using either the `find` or `observeLocal` methods, retrieve documents based on the specific criteria you pass as parameters in your function.                                          |
| [Update](./#update) | Using either `update` or `upsert` methods, write changes to Ditto.                                                                                                                     |
| [Delete](./#delete) | Using either `remove` or `evict` methods, delete data in Ditto.  <br />In addition, using a *soft-delete pattern*, indicate data as deleted without physically removing it from Ditto. |

:::hint{type="info"}
For detailed information on CRUD, see *Platform Manual&#x20;*> [CRUD Operations](docId:1CEXFnSWhjeqa_R6yyL-8).

For an overview of the various operators and path navigations you can use to construct sophisticated queries in your app, see *Platform Manual&#x20;*> [Query Syntax](docId\:MMTiSmYkR3HxZmYS1bi2w).
:::

# Create

Due to Ditto's conflict-free concurrency model, there is no concept of "insert." This is because each peer functions under the assumption that its write transaction already exists somewhere within the mesh network.&#x20;

Therefore, Ditto uses a combined approach known as "upsert." This approach focuses on updating only the fields within the document that have changed, the *delta*. (See [Sync Overview](docId\:hFWWxTojEPJkcXQKp4pvR))

If all of the fields in the document are new, however, Ditto creates an entirely new document object. (See *Platform Manual&#x20;*> [Updating and Upserting](docId:1CEXFnSWhjeqa_R6yyL-8))

## Upserting Documents

With the `upsert` method, you can execute any of the following actions in your app:&#x20;

| **Action**                                | **Description**                                                                                                                                                                                                                              |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Apply delta updates to existing documents | Write changes to only the specific fields within the document that are modified. (See *Platform Manual&#x20;*> [Upserting Delta Updates](docId\:L2hFjvzUYyNu5ahZXHwAJ).                                                                      |
| Insert a document                         | If all of the fields are new, create a new document object. (See *Platform Manual&#x20;*> [Creating New Documents](docId\:L2hFjvzUYyNu5ahZXHwAJ))                                                                                            |
| Load initial data                         | Upsert and flag data you want to be accessible to end users at app startup, such as sample chat messages from a central backend API. (See *Platform Manual&#x20;*> [Example Use Case: Upserting Initial Data](docId\:C-V5Q9JYhmVeMVSDIpj2U)) |
| Supply a document ID                      | When creating a new document, if desired, you can assign your own unique identifier. Otherwise, Ditto automatically generates and assigns one for you. (See *Platform Manual&#x20;*> [Supplying a Custom ID](docId\:L2hFjvzUYyNu5ahZXHwAJ))  |

# Read

To retrieve data in Ditto, depending on your goals and use case, use any of the following query types:

- **Local query** — Using the `find` and `findById` methods, quickly get your own data in a one-time executable to your local Ditto store.&#x20;

  For instance, call the `findById` method to target a specific document.  Or if you want to fetch one or more documents based on certain criteria and conditions, call the`find` method instead. (See [Local Queries](./#local-queries))

- **Live query** — Using the `observeLocal` method, establish a listener to observe your local changes in realtime. (See [Live Queries](./#live-queries))

- **Replication query&#x20;**— Using the `subscribe` method, keep your data consistent with other peers connected in the mesh network. (See [Replication Queries](./#replication-queries))

## Local Queries

Similar to a traditional database query, the `find` and `findById` methods are based on a local query that fetches and returns all relevant documents.&#x20;

Intended for quick access to data stored locally on its own device, such as a profile image, local queries are one-time executables that do not involve other peers connected in the mesh network.

For more information and how-to instructions, see *Platform Manual&#x20;*> [CRUD Operations](docId:1CEXFnSWhjeqa_R6yyL-8) and [Finding and Observing](docId\:HvfMp8TX4CwKI4h2xoTuy).

## Live Queries

A live query subscription is essentially a local query, but it includes an `observeLocal` method to establish continuous listening to real-time changes written to its own Ditto store.&#x20;

Live queries are useful when you want to monitor changes from your local Ditto store and react to them immediately. For instance, when your end user updates their own profile, you can asynchronously display the changes to the end user in realtime.

For more information and how-to instructions, see *Platform Manual&#x20;*> [CRUD Operations](docId:1CEXFnSWhjeqa_R6yyL-8) and [Finding and Observing](docId\:HvfMp8TX4CwKI4h2xoTuy).

## Replication Queries

A replication query is executed asynchronously through the `subscribe` method on each remote peer connected within the mesh network.&#x20;

This query specifies the data for which your Small Peer local Ditto store is interested in receiving updates. When remote peers make modifications to the data you've indicated an interest in, they publish the changes back to you — the *subscribing originating peer*.

Rather than continuously polling for updates, which is resource-intensive and generally inefficient, the asynchronous listener you set up triggers only when the data matching your query undergoes changes in the Ditto store.

# Update

The following table provides an overview of the CRDTs and associated behavior for a given operation:

| **Operation**          | **Description**                                                                  |
| ---------------------- | -------------------------------------------------------------------------------- |
| `set register`         | Sets the value for a given field in the document.                                |
| `set map`              | Sets value for a given field in the `map`.                                       |
| `remove register`      | Removes a value for a given field in the document.                               |
| `remove map`           | Removes a value for a given key in the `map` structure.                          |
| `replace with counter` | Converts a `number` value for a given field into a `counter`.                    |
| `increment counter`    | Unlike a `number`, increments the `counter` by the given positive integer value. |
| `decrement counter`    | Unlike a `number`, decrements the `counter` by the given negative integer value. |

## Updating a Single Document&#x20;

To update a single document, using the `upsert` method:

:::::WorkflowBlock
:::WorkflowBlockItem
Specify the document collection you want to make an update to.
:::

::::WorkflowBlockItem
Pass your field changes.&#x20;

:::hint{type="info"}
Only delta changes are written to local Ditto stores; if the Ditto store is already up to date, the changes do not commit. For more information, see [Sync Overview](docId\:wtubn9e3aDoVxzSy6ZB7w).
:::

:::CodeblockTabs
```swift
do {
    // upsert JSON-compatible data into Ditto
    let docID = try ditto.store["people"].upsert([
        "name": "Susan",
        "age": 31
    ])
} catch {
    //handle error
    print(error)
}
```

```kotlin
val docId2 = ditto.store["people"].upsert(
    mapOf(
        "name" to "Susan",
        "age" to 31
    )
)
```

JS

```javascript
const cdocID = await ditto.store.collection('people').upsert({
  name: 'Susan',
  age: 31,
})
console.log(docID) // "123abc"
```

```java
Map<String, Object> content = new HashMap<>();
content.put("name", "Susan");
content.put("age", 31);
DittoDocumentId docId = ditto.store.collection("people").upsert(content);
// docId => 507f191e810c19729de860ea
```

```csharp
var docId = ditto.Store.Collection("people").Upsert(
    new Dictionary<string, object> {
    { "name", "Susan" },
    { "age", 31 },
    }
);
```

```cpp
json person = json({{"name", "Susan"}, {"age", 31}});
DocumentId doc_id = ditto.get_store().collection("people").upsert(person);
```

```rust
let person = json!({
    "name": "Susan".to_string(),
    "age": 31,
});
let collection = ditto.store().collection("people").unwrap();
let id = collection.upsert(person).unwrap();
```
:::
::::
:::::

## Updating Multiple Documents

If you want to perform writes to multiple documents, start the transaction asynchronously to avoid blocking the main thread.&#x20;

For example, in Swift, use `DispatchQueue.global`, as demonstrated in the following snippet.

For more information, see the *Platform Manual&#x20;*> [Best Practices for CRUD](docId\:C-V5Q9JYhmVeMVSDIpj2U).

:::CodeblockTabs
Swift

```swift
DispatchQueue.global(qos: .default).async {

    ditto.store.write { transaction in

        let scope = transaction.scoped(toCollectionNamed: "passengers-\(thisFlight)")

    // Loop inside the transaction to avoid writing to database too frequently
        self.passengers.forEach {
            scope.upsert($0.dict)
        }
    }
}
```

```kotlin
val results = ditto.store.write { transaction ->
    val cars = transaction.scoped("cars")
    val people = transaction.scoped("people")
    val docId = "abc123"
    people.upsert(mapOf("_id" to docId, "name" to "Susan"))
    cars.upsert(mapOf("make" to "Hyundai", "color" to "red", "owner" to docId))
    cars.upsert(mapOf("make" to "Jeep", "color" to "pink", "owner" to docId))
    people.findById(DittoDocumentId(docId)).evict()
}
```

```javascript
const results = await ditto.store.write(async (transaction) => {
  const cars = transaction.scoped('cars')
  const people = transaction.scoped('people')

  // In this example a new person and car document are created, and
  // finally the person document that was just created is evicted.
  // If any of these operations fail, all others are not applied.
  const susanId = await people.upsert({
    name: 'Susan',
  })
  await cars.upsert({
    make: 'Hyundai',
    color: 'red',
    owner: susanId,
  })
  await people.findByID(susanId).evict()
})

// The return value of a transaction is a list that contains a
// summary of all operations in the transaction and the document IDs
// that were affected:

// results == [
//   {
//     type: 'inserted',
//     docID: DocumentID { ... },
//     collectionName: 'people'
//   },
//   {
//     type: 'inserted',
//     docID: DocumentID { ... },
//     collectionName: 'cars'
//   },
//   {
//     type: 'evicted',
//     docID: DocumentID { ... },
//     collectionName: 'people'
//   }
// ]
```

```java
DispatchQueue.global(qos: .default).async {

    ditto.store.write { transaction in

        let scope = transaction.scoped(toCollectionNamed: "passengers-\(thisFlight)")

    // Loop inside the transaction to avoid writing to database too frequently
        self.passengers.forEach {
            scope.upsert($0.dict)
        }
    }
}
```

```csharp
DispatchQueue.global(qos: .default).async {

    ditto.store.write { transaction in

        let scope = transaction.scoped(toCollectionNamed: "passengers-\(thisFlight)")

    // Loop inside the transaction to avoid writing to database too frequently
        self.passengers.forEach {
            scope.upsert($0.dict)
        }
    }
}
```

```cpp
auto results = ditto.get_store().write([&](WriteTransaction &write_txn) {
  ScopedWriteTransaction people = write_txn.scoped("people");
  ScopedWriteTransaction cars = write_txn.scoped("cars");
  auto docId = "abc123";
  people.upsert({{"name", "Susan"}, {"_id", DocumentId(docId)}});
  cars.upsert({{"make", "Hyundai"}, {"owner", DocumentId(docId)}});
  cars.upsert({{"make", "Toyota"}, {"owner", DocumentId(docId)}});
});
```

```rust
DispatchQueue.global(qos: .default).async {

    ditto.store.write { transaction in

        let scope = transaction.scoped(toCollectionNamed: "passengers-\(thisFlight)")

    // Loop inside the transaction to avoid writing to database too frequently
        self.passengers.forEach {
            scope.upsert($0.dict)
        }
    }
}
```
:::

# Delete

Managing the amount of data stored and replicated across resource-constrained Small Peers interconnected in a bandwidth‑limited mesh is crucial for maintaining optimal performance in a peer‑to‑peer environment.&#x20;

In distributed system architecture, you must strike a balance between data availability and system efficiency:

- The greater the amount of data replicated across connected peers, the more timely offline read access becomes.

- The fewer the number of data replicated across connected peers, the less likelihood that peer devices run out of disk space and experience memory leaks.

## Evict and Remove

Depending on your use case, use either the `evict` or `remove` method to implement memory management practices like automatic resource allocation, memory deallocation, cleaning and maintenance, upon many other tools to help optimize memory usage in your app.&#x20;

### Balancing Syncing and Evicting

Given these technical tradeoffs, use the Subscribe and Eviction methods carefully to implement your tradeoff design decisions:

- To sync more data across peers connected in the mesh, call Subsribe.
- To remove data stored locally on a peer device, call Evict.

Evicting a document is not permanent; as long as there is at least one active subscription with a query that includes an evicted document, that document will reappear as soon as it is available in the mesh.

### Controlling Which Documents Sync

You can signify that data is irrelevant for peer-to-peer replication but should still be retained locally by adding `isSafeToEvict`  to the document field property tree.&#x20;

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

To ensure that small peers continue syncing documents that are considered relevant, include `isSafeToEvict == false` in their subscription queries and then use some means to inform clients to flag any documents that they consider irrelevant.

That way, only the document that a client sets to `'true'` is prevented from syncing.&#x20;

Once flagged, the clients purge the irrelevant documents from their caches, all the while normal transactional operations continue without interruption.

```javascript
collection.find("createdAt > "'2023-05-22T22:24:24.217Z" && isSafeToEvict == false").subscribe()
```

### Permanently Removing Data

The Remove method, once invoked, permanently deletes specified documents from the local datastore as well as all other connected peers.&#x20;

:::hint{type="danger"}
Use the Remove method with extreme caution; invoking Remove results in irreversible data loss. &#x20;
:::

:::CodeblockTabs
```swift
collection.findByID(docID).remove()
```

```kotlin
collection.findById(docId).remove()
```

```javascript
await ditto.store
  .collection("your_collection_name")
  .findByID("unique_document_id")
  .remove()
```

```java
ditto.store
    .collection("your_collection_name")
    .findByID("unique_document_id")
    .remove();
```

```csharp
ditto.store
  .Collection("your_collection_name")
  .FindByID("unique_document_id")
  .Remove()
```

```cpp
ditto.get_store()
  .collection("your_collection_name")
  .find_by_id("unique_document_id")
  .remove()
```

```rust
collection.find_by_id(id).remove().unwrap();
```
:::

## Flagging Soft-Deletes

If you need to ensure that, although deleted, the data remains recoverable, you can add a soft-delete pattern to the document property tree:

:::CodeblockTabs
JSON

```json
{
  "_id": "123abc",
  "name": "Foo",
  "isArchived": true
}
```
:::

:::hint{type="info"}
For comprehensive information on deleting data in Ditto, see *Platform Manual&#x20;*> [Evicting and Removing](docId\:Q6vsBHCzgyPt1HlZgOW4E).
:::

