Module dittolive_ditto::types
source · [−]Expand description
The Ditto custom types.
These types have a dedicated CRDT logic. Each type has two variants: a Mutable and a non Mutable
one. The non Mutable version correspond to a readonly value. The Mutable version allows the
developer to read and update its content and is returned by the
DittoMutDocument::get_mut
method.
Counter
The DittoCounter
and DittoMutableCounter
represent counter which can be updated
simultaneously on several peers at once without fearing to lose concurrent increments.
use serde_json::json;
let store = ditto.store();
let collection = store.collection("foo").unwrap();
// 👇 insert a new `Counter`at path "counter" with default value `0`
let id = collection.upsert(json!({ "counter": DittoCounter::new() }))?;
let doc = collection.find_by_id(&id).exec()?;
let counter: DittoCounter = doc.get("counter")?;
assert_eq!(counter.value(), 0.0);
collection.find_by_id(&id).update(|mut_doc| {
let mut_doc = mut_doc.unwrap();
// 👇 increment counter by `5.0`
mut_doc
.get_mut::<DittoMutableCounter>("counter")
.unwrap()
.increment(5.0)
.unwrap();
})?;
let counter: DittoCounter = doc.get("counter")?;
assert_eq!(counter.value(), 5.0);
Register
The DittoRegister
and DittoMutableRegister
are containers for a single value following
the concept of LWW : “Last Write Win”. Primitive types such as String
, u32
, bool
, etc…,
are implicitly wrapped to and unwrapped from registers.
let id = collection.upsert(json!({ "register": DittoRegister::new(42)?, "string":"SomeString"}))?;
let doc = collection.find_by_id(&id).exec()?;
let register: DittoRegister = doc.get("register")?;
assert_eq!(register.value::<u32>()?, 42);
// 👇 you can access a primitive type inside a Register
let register: DittoRegister = doc.get("string")?;
assert_eq!(register.value::<String>()?, "SomeString");
collection
.find_by_id(&id)
.update(|mut_doc| {
let mut_doc = mut_doc.unwrap();
// 👇 set register value to `5.0`
mut_doc
.get_mut::<DittoMutableRegister>("register")
.unwrap()
.set(5.0)
.unwrap();
})
.unwrap();
// Value changed!
let register: DittoRegister = doc.get("register")?;
assert_eq!(register.value::<f32>()?, 5.0);
let direct_access: f32 = doc.get("register")?;
assert_eq!(direct_access, 5.0);
The main purpose of the DittoRegister
type is to treat structs as a single CRDT value.
Without Register, fields can be mixed up, for instance:
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Serialize, Deserialize)]
struct Car {
color: String,
price: u32,
}
#[derive(Serialize, Deserialize)]
struct Boat {
length: f32,
name: String,
}
let car = Car {
color: String::from("red"),
price: 42_000,
};
let id = collection.upsert(json!({ "vehicle": car }))?;
collection
.find_by_id(&id)
.update(|mut_doc| {
let boat = Boat {
length: 248.0,
name: String::from("Richelieu"),
};
let mut_doc = mut_doc.unwrap();
// 👇 update value to boat
mut_doc.set("vehicle", boat).unwrap();
})
.unwrap();
let document = collection.find_by_id(&id).exec().unwrap();
// document == {
// "vehicle": {
// "color": "red",
// "length": 248,
// "name" : "Richelieu",
// "price": 42_000,
// }
//}
whereas with Register, content is kept clean, but the initial value is lost:
use serde_json::json;
let car = Car{color: String::from("red"), price:42_000};
let id = collection.upsert(json!({"vehicle": DittoRegister::new(car)?}))?;
collection
.find_by_id(&id)
.update(move |mut_doc| {
let boat = Boat{length:248.0, name:String::from("Richelieu")};
let mut_doc = mut_doc.unwrap();
// 👇 set register value to `boat`
mut_doc
.get_mut::<DittoMutableRegister>("vehicle")
.unwrap()
.set(boat)
.unwrap();
})?;
let document = collection.find_by_id(&id).exec()?;
// document == {
// "vehicle": {
// "length": 248,
// "name" : "Richelieu",
// }
//}