From cff9191704a589dea65a9c8b4da7d7311ebe2eb0 Mon Sep 17 00:00:00 2001 From: gudaoxuri Date: Tue, 29 Nov 2022 03:40:37 -0800 Subject: [PATCH] Add postgresql example(using low-level api). --- examples/pg-graph-search/Cargo.toml | 9 ++ .../pg-graph-search/config/conf-default.toml | 8 + examples/pg-graph-search/src/main.rs | 143 ++++++++++++++++++ tardis/README.md | 19 +-- tardis/src/lib.rs | 19 +-- 5 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 examples/pg-graph-search/Cargo.toml create mode 100644 examples/pg-graph-search/config/conf-default.toml create mode 100644 examples/pg-graph-search/src/main.rs diff --git a/examples/pg-graph-search/Cargo.toml b/examples/pg-graph-search/Cargo.toml new file mode 100644 index 00000000..adc8faae --- /dev/null +++ b/examples/pg-graph-search/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tardis-example-pg-graph_search" +version = "0.1.0" +authors = ["gudaoxuri"] +edition = "2021" +publish = false + +[dependencies] +tardis = { path = "../../tardis", features = ["reldb", "test"] } \ No newline at end of file diff --git a/examples/pg-graph-search/config/conf-default.toml b/examples/pg-graph-search/config/conf-default.toml new file mode 100644 index 00000000..c10a696e --- /dev/null +++ b/examples/pg-graph-search/config/conf-default.toml @@ -0,0 +1,8 @@ +[fw.app] +id = "pg_graph_search" +name = "Postgresql图搜索示例" +desc = "Postgresql图搜索示例" +version = "1.0.0" + +[fw.db] +url = "postgres://postgres:123456@localhost:5432/test" diff --git a/examples/pg-graph-search/src/main.rs b/examples/pg-graph-search/src/main.rs new file mode 100644 index 00000000..e83c0b45 --- /dev/null +++ b/examples/pg-graph-search/src/main.rs @@ -0,0 +1,143 @@ +use std::env; +use std::vec; + +use tardis::basic::result::TardisResult; +use tardis::test::test_container::TardisTestContainer; +use tardis::testcontainers::clients; +use tardis::tokio; +use tardis::TardisFuns; + +#[tokio::main] +async fn main() -> TardisResult<()> { + // Here is a demonstration of using docker to start a mysql simulation scenario. + let docker = clients::Cli::default(); + let mysql_container = TardisTestContainer::postgres_custom(None, &docker); + let port = mysql_container.get_host_port_ipv4(5432); + let url = format!("postgres://postgres:123456@localhost:{}/test", port); + env::set_var("TARDIS_FW.DB.URL", url); + + env::set_var("RUST_LOG", "debug"); + env::set_var("PROFILE", "default"); + + // Initial configuration + TardisFuns::init("config").await?; + + let db = TardisFuns::reldb().conn(); + + db.execute_one( + r###"CREATE TABLE graph +( + node1 character varying NOT NULL, + node2 character varying NOT NULL, + kind character varying NOT NULL, + reverse bool DEFAULT false NOT NULL, + ts timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + check (node1<>node2), + unique (node1,node2,kind) +);"###, + Vec::new(), + ) + .await?; + + db.execute_one(r###"CREATE INDEX idx_node1 ON graph USING btree(node1);"###, Vec::new()).await?; + db.execute_one(r###"CREATE INDEX idx_node2 ON graph USING btree(node2);"###, Vec::new()).await?; + db.execute_one(r###"CREATE INDEX idx_kind ON graph USING btree(kind);"###, Vec::new()).await?; + db.execute_one(r###"CREATE INDEX idx_ts ON graph USING btree(ts);"###, Vec::new()).await?; + + db.execute_one( + r###"CREATE OR REPLACE FUNCTION GRAPH_SEARCH( + IN I_ROOT CHARACTER varying, + IN I_KIND CHARACTER varying DEFAULT '', IN I_LIMIT int8 DEFAULT 2000000000, + IN I_DEPTH int DEFAULT 99999, + OUT O_PATHS CHARACTER varying[], + OUT O_NODE1 CHARACTER varying, + OUT O_NODE2 CHARACTER varying, + OUT O_KIND CHARACTER varying, + OUT O_KINDS CHARACTER varying[], + OUT O_DEPTH int, + OUT O_REVERSE BOOL) RETURNS +SETOF RECORD AS $$ +declare + sql text; +begin +sql := format($_$ +WITH RECURSIVE search_graph( + node1, + node2, + kind, + kinds, + depth, + paths, + reverse +) AS ( + select node1,node2,kind,kinds,depth,paths,reverse from ( + SELECT + g.node1, + g.node2, + g.kind as kind, + ARRAY[g.kind] as kinds, + 1 as depth, + ARRAY[g.node1, g.node2] as paths, + g.reverse + FROM graph AS g + WHERE + node1 = '%s' + limit %s + ) t + UNION ALL + select node1,node2,kind,kinds,depth,paths,reverse from ( + SELECT + DISTINCT ON (g.node1,g.node2,g.kind) + g.node1, + g.node2, + g.kind as kind, + sg.kinds || g.kind as kinds, + sg.depth + 1 as depth, + sg.paths || g.node2 as paths, + g.reverse + FROM graph AS g, search_graph AS sg + WHERE + g.node1 = sg.node2 + AND g.node2 <> ALL(sg.paths) + AND sg.depth <= %s + limit %s + ) t +) +SELECT paths as o_paths, node1 as o_node1, node2 as o_node2, kind as o_kind, kinds as o_kinds, depth as o_depth, reverse as o_reverse +FROM search_graph; +$_$, i_root, i_limit,i_depth,i_limit +); +return query execute sql; +end; +$$ LANGUAGE PLPGSQL STRICT;"###, + Vec::new(), + ) + .await?; + + db.insert_raw_many( + r###"INSERT INTO graph(node1, node2, kind, reverse) VALUES ($1, $2, $3, $4)"###, + vec![ + vec!["req1".into(), "task1".into(), "req-task".into(), false.into()], + vec!["task1".into(), "req1".into(), "req-task".into(), true.into()], + vec!["req1".into(), "task2".into(), "req-task".into(), false.into()], + vec!["task2".into(), "req1".into(), "req-task".into(), true.into()], + vec!["task1".into(), "bug1".into(), "task-bug".into(), false.into()], + vec!["bug1".into(), "task1".into(), "task-bug".into(), true.into()], + vec!["task1".into(), "bug2".into(), "task-bug".into(), false.into()], + vec!["bug2".into(), "task1".into(), "task-bug".into(), true.into()], + ], + ) + .await?; + + let result = db.query_all("SELECT * FROM GRAPH_SEARCH($1) ORDER BY O_DEPTH, O_KIND;", vec!["task1".into()]).await?; + + assert_eq!(result.len(), 4); + assert_eq!(result[0].try_get::>("", "o_paths").unwrap(), vec!["task1", "req1"]); + assert_eq!(result[0].try_get::("", "o_node1").unwrap(), r#"task1"#); + assert_eq!(result[0].try_get::("", "o_node2").unwrap(), r#"req1"#); + assert_eq!(result[0].try_get::("", "o_kind").unwrap(), r#"req-task"#); + assert_eq!(result[0].try_get::>("", "o_kinds").unwrap(), vec![r#"req-task"#]); + assert_eq!(result[0].try_get::("", "o_depth").unwrap(), 1); + assert!(result[0].try_get::("", "o_reverse").unwrap()); + Ok(()) +} diff --git a/tardis/README.md b/tardis/README.md index d34f8699..f45885cd 100644 --- a/tardis/README.md +++ b/tardis/README.md @@ -108,15 +108,16 @@ async fn main() -> TardisResult<()> { ``` |-- examples - |-- reldb Relational database usage example - |-- web-basic Web service Usage Example - |-- web-client Web client Usage Example - |-- websocket WebSocket Usage Example - |-- cache Cache Usage Example - |-- mq Message Queue Usage Example - |-- todos A complete project usage example - |-- multi-apps Multi-application aggregation example - |-- perf-test Performance test case + |-- reldb Relational database usage example + |-- web-basic Web service Usage Example + |-- web-client Web client Usage Example + |-- websocket WebSocket Usage Example + |-- cache Cache Usage Example + |-- mq Message Queue Usage Example + |-- todos A complete project usage example + |-- multi-apps Multi-application aggregation example + |-- pg-graph-search Graph search by Postgresql example + |-- perf-test Performance test case ``` ### FAQ diff --git a/tardis/src/lib.rs b/tardis/src/lib.rs index c2c4af49..0b40362a 100644 --- a/tardis/src/lib.rs +++ b/tardis/src/lib.rs @@ -92,15 +92,16 @@ //! ### use sqlparser::ast::Action::Usage;More examples //! //!> |-- examples -//!> |-- reldb Relational database usage example -//!> |-- web-basic Web service Usage Example -//!> |-- web-client Web client Usage Example -//!> |-- webscoket WebSocket Usage Example -//!> |-- cache Cache Usage Example -//!> |-- mq Message Queue Usage Example -//!> |-- todos A complete project usage example -//!> |-- multi-apps Multi-application aggregation example -//!> |-- perf-test Performance test case +//!> |-- reldb Relational database usage example +//!> |-- web-basic Web service Usage Example +//!> |-- web-client Web client Usage Example +//!> |-- websocket WebSocket Usage Example +//!> |-- cache Cache Usage Example +//!> |-- mq Message Queue Usage Example +//!> |-- todos A complete project usage example +//!> |-- multi-apps Multi-application aggregation example +//!> |-- pg-graph-search Graph search by Postgresql example +//!> |-- perf-test Performance test case #![doc(html_logo_url = "https://raw.githubusercontent.com/ideal-world/tardis/main/logo.png")] #![cfg_attr(docsrs, feature(doc_cfg))]