mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 12:57:20 +00:00
feat: allow users to query the changed containers in the target id range (#549)
* feat: allow users to query the changed containers in the target id range * chore: add changeset note * chore: update cargo toml * test: add related tests and add a commit before get_changed_container_in
This commit is contained in:
parent
6e878d216a
commit
778ca5452d
9 changed files with 122 additions and 7 deletions
5
.changeset/hip-squids-train.md
Normal file
5
.changeset/hip-squids-train.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"loro-crdt": minor
|
||||
---
|
||||
|
||||
Feat: allow users to query the changed containers in the target id range
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1069,6 +1069,7 @@ dependencies = [
|
|||
"ctor 0.2.6",
|
||||
"dev-utils",
|
||||
"enum-as-inner 0.6.0",
|
||||
"fxhash",
|
||||
"generic-btree",
|
||||
"loro-common 1.0.0-beta.5",
|
||||
"loro-delta 1.0.0-beta.5",
|
||||
|
|
|
@ -1653,6 +1653,18 @@ impl LoroDoc {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_changed_containers_in(&self, id: ID, len: usize) -> FxHashSet<ContainerID> {
|
||||
self.commit_then_renew();
|
||||
let mut set = FxHashSet::default();
|
||||
let oplog = &self.oplog().try_lock().unwrap();
|
||||
for op in oplog.iter_ops(id.to_span(len)) {
|
||||
let id = oplog.arena.get_container_id(op.container()).unwrap();
|
||||
set.insert(id);
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: PERF: This method is quite slow because it iterates all the changes
|
||||
|
|
|
@ -328,6 +328,10 @@ impl<'a> RichOp<'a> {
|
|||
self.peer
|
||||
}
|
||||
|
||||
pub fn container(&self) -> ContainerIdx {
|
||||
self.op.container
|
||||
}
|
||||
|
||||
pub fn timestamp(&self) -> i64 {
|
||||
self.timestamp
|
||||
}
|
||||
|
|
|
@ -190,6 +190,8 @@ extern "C" {
|
|||
pub type JsCommitOption;
|
||||
#[wasm_bindgen(typescript_type = "ImportStatus")]
|
||||
pub type JsImportStatus;
|
||||
#[wasm_bindgen(typescript_type = "(change: ChangeMeta) => boolean")]
|
||||
pub type JsTravelChangeFunction;
|
||||
}
|
||||
|
||||
mod observer {
|
||||
|
@ -606,7 +608,15 @@ impl LoroDoc {
|
|||
/// @param ids - the changes to visit
|
||||
/// @param f - the callback function, return `true` to continue visiting, return `false` to stop
|
||||
#[wasm_bindgen(js_name = "travelChangeAncestors")]
|
||||
pub fn travel_change_ancestors(&self, ids: Vec<JsID>, f: js_sys::Function) -> JsResult<()> {
|
||||
pub fn travel_change_ancestors(
|
||||
&self,
|
||||
ids: Vec<JsID>,
|
||||
f: JsTravelChangeFunction,
|
||||
) -> JsResult<()> {
|
||||
let f: js_sys::Function = match f.dyn_into::<js_sys::Function>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Err(JsValue::from_str("Expected a function")),
|
||||
};
|
||||
let observer = observer::Observer::new(f);
|
||||
self.0
|
||||
.travel_change_ancestors(
|
||||
|
@ -1652,6 +1662,30 @@ impl LoroDoc {
|
|||
)?;
|
||||
Ok(JsValue::from(obj).into())
|
||||
}
|
||||
|
||||
/// Gets container IDs modified in the given ID range.
|
||||
///
|
||||
/// **NOTE:** This method will implicitly commit.
|
||||
///
|
||||
/// This method identifies which containers were affected by changes in a given range of operations.
|
||||
/// It can be used together with `doc.travelChangeAncestors()` to analyze the history of changes
|
||||
/// and determine which containers were modified by each change.
|
||||
///
|
||||
/// @param id - The starting ID of the change range
|
||||
/// @param len - The length of the change range to check
|
||||
/// @returns An array of container IDs that were modified in the given range
|
||||
pub fn getChangedContainersIn(&self, id: JsID, len: usize) -> JsResult<Vec<JsContainerID>> {
|
||||
let id = js_id_to_id(id)?;
|
||||
Ok(self
|
||||
.0
|
||||
.get_changed_containers_in(id, len)
|
||||
.into_iter()
|
||||
.map(|cid| {
|
||||
let v: JsValue = (&cid).into();
|
||||
v.into()
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
|
@ -3271,16 +3305,12 @@ impl LoroMovableList {
|
|||
|
||||
/// Get the last mover of the list item at the given position.
|
||||
pub fn getLastMoverAt(&self, pos: usize) -> Option<JsStrPeerID> {
|
||||
self.handler
|
||||
.get_last_mover_at(pos)
|
||||
.map(peer_id_to_js)
|
||||
self.handler.get_last_mover_at(pos).map(peer_id_to_js)
|
||||
}
|
||||
|
||||
/// Get the last editor of the list item at the given position.
|
||||
pub fn getLastEditorAt(&self, pos: usize) -> Option<JsStrPeerID> {
|
||||
self.handler
|
||||
.get_last_editor_at(pos)
|
||||
.map(peer_id_to_js)
|
||||
self.handler.get_last_editor_at(pos).map(peer_id_to_js)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
encodeFrontiers,
|
||||
decodeFrontiers,
|
||||
} from "../bundler/index";
|
||||
import { ContainerID } from "loro-wasm";
|
||||
|
||||
it("basic example", () => {
|
||||
const doc = new LoroDoc();
|
||||
|
@ -675,3 +676,16 @@ it("can push container to movable list", () => {
|
|||
const map = list.pushContainer(new LoroMap());
|
||||
expect(list.toJSON()).toStrictEqual([{}]);
|
||||
})
|
||||
|
||||
it("can query the history for changed containers", () => {
|
||||
const doc = new LoroDoc();
|
||||
doc.setPeerId("0");
|
||||
doc.getText("text").insert(0, "H");
|
||||
doc.getMap("map").set("key", "H");
|
||||
const changed = doc.getChangedContainersIn({ peer: "0", counter: 0 }, 2)
|
||||
const changedSet = new Set(changed);
|
||||
expect(changedSet).toEqual(new Set([
|
||||
"cid:root-text:Text" as ContainerID,
|
||||
"cid:root-map:Map" as ContainerID,
|
||||
]))
|
||||
})
|
||||
|
|
|
@ -21,6 +21,7 @@ delta = { path = "../delta", package = "loro-delta", version = "1.0.0-beta.5" }
|
|||
generic-btree = { version = "^0.10.5" }
|
||||
enum-as-inner = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
fxhash = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.87"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#![warn(missing_docs)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
use event::{DiffEvent, Subscriber};
|
||||
use fxhash::FxHashSet;
|
||||
use loro_common::InternalString;
|
||||
pub use loro_internal::cursor::CannotFindRelativePosition;
|
||||
use loro_internal::cursor::Cursor;
|
||||
|
@ -848,6 +849,21 @@ impl LoroDoc {
|
|||
pub fn is_shallow(&self) -> bool {
|
||||
self.doc.is_shallow()
|
||||
}
|
||||
|
||||
/// Gets container IDs modified in the given ID range.
|
||||
///
|
||||
/// **NOTE:** This method will implicitly commit.
|
||||
///
|
||||
/// This method can be used in conjunction with `doc.travel_change_ancestors()` to traverse
|
||||
/// the history and identify all changes that affected specific containers.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - The starting ID of the change range
|
||||
/// * `len` - The length of the change range to check
|
||||
pub fn get_changed_containers_in(&self, id: ID, len: usize) -> FxHashSet<ContainerID> {
|
||||
self.doc.get_changed_containers_in(id, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// It's used to prevent the user from implementing the trait directly.
|
||||
|
|
|
@ -2190,3 +2190,35 @@ fn get_editor() {
|
|||
let mov_id = tree.get_last_move_id(&node_0).unwrap();
|
||||
assert_eq!(mov_id.peer, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_changed_containers_in() {
|
||||
let doc = LoroDoc::new();
|
||||
doc.set_peer_id(0).unwrap();
|
||||
let text = doc.get_text("text");
|
||||
text.insert(0, "H").unwrap();
|
||||
let map = doc.get_map("map");
|
||||
map.insert("key", "value").unwrap();
|
||||
let changed_set = doc.get_changed_containers_in(ID::new(0, 0), 2);
|
||||
assert_eq!(
|
||||
changed_set,
|
||||
vec![
|
||||
ContainerID::new_root("text", ContainerType::Text),
|
||||
ContainerID::new_root("map", ContainerType::Map),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
);
|
||||
|
||||
map.insert("key1", "value1").unwrap();
|
||||
assert_eq!(
|
||||
doc.get_deep_value().to_json_value(),
|
||||
json!({
|
||||
"text": "H",
|
||||
"map": {
|
||||
"key": "value",
|
||||
"key1": "value1"
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue