Skip to content

Commit

Permalink
index add_vertex and search by name (Pometry#1084)
Browse files Browse the repository at this point in the history
Basic search functionality for raphtory Graph
  • Loading branch information
fabianmurariu authored Jul 5, 2023
1 parent 44354ea commit fdf064c
Show file tree
Hide file tree
Showing 17 changed files with 1,188 additions and 29 deletions.
405 changes: 403 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions raphtory-graphql/src/data.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use raphtory::prelude::{Graph, GraphViewOps};
use raphtory::{prelude::{Graph, GraphViewOps}, search::IndexedGraph};
use std::{
collections::{HashMap, HashSet},
path::Path,
};
use walkdir::WalkDir;

pub(crate) struct Data {
pub(crate) graphs: HashMap<String, Graph>,
pub(crate) graphs: HashMap<String, IndexedGraph<Graph>>,
}

impl Data {
Expand Down Expand Up @@ -36,7 +36,7 @@ impl Data {
}
};

let graphs: HashMap<String, Graph> = valid_paths
let graphs: HashMap<String, IndexedGraph<Graph>> = valid_paths
.into_iter()
.map(|path| {
println!("loading graph from {path}");
Expand All @@ -54,7 +54,7 @@ impl Data {
(graph_name.to_string(), graph)
}
};
})
}).map(|(name, g)| (name, IndexedGraph::from_graph(&g).expect("Unable to index graph")))
.collect();

