Platform Manual
CRUD Operations
Finding and Observing
this article provides an overview of essential methods for document retrieval, query formulation, and realtime monitoring just like with conventional database querying, you execute query operations to fetch one or more documents that satisfy specific criteria and conditions, as well as to set up listeners, referred to as subscriptions , for the data you're interested in watching /#find method /#observe local method find method use the find and findbyid methods for executing single queries against the local ditto store once invoked, these methods retrieve documents based on the query you provided as an argument to search for multiple documents based on various filters, use the find method ( /#finding by document id ) to target a single document by its id , use the findbyid method ( /#finding by document id ) additionally, when working with dynamic changes , for ease, convenience, and flexibility, you can define and use values within a top level args variable for your queries, as opposed to needing to separately adjust each individual query structure and conditions change for more information, see /#using args for dynamic data f inding by query the find method executes a single query against the ditto store, targeting to retrieve one or more documents within a given collection based on certain criteria you can search for documents within a collection against a wide range of criteria, from very broad conditions to more specific filters for instance, you can look for documents in a collection based on general conditions, such as "find all documents in the 'cars' collection with the field 'color' set to 'blue' " or narrow the focus of your search and use various filter options, including comparison operators (like greater than, less than), logical operators ( and , or ), upon many other tools ditto offers for precisely adjusting your filters for an overview of the filter options you can use to create complex search criteria in your queries, see ditto basics > docid\ mmtismykr3hxzmys1bi2w in your find function, indicate the document collection to query and define a condition that determines which documents to return val documents = ditto store collection("your collection name") find(\[query], \[arguments]) exec()val documents = ditto store collection("your collection name") find(\[query], \[arguments]) exec()const documents = await ditto store collection("your collection name") find(\[query], \[arguments]) exec()list\<dittodocument> documents = ditto store collection("your collection name") find(\[query], \[arguments]) exec();var documents = ditto store collection("your collection name") find(\[query], \[arguments]) exec();std vector\<document> documents = ditto get store() collection("your collection name") find(\[query], \[arguments]) exec()let collection = ditto store() collection("your collection name") unwrap(); collection find(\[query]) exec()?; for example, the following find function, when invoked, searches the cars collection for documents with the field color set to blue let documents = ditto store collection("your collection name") find("color == blue") exec()val documents = ditto store collection('cars') find("color == blue") exec()const documents = await ditto store collection('cars') find("color == blue") exec()list\<dittodocument> documents = ditto store collection('cars') find("color == blue") exec(); c# var documents = ditto store collection("cars") find("color == blue") exec();std vector\<document> documents = ditto get store() collection("cars") find("color == 'blue'")let documents = collection find("color == \\'blue\\'") exec()?; finding by document id the ditto query engine supports various filter operations for optimal data retrieval as the basis of data organization and access in ditto, the query engine indexes the document id so you can quickly and precisely access your data the first set of fields within each document uniquely identifies the data that its document object encodes when grouped in a collection, this id serves as the primary key identifying the document in the collection each document must be assigned a unique identifier when invoking the upsert method to create a new document, unless manually supplied, ditto automatically generates and assigns the new document a 128‑bit universally unique identifier (uuid) for instructions on how to fetch a document by id , see any of the following as appropriate default uuid if you did not supply a custom document id, see /#retrieving by string id for instructions on retrieving a document by its ditto generated and assigned uuid custom string if you provided a string value for the id parameter when calling the upsert api during document creation, see /#retrieving by string id custom json blob object if you provided two or more embedded fields for the id parameter when calling the upsert api during document creation, see /#retrieving by composite id retrieving by string id fetch a single document by its primary key the document id field by invoking the find by id method let documents = ditto store collection("your collection name") findbyid(\[id],) exec()val documents = ditto store collection("your collection name") findbyid("id") exec()const document = await ditto store collection("cars") findbyid("123456") exec()list\<dittodocument> documents = ditto store collection("your collection name") findbyid(\[id]) exec();var documents = ditto store collection("your collection name") find(\[query], \[arguments]) exec();document document = ditto get store() collection("your collection name") find by id(\[id]);let document = collection find by id(\[id]) exec()?; the following snippet demonstrates a find by id operation that, once called, searches for the document assigned the 123456 unique identifier let document = ditto store collection("cars") findbyid("123456") exec()val documents = ditto store collection("cars") findbyid("123456") exec()const document = ditto store collection("your collection name") findbyid(\[compositekey]) exec()list\<dittodocument> documents = ditto store collection("cars") findbyid("123456") exec();var document = ditto store collection("cars") findbyid("123456") exec();document document = ditto get store() collection("cars") find by id("123456");let document = collection find by id("123456") exec()?; retrieving by composite id if supplying your own document id, you can encode your value in a string or, if forming a composite key, a json object you can configure a custom document id only at the time of document creation once a document is created, to ensure consistency and uniqueness throughout the platform, the unique identifier that either ditto automatically generated and assigned or you manually assigned becomes permanent and cannot be changed at a later time let document = ditto store collection("cars") findbyid(\[compositekey]) exec()val document = ditto store collection("cars") findbyid(compositekey) exec()const query = "field == $args field" const args = { value set value } const documents = await ditto store collection("your collection name") find(query, args) exec()list\<dittodocument> documents = ditto store collection("your collection name") findbyid(\[composite key]) exec();var document = ditto store collection("your collection name") findbyid(\[id]) exec();const document = ditto get store() collection("your collection name") find by id(composite key);let document = ditto get store() collection("your collection name") find by id(composite key); using $args for dynamic data handling dynamic changing data introduces challenges that impact your end user experience, as well as the maintainability, security, and performance of your app when invoking the find and observe local methods to query values that may change dynamically during runtime, you can declare a top level args variable that defines the dynamic values, and then pass the the $args identifier within your query conditions that way, the query engine separates your query logic from the data so you can easily define and pass values as needed to adapt without having to change the query structure itself using strings to filter dynamic data may impact the maintainability, security, and performance of your app by introducing various issues such as syntax errors in your code let args \[string any] = \[ "field1" "your dynamic field", "field2" "and assigned value" ] let documents = ditto store collection("your collection name") find("field1 == $args field1 && field2 == $args field2", args) exec()val args map\<string, any> = mapof( "field1" to "your dynamic field", "field2" to "and assigned value" ) val documents = ditto store collection("your collection name") find("\\$args field1 == field1 && \\$args field2 == field2", args) exec()const query = "field == $args field" const args = { value set value } const documents = await ditto store collection("your collection name") find(query, args) exec()string query = "your query" hashmap\<string, string> args = new hashmap<>(); argsmap put("your dynamic field", "and assigned value"); list\<dittodocument> documents = ditto store collection("your collection name") find(query, args) exec();var query = "field == $args field"; var args = new map\<string, object> { { "field", "value" }}; var documents = ditto store collection("your collection name") find(query, args) exec();let query = "field == $args field"; let args = json!({ value set value }); let documents = ditto store collection("your collection name") find(query, args) exec() for example, the following snippet illustrates a local query using the string "color == $args color" let query = "color == $args color" let args = \[ "color" "blue" ] let documents = ditto store collection("your collection name") find(query, args) exec()val founddocs = ditto store collection("cars") find("color == \\$args color", mapof("color" to "blue"))const query = "color == $args color"; const args = { color "blue" }; const documents = await ditto store collection("cars") find(query, args)string query = "color == $args color"; hashmap\<string, object> args = new hashmap<>(); args put("color", "blue"); list\<dittodocument> documents = ditto store collection("your collection name") find(query, args) exec();var query = "color == $args color"; var args = new map\<string, object> { { "color", "blue" }}; var documents = ditto store collection("cars") find("color == blue", args) exec();const query = "color == $args color" const args = { color blue } std vector\<document> documents = ditto get store() collection('cars') find(query, args)let query = "color == $args color" let args = \[ "color" "blue" ] let documents = ditto store collection("cars") find(query, args) exec() limiting and sorting results in addition to using various operators and string formats to construct conditions in your queries, you can also control the results that are returned from calls to the find and find by id functions /#limit method restricting results /#s ort method arranging results /#limit and sort methods in a single executable limit method restricting results restrict the number of results returned by your query by using the limit method to specify the number of results you want to return let documents = ditto store collection("your collection name") find(\[query], \[arguments]) limit(\[value]) exec()val sortedandlimitedredcars = ditto store collection("cars") collection("your collection name") find(\[query], \[arguments]) limit(\[value]) exec()const documents = await ditto store collection("your collection name") find(\[query], \[arguments]) limit(\[value]) exec()list\<dittodocument> documents = ditto store collection("your collection name") find(\[query], \[arguments]) limit(\[value]) exec();var documents = ditto store collection("your collection name") find(\[query], \[arguments]) limit(\[value]) exec();std vector\<document> documents = ditto get store() collection("your collection name") find(\[query], \[arguments]) limit(\[value]);let documents = collection find(\[query], \[arguments]) limit(\[value]) exec()?; the following snippet demonstrates a limit operation set for 100 query results let documents = ditto store collection("cars") find("color == 'blue'") limit(100) exec()val sortedandlimitedredcars = ditto store collection("cars") find("color == 'red'") sort("miles", dittosortdirection ascending) limit(100) exec()const documents = await ditto store collection("cars") find("color == 'blue'") limit(100) exec()list\<dittodocument> documents = ditto store collection("cars") find("color == 'blue'") limit(100) exec();var documents = ditto store collection("cars") find("color == 'blue'") limit(100) exec();std vector\<document> documents = ditto get store() collection("cars") find("color == 'blue'") limit(100);let documents =collection find("color == \\'blue\\'") limit(100) exec()?; s ort method arranging results sort the results by a specific order using the sort method with the sort method, you specify the field to match and the desired order for arranging the matching query results, as follows by default, queries that do not include a sort operation filter by document id let documents = ditto store collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ ascending| decending]) exec()let documents = ditto store collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ ascending| decending]) exec()const documents = await ditto store collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ascending|decending]) exec()list\<dittodocument> documents = ditto store collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ascending|decending]) exec();var documents = ditto store collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ascending|decending]) exec();std vector\<document> documents = ditto get store() collection("your collection name") find(\[query], \[arguments]) sort(\[field], \[ascending|decending])let documents = collection find(\[query]) sort(\[field], \[ascending|decending]) exec()?; the following snippet demonstrates a sort operation on the miles field with results arranged in ascending order let documents = ditto store collection("cars") find("color == 'blue'") sort("miles", direction ascending) exec()val sortedredcars = ditto store collection("cars") find("color == 'blue'") sort("miles", dittosortdirection ascending) exec()const documents = await ditto store collection("cars") find("color == 'blue'") sort("miles", direction ascending) exec()list\<dittodocument> documents = ditto store collection("cars") find("color == 'blue'") sort("miles", dittosortdirection ascending) exec();var documents = ditto store collection("cars") find("color == 'blue'") sort("miles", dittosortdirection ascending) exec();std vector\<document> documents = ditto get store() collection("cars") find("color == 'blue'") sort("miles", "ascending");let sort param = ffi sdk corderbyparam { query c str c!("miles"), direction ffi sdk querysortdirection ascending, }; let documents = collection find("color == \\'blue\\'") sort(sort param) exec()?; limit and sort methods in a single executable to enhance query efficiency, you can perform both sort and limit operations within a single query, as follows let documents = ditto store collection("cars") find("color == 'blue'") sort("miles", direction ascending) limit(100) exec()val documents = ditto store collection("cars") find("color == 'blue'") sort("miles", dittosortdirection ascending) limit(100) exec()const documents = await ditto store collection("cars") find("color == 'blue'") sort("miles", "ascending") limit(100) exec()list\<dittodocument> documents = ditto store collection("cars") find("color == 'blue'") sort("miles", dittosortdirection ascending) limit(100) exec();var documents = ditto store collection("cars") find("color == 'blue'") sort("miles", "ascending") limit(100) exec();std vector\<document> documents = ditto get store() collection("cars") find("color == 'blue'") sort("miles", "ascending") limit(100);let sort param = ffi sdk corderbyparam { query c str c!("miles"), direction ffi sdk querysortdirection ascending, }; let documents = collection find("color == \\'blue\\'") sort(sort param) limit(100) exec()?; observe local method in scenarios where you want to continuously watch changes occurring to your local ditto store in realtime, use live queries with a live query, you signal which changes you're interested in watching and define the callback function that you want ditto to trigger in response additionally, you can incorporate both time based and state based replication techniques into your app's design patterns for more information, see use cases > docid\ t97k1azahhrwqrqtztzhq and docid\ kt8y44uqmtdjxu5yv2 kk for organization and management, you can create a live query object to store a single live query or multiple live queries, as well as a custom class that encapsulates the logic for managing your live queries let livequery = ditto store collection("your collection name") find(query, arguments) observelocal { callback }val livequery = ditto store collection("your collection name") find(\[query], \[arguments]) observelocal{\[callback]}const livequery = ditto store collection('your collection name') find(your query) observelocal((result, event) => { // do something with the result });dittolivequery livequery = ditto store collection("your collection name") find(query, arguments) observelocal(callback);var livequery = ditto store collection("your collection name") find(\[query], \[arguments]) observelocal(\[callback]);const livequery = ditto get store() collection("your collection name") find(\[query], \[arguments]) observe local(\[callback])let livequery = collection find(\[query], \[arguments]) observe local(\[callback]) creating live queries to set up a single live query, as demonstrated in the following snippet for instructions on creating multiple live queries stored in a single livequery object, see /#creating a batch of live queries in the top most scope of your app, declare a live query object specify the event you're interested in watching; for example, the cars collection where the color is black using the observe local method, define the subsequent livequery callback function that you want ditto to execute in your app for more information, see /#constructing live query callback functions , as follows // action somewhere in your application func userdidinsertcar() { = try? ditto store collection("cars") upsert(\[ "model" "ford", "color" "black" ] as \[string any?]) } // register live query to update ui let livequery = ditto store collection("cars") find("color == 'red'") observelocal { cars, event in // do something }// action somewhere in your application fun userdidinsertcar() { ditto store collection("cars") upsert(mapof( "model" to "ford", "color" to "black" )) } // dittoregister live query to update ui val observelocalquery = ditto store collection("cars") find("issold == false") observelocal { docs, event > // do something }const livequery = ditto store collection('cars') find("color == 'red'") observelocal((cars, event) => { // do something })// action somewhere in your application map\<string, object> content = new hashmap<>(); content put("model", "ford"); content put("color", "black"); ditto store collection("cars") upsert(content); // register live query to update ui dittolivequery livequerylocal = ditto store collection("cars") find("owner == 'susan'") observelocal((docs, event) > { // do something });var livequery = ditto store collection("your collection name") find(\[query], \[arguments]) observelocal(\[callback]);// register live query to update ui std shared ptr\<livequery> query = collection find("color == 'red'") observe local(\[&]\(std vector\<document> docs, livequeryevent event) { });let livequery = collection find("color == \\'black\\'") observe local(move |docs, event| { // print the number of blue cars println!(docs len()); } for example, the following snippet demonstrates an observelocal method that monitors changes in the cars collection where the color is "blue" once the changes occur, the total number of documents displays to the end user dittolivequery livequery = ditto store collection("cars") find("color == 'blue'") observelocal((docs, event) > { // log the number of blue cars log v(docs length) }) constructing live query callback functions if you're not using a reactive framework , consider reacting to changes from a live query event object a live query event object is a dynamic query that, as changes occur, automatically updates ditto in realtime reactive frameworks — like react, jetpack compose, and swiftui — provide tooling and various features to make ui development more efficient and help you build responsive apps with minimal effort these reactive frameworks do this by abstracting away the low level details of updating ui, managing state, and rendering components; eliminating the need to manually implement a diffing algorithm for more information, see react's official documentation > https //react dev/learn events argument a livequery callback consists of two arguments docs and event the docs argument specifies your collection and the event argument identifies which documents have changed the following table provides an overview of events in ditto event type description initial the first event that will be delivered (and delivered only once) update the event to return each time the results of your live query change it contains information about the set of documents that previously matched the query before the update, along with information about what documents have been inserted , removed , updated , or moved ( upserted ), as part of the set of matching documents creating a batch of live queries to create multiple live queries stored in a single livequery object that you can use to add live queries, remove specific ones, and stop all live queries at once, using either an array or a map , create a custom class that encapsulates your management logic for more information about batching live queries, see livequeryevent in the docid\ eaz0kkfoztspuhjja5snj > ditto api reference for your language (for a complete listing, see docid\ oxv2zq7x15t1k8sl ztpk ) for more information about ditto arrays and maps , see docid\ yxemkh1com3csuumqcnlf canceling live queries to cancel a live query, call cancel or stop on the live query object you initially set up to establish the live query once canceled, the live query stops receiving updates swift pseudocode // subscription = nil // this does not work sub cancel() // this is correct! swift pseudocode // subscriptions removeall() do not do this < this will not work for sub in subscriptions { sub cancel() } subscriptions removeall() // after calling cancel, it is safe to remove them from the array