Inserting Documents

Insert data into a collection with the insert function. It will return the id of the document that was inserted.

// Insert JSON-compatible data into Ditto
let docId = ditto.store["people"].insert([
    "name": "Susan",
    "age": 31
])
// Insert JSON-compatible data into Ditto
DITDocumentID *docID = [[ditto.store collection:@"people"]
     insert:@{ @"name": @"Susan", @"age": [NSNumber numberWithInt:31] }
     isDefault:false
     error:nil];
val docId = ditto.store["people"].insert(mapOf(
    "name" to "Susan",
    "age" to 31
))
Map<String, Object> content = new HashMap<>();
content.put("name", "Susan");
content.put("age", 31);
DittoDocumentID docId = ditto.store.collection("people").insert(content);
var content = new Dictionary<string, object>
{
    { "name", "Susan" },
    { "age", 31 }
};
var docId = ditto.Store.Collection("people").Insert(content);
json content = json({
    { "name", "Susan" },
    { "age", 31 }
});
DocumentId doc_id = ditto.store.collection("people").insert(content);
const docID = await ditto.store.collection('people').insert({
    value: {name: "Susan", age: 31}
})

Specifying an id when inserting

There are times where you'd like to insert a document with a specific id in mind. The following example attempts insert an id with the string "123abc".

If you attempt to insert a document with a conflicting id that is already in the datastore then the document's contents will get overwritten with the most-recently `insert`-ed document's contents.
// Insert JSON-compatible data into Ditto
let docId = ditto.store["people"].insert([
    "name": "Susan",
    "age": 31
], withID: DittoDocumentID("123abc"))
// Insert JSON-compatible data into Ditto
DITDocumentID *docID = [[ditto.store collection:@"people"]
     insert:@{ @"name": @"Susan", @"age": [NSNumber numberWithInt:31] }
     withId: [DITDocumentID alloc] initWithValue:@"123abc"]
     isDefault:false
     error:nil];
val docId = ditto.store["people"].insert(mapOf(
    "name" to "Susan",
    "age" to 31
), DittoDocumentID("123abc"))
Map<String, Object> content = new HashMap<>();
content.put("name", "Susan");
content.put("age", 31);
DittoDocumentID docId = ditto.store.collection("people").insert(content, new DittoDocumentID("123abc"));
var content = new Dictionary<string, object>
{
    { "name", "Susan" },
    { "age", 31 }
};
ditto.Store.Collection("people").Insert(content, DittoDocumentID("123abc"));
ditto.store.collection("people").insert({{ "name", "Susan" }, { "age", 31 }});
const docID = await ditto.store.collection('people').insert({
    id: new DocumentID(123),
    value: {name: "Susan", age: 31}
})

Default Data

Ditto's approach to conflict resolution orders changes by time. In most situations, this leads to predictable behavior. However, if your application is inserting the same initial data into multiple devices, such as common data from a central backend API, this could result in overwriting later changes:

  1. Device A inserts a document {"firstName": "Adam"} at time = 0 after downloading from a central API.
  2. Device A updates the document to {"firstName": "Max"} at time = 1.
  3. Device B synchronizes with Device A receiving the document {"firstName": "Max"} at time = 2.
  4. Device B downloads the same document from the backend API {"firstName": "Adam"} and inserts at t = 3, which overwrites the previous change synced at time = 1.

In the above example, both Device A and B want to preserve the change by Device A that occurred after downloading the common data. To do so, Ditto offers an additional parameter: isDefault.

let docId = ditto.store.collection("people").insert([
    "name": "Susan",
    "age": 31
], isDefault: true)
DITDocumentID *docID = [[ditto.store collection:@"people"]
     insert:@{ @"name": @"Susan", @"age": [NSNumber numberWithInt:31] }
     isDefault:true
     error:nil];
val docId = ditto.store.collection("people").insert(mapOf(
    "name" to "Susan",
    "age" to 31
), isDefault: true)
Map<String, Object> content = new HashMap<>();
content.put("name", "Susan");
content.put("age", 31);
DittoDocumentID docId = ditto.store
                             .collection("people")
                             .insert(content, new DittoDocumentID("doc-id"), true);
DittoDocumentID docID = ditto.Store
                        .Collection("people")
                        .Insert(
                            {{"name", "Susan"}, {"age", 31}},
                            new DittoDocumentID("doc-id"),
                            true
                        );
json content = json({{ "name", "Susan" }, { "age", 31 }}):
DocumentId doc_id = ditto.store.collection("people")
                               .insert(content, DocumentId("doc-id"), true);
const docID = await ditto.store.collection('people').insert({
    id: new DocumentID(123),
    value: {name: "Susan", age: 31},
    isDefault: true
})

When you pass true for isDefault this informs Ditto's merge algorithm to ensure that any changes that occurred after a device inserted data will always remain.


