Skip to content

Commit

Permalink
issue #111 add reverse in neighbor result
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhongFuze committed Nov 25, 2023
1 parent 451c483 commit 88d79d8
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 15 deletions.
69 changes: 69 additions & 0 deletions src/config/tdb/migrations/DBImportExport_IdentityGraph.gsql
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,75 @@ CREATE OR REPLACE QUERY neighbors(VERTEX<Identities> p, INT depth) FOR GRAPH Ide
PRINT @@vertices AS vertices, @@edges AS edges;
}

CREATE OR REPLACE QUERY neighbors_with_source_reverse(VERTEX<Identities> p, INT depth=10, INT reverse_flag=0) FOR GRAPH IdentityGraph SYNTAX V2 {
MinAccum<INT> @min_dis;
OrAccum @or_visited = false;
OrAccum @or_visited2 = false;
OrAccum @reverse = false;
ListAccum<VERTEX<Identities>> @path_list;
SetAccum<STRING> @source_list;
SetAccum<EDGE> @@edge_set;
SetAccum<VERTEX<Identities>> @@vertices;
ListAccum<STRING> @@domainSystems = ["dotbit", "lens", "unstoppabledomains", "space_id", "crossbell"];

##### Initialization #####
seed (Identities) = {p};
seed = SELECT s
FROM seed:s
ACCUM s.@or_visited += true,
s.@or_visited2 += true,
s.@min_dis = 0,
s.@path_list = s;
ResultSet (Identities) = seed;

##### Calculate distances and paths #####
WHILE(seed.size()>0) LIMIT depth DO
SetAccum<VERTEX> @@pool;
proof = SELECT t
FROM seed:s1-((Proof_Forward>|<Proof_Forward|Proof_Backward>|<Proof_Backward):pf)-Identities:t-((Proof_Forward>|<Proof_Forward|Proof_Backward>|<Proof_Backward):pw)-seed:s2
WHERE t.@or_visited == false AND s1 == s2
ACCUM
t.@min_dis += s1.@min_dis + 1,
t.@path_list = s1.@path_list + [t],
t.@source_list += s1.@source_list,
t.@source_list += pf.source,
t.@source_list += pw.source,
t.@or_visited += true
POST-ACCUM @@pool += t
ORDER BY getvid(t);
ResultSet (Identities) = ResultSet UNION proof;

hold = SELECT v
FROM seed:s-((Hold_Identity>|<Hold_Identity):e)-Identities:v
WHERE v.@or_visited2 == false
ACCUM
v.@min_dis += s.@min_dis + 1,
v.@path_list = s.@path_list + [v],
v.@source_list += s.@source_list,
v.@source_list += e.source,
v.@or_visited2 += true
POST-ACCUM @@pool += v
ORDER BY getvid(v);
ResultSet (Identities) = ResultSet UNION hold;

seed (Identities) = {@@pool};
END;

address = SELECT addr FROM ResultSet:start-((<Reverse_Resolve):r)-ResultSet:addr
WHERE @@domainSystems.contains(r.system) == TRUE
ACCUM start.@reverse += true;

IF reverse_flag == 0 THEN
PRINT ResultSet as vertices;
ELSE IF reverse_flag == 1 THEN
PRINT ResultSet as vertices WHERE (@@domainSystems.contains(ResultSet.platform) == TRUE AND ResultSet.@reverse == TRUE) OR
(@@domainSystems.contains(ResultSet.platform) == FALSE);
ELSE IF reverse_flag == 2 THEN
PRINT ResultSet as vertices WHERE (@@domainSystems.contains(ResultSet.platform) == TRUE AND ResultSet.@reverse == FALSE) OR
(@@domainSystems.contains(ResultSet.platform) == FALSE);
END;
}

CREATE OR REPLACE QUERY neighbors_with_source(VERTEX<Identities> p, INT depth=10) FOR GRAPH IdentityGraph SYNTAX V2 {
MinAccum<INT> @min_dis;
OrAccum @or_visited = false;
Expand Down
13 changes: 12 additions & 1 deletion src/controller/tigergraphql/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ impl IdentityWithSource {
self.sources.clone()
}

async fn reverse(&self) -> Option<bool> {
self.reverse.clone()
}

async fn identity(&self) -> IdentityRecord {
self.identity.clone()
}
Expand Down Expand Up @@ -138,9 +142,16 @@ impl IdentityRecord {
// )]
// upstream: Option<String>,
#[graphql(desc = "Depth of traversal. 1 if omitted")] depth: Option<u16>,
#[graphql(
desc = "This reverse flag can be used as a filtering for Identity which type is domain system .
If `reverse=None` if omitted, there is no need to filter anything.
When `reverse=true`, just return `primary domain` related identities.
When `reverse=false`, Only `non-primary domain` will be returned, which is the inverse set of reverse=true."
)]
reverse: Option<bool>,
) -> Result<Vec<IdentityWithSource>> {
let client = make_http_client();
self.neighbors(&client, depth.unwrap_or(1)).await
self.neighbors(&client, depth.unwrap_or(1), reverse).await
}