Self { graphs }
Expand Down
58 changes: 52 additions & 6 deletions raphtory-graphql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,58 @@ mod graphql_test {
use std::collections::HashMap;

#[tokio::test]
async fn basic_query() {
async fn search_for_gandalf_query() {
let graph = Graph::new();
if let Err(err) = graph.add_vertex(0, 11, []) {
panic!("Could not add vertex! {:?}", err);
graph
.add_vertex(0, "Gandalf", [("kind".to_string(), Prop::str("wizard"))])
.expect("Could not add vertex!");
graph
.add_vertex(0, "Frodo", [("kind".to_string(), Prop::str("Hobbit"))])
.expect("Could not add vertex!");

let graphs = HashMap::from([("lotr".to_string(), graph.into())]);
let data = data::Data { graphs };

#[derive(App)]
struct App(model::QueryRoot);
let schema = App::create_schema().data(data).finish().unwrap();

let query = r#"
{
graph(name: "lotr") {
search(query: "kind:wizard", limit: 10, offset: 0) {
name
}
}
}
let graphs = HashMap::from([("lotr".to_string(), graph)]);
"#;

let root = model::QueryRoot;
let req = dynamic_graphql::Request::new(query).root_value(FieldValue::owned_any(root));

let res = schema.execute(req).await;
let data = res.data.into_json().unwrap();

assert_eq!(
data,
serde_json::json!({
"graph": {
"search": [
{
"name": "Gandalf"
}
]
}
}),
);
}

#[tokio::test]
async fn basic_query() {
let graph = Graph::new();
graph.add_vertex(0, 11, []).expect("Could not add vertex!");

let graphs = HashMap::from([("lotr".to_string(), graph.into())]);
let data = data::Data { graphs };

#[derive(App)]
Expand Down Expand Up @@ -70,7 +116,7 @@ mod graphql_test {
panic!("Could not add vertex! {:?}", err);
}

let graphs = HashMap::from([("lotr".to_string(), graph)]);
let graphs = HashMap::from([("lotr".to_string(), graph.into())]);
let data = data::Data { graphs };

#[derive(App)]
Expand Down Expand Up @@ -158,7 +204,7 @@ mod graphql_test {
panic!("Could not add vertex! {:?}", err);
}

let graphs = HashMap::from([("lotr".to_string(), graph)]);
let graphs = HashMap::from([("lotr".to_string(), graph.into())]);
let data = data::Data { graphs };

#[derive(App)]
Expand Down
21 changes: 16 additions & 5 deletions raphtory-graphql/src/model/graph/graph.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use std::ops::Deref;

use crate::model::{
algorithm::Algorithms,
filters::{edgefilter::EdgeFilter, nodefilter::NodeFilter},
graph::{edge::Edge, node::Node, property::Property},
};
use dynamic_graphql::{ResolvedObject, ResolvedObjectFields};
use itertools::Itertools;
use raphtory::db::api::view::{
use raphtory::{db::api::view::{
internal::{DynamicGraph, IntoDynamic},
GraphViewOps, TimeOps, VertexViewOps,
};
}, search::IndexedGraph};

#[derive(ResolvedObject)]
pub(crate) struct GraphMeta {
Expand Down Expand Up @@ -47,13 +49,13 @@ impl GraphMeta {

#[derive(ResolvedObject)]
pub(crate) struct GqlGraph {
graph: DynamicGraph,
graph: IndexedGraph<DynamicGraph>,
}

impl<G: GraphViewOps + IntoDynamic> From<G> for GqlGraph {
fn from(value: G) -> Self {
Self {
graph: value.into_dynamic(),
graph: value.into_dynamic().into(),
}
}
}
Expand Down Expand Up @@ -86,6 +88,15 @@ impl GqlGraph {
}
}

async fn search(&self, query: String, limit: usize, offset: usize) -> Vec<Node> {
self.graph
.search(&query, limit, offset)
.into_iter()
.flat_map(|vv| vv)
.map(|vv| vv.into())
.collect()
}

async fn edges<'a>(&self, filter: Option<EdgeFilter>) -> Vec<Edge> {
match filter {
Some(filter) => self
Expand Down Expand Up @@ -116,6 +127,6 @@ impl GqlGraph {
}

async fn algorithms(&self) -> Algorithms {
self.graph.clone().into()
self.graph.deref().clone().into()
}
}
7 changes: 5 additions & 2 deletions raphtory-graphql/src/model/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::Deref;

use crate::{
data::Data,
model::graph::graph::{GqlGraph, GraphMeta},
Expand Down Expand Up @@ -25,14 +27,15 @@ impl QueryRoot {
async fn graph<'a>(ctx: &Context<'a>, name: &str) -> Option<GqlGraph> {
let data = ctx.data_unchecked::<Data>();
let g = data.graphs.get(name)?;
Some(g.clone().into())
let graph = g.deref();
Some(graph.clone().into())
}

async fn graphs<'a>(ctx: &Context<'a>) -> Vec<GraphMeta> {
let data = ctx.data_unchecked::<Data>();
data.graphs
.iter()
.map(|(name, g)| GraphMeta::new(name.clone(), g.clone().into_dynamic()))
.map(|(name, g)| GraphMeta::new(name.clone(), g.deref().clone().into_dynamic()))
.collect_vec()
}
}
7 changes: 6 additions & 1 deletion raphtory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ serde_json = {version="1", optional=true}
reqwest = { version = "0.11.14", features = ["blocking"], optional=true}
tokio = { version = "1.27.0", features = ["full"], optional=true}

# search optional dependencies
tantivy = {version="0.20", optional=true}

# python binding optional dependencies
pyo3 = {version= "0.19.0", features=["multiple-pymethods", "chrono"], optional=true}
num = {version="0.4.0", optional=true}
Expand All @@ -69,8 +72,10 @@ quickcheck_macros = "1"


[features]
default = ["search"]
# Enables the graph loader io module
io = ["dep:zip","dep:neo4rs", "dep:bzip2", "dep:flate2", "dep:csv", "dep:serde_json", "dep:reqwest", "dep:tokio"]
# Enables generating the pyo3 python bindings
python = ["io", "dep:pyo3", "dep:num", "dep:display-error-chain"]

# search
search = ["dep:tantivy"]
8 changes: 6 additions & 2 deletions raphtory/src/core/entities/graph/tgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ impl<const N: usize> Default for InnerTemporalGraph<N> {
}

impl<const N: usize> InnerTemporalGraph<N> {
pub(crate) fn get_all_vertex_property_names(&self, is_static: bool) -> Vec<String> {
self.vertex_meta.get_all_property_names(is_static)
}

pub(crate) fn get_all_layers(&self) -> Vec<usize> {
self.edge_meta.get_all_layers()
}
Expand Down Expand Up @@ -216,7 +220,7 @@ impl<const N: usize> InnerTemporalGraph<N> {
v: u64,
name: Option<&str>,
props: Vec<(String, Prop)>,
) -> Result<(), GraphError> {
) -> Result<VID, GraphError> {
let t = time.try_into_time()?;
self.update_time(t);

Expand Down Expand Up @@ -255,7 +259,7 @@ impl<const N: usize> InnerTemporalGraph<N> {
node.add_static_prop(*prop_id, name, prop.clone())?;
}

Ok(())
Ok(v_id.into())
}

pub(crate) fn add_vertex_no_props(&self, t: i64, v: u64) -> Result<VID, GraphError> {
Expand Down
9 changes: 9 additions & 0 deletions raphtory/src/core/entities/properties/props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,22 @@ impl Meta {
.collect()
}

pub fn get_all_property_names(&self, is_static: bool) -> Vec<String> {
if is_static {
self.meta_prop_static.map.iter().map(|entry| entry.key().clone()).collect()
} else {
self.meta_prop_temporal.map.iter().map(|entry| entry.key().clone()).collect()
}
}

pub fn reverse_prop_id(&self, prop_id: usize, is_static: bool) -> Option<String> {
if is_static {
self.meta_prop_static.reverse_lookup(&prop_id)
} else {
self.meta_prop_temporal.reverse_lookup(&prop_id)
}
}

}

#[derive(Serialize, Deserialize, Default, Debug)]
Expand Down
6 changes: 6 additions & 0 deletions raphtory/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ pub enum Prop {
Graph(Graph),
}

impl Prop{
pub fn str(s: &str) -> Prop {
Prop::Str(s.to_string())
}
}

pub trait PropUnwrap: Sized {
fn into_str(self) -> Option<String>;
fn unwrap_str(self) -> String {
Expand Down
24 changes: 24 additions & 0 deletions raphtory/src/core/utils/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::core::{storage::lazy_vec::IllegalSet, utils::time::error::ParseTimeError, Prop};

#[cfg(feature = "search")]
use tantivy;
use tantivy::query::QueryParserError;

#[derive(thiserror::Error, Debug)]
pub enum GraphError {
#[error("Immutable graph reference already exists. You can access mutable graph apis only exclusively.")]
Expand All @@ -24,6 +28,13 @@ pub enum GraphError {
BinCodeError { source: Box<bincode::ErrorKind> },
#[error("IO operation failed")]
IOError { source: std::io::Error },
#[cfg(feature = "search")]
#[error("Index operation failed")]
IndexError { source: tantivy::TantivyError },

#[cfg(feature = "search")]
#[error("Index operation failed")]
QueryError { source: QueryParserError },
}

impl From<bincode::Error> for GraphError {
Expand All @@ -44,6 +55,19 @@ impl From<MutateGraphError> for GraphError {
}
}

#[cfg(feature = "search")]
impl From<tantivy::TantivyError> for GraphError {
fn from(source: tantivy::TantivyError) -> Self {
GraphError::IndexError { source }
}
}
#[cfg(feature = "search")]
impl From<QueryParserError> for GraphError {
fn from(source: QueryParserError) -> Self {
GraphError::QueryError { source }
}
}

#[derive(thiserror::Error, Debug, PartialEq)]
pub enum MutateGraphError {
#[error("Create vertex '{vertex_id}' first before adding static properties to it")]
Expand Down
3 changes: 2 additions & 1 deletion raphtory/src/db/api/mutation/addition_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ impl<G: InternalAdditionOps> AdditionOps for G {
v.id(),
v.id_str(),
props.collect_properties(),
)
)?;
Ok(())
}

fn add_edge<V: InputVertex, T: TryIntoTime, P: Properties>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
core::{utils::errors::GraphError, Prop},
core::{utils::errors::GraphError, Prop, entities::vertices::vertex_ref::VertexRef},
db::api::view::internal::Base,
};

Expand All @@ -10,7 +10,7 @@ pub trait InternalAdditionOps {
v: u64,
name: Option<&str>,
props: Vec<(String, Prop)>,
) -> Result<(), GraphError>;
) -> Result<VertexRef, GraphError>;

fn internal_add_edge(
&self,
Expand Down Expand Up @@ -48,7 +48,7 @@ impl<G: DelegateAdditionOps> InternalAdditionOps for G {
v: u64,
name: Option<&str>,
props: Vec<(String, Prop)>,
) -> Result<(), GraphError> {
) -> Result<VertexRef, GraphError> {
self.graph().internal_add_vertex(t, v, name, props)
}

Expand Down
11 changes: 11 additions & 0 deletions raphtory/src/db/api/view/internal/core_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ pub trait CoreGraphOps {
/// A vector of strings representing the names of the temporal properties
fn temporal_vertex_prop_names(&self, v: VID) -> Vec<String>;

/// Returns a vector of all names of temporal properties that exist on at least one vertex
///
/// # Returns
///
/// A vector of strings representing the names of the temporal properties
fn all_vertex_prop_names(&self, is_static: bool) -> Vec<String>;

/// Returns the static edge property with the given name for the
/// given edge reference.
///
Expand Down Expand Up @@ -239,6 +246,10 @@ impl<G: DelegateCoreOps + ?Sized> CoreGraphOps for G {
self.graph().temporal_vertex_prop_names(v)
}

fn all_vertex_prop_names(&self, is_static: bool) -> Vec<String> {
self.graph().all_vertex_prop_names(is_static)
}

fn static_edge_prop(&self, e: EdgeRef, name: &str) -> Option<Prop> {
self.graph().static_edge_prop(e, name)
}
Expand Down
Loading

0 comments on commit fdf064c

Please sign in to comment.