finish shortest path and adding stuff to db
This commit is contained in:
parent
e589e974db
commit
10557ec498
@ -1,21 +1,56 @@
|
|||||||
use neo4rs::Graph;
|
use crate::backend::types::{City, Road, ShortestPath};
|
||||||
use rocket::{get, Route, State};
|
use crate::graph::graph::{all_cities, create_city, create_road, shortest_path};
|
||||||
use rocket::response::status::NotFound;
|
use neo4rs::{Graph};
|
||||||
use rocket_okapi::{openapi, openapi_get_routes};
|
use rocket::response::status::{BadRequest};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
|
use rocket::{get, post, Route, State};
|
||||||
|
use rocket_okapi::{openapi, openapi_get_routes};
|
||||||
|
|
||||||
|
/// configure available api nodes
|
||||||
pub fn build_api() -> Vec<Route> {
|
pub fn build_api() -> Vec<Route> {
|
||||||
openapi_get_routes![
|
openapi_get_routes![addcity, addroad, getshortestpath, getallcities]
|
||||||
append
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get general build-server stats
|
/// endpoint to add new city to graph
|
||||||
#[openapi(tag = "stats")]
|
#[openapi]
|
||||||
#[get("/append")]
|
#[post("/addcity", data = "<msg>")]
|
||||||
pub async fn append(graph: &State<Graph>) -> Result<String, NotFound<String>> {
|
pub async fn addcity(graph: &State<Graph>, msg: Json<City>) -> Result<String, BadRequest<String>> {
|
||||||
let graph = graph as &Graph;
|
match create_city(graph, msg.into_inner()).await {
|
||||||
|
Ok(_) => Ok("ok".to_string()),
|
||||||
Ok("worked".to_string())
|
Err(e) => Err(BadRequest(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// endpoint to add new road between two cities
|
||||||
|
#[openapi]
|
||||||
|
#[post("/road", data = "<msg>")]
|
||||||
|
pub async fn addroad(graph: &State<Graph>, msg: Json<Road>) -> Result<String, BadRequest<String>> {
|
||||||
|
match create_road(graph, msg.into_inner()).await {
|
||||||
|
Ok(_) => Ok("ok".to_string()),
|
||||||
|
Err(e) => Err(BadRequest(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// endpoint to get all available cities with optional limit
|
||||||
|
#[openapi]
|
||||||
|
#[get("/cities?<limit>")]
|
||||||
|
pub async fn getallcities(graph: &State<Graph>, limit: Option<i32>) -> Result<Json<Vec<String>>, BadRequest<String>> {
|
||||||
|
match all_cities(graph, limit).await {
|
||||||
|
Ok(v) => Ok(Json::from(v)),
|
||||||
|
Err(e) => Err(BadRequest(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// endpoint to get shortest path between two cities with the resulting length
|
||||||
|
#[openapi]
|
||||||
|
#[get("/shortestpath?<city1>&<city2>")]
|
||||||
|
pub async fn getshortestpath(
|
||||||
|
graph: &State<Graph>,
|
||||||
|
city1: String,
|
||||||
|
city2: String,
|
||||||
|
) -> Result<Json<ShortestPath>, BadRequest<String>> {
|
||||||
|
match shortest_path(graph, city1, city2).await {
|
||||||
|
Ok(v) => Ok(Json::from(v)),
|
||||||
|
Err(e) => Err(BadRequest(e.to_string())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
pub mod types;
|
||||||
|
24
src/backend/types.rs
Normal file
24
src/backend/types.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
use rocket_okapi::okapi::schemars;
|
||||||
|
use rocket_okapi::JsonSchema;
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct City {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Road {
|
||||||
|
pub city1name: String,
|
||||||
|
pub city2name: String,
|
||||||
|
pub distance: i32,
|
||||||
|
pub oneway: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct ShortestPath {
|
||||||
|
pub(crate) path: Vec<String>,
|
||||||
|
pub(crate) distance: i32,
|
||||||
|
}
|
90
src/graph/graph.rs
Normal file
90
src/graph/graph.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use crate::backend::types::{City, Road, ShortestPath};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use neo4rs::{query, Graph, Node, Path};
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn shortest_path(
|
||||||
|
graph: &Graph,
|
||||||
|
city1: String,
|
||||||
|
city2: String,
|
||||||
|
) -> anyhow::Result<ShortestPath> {
|
||||||
|
if city1 == city2 {
|
||||||
|
return Err(anyhow!("Two cities must be different"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = graph.execute(
|
||||||
|
query("MATCH (c1:City {name: $c1}), (c2:City {name: $c2})
|
||||||
|
WITH c1, c2
|
||||||
|
MATCH path = (c1)-[:ROAD*]->(c2)
|
||||||
|
WITH path, reduce(distance = 0, rel in relationships(path) | distance + rel.distance) AS totalDistance
|
||||||
|
ORDER BY totalDistance ASC
|
||||||
|
LIMIT 1
|
||||||
|
RETURN path, totalDistance AS distance")
|
||||||
|
.param("c1", city1)
|
||||||
|
.param("c2", city2)
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
return if let Ok(Some(row)) = result.next().await {
|
||||||
|
let path: Path = row.get("path")?;
|
||||||
|
let distance: i32 = row.get("distance")?;
|
||||||
|
|
||||||
|
let city_names: Vec<String> = path
|
||||||
|
.nodes()
|
||||||
|
.iter()
|
||||||
|
.map(|node| node.get::<String>("name").unwrap())
|
||||||
|
.collect();
|
||||||
|
Ok(ShortestPath {
|
||||||
|
path: city_names,
|
||||||
|
distance,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("couldn't find a valid path"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn all_cities(graph: &Graph, limit: Option<i32>) -> anyhow::Result<Vec<String>> {
|
||||||
|
let mut cities = vec![];
|
||||||
|
|
||||||
|
let query = match limit {
|
||||||
|
None => query("MATCH (c:City) RETURN c"),
|
||||||
|
Some(limit) => query("MATCH (c:City) RETURN c LIMIT $limit").param("limit", limit)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = graph
|
||||||
|
.execute(query)
|
||||||
|
.await?;
|
||||||
|
while let Ok(Some(row)) = result.next().await {
|
||||||
|
let node: Node = row.get("c")?;
|
||||||
|
let name: String = node.get("name")?;
|
||||||
|
cities.push(name);
|
||||||
|
}
|
||||||
|
Ok(cities)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_road(graph: &Graph, msg: Road) -> anyhow::Result<()> {
|
||||||
|
let mut txn = graph.start_txn().await?;
|
||||||
|
txn.run_queries([
|
||||||
|
query("MATCH (c1:City{name: $city1}) WITH c1 MATCH (c2:City{name: $city2}) WITH c1,c2 CREATE (c1)-[:ROAD{distance: $distance}]->(c2)")
|
||||||
|
.param("city1", msg.city1name.clone())
|
||||||
|
.param("city2", msg.city2name.clone())
|
||||||
|
.param("distance", msg.distance.clone()),
|
||||||
|
]).await?;
|
||||||
|
if !msg.oneway {
|
||||||
|
txn.run_queries([
|
||||||
|
query("MATCH (c1:City{name: $city1}) WITH c1 MATCH (c2:City{name: $city2}) WITH c1,c2 CREATE (c1)<-[:ROAD{distance: $distance}]-(c2)")
|
||||||
|
.param("city1", msg.city1name.clone())
|
||||||
|
.param("city2", msg.city2name.clone())
|
||||||
|
.param("distance", msg.distance.clone())
|
||||||
|
]).await?;
|
||||||
|
}
|
||||||
|
txn.commit().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_city(graph: &Graph, msg: City) -> anyhow::Result<()> {
|
||||||
|
let mut txn = graph.start_txn().await?;
|
||||||
|
txn.run_queries([query("CREATE (c:City {name: $name})").param("name", msg.name.clone())])
|
||||||
|
.await?;
|
||||||
|
txn.commit().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
1
src/graph/mod.rs
Normal file
1
src/graph/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod graph;
|
42
src/main.rs
42
src/main.rs
@ -1,51 +1,29 @@
|
|||||||
use neo4rs::{Graph, Node, query};
|
use crate::backend::backend::build_api;
|
||||||
|
use neo4rs::Graph;
|
||||||
use rocket::Config;
|
use rocket::Config;
|
||||||
use rocket_okapi::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
|
use rocket_okapi::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
|
||||||
use crate::backend::backend::build_api;
|
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
|
mod graph;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let tokio = tokio::runtime::Runtime::new().expect("Failed to spawn tokio runtime");
|
||||||
|
|
||||||
let t = tokio::runtime::Runtime::new().unwrap();
|
tokio.block_on(async move {
|
||||||
|
|
||||||
let tt = Box::new("hello".to_string());
|
|
||||||
|
|
||||||
let iii = 1;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for i in 1..5 {
|
|
||||||
|
|
||||||
if tt == "hello" {
|
|
||||||
println!("{tt}");
|
|
||||||
} else {
|
|
||||||
println!("not");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{tt}");
|
|
||||||
|
|
||||||
t.block_on(async move {
|
|
||||||
// concurrent queries
|
|
||||||
let uri: String = "127.0.0.1:7687".to_string();
|
let uri: String = "127.0.0.1:7687".to_string();
|
||||||
let user = "neo4j";
|
let user = "";
|
||||||
let pass = "neo";
|
let pass = "";
|
||||||
|
// connect to neo4j database
|
||||||
let graph = Graph::new(uri, user, pass).await.unwrap();
|
let graph = Graph::new(uri, user, pass).await.unwrap();
|
||||||
|
|
||||||
//println!("{uri}");
|
// configure api port and interface
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
address: "0.0.0.0".parse().unwrap(),
|
address: "0.0.0.0".parse().unwrap(),
|
||||||
port: 8081,
|
port: 8081,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// configure api and swagger ui
|
||||||
let rock = rocket::custom(config)
|
let rock = rocket::custom(config)
|
||||||
.manage(graph)
|
.manage(graph)
|
||||||
.mount("/api/", build_api())
|
.mount("/api/", build_api())
|
||||||
|
Loading…
Reference in New Issue
Block a user