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 rocket::{get, Route, State};
|
||||
use rocket::response::status::NotFound;
|
||||
use rocket_okapi::{openapi, openapi_get_routes};
|
||||
use crate::backend::types::{City, Road, ShortestPath};
|
||||
use crate::graph::graph::{all_cities, create_city, create_road, shortest_path};
|
||||
use neo4rs::{Graph};
|
||||
use rocket::response::status::{BadRequest};
|
||||
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> {
|
||||
openapi_get_routes![
|
||||
append
|
||||
]
|
||||
openapi_get_routes![addcity, addroad, getshortestpath, getallcities]
|
||||
}
|
||||
|
||||
/// get general build-server stats
|
||||
#[openapi(tag = "stats")]
|
||||
#[get("/append")]
|
||||
pub async fn append(graph: &State<Graph>) -> Result<String, NotFound<String>> {
|
||||
let graph = graph as &Graph;
|
||||
|
||||
Ok("worked".to_string())
|
||||
/// endpoint to add new city to graph
|
||||
#[openapi]
|
||||
#[post("/addcity", data = "<msg>")]
|
||||
pub async fn addcity(graph: &State<Graph>, msg: Json<City>) -> Result<String, BadRequest<String>> {
|
||||
match create_city(graph, msg.into_inner()).await {
|
||||
Ok(_) => Ok("ok".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 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_okapi::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
|
||||
use crate::backend::backend::build_api;
|
||||
|
||||
mod backend;
|
||||
mod graph;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let tokio = tokio::runtime::Runtime::new().expect("Failed to spawn tokio runtime");
|
||||
|
||||
let t = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
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
|
||||
tokio.block_on(async move {
|
||||
let uri: String = "127.0.0.1:7687".to_string();
|
||||
let user = "neo4j";
|
||||
let pass = "neo";
|
||||
let user = "";
|
||||
let pass = "";
|
||||
// connect to neo4j database
|
||||
let graph = Graph::new(uri, user, pass).await.unwrap();
|
||||
|
||||
//println!("{uri}");
|
||||
|
||||
// configure api port and interface
|
||||
let config = Config {
|
||||
address: "0.0.0.0".parse().unwrap(),
|
||||
port: 8081,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// configure api and swagger ui
|
||||
let rock = rocket::custom(config)
|
||||
.manage(graph)
|
||||
.mount("/api/", build_api())
|
||||
|
Loading…
Reference in New Issue
Block a user