Skip to content

Commit

Permalink
feat: add function to get random results from index
Browse files Browse the repository at this point in the history
  • Loading branch information
mattyg committed May 16, 2023
1 parent 8a3403f commit 8b0eb98
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 23 deletions.
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions dnas/demo/zomes/coordinator/demo/src/demo_prefix_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ pub fn search_index_a(input: SearchIndexInput) -> ExternResult<Vec<String>> {
index.get_results(input.query, input.limit)
}

#[hdk_extern]
pub fn get_random_results_index_a(limit: usize) -> ExternResult<Vec<String>> {
let index = PrefixIndex::new(
PREFIX_INDEX_A_NAME.into(),
LinkTypes::PrefixIndexA,
PREFIX_INDEX_A_WIDTH,
PREFIX_INDEX_A_DEPTH,
)?;

index.get_random_results(limit)
}

#[hdk_extern]
pub fn add_to_index_b(text: String) -> ExternResult<()> {
let index = PrefixIndex::new(
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/prefix_index/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ name = "prefix_index"
hdk = { workspace = true }
holochain_integrity_types = { workspace = true }
serde = { workspace = true }
rand = "0.8.5"
64 changes: 47 additions & 17 deletions lib/prefix_index/src/prefix_index.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use hdk::{hash_path::path::Component, prelude::*};
use crate::utils::*;
use crate::validate::*;
use rand::prelude::*;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, SerializedBytes)]
pub struct PrefixIndex {
Expand Down Expand Up @@ -129,20 +130,19 @@ impl PrefixIndex {
path_to_string(path.clone())
);

let results = self.get_results_from_path(path, limit)?;

let leafs: Vec<Component> = results
.into_iter()
.filter(|r| r.leaf().is_some())
.map(|p| p.leaf().unwrap().clone())
.collect();
self.inner_get_results(path, limit, false)
}

let strings = leafs
.into_iter()
.filter_map(|c| String::try_from(&c).ok())
.collect();
pub fn get_random_results(&self, limit: usize) -> ExternResult<Vec<String>> {
if limit == 0 {
return Err(wasm_error!(WasmErrorInner::Guest(
"limit must be > 0".into()
)));
}

Ok(strings)
let base_path = Path::from(self.index_name.clone()).typed(self.link_type)?;

self.inner_get_results(base_path, limit, true)
}

/// Make a Path to the result following the ShardStrategy specified by PrefixIndex width + depth
Expand Down Expand Up @@ -184,20 +184,43 @@ impl PrefixIndex {
}

/// Gets the deepest-most Paths that descend from `path`, or it's parents, up to limit
fn get_results_from_path(&self, path: TypedPath, limit: usize) -> ExternResult<Vec<TypedPath>> {
self.inner_get_results_from_path(path, limit, vec![], vec![])
fn get_results_from_path(&self, path: TypedPath, limit: usize, shuffle: bool) -> ExternResult<Vec<TypedPath>> {
self.inner_get_results_from_path(path, limit, shuffle, vec![], vec![])
}

fn inner_get_results(
&self,
path: TypedPath,
limit: usize,
shuffle: bool,
) -> ExternResult<Vec<String>> {
let results = self.get_results_from_path(path, limit, shuffle)?;

let leafs: Vec<Component> = results
.into_iter()
.filter(|r| r.leaf().is_some())
.map(|p| p.leaf().unwrap().clone())
.collect();

let strings = leafs
.into_iter()
.filter_map(|c| String::try_from(&c).ok())
.collect();

Ok(strings)
}

fn inner_get_results_from_path(
&self,
path: TypedPath,
limit: usize,
shuffle: bool,
mut visited: Vec<TypedPath>,
mut results: Vec<TypedPath>,
) -> ExternResult<Vec<TypedPath>> {
visited.push(path.clone());

let children = get_children_paths(path.clone())?;
let mut children = get_children_paths(path.clone())?;
match children.len() == 0 {
true => {
if path.exists()? && !results.contains(&path) && results.len() < limit {
Expand All @@ -208,7 +231,7 @@ impl PrefixIndex {
Some(parent) => {
if !visited.contains(&parent) && !parent.is_root() {
return self
.inner_get_results_from_path(parent, limit, visited, results);
.inner_get_results_from_path(parent, limit, shuffle, visited, results);
}

Ok(results)
Expand All @@ -217,11 +240,18 @@ impl PrefixIndex {
}
}
false => {
if shuffle {
let mut rng = rand::thread_rng();
let y: f64 = rng.gen();
children.shuffle(&mut rng)
}

for child in children.into_iter() {
let grandchildren = self
.inner_get_results_from_path(
child.clone(),
limit,
shuffle,
visited.clone(),
results.clone(),
)
Expand All @@ -241,7 +271,7 @@ impl PrefixIndex {
Some(parent) => {
if !visited.contains(&parent) && !parent.is_root() {
return self
.inner_get_results_from_path(parent, limit, visited, results);
.inner_get_results_from_path(parent, limit, shuffle, visited, results);
}

Ok(results)
Expand Down
86 changes: 83 additions & 3 deletions tests/src/demo/demo/prefix-index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,9 +742,6 @@ test("add results with labels", async () => {
);
});




test("preserve letter case in result, but ignore letter case in indexing", async () => {
await runScenario(
async (scenario) => {
Expand Down Expand Up @@ -793,4 +790,87 @@ test("preserve letter case in result, but ignore letter case in indexing", async
]);
}
)
});

test("get_random_results returns random results from prefix index", async () => {
await runScenario(
async (scenario) => {
// Set up the app to be installed
const appSource = { appBundleSource: { path: "../workdir/prefix-index.happ"}};

// Add 2 players with the test app to the Scenario. The returned players
// can be destructured.
const [alice] = await scenario.addPlayersWithApps([appSource]);

// Shortcut peer discovery through gossip and register all agents in every
// conductor of the scenario.
await scenario.shareAllAgents();

await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_hashtag_to_index_a",
payload: "#HOLOCHAIN",
});
await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_hashtag_to_index_a",
payload: "#holosapian",
});
await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_cashtag_to_index_a",
payload: "$HOLY",
});
await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_cashtag_to_index_a",
payload: "$CAT",
});
await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_cashtag_to_index_a",
payload: "$DOGGO",
});
await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "add_hashtag_to_index_a",
payload: "#monkeys",
});

await pause(1000);

const [ result1 ]: string[] = await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "get_random_results_index_a",
payload: 1
});

const [ result2 ]: string[] = await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "get_random_results_index_a",
payload: 1
});

const [ result3 ]: string[] = await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "get_random_results_index_a",
payload: 1
});

const [ result4 ]: string[] = await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "get_random_results_index_a",
payload: 1
});

const [ result5 ]: string[] = await alice.cells[0].callZome({
zome_name: "demo",
fn_name: "get_random_results_index_a",
payload: 1
});

// Assert we did not get the exact same result 5 times
assert.ok(new Set([result1, result2, result3, result4, result5]).size > 1);
}
)
});

0 comments on commit 8b0eb98

Please sign in to comment.