CRUD Operations
CREATE
this article provides how to instructions for creating and organizing documents, linking them to associated files, called attachments , and creating map structures for additional document hierarchies attachments in ditto are represented using the attachment data type, which you can use to store binary data, such as images, alongside queryable descriptive information, such as file name and description database operations syncing data across the mesh is an entirely separate process from crud, involving replicating documents across different databases rather than interacting with a single datastore creating documents to create a document, call the execute api method against the ditto store object and include an insert into query that specifies the document to be inserted ditto does not support nesting documents within documents instead, opt for a foreign key relationship by referencing the document id for more information, see docid\ hzs9xpjv6swbov5j3bjoc for example, the following snippet demonstrates how to insert a new document with a single field "color" set to "blue" await ditto store execute( query "insert into cars documents (\ newcar)", arguments \["newcar" \["color" "blue"]]);ditto store execute( "insert into cars documents (\ newcar)", mapof("newcar" to mapof("color" to "blue")))await ditto store execute( "insert into cars documents (\ newcar)", { newcar { color "blue" } });dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ newcar)", collections singletonmap("newcar", collections singletonmap("color", "blue")), );var args = new dictionary\<string, object>(); args add("newcar", new { color = "blue" }); await ditto store executeasync( "insert into cars documents (\ newcar)", args);std map\<std string, std map\<std string, std string>> args; args\["newcar"] = {{"color", "blue"}}; auto result = ditto get store() execute( "insert into cars documents (\ newcar)", args) get();use serde serialize; \#\[derive(serialize)] struct args { newcar car, } \#\[derive(serialize)] struct car { color string } // let args = args { newcar car { color "blue" to string() }, }; ditto store() execute( "insert into cars documents (\ newcar)", some(args into())); to create multiple documents efficiently, batch your create operations in a single operation if desired, supply a document id in your creation request; otherwise, ditto automatically generates and assigns one inserting multiple documents to create multiple documents in a single operation, use the insert into operation as follows await ditto store execute( query "insert into cars documents (\ car1), (\ car2)", arguments \[ "car1" \["color" "blue"], "car2" \["color" "red"] ]);ditto store execute( "insert into cars documents (\ car1),(\ car2)", mapof( "car1" to mapof("color" to "blue"), "car2" to mapof("color" to "red") ))await ditto store execute( "insert into cars documents (\ car1),(\ car2)", { car1 { color 'blue' }, car2 { color 'red' } });map\<string, map\<string, string>> args = new hashmap<>(); args put("car1", collections singletonmap("color", "blue")); args put("car2", collections singletonmap("color", "red")); dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ car1),(\ car2)", args, new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } );var args = new dictionary\<string, object>(); args add("car1", new { color = "blue" }); args add("car2", new { color = "red" }); await ditto store executeasync( "insert into cars documents (\ car1),(\ car2)", args);std map\<std string, std map\<std string, std string>> args; args\["car1"] = {{"color", "blue"}}; args\["car2"] = {{"color", "red"}}; auto result = ditto get store() execute( "insert into cars documents (\ car1),(\ car2)", args) get();use serde serialize; \#\[derive(serialize)] struct args { newcar car, } \#\[derive(serialize)] struct car { color string } // let args = args { car1 car { color "blue" to string() }, car2 car { color "red" to string() }, }; ditto store() execute( "insert into cars documents (\ car1),(\ car2)", some(args into())); identifying documents unless manually supplied, ditto automatically generates and assigns the new document a 128‑bit universally unique identifier (uuid) the document identifier is represented as id and serves as the primary key for the document retrieving document ids t o access the ids of the documents affected by the insertion into operation, call the mutateddocumentids method on the result object after the insertion like this let result = await ditto store execute( query "insert into cars documents (\ newcar)", arguments \[ newcar \["color" "blue"] ]); // "507f191e810c19729de860ea" print(result mutateddocumentids()\[0])var result = ditto store execute( "insert into cars documents (\ newcar)", mapof("newcar" to mapof("color" to "blue"))) // "507f191e810c19729de860ea" println(result mutateddocumentids() first())const result = await ditto store execute( "insert into cars documents (\ newcar)", { newcar { color 'blue' } }); // "507f191e810c19729de860ea" console log(result mutateddocumentids()\[0])dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ newcar)", collections singletonmap("newcar", collections singletonmap("color", "blue")), new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } ); // "507f191e810c19729de860ea" system out println(result mutateddocumentids()\[0]);var insertargs = new dictionary\<string, object>(); insertargs add("newcar", new { color = "blue" }); var result = await ditto store executeasync( "insert into cars documents (\ newcar)", insertargs); // "507f191e810c19729de860ea" result mutateddocumentids foreach(id => console writeline(id));std map\<std string, std map\<std string, std string>> args; args\["newcar"] = {{"color", "blue"}}; auto result = ditto get store() execute( "insert into cars documents (\ newcar)", args) get(); // "507f191e810c19729de860ea" std cout << result mutated document ids()\[0] to string();use serde serialize; \#\[derive(serialize)] struct args { newcar car, } \#\[derive(serialize)] struct car { color string } // let args = args { newcar car { color "blue" to string() }, }; let result = ditto store() execute( "insert into cars documents (\ newcar)", some(args into())); // "507f191e810c19729de860ea" println!("{}", result mutated document ids()\[0] to string()) supplying string ids w hen creating a document, you can assign it a custom id this custom id can be generated using a single string value or a combination of two or more string values this flexibility in structuring document identifiers allows you to customize document ids to your specific requirements, use cases, or standard naming conventions the following snippet demonstrates a new document assigned the custom id "123" let result = await ditto store execute( query "insert into cars documents (\ newcar)", arguments \[ newcar \[ " id" "123", "color" "blue"] ]); // "123" print(result mutateddocumentids()\[0])var result = ditto store execute( "insert into cars documents (\ newcar)", mapof("newcar" to mapof(" id" to "123", "color" to "blue"))) // "507f191e810c19729de860ea" println(result mutateddocumentids() first())const newcar = { id "123", color "blue" } const result = await ditto store execute(` insert into cars documents (\ newcar)`, { newcar }); // "123" console log(result mutateddocumentids()\[0])map\<string, string> newcar = new hashmap<>(); newcar put(" id", "123"); newcar put("color", "blue"); dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ newcar)", collections singletonmap("newcar", newcar), new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } ); // "123" system out println(result mutateddocumentids()\[0]);var insertargs = new dictionary\<string, object>(); insertargs add("newcar", new { id = "123", color = "blue" }); var result = await ditto store executeasync( "insert into your collection name documents (\ newcar)", insertargs); // "123" result mutateddocumentids foreach(id => console writeline(id));std map\<std string, std map\<std string, std string>> args; args\["newcar"] = {{" id", "123"},{"color", "blue"}}; auto result = ditto get store() execute( "insert into cars documents (\ newcar)", args) get(); // "123" std cout << result mutated document ids()\[0] to string();use serde serialize; \#\[derive(serialize)] struct args { newcar car, } \#\[derive(serialize)] struct car { id string, color string } // let args = args { newcar car { id "123" to string(), color "blue" to string() }, }; let result = ditto store() execute( "insert into cars documents (\ newcar)", some(args into())); // "123" println!("{}", result mutated document ids()\[0] to string()) following is the new 123 document that results ditto document { " id" "123", "color" "blue" } forming composite keys the following demonstrates combining the vin and make fields to form the composite key let arguments = \[ newcar \[ " id" \[vin "123", make "toyota"], "color" "blue"] ]; let result = await ditto store execute( query "insert into cars documents (\ newcar)", arguments arguments); // "{vin "123", make "toyota"}" print(result mutateddocumentids()\[0])var result = ditto store execute( "insert into cars documents (\ newcar)", mapof( "newcar" to mapof( " id" to mapof( "vin" to "123", "make" to "toyota" ), "color" to "blue" ))) // "{vin "123", make "toyota"}" println(result mutateddocumentids() first())const newcar = { id { vin "123", make "toyota" }, color "blue" } const result = await ditto store execute(` insert into cars documents (\ newcar)`, { newcar }); // "{vin "123", make "toyota"}" console log(result mutateddocumentids()\[0])map\<string, string> newcarid = new hashmap<>(); newcarid put("vin", "123"); newcarid put("make", "toyota"); map\<string, object> newcar = new hashmap<>(); newcar put(" id", newcarid); newcar put("color", "blue"); dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ newcar)", collections singletonmap("newcar", newcar), new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } ); // "{vin "123", make "toyota"}" system out println(result mutateddocumentids()\[0]);var newid = new { vin "123", make "toyota"}; var insertargs = new dictionary\<string, object>(); insertargs add("newcar", new { id = newid, color = "blue" }); var result = await ditto store executeasync( "insert into your collection name documents (\ newcar)", insertargs); // "{vin "123", make "toyota"}" result mutateddocumentids foreach(id => console writeline( system text json jsonserializer serialize(id)));struct carid { std string vin; std string make; }; struct car { carid id; std color string; }; std map\<std string, car> args; args\["newcar"] = {{"123", "toyota"}, "blue"}; auto result = ditto get store() execute( "insert into cars documents (\ newcar)", args) get(); // "{vin "123", make "toyota"}" std cout << result mutated document ids()\[0] to string();use serde serialize; \#\[derive(serialize)] struct args { newcar car, } \#\[derive(serialize)] struct carid { vin string, make string } \#\[derive(serialize)] struct car { id carid, color string } // let args = args { newcar car { id carid { vin "123" to string(), make "toyota" to string() }, color "blue" to string() }, }; let result = ditto store() execute( "insert into cars documents (\ newcar)", some(args into())); // "{vin "123", make "toyota"}" println!("{}", result mutated document ids()\[0] to string()) creating attachments there are two separate steps in the process of creating an attachment generate the attachment in the ditto store ( /#initiating attachment objects ) reference the returned attachment token in a document ( /#referencing attachment tokens for a realworld usage scenario, see either the demo chat app for ios or android in the getditto > https //github com/getditto/demoapp chat/tree/main github repository initiating attachment objects to create the attachment object that encodes your attachment data, call the newattachment method on the store namespace when creating an attachment when creating an attachment, provide a valid path to the large file or deeply embedded document you want to encode as an attachment failure to do so may result in errors related to input output (io) operations creating an attachment in the local ditto store from data is currently only available in the ditto sdk for javascript to use a dql type other than a register — the default data type in ditto — you must explicitly specify the type in your query; otherwise, ditto defaults to the register type as follows if desired, enclose extra information about the attachment, such as name and description this metadata is useful for attachment fetching operations let newattachment = try await ditto store newattachment(path filepath)const newattachment = await ditto store newattachment(filepathordata)// snippet currently unavailable use dittolive ditto prelude ; use serde json json; use std {collections hashmap, path path}; use serde serialize; async fn attachment doc snippet( peer a \&ditto, peer b \&ditto, filepath \&path, ) > result<(), error> { // creating an attachment in the local ditto store from a file let new attachment = peer a store() new attachment(filepath, < > default()) await?; adding optional metadata if you want to include information about the attachment, such as name, description, and other relevant details, enclose it in a metadata object as key value pairs let metadata = \["name" "image png"] let newattachmentwithmetadata = try await ditto store newattachment(path filepath, metadata metadata)val attachmentmetadata = mapof("name" to attachmentfilename) val attachment = runblocking { ditto store newattachment(attachmentpathorinputstream, attachmentmetadata) }const metadata = { name 'image png' } const newattachment = await ditto store newattachment(imagebytes, metadata)val attachmentmetadata = mapof("name" to attachmentfilename) val attachment = runblocking { ditto store newattachment(attachmentpathorinputstream, attachmentmetadata) }// creating an attachment in the local ditto store from a file var newattachment = await ditto store newattachmentasync(path filepath); // using the optional `metadata` parameter to store arbitrary metadata with the attachment var metadata = new dictionary\<string, string>() { { "name", "image png" } }; var newattachmentwithmetadata = await ditto store newattachmentasync(filepath, metadata); // creating a ditto document with an attachment // create a new document object and store the attachment on the `my attachment` field var newdocument = new dictionary\<string, object>() { { " id", "123" }, { "my attachment", newattachment } };// using the optional `metadata` parameter to store arbitrary metadata with the attachment let user data = hashmap from iter(\[ ("name" into(), "image png" into()), // idea attach a small "summary" of the file contents to preview them in // the ui so that the user has some idea of what to "download"/ fetch // // for a text file, it could be some title or the first words of it // for an image, it could be a base64 encoded thumbnail ( "thumbnail" into(), "/9j/4atg9y zw0ga x bzd w0g zg9sb " into(), ), ]); for example, in the following snippet, the metadata property encapsulates the name of the attachment, as well as its description val attachmentmetadata = mapof( "name" to "japan", "description" to "this is an image of a sunset in japan " ) val attachment = runblocking { ditto store newattachment(attachmentpathorinputstream, attachmentmetadata) }val attachmentmetadata = mapof("name" to "japan") val attachment = runblocking { ditto store newattachment(attachmentpathorinputstream, attachmentmetadata) }// attachment data from base64 encoded image to bytes const imagebase64 = 'ivborw0kggoaaaansuheugaaaqaaaaeaaqmaaabmvdolaaaaa1bmvew10nbjbbbqaaaah0leqvroge3baq0aaadcopdpbq43oaaaaaaaaaaavg0haaabmmdh1qaaaabjru5erkjggg==' const imagebytes = uint8array from(imagebase64, (character) => character charcodeat(0)) // using the optional `metadata` parameter to store arbitrary metadata with the attachment const metadata = { name 'japan png', description 'this is an image of a sunset in japan ' } const newattachment = await ditto store newattachment(imagebytes, metadata)val attachmentmetadata = mapof( "name" to "japan", "description" to "this is an image of a sunset in japan " ) val attachment = runblocking { ditto store newattachment(attachmentpathorinputstream, attachmentmetadata) }// creating an attachment in the local ditto store from a file var newattachment = await ditto store newattachmentasync(path filepath); // using the optional `metadata` parameter to store arbitrary metadata with the attachment var metadata = new dictionary\<string, string>() { { "name", "image png" }, { "description", "this is a picture of a sunset in japan" } }; var newattachmentwithmetadata = await ditto store newattachmentasync(filepath, metadata);// using the optional `metadata` parameter to store arbitrary metadata with the attachment let user data = hashmap from iter(\[ ("name" into(), "japan png" into()), ( "thumbnail" into(), "/9j/4atg9y zw0ga x bzd w0g zg9sb " into(), ), ( "description" into(), "this is an image of a sunset in japan" into() ), ]); let new attachment with metadata = peer a store() new attachment(filepath, user data) await?; referencing attachment tokens after creating and storing a new attachment object in ditto, you receive an attachment token as part of the response an attachment token is an attachment token is a pointer that references its associated attachment object this token is how you interact with the attachment, such as when fetching or linking it with a document t he following snippet demonstrates how to create a new document object containing a new attachment, and then insert it into the cars collection // create a new document object and store the attachment on the `my attachment` field let newdocument \[string any] = \[" id" "123", "my attachment" newattachment] // insert the new document into the collection // note that the `my attachment` field needs to be declared as an `attachment` data type try await ditto store execute( query """ insert into collection cars (my attachment attachment) documents (\ newdocument) """, arguments \["newdocument" newdocument] )// create a new document object and store the attachment on the `my attachment` field val document = mapof("some" to "string", "my attachment" to attachment) val insertquery = "insert into collection cars (my attachment attachment) documents (\ document)" // insert the new document into the collection // note that the `my attachment` field needs to be declared as an `attachment` data type val insertquery = "insert into collection cars (my attachment attachment) documents (\ document)" val insertarguments = mapof("document" to document) val insertqueryresult = runblocking { ditto store execute(insertquery, insertarguments) }// create a new document object and store the attachment on the `my attachment` field const newdocument = { id "123", my attachment newattachment } // insert the new document into the collection // note that the `my attachment` field needs to be defined as an attachment data type await ditto store execute(` insert into collection cars (my attachment attachment) documents (\ newdocument)`, { newdocument })// create a new document object and store the attachment on the `my attachment` field val document = mapof("some" to "string", "my attachment" to attachment) val insertquery = "insert into collection cars (my attachment attachment) documents (\ document)" // insert the new document into the collection // note that the `my attachment` field needs to be declared as an `attachment` data type val insertquery = "insert into collection cars (my attachment attachment) documents (\ document)" val insertarguments = mapof("document" to document) val insertqueryresult = runblocking { ditto store execute(insertquery, insertarguments) }// creating a ditto document with an attachment // create a new document object and store the attachment on the `my attachment` field var newdocument = new dictionary\<string, object>() { { " id", "123" }, { "my attachment", newattachment } }; // insert the new document into the collection // note that the `my attachment` field needs to be declared as an `attachment` data type await ditto store executeasync( query @" insert into collection cars (my attachment attachment) documents (\ newdocument) ", arguments new dictionary\<string, object>() { { "newdocument", newdocument } } );// creating a ditto document with an attachment let new document / impl serialize / = json!({ " id" "123", "my attachment" new attachment, }); // insert the new document into the collection // note that the `my attachment` field needs to be declared as an `attachment` data type peer a store() execute( r#" insert into collection cars (my attachment attachment) documents (\ new document) "#, some( json!({ "new document" new document, }) into(), ), ) await?; creating maps there are two ways to create a map structure within a document in an insert operation — create a new document and nest it with this set of fields in an update operation — if the map does not exist, create it (see docid 5lh6dqad0itazoy8 i16 ) to represent a highly complex data structure in a map , consider embedding it with an additional map embedding a map within a map establishes an additional hierarchy the decision to use deeply embedded maps in a single document or opt for a flat model depends on your requirements, relationships between data, and tolerance for certain tradeoffs the flat model is a simple, non‑embedded structure in which you spread your data across multiple, separate documents inserting to create a map when inserting a new document in ditto, you can define a field as a map and include the structure of key value pairs nested within it — a two in one approach for example let arguments \[string any] = \[ "newcar" \[ " id" "123", "properties" \[ "color" "blue", "mileage" 3000 ] } ]; await ditto store execute( query "insert into collection cars (properties map) documents (\ newcar)", arguments arguments);val arguments = mapof( "newcar" to mapof( " id" to "123", "properties" to mapof( "color" to "blue", "mileage" to 3000 ) ) ) ) ditto store execute(""" insert into collection cars (properties map) documents (\ newcar) """, arguments)const newcar = { id "123", properties { color 'blue', mileage 3000 } } await ditto store execute(` insert into collection cars (properties map) documents (\ newcar)`, { newcar });map\<string, object> properties = new hashmap<>(); properties put("color", "blue"); properties put("mileage", 3000); map\<string, object> newcar = new hashmap<>(); newcar put(" id", "123"); newcar put("properties", properties); dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into collection cars (properties map) documents (\ newcar)", collections singletonmap("newcar", newcar), new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } );var submap = new { color = "blue", mileage = 3000 }; var insertargs = new dictionary\<string, object>(); insertargs add("newcar", new { id = newid, properties = submap }) var result = await ditto store executeasync( "insert into collection cars (properties map) documents (\ newcar)", insertargs);struct properties { std string color; int mileage; }; struct car { std string id; properties properties; }; // std map\<std string, car> args; args\["newcar"] = {"123", {"blue", 3000}}; ditto get store() execute( "insert into collection your collection name (properties map)" \+ " documents (\ newcar)", args) get();struct args { newcar car, } struct properties { color string, mileage i32 } struct car { id string, properties properties } // let args = args { newcar car { id "123" to string(), properties properties { color "blue" to string(), mileage 3000 } }, }; await ditto store() execute( "insert into collection cars (properties map) documents (\ newcar)", args); another method to creating a map within a document is to perform an update operation in the update approach, you set the structure of key value pairs and specify the document id for storage for example, this statement creates the map structure — unless it already exists — setting a single key value pair of color is red within the properties map for the document with the id 123 in the cars collection dql update collection cars (properties map) set properties > ( color = 'red' ) where id = '123' handling query execution results when writing your insert query, include a completion handler to respond to the result of the query execution once defined, you can reuse this handler throughout your codebase, resulting in greater consistency throughout your app for example await ditto store execute( query "insert into cars documents (\ newcar)", arguments \["newcar" \["color" "blue"]]);ditto store execute( "insert into cars documents (\ newcar)", mapof("newcar" to mapof("color" to "blue")))await ditto store execute( "insert into cars documents (\ newcar)", { newcar { color "blue" } });dittoqueryresult result = (dittoqueryresult) ditto store execute( "insert into cars documents (\ newcar)", collections singletonmap("newcar", collections singletonmap("color", "blue")), new continuation<>() { @nonnull @override public coroutinecontext getcontext() { return emptycoroutinecontext instance; } @override public void resumewith(@nonnull object o) { if (o instanceof result failure) { // handle failure } } } );var args = new dictionary\<string, object>(); args add("newcar", new { color = "blue" }); await ditto store executeasync( "insert into cars documents (\ newcar)", args);std map\<std string, std map\<std string, std string>> args; args\["newcar"] = {{"color", "blue"}}; auto result = ditto get store() execute( "insert into cars documents (\ newcar)", args) get();struct args { newcar car, } struct car { color string } // let args = args { newcar car { color "blue" to string() }, }; ditto store() execute( "insert into cars documents (\ newcar)", args);