Documents
Ditto is a NoSQL database, that can store JSON-like Documents organized by Collections. However, unlike JSON, Ditto allows you to apply updates to the document which will be synchronized with any other copy on other devices. In addition, it supports additional data types.
Collections
You can think of a collection as a table in a traditional database. However, unlike traditional SQL databases, Ditto's collections are far simpler and more flexible. A collection is merely referenced by its string value, there is no need to "create" a collection. While it is typically common for all documents in a collection to have the same structure, it is not a technical requirement. For example, all documents referencing cars can go in the "cars" collection and boat documents in the "boats" collection. You can create any number of collections that best represent your data model.
To get a reference to a collection:
const carsCollection = ditto.store.collection('cars')
let carsCollection = ditto.store["cars"]// orlet carsCollection = ditto.store.collection("cars")
DITCollection *collection = [store collection:@"people"];
val carsCollection = ditto.store["cars"]// orval carsCollection = ditto.store.collection("cars")
DittoCollection carsCollection = ditto.store.collection("cars");
var coll = ditto.Store.Collection("people");
Collection cars_collection = ditto.get_store().collection("cars");
let store = ditto.store();let collection = store.collection("people").unwrap();
Documents
A Ditto document is a schema flexible unit of data in Ditto. If collections are similar to tables, then a document is similar to a row. A document, at its highest level, is a map that can contain arbitrarily nested keys and values. Each document has a primary key, often referred to as an id.
Document _id
, the Primary Key
In order for documents to sync, each document must have a unique identifier which we refer to as the id. This is the primary key of the document and is not optional.
As an example, let's say we have a document in the people
collection that looks like this:
{ "_id": "123abc", "name": "Sam", "age": 45, "isOnline": false}
You can also supply your own unique identifier when creating a document:
const docID = await ditto.store.collection('people').upsert({ _id: '123abc', name: 'Susan', age: 31,})
console.log(docID) // "123abc"
do { // upsert JSON-compatible data into Ditto let docID = try ditto.store["people"].upsert([ "_id": "abc123", "name": "Susan", "age": 31 ]) XCTAssertEqual(docID, "abc123")} catch { //handle error print(error)}
DITDocumentID *docId = [collection upsert:@{@"_id": @"123abc", @"name": @"Susan", @"age": @32 } error:nil];NSLog(@"%@", docId); // => "123abc"
val docId = ditto.store["people"].upsert( mapOf( "_id" to "123abc", "name" to "Susan", "age" to 31 ))
Map<String, Object> content = new HashMap<>();content.put("_id", "123abc");content.put("name", "Susan");content.put("age", 31);DittoDocumentId docId = ditto.store.collection("people").upsert(content);// docId => 123abc
var returnedId = ditto.Store.Collection("people").Upsert( new Dictionary<string, object> { { "_id", "123abc" }, { "name", "Joe" }, { "age", 32 }, { "isOnline", true } });
json person = json({{"_id", "123abc"}, {"name", "Susan"}, {"age", 31}});DocumentId doc_id = ditto.get_store().collection("people").upsert(person);
let doc_id = DocumentId::new(&"123abc".to_string()).unwrap();let person = json!({ // Person implements serde::Serialize "_id": doc_id, "name": "Susan".to_string(), "age": 31,});collection.upsert(person).unwrap();
curl -X POST 'https://<CLOUD_ENDPOINT>/api/v3/store/write' \ --header 'X-DITTO-CLIENT-ID: AAAAAAAAAAAAAAAAAAAABQ==' \ --header 'Content-Type: application/json' \ --data-raw '{ "commands": [{ "method": "upsert", "collection": "people", "id": "456abc", "value": { "name": "Susan", "age": 31 } }] }'
The id parameter is optional during insertion. If you do not supply a document _id
, Ditto will automatically generate a random, unique string and use that as the document's _id
instead.
const docID = await ditto.store.collection('people').upsert({ name: 'Susan', age: 31,})console.log(docID) // "507f191e810c19729de860ea"
do { // upsert JSON-compatible data into Ditto let docID = try ditto.store["people"].upsert([ "name": "Susan", "age": 31 ])} catch { //handle error print(error)}
DITDocumentID *docID = [[ditto.store collection:@"people"] upsert:@{ @"name": @"Susan", @"age": @31 } error:nil];
val docId2 = ditto.store["people"].upsert( 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").upsert(content);// docId => 507f191e810c19729de860ea
var docId = ditto.Store.Collection("people").Upsert( new Dictionary<string, object> { { "name", "Susan" }, { "age", 31 }, });
json person = json({{"name", "Susan"}, {"age", 31}});DocumentId doc_id = ditto.get_store().collection("people").upsert(person);
let person = json!({ "name": "Susan".to_string(), "age": 31,});let collection = ditto.store().collection("people").unwrap();let id = collection.upsert(person).unwrap();
curl -X POST 'https://<CLOUD_ENDPOINT>/api/v3/store/write' \ --header 'X-DITTO-CLIENT-ID: AAAAAAAAAAAAAAAAAAAABQ==' \ --header 'Content-Type: application/json' \ --data-raw '{ "commands": [{ "method": "upsert", "collection": "people", "id": "abc123", "value": { "name": "Susan", "age": 31 } }] }'
Composite Primary Keys
You may have a data model where documents are considered unique due to two or more values. Ditto's _id
field allows for a map of values. For example, we may have a person document with a unique identifier as a combination of a string userId
and an int workId
. We can insert each document as a composite key as a nested map structure under the _id
field:
const docID = await ditto.store.collection('people').upsert({ _id: { userID: '456abc', workID: 789 }, name: 'Susan', age: 31,})console.log(docID) // "{ "userID": "456abc", "workID": 789 }"
do { let docID = try ditto.store["people"].upsert([ "_id": [ "userId": "456abc", "workId": 789 ], "name": "Susan", "age": 31 ]) print(docID) // "[ "userId": "456abc", "workId": 789 ]"} catch { //handle error print(error)}
DITDocumentID *docID = [[ditto.store collection:@"people"] upsert:@{ @"_id": @{ @"userId": @"456abc", @"workId": @789 }, @"name": @"John", @"age": @31 } error:nil];NSLog(@"%@", docID); // => "NSDictionary @{ @"userId": "456abc": @"workId": @789 }"
val docId = ditto.store["people"].upsert( mapOf( "_id" to mapOf( "userId" to "456abc", "workId" to 789), "name" to "Susan", "age" to 31 ))
Map<String, Object> _id = new HashMap<>();_id.put("userId", "456abc");_id.put("workId", 789);
Map<String, Object> value = new HashMap<>();value.put("_id", _id);value.put("name", "Susan");value.put("age", 31);DittoDocumentId docID = ditto.store.collection("people").upsert(value);// docId=> "_id.put("userId", "456abc"); _id.put("workId", 789);"
// Insert JSON-compatible data into Dittovar content = new Dictionary<string, object> { { "_id", new Dictionary<string, object> {{ "userId", "456abc" }, { "workId", 789 }} }, { "name", "Susan" }, { "age", 31 }};var docId = ditto.Store.Collection("people").Upsert(content);
json content = json({{"_id", {{"userId", "456abc"}, {"workId", 789}}}, {"name", "Susan"}, {"age", 31}});DocumentId doc_ID = ditto.get_store().collection("people").upsert(content);
let collection = ditto.store().collection("people").unwrap();let complex_id = PersonId { user_id: "456abc".to_string(), work_id: 789,};let doc_id = DocumentId::new(&serde_json::json!(complex_id)).unwrap();let doc = json!({ "_id": doc_id, "name": "Susan".to_string(), "age": 31,});collection.upsert(doc).unwrap();
curl -X POST 'https://<CLOUD_ENDPOINT>/api/v3/store/write' \ --header 'X-DITTO-CLIENT-ID: AAAAAAAAAAAAAAAAAAAABQ==' \ --header 'Content-Type: application/json' \ --data-raw '{ [{ "method": "upsert", "collection": "people", "id": { "user_id": "456abc", "work_id": 789 }, "value": { "name": "Susan", "age": 31 } }] }'
Note: _id
s with the same key and values will be equal regardless of their defined order. Thus, {a: "foo", b: 1} == { b: 1, a: "foo" }
. This is because Ditto maps check for equality of key and value combinations and do not consider for literal order.
Allowed Document Data Types
Like JSON, Ditto only supports strings as keys in documents. That means attempting to insert a document like the following will throw an error:
{ 1: "numeric_keys_are_invalid", "this_part": "is_valid_though"}
The following snippets show a various set of data types
// Insert JSON-compatible data into Dittoawait ditto.store.collection('people').upsert({ boolean: true, string: 'Hello World', number: 10, map: { key: 'value' }, array: [], null: null,})
do { // Insert JSON-compatible data into Ditto try ditto.store["foo"].upsert([ "boolean": true, "string": "Hello World", "number": 10, "map": ["key": "value"], "array": [1,2,3], "null": nil ])}catch { //handle error print(error)}
[[ditto.store collection:@"foo"] upsert:@{ @"boolean": @true, @"string": @"Hello World", @"number": @10, @"map": @{ @"key": @"value" }, @"array": @[ @1, @2, @3 ], @"null": [NSNull null] } error:nil];
ditto.store["foo"].upsert(mapOf( "boolean" to true, "string" to "Hello World", "number" to 10, "map" to mapOf("key" to "value"), "array" to listOf(1,2,3), "null" to null))
// Insert JSON-compatible data into DittoMap<String, Object> content = new HashMap<>();content.put("boolean", true);content.put("string", "Hello World");content.put("number", 10);Map<String, String> innerMap = new HashMap<>();innerMap.put("key", "value");content.put("map", innerMap);content.put("array", Arrays.asList(1, 2, 3));content.put("null", null);ditto.store.collection("foo").upsert(content);
// Insert JSON-compatible data into Dittovar content = new Dictionary<string, object>{ { "boolean", true }, { "string", "Hello World" }, { "number", 10 }, { "map", new Dictionary<string, string>{{ "key", "value"}} }, { "array", new[] {1, 2, 3} }, { "null", null }};Ditto.Store.Collection("foo").Upsert(content);
// Insert JSON-compatible data into Dittoditto.get_store().collection("foo").upsert(json({{"boolean", true}, {"string", "Hello World"}, {"number", 10}, {"map", {{"key", "value"}}}, {"array", {1, 2, 3}}, {"null", NULL}}));
collection .upsert(json!({ "boolean": true, "string": "Hello World", "number": 10, "map": { "key": "value" }, "array": [1,2,3], "null": null, })) .unwrap();
curl -X POST 'https://<CLOUD_ENDPOINT>/api/v3/store/write' \ --header 'X-DITTO-CLIENT-ID: AAAAAAAAAAAAAAAAAAAABQ==' \ --header 'Content-Type: application/json' \ --data-raw '{ "commands": [{ "method": "upsert", "collection": "people", "id": "456abc", "value": { "boolean": true, "string": "Hello World", "number": 10, "map": { "key": "value", }, "array": [1,2,3], "null": null } }] }'