/// Neighbor identity from current. The entire topology can be restored by return records.
Expand Down
4 changes: 3 additions & 1 deletion src/tigergraph/edge/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,9 @@ impl Resolve {
}));

resolve_edge.reverse = domain.reverse;
resolve_edge.reverse_record = None;
resolve_edge.reverse_record = domain
.reverse_record
.and_then(|records| records.first().cloned());
resolve_edge.owner = domain.owner.first().cloned();
resolve_edge.resolved = None;
resolve_edge
Expand Down
10 changes: 7 additions & 3 deletions src/tigergraph/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,12 @@ mod tests {
#[tokio::test]
async fn test_neighbors_with_traversal() -> Result<(), Error> {
let client = make_http_client();
if let Some(found) =
Identity::find_by_platform_identity(&client, &Platform::Ethereum, "d").await?
if let Some(found) = Identity::find_by_platform_identity(
&client,
&Platform::Ethereum,
"0xb2be2887a26f44555835eeacc47d65b88b6b42c2",
)
.await?
{
println!("found = {:?}", found);
let edges = found.neighbors_with_traversal(&client, 1).await?;
Expand All @@ -240,7 +244,7 @@ mod tests {
Identity::find_by_platform_identity(&client, &Platform::Ethereum, "d").await?
{
println!("found = {:?}", found);
let edges = found.neighbors(&client, 3).await?;
let edges = found.neighbors(&client, 3, None).await?;
let json_raw =
serde_json::to_string(&edges).map_err(|err| Error::JSONParseError(err))?;
println!("neighbors_with_source: {}", json_raw);
Expand Down
62 changes: 52 additions & 10 deletions src/tigergraph/vertex/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::{
vertex::{FromWithParams, Vertex, VertexRecord},
Attribute, BaseResponse, Graph, OpCode, Transfer, UpsertGraph, Vertices,
},
upstream::{vec_string_to_vec_datasource, ContractCategory, DataSource, Platform},
upstream::{
vec_string_to_vec_datasource, ContractCategory, DataSource, DomainNameSystem, Platform,
},
util::{
naive_datetime_from_string, naive_datetime_to_string, naive_now,
option_naive_datetime_from_string, option_naive_datetime_to_string, parse_body,
Expand Down Expand Up @@ -382,14 +384,26 @@ impl<'de> Deserialize<'de> for IdentityWithSource {
.transpose()
.map_err(de::Error::custom)?;

let attributes = serde_json::from_value(serde_json::Value::Object(attributes))
let domain_reverse: Option<bool> = attributes
.remove("@reverse")
.map(serde_json::from_value)
.transpose()
.map_err(de::Error::custom)?;

let attributes: Identity =
serde_json::from_value(serde_json::Value::Object(attributes))
.map_err(de::Error::custom)?;

let v_type = v_type.ok_or_else(|| de::Error::missing_field("v_type"))?;
let v_id = v_id.ok_or_else(|| de::Error::missing_field("v_id"))?;
let source_list =
source_list.ok_or_else(|| de::Error::missing_field("@source_list"))?;
let sources =
vec_string_to_vec_datasource(source_list).map_err(de::Error::custom)?;
let domain_system: DomainNameSystem = attributes.platform.clone().into();
if domain_system != DomainNameSystem::Unknown {
reverse = domain_reverse;
}

Ok(IdentityWithSource {
identity: IdentityRecord(VertexRecord {
Expand All @@ -398,6 +412,7 @@ impl<'de> Deserialize<'de> for IdentityWithSource {
attributes,
}),
sources,
reverse,
})
}
}
Expand Down Expand Up @@ -498,7 +513,14 @@ impl Identity {
identity.to_string(),
)
.parse()
.map_err(|_err: InvalidUri| Error::ParamError(format!("Uri format Error | {}", _err)))?;
.map_err(|_err: InvalidUri| {
Error::ParamError(format!(
"QUERY filter=platform=%22{}%22,identity=%22{}%22 Uri format Error | {}",
platform.to_string(),
identity.to_string(),
_err
))
})?;
let req = hyper::Request::builder()
.method(Method::GET)
.uri(uri)
Expand All @@ -508,7 +530,9 @@ impl Identity {

let mut resp = client.request(req).await.map_err(|err| {
Error::ManualHttpClientError(format!(
"query filter error | Fail to request: {:?}",
"query filter=platform=%22{}%22,identity=%22{}%22 error | Fail to request: {:?}",
platform.to_string(),
identity.to_string(),
err.to_string()
))
})?;
Expand Down Expand Up @@ -550,14 +574,24 @@ impl IdentityRecord {
&self,
client: &Client<HttpConnector>,
depth: u16,
reverse: Option<bool>,
) -> Result<Vec<IdentityWithSource>, Error> {
// This reverse flag can be used as a filtering for Identity which type is domain system .
// flag = 0, If `reverse=None` if omitted, there is no need to filter anything.
// flag = 1, When `reverse=true`, just return `primary domain` related identities.
// flag = 2, When `reverse=false`, Only `non-primary domain` will be returned, which is the inverse set of reverse=true.
let flag = reverse.map_or(0, |r| match r {
true => 1,
false => 2,
});
// query see in Solution: CREATE QUERY neighbors_with_source(VERTEX<Identities> p, INT depth)
let uri: http::Uri = format!(
"{}/query/{}/neighbors_with_source?p={}&depth={}",
"{}/query/{}/neighbors_with_source_reverse?p={}&depth={}&reverse_flag={}",
C.tdb.host,
Graph::IdentityGraph.to_string(),
self.v_id,
depth,
flag,
)
.parse()
.map_err(|_err: InvalidUri| Error::ParamError(format!("Uri format Error {}", _err)))?;
Expand Down Expand Up @@ -624,8 +658,13 @@ impl IdentityRecord {
depth,
)
.parse()
.map_err(|_err: InvalidUri| Error::ParamError(format!("Uri format Error {}", _err)))?;
tracing::trace!("query neighbors Url {:?}", uri);
.map_err(|_err: InvalidUri| {
Error::ParamError(format!(
"QUERY neighbors_with_traversal({},{}) Uri format Error {}",
self.v_id, depth, _err
))
})?;
tracing::trace!("query neighbors_with_traversal Url {:?}", uri);
let req = hyper::Request::builder()
.method(Method::GET)
.uri(uri)
Expand All @@ -634,15 +673,15 @@ impl IdentityRecord {
.map_err(|_err| Error::ParamError(format!("ParamError Error {}", _err)))?;
let mut resp = client.request(req).await.map_err(|err| {
Error::ManualHttpClientError(format!(
"query neighbors | Fail to request: {:?}",
"query neighbors_with_traversal | Fail to request: {:?}",
err.to_string()
))
})?;
match parse_body::<NeighborsResponse>(&mut resp).await {
Ok(r) => {
if r.base.error {
let err_message = format!(
"TigerGraph query neighbors error | Code: {:?}, Message: {:?}",
"TigerGraph query neighbors_with_traversal error | Code: {:?}, Message: {:?}",
r.base.code, r.base.message
);
error!(err_message);
Expand All @@ -656,7 +695,10 @@ impl IdentityRecord {
Ok(result)
}
Err(err) => {
let err_message = format!("TigerGraph query neighbors parse_body error: {:?}", err);
let err_message = format!(
"TigerGraph query neighbors_with_traversal parse_body error: {:?}",
err
);
error!(err_message);
return Err(err);
}
Expand Down
14 changes: 14 additions & 0 deletions src/upstream/types/platform.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::upstream::DomainNameSystem;
use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumIter, EnumString};

Expand Down Expand Up @@ -110,3 +111,16 @@ pub enum Platform {
#[default]
Unknown,
}

impl From<Platform> for DomainNameSystem {
fn from(platform: Platform) -> Self {
match platform {
Platform::Dotbit => DomainNameSystem::DotBit,
Platform::UnstoppableDomains => DomainNameSystem::UnstoppableDomains,
Platform::Lens => DomainNameSystem::Lens,
Platform::SpaceId => DomainNameSystem::SpaceId,
Platform::Crossbell => DomainNameSystem::SpaceId,
_ => DomainNameSystem::Unknown,
}
}
}

0 comments on commit 88d79d8

Please sign in to comment.