Supported Values

Document values support all JSON compatible values like string , boolean, number , null and arrays or nested maps. In addition, document values can also support special types like attachments for large binary data or counter types. For more details on supported values, see the Data Model section:

Data Model


Updating Documents

Updating documents are categorized into 3 types of operations

Standard

  • Set - sets value for a given key in the document
  • Remove - removes a value for a given key in the document

Counters

  • Replace with counter - will convert a number value for a given key into a counter
  • Increment - unlike a number, a counter allows for increment operations (decrement is performed by incrementing by a negative increment) and these operations will converge

Arrays

  • Push - inserts a value on to the end of an array at the document's key
  • Pop - removes a value at the end of an array at the document's key
let docId = ditto.store["people"].insert([
    "name": "Frank",
    "age": 31,
    "ownedCars": 0,
    "friends": []
])

ditto.store["people"].findByID(docId).update { mutableDoc in
    mutableDoc?["age"].set(32)
    mutableDoc?["ownedCars"].replaceWithCounter()
    mutableDoc?["ownedCars"].increment(amount: 1)
    mutableDoc?["friends"].push("Susan")
}
NSString *docId = [[ditto.store collection:@"people"] insert:@{
    @"name": @"Frank",
    @"age": [NSNumber numberWithInt:31],
    @"ownedCars": [NSNumber numberWithInt:0],
    @"friends": @[]
} error:nil];

[[[ditto.store collection:@"people"] findByID:docId] update:^(DITMutableDocument *doc) {
   [doc[@"age"] set:[NSNumber numberWithInt:32]];
   [doc[@"ownedCars"] replaceWithCounter];
   [doc[@"ownedCars"] increment:1];
   [doc[@"friends"] push:@"Susan" error:nil];
} error:nil];
val docId = ditto.store["people"].insert(mapOf(
    "name" to "Frank",
    "age" to 31,
    "ownedCars" to 0,
    "friends" to emptyList<String>()
))

ditto.store.collection("people").findByID(docId).update { mutableDoc ->
    mutableDoc?.let {
        it["age"].set(32)
        it["ownedCars"].replaceWithCounter()
        it["ownedCars"].increment(1)
        it["friends"].push("Susan")
        it["name"].replaceText(5, 0, " Smith")
    }
}
Map<String, Object> content = new HashMap<>();
content.put("name", "Frank");
content.put("age", 31);
content.put("ownedCars", 0);
content.put("friends", Arrays.asList());
DittoDocumentID docId = ditto.store.collection("people").insert(content);

ditto.store.collection("people").findByID(docId).update(doc -> {
    try {
      doc.get("age").set(32);
      doc.get("ownedCars").replaceWithCounter();
      doc.get("ownedCars").increment(1);
      doc.get("friends").push("Susan");
    } catch (DittoError err) {
        // Do something with error
    }
});
var content = new Dictionary<string, object>
{
    { "name", "Bob" },
    { "age", 40 },
    { "ownedCars", 0 },
    { "friends", new List<object>() }
};

var docId = ditto.Store.Collection("people").Insert(content);
ditto.Store.Collection("people").FindById(docId).Update((mutableDoc) => {
    mutableDoc["age"].Set(32);
    mutableDoc["ownedCars"].ReplaceWithCounter();
    mutableDoc["ownedCars"].Increment(1);
    mutableDoc["friends"].Push("Susan");
});
DocumentId doc_id = ditto.store.collection("people").insert({
  {"name", "Frank"},
  {"age", 31},
  {"ownedCars", 0},
  {"friends": {}}
});

ditto.store
     .collection("people")
     .find_by_id(doc_id)
     .update([](MutableDocument &doc) {
       doc["age"].set(32);
       doc["ownedCars"].replace_with_counter();
       doc["ownedCars"].increment(1);
       doc["friends"].push("Susan");
     });
const docID = await ditto.store.collection('people').insert({
    value: {
        name: "Frank",
        age: 31,
        ownedCars: 0,
        friends: []
    }
})

await ditto.store.collection('people').findByID(docID).update((mutableDoc) => {
    mutableDoc['age'] = 32
    mutableDoc['friends'].push('Susan')
    
    // Counters aren't supported by the JS SDK yet but will be soon:
    mutableDoc['ownedCars'].replaceWithCounter()
    mutableDoc['ownedCars'].increment(amount: 1)    
})

Removing Documents

Removes will delete or remove one or more documents from the collection. These remove operations will also sync and remove the relevant documents from connected peers.

Removing by id

To remove a single document by its id

ditto.store["people"].findByID(docId).remove()
DITCollection *collection = [ditto.store collection:@"people"];
[[collection findByID:@"123"] remove];
ditto.store["people"].findByID(docId).remove()
ditto.store.collection("people").findByID(docId).remove();
ditto.Store.Collection("people").FindByID(docId).Remove();
ditto.store.collection("people").find_by_id(docId).remove();
await ditto.store.collection('people').findByID(docID).remove()

Removing by query

You can also remove several documents with a query string. This example will remove all documents in the "people" collection who has an age property lower than or equal to 32 ("age <= 32")

ditto.store["people"].find("age <= 32").remove()
DITCollection *collection = [ditto.store collection:@"people"];
[[collection find:@"age <= 32"] remove];
ditto.store["people"].find("age <= 32").remove()
ditto.store.collection("people").find("age <= 32").remove();
ditto.Store.Collection("people").Find("age <= 32").Remove();
ditto.store.collection("people").find("age <= 32").remove();
await ditto.store.collection('people').find('age <= 32').remove()

Evicting Data

This operation is different than remove in that it only removes one of more documents from the local storage and does not sync the change to other devices.

For example, you might use this operation when a device has completed an activity that involved synchronizing data with other devices and has transmitted the data to a backend. At this point, the device can evict the data locally to free up storage, but it does not want to remove the data from any other device.

If you evict data, but then continue to subscribe for this data from other devices, it will reappear as the devices synchronize. When evicting, it should be common practice to subscribe from live queries prior to evicting data.
ditto.store.collection("people").findAll().evict()
DITCollection *collection = [ditto.store collection:@"people"];
[[collection findAll] evict];
ditto.store.collection("people").findAll().evict()
ditto.store.collection("people").findAll().evict();
var collection = ditto.Store.Collection("people");
collection.FindAll().Evict();
Collection collection = ditto.store.collection("people");
collection.find_all().evict();
await ditto.store.collection('people').findAll().evict()

Transactions

Groups of writes can be performed together in a transaction. In addition, you can group writes across different collections. Ditto will preserve the transactions when synchronizing. Other devices will only provide data change events once the entire transaction is processed.

Ditto transactions will keep data queries consistent. These are processed serially and are considered atomic. This commonly known as "all or nothing". When a transaction is successful, all changes will be visible to subsequent reads. Transactions will not commit partial set of instructions while rolling back others.

If a transaction fails or is aborted, all changes will be ignored. No aborted changes will be visible to queries either local or when syncing. Note, data change events can include multiple transactions, such as when a device with no data synchronizes with another device for the first time, however, it will never create an event for a partial transaction.
let results = ditto.store.write({ txn in
    let scope = txn.scoped(toCollectionNamed: "people")
    let docID = scope.insert([
        "name": "Susan",
        "age": 31
    ])
    scope.findByID(docID).update { doc in
        doc?["age"].set(32)
    }
    scope.findByID(docID).remove()
})
NSArray *results = [store write:^(DITWriteTransaction *tx) {
    DITScopedWriteTransaction *scope = [tx scopedToCollectionNamed:@"people"];

    DITDocumentID *docID = [scope insert:@{@"name": @"Susan", @"age": [NSNumber numberWithInt: 31]}
        isDefault:false
            error:nil];

    [[scope findByID:docID] update:^(DITMutableDocument *doc) {
        [doc[@"age"] set:[NSNumber numberWithInt:32] isDefault:false error:nil];
    }];
    [scope findByID:docID] remove];
}];
val results = ditto.store.write { txn ->
    val docID = txn.scoped("people").insert(mapOf(
        "name" to "Susan",
        "age" to 31
    ))
    txn.scoped("people").findByID(docID).update { mutDoc ->
        mutDoc["age"].set(32)
    }
    txn.scoped("people").findByID(docID).remove()
}
List<DittoUpdateResult> results = ditto.store.write(txn -> {
    Map<String, Object> content = new HashMap<>();
    content.put("name", "Susuan");
    content.put("age", 31);
    DittoDocumentID docID = transaction.scoped("people").insert(content);
    transaction.scoped("people").findByID(docID).update(doc -> {
        try {
            doc.get("age").set(32);
        } catch (DittoError err) {
            // Do something
        }
    });
    transaction.scoped("people").findByID(docID).remove();
});
var results = ditto.Store.Write(txn =>
{
  ScopedWriteTransaction scope = txn.scoped("people");
  DittoDocumentID docID = scope.insert({{"name", "Susan"}, {"age", 31}});
  scope.FindByID(docID).Update(mutDoc =>
  {
    mutDoc["age"].Set(32);
  });
  scope.FindByID(docID).remove();
});
auto results = ditto.store.write([&](WriteTransaction &txn) {
  ScopedWriteTransaction scope = txn.scoped("people");
  scope.insert({{"name", "Susan"}, {"age", 31}}, "123");
  scope.find("123").update([&](MutableDocument &doc) {
    doc["age"].set(32);
  });
  scope.find_by_id("123").remove();
});

Not supported yet, coming soon.

Top