Crate neo4rs

Source
Expand description

Neo4j driver compatible with neo4j 4.x versions

  • An implementation of the bolt protocol to interact with Neo4j server
  • async/await apis using tokio
  • Supports bolt 4.2 specification
  • tested with Neo4j versions: 4.0, 4.1, 4.2

§Examples

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let id = uuid::Uuid::new_v4().to_string();

   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let id = uuid::Uuid::new_v4().to_string();
    graph
        .run(query("CREATE (p:Person {id: $id})").param("id", id.clone()))
        .await
        .unwrap();

    let mut handles = Vec::new();
    let count = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
    for _ in 1..=42 {
        let graph = graph.clone();
        let id = id.clone();
        let count = count.clone();
        let handle = tokio::spawn(async move {
            let mut result = graph
                .execute(query("MATCH (p:Person {id: $id}) RETURN p").param("id", id))
                .await
                .unwrap();
            while let Ok(Some(_row)) = result.next().await {
                count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
            }
        });
        handles.push(handle);
    }

    futures::future::join_all(handles).await;
    assert_eq!(count.load(std::sync::atomic::Ordering::Relaxed), 42);
}
}

§Configurations

Use the config builder to override the default configurations like

  • fetch_size - number of rows to fetch in batches (default is 200)
  • max_connections - maximum size of the connection pool (default is 16)
  • db - the database to connect to (default is neo4j)
use neo4rs::*;

#[tokio::main]
async fn main() {
   let config = ConfigBuilder::default()
       .uri("127.0.0.1:7687")
       .user("neo4j")
       .password("neo")
       .db("neo4j")
       .fetch_size(500)
       .max_connections(10)
       .build()
       .unwrap();
   let graph = Graph::connect(config).await.unwrap();

{ 
    let mut result = graph.execute(query("RETURN 1")).await.unwrap();
    let row = result.next().await.unwrap().unwrap();
    let value: i64 = row.get("1").unwrap();
    assert_eq!(1, value);
    assert!(result.next().await.unwrap().is_none());
}
}

§Nodes

A simple example to create a node and consume the created node from the row stream.

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    assert!(graph.run(query("RETURN 1")).await.is_ok());

    let mut result = graph
        .execute(
            query("CREATE (friend:Person {name: $name}) RETURN friend").param("name", "Mr Mark"),
        )
        .await
        .unwrap();

    #[derive(serde::Deserialize)]
    struct Person {
        labels: Labels,
        keys: Keys<Vec<String>>,
        name: String,
    }

    while let Ok(Some(row)) = result.next().await {
        // use serde to extract the relationship data
        let friend: Person = row.get("friend").unwrap();
        assert_eq!(friend.name, "Mr Mark");
        assert_eq!(friend.labels.0, vec!["Person"]);
        assert_eq!(friend.keys.0, vec!["name"]);

        // or use the neo4rs::Relation type
        let node: Node = row.get("friend").unwrap();
        assert_eq!(node.get::<String>("name").unwrap(), "Mr Mark");
        assert_eq!(node.labels(), vec!["Person"]);
        assert_eq!(node.keys(), vec!["name"]);
        assert!(node.id() >= 0);
    }
}
}

§Transactions

Start a new transaction using Graph::start_txn, which will return a handle Txn that can be used to Txn::commit or Txn::rollback the transaction.

Note that the handle takes a connection from the connection pool, which will be released once the Txn is dropped

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{
    let mut txn = graph.start_txn().await.unwrap();
    let id = uuid::Uuid::new_v4().to_string();
    let result = txn
        .run_queries([
            query("CREATE (p:Person {id: $id})").param("id", id.clone()),
            query("CREATE (p:Person {id: $id})").param("id", id.clone()),
        ])
        .await;

    assert!(result.is_ok());
    txn.commit().await.unwrap();
    let mut result = graph
        .execute(query("MATCH (p:Person) WHERE p.id = $id RETURN p.id").param("id", id.clone()))
        .await
        .unwrap();
    assert!(result.next().await.unwrap().is_some());
    assert!(result.next().await.unwrap().is_some());
    assert!(result.next().await.unwrap().is_none());
}
}

§Streams within a transaction

Each RowStream returned by various execute functions within the same transaction are well isolated, so you can consume the stream anytime within the transaction using RowStream::next

use neo4rs::*;

#[tokio::main]
async fn main() {
   let config = ConfigBuilder::default()
       .uri("127.0.0.1:7687")
       .user("neo4j")
       .password("neo")
       .fetch_size(1)
       .build()
       .unwrap();
   let graph = Graph::connect(config).await.unwrap();

{ 
    let name = uuid::Uuid::new_v4().to_string();
    let mut txn = graph.start_txn().await.unwrap();

    #[derive(serde::Deserialize)]
    struct Person {
        name: String,
    }

    txn.run_queries([
        query("CREATE (p { name: $name })").param("name", name.clone()),
        query("CREATE (p { name: $name })").param("name", name.clone()),
    ])
        .await
        .unwrap();

    //start stream_one
    let mut stream_one = txn
        .execute(query("MATCH (p {name: $name}) RETURN p").param("name", name.clone()))
        .await
        .unwrap();
    let row = stream_one.next(txn.handle()).await.unwrap().unwrap();
    assert_eq!(row.to::<Person>().unwrap().name, name);

    //start stream_two
    let mut stream_two = txn.execute(query("RETURN 1")).await.unwrap();
    let row = stream_two.next(txn.handle()).await.unwrap().unwrap();
    assert_eq!(row.to::<i64>().unwrap(), 1);

    //stream_one is still active here
    let row = stream_one.next(txn.handle()).await.unwrap().unwrap();
    assert_eq!(row.to::<Person>().unwrap().name, name);

    //stream_one completes
    assert!(stream_one.next(txn.handle()).await.unwrap().is_none());
    //stream_two completes
    assert!(stream_two.next(txn.handle()).await.unwrap().is_none());
    txn.commit().await.unwrap();
}
}

§Streams are evaluated lazily

The RowStream returned by various execute functions need to be consumed with RowStream::next in order to actually execute the query. The various run functions on the other hand are always executed eagerly.

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{
    let before = graph
        .execute(query("MATCH (n:MyNode) RETURN COUNT(n) AS n"))
        .await
        .unwrap()
        .next()
        .await
        .unwrap()
        .unwrap()
        .get::<i64>("n")
        .unwrap();

    // use `run` for fire-and-forget queries, that are being executed on the server
    graph
        .run(query("CREATE (n:MyNode {p: 'prop'})"))
        .await
        .unwrap();

    // using `execute` without consuming the result will do nothing
    // This will trigger a `unused_must_use` warning
    graph
        .execute(query("CREATE (n:MyNode {p: 'prop'})"))
        .await
        .unwrap();

    // consuming the result stream of`execute` will run the query on the server
    graph
        .execute(query("CREATE (n:MyNode {p: 'prop'})"))
        .await
        .unwrap()
        .next()
        .await
        .unwrap();

    let after = graph
        .execute(query("MATCH (n:MyNode) RETURN COUNT(n) AS n"))
        .await
        .unwrap()
        .next()
        .await
        .unwrap()
        .unwrap()
        .get::<i64>("n")
        .unwrap();

    assert_eq!(after, before + 2);
}
}

§Rollback a transaction

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let mut txn = graph.start_txn().await.unwrap();
    let id = uuid::Uuid::new_v4().to_string();
    // create a node
    txn.run(query("CREATE (p:Person {id: $id})").param("id", id.clone()))
        .await
        .unwrap();
    // rollback the changes
    txn.rollback().await.unwrap();

    // changes not updated in the database
    let mut result = graph
        .execute(query("MATCH (p:Person) WHERE p.id = $id RETURN p.id").param("id", id.clone()))
        .await
        .unwrap();
    assert!(result.next().await.unwrap().is_none());
}
}

§Txn vs Graph

Everytime you execute a query using Graph::run or Graph::execute, a new connection is taken from the pool and released immediately.

However, when you execute a query on a transaction using Txn::run or Txn::execute the same connection will be reused, the underlying connection will be released to the pool in a clean state only after you commit/rollback the transaction and the Txn handle is dropped.

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let mut txn = graph.start_txn().await.unwrap();
    let id = uuid::Uuid::new_v4().to_string();
    txn.run(query("CREATE (p:Person {id: $id})").param("id", id.clone()))
        .await
        .unwrap();
    txn.run(query("CREATE (p:Person {id: $id})").param("id", id.clone()))
        .await
        .unwrap();
    // graph.execute(..) will not see the changes done above as the txn is not committed yet
    let mut result = graph
        .execute(query("MATCH (p:Person) WHERE p.id = $id RETURN p.id").param("id", id.clone()))
        .await
        .unwrap();
    assert!(result.next().await.unwrap().is_none());
    txn.commit().await.unwrap();

    //changes are now seen as the transaction is committed.
    let mut result = graph
        .execute(query("MATCH (p:Person) WHERE p.id = $id RETURN p.id").param("id", id.clone()))
        .await
        .unwrap();
    assert!(result.next().await.unwrap().is_some());
    assert!(result.next().await.unwrap().is_some());
    assert!(result.next().await.unwrap().is_none());
}
}

§Relationships

Bounded Relationship between nodes are created using cypher queries and the same can be parsed from the RowStream

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let mut result = graph.execute(
        query("CREATE (p:Person { name: 'Oliver Stone' })-[r:WORKS_AT {as: 'Engineer'}]->(neo) RETURN r")
    ).await.unwrap();

    let row = result.next().await.unwrap().unwrap();
    let relation: Relation = row.get("r").unwrap();
    assert!(relation.id() > -1);
    assert!(relation.start_node_id() > -1);
    assert!(relation.end_node_id() > -1);
    assert_eq!(relation.typ(), "WORKS_AT");
    assert_eq!(relation.keys(), vec!["as"]);
    assert_eq!(relation.get::<String>("as").unwrap(), "Engineer");
}
}

Similar to bounded relation, an unbounded relation can also be created/parsed.

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let mut result = graph.execute(
        query("MERGE (p1:Person { name: 'Oliver Stone' })-[r:RELATED {as: 'friend'}]-(p2: Person {name: 'Mark'}) RETURN r")
    ).await.unwrap();
    let row = result.next().await.unwrap().unwrap();

    #[derive(serde::Deserialize)]
    struct Related {
        id: Id,
        start_node_id: StartNodeId,
        end_node_id: EndNodeId,
        typ: Type,
        keys: Keys<Vec<String>>,
        #[serde(rename = "as")]
        related_as: String,
    }

    // use serde to extract the relationship data
    let relation: Related = row.get("r").unwrap();

    // The following checks are always true, but are included here
    // to demonstrate the types of the fields.
    #[allow(clippy::absurd_extreme_comparisons, unused_comparisons)]
    {
        assert!(relation.id.0 >= 0);
        assert!(relation.start_node_id.0 >= 0);
        assert!(relation.end_node_id.0 >= 0);
    }

    assert_eq!(relation.typ.0, "RELATED");
    assert_eq!(relation.keys.0, vec!["as"]);
    assert_eq!(relation.related_as, "friend");

    // or use the neo4rs::Relation type
    let relation: Relation = row.get("r").unwrap();
    assert!(relation.id() > -1);
    assert!(relation.start_node_id() > -1);
    assert!(relation.end_node_id() > -1);
    assert_eq!(relation.typ(), "RELATED");
    assert_eq!(relation.keys(), vec!["as"]);
    assert_eq!(relation.get::<String>("as").unwrap(), "friend");
}
}

§Points

A 2d or 3d point can be represented with the types Point2D and Point3D

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

   let qry = "
       WITH point({ x: 2.3, y: 4.5, crs: 'cartesian' }) AS p1,
            point({ x: 1.1, y: 5.4, crs: 'cartesian' }) AS p2
       RETURN point.distance(p1,p2) AS dist, p1, p2
    ";
{ 
    let mut result = graph.execute(query(qry)).await.unwrap();
    let row = result.next().await.unwrap().unwrap();
    let dist: f64 = row.get("dist").unwrap();
    let p1: Point2D = row.get("p1").unwrap();
    let p2: Point2D = row.get("p2").unwrap();
    assert_eq!(1.5, dist);
    assert_eq!(p1.sr_id(), 7203);
    assert_eq!(p1.x(), 2.3);
    assert_eq!(p1.y(), 4.5);
    assert_eq!(p2.sr_id(), 7203);
    assert_eq!(p2.x(), 1.1);
    assert_eq!(p2.y(), 5.4);
    assert!(result.next().await.unwrap().is_none());

    let mut result = graph
        .execute(query(
            "RETURN point({ longitude: 56.7, latitude: 12.78, height: 8 }) AS point",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let point: Point3D = row.get("point").unwrap();
    assert_eq!(point.sr_id(), 4979);
    assert_eq!(point.x(), 56.7);
    assert_eq!(point.y(), 12.78);
    assert_eq!(point.z(), 8.0);
    assert!(result.next().await.unwrap().is_none());
}
}

§Raw bytes

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let bytes = b"Hello, Neo4j!";
    let mut result = graph
        .execute(query("RETURN $b as output").param("b", bytes.as_ref()))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let b: Vec<u8> = row.get("output").unwrap();
    assert_eq!(b, bytes);
    assert!(result.next().await.unwrap().is_none());
}
}

§Durations

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let duration = std::time::Duration::new(5259600, 7);
    let mut result = graph
        .execute(query("RETURN $d as output").param("d", duration))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let d: std::time::Duration = row.get("output").unwrap();
    assert_eq!(d.as_secs(), 5259600);
    assert_eq!(d.subsec_nanos(), 7);
    assert!(result.next().await.unwrap().is_none());
}
}

§Date

See NaiveDate for date abstraction, it captures the date without time component.

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let date = chrono::NaiveDate::from_ymd_opt(1985, 2, 5).unwrap();
    let mut result = graph
        .execute(query("RETURN $d as output").param("d", date))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let d: chrono::NaiveDate = row.get("output").unwrap();
    assert_eq!(d.to_string(), "1985-02-05");
    assert!(result.next().await.unwrap().is_none());
}
}

§Time

§Time as param

Pass a time as a parameter to the query:

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    //send time without offset as param
    let time = chrono::NaiveTime::from_hms_nano_opt(11, 15, 30, 200).unwrap();
    let mut result = graph
        .execute(query("RETURN $d as output").param("d", time))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: (chrono::NaiveTime, Option<Offset>) = row.get("output").unwrap();
    assert_eq!(t.0.to_string(), "11:15:30.000000200");
    assert_eq!(t.1, None);
    assert!(result.next().await.unwrap().is_none());

    //send time with offset as param
    let time = chrono::NaiveTime::from_hms_nano_opt(11, 15, 30, 200).unwrap();
    let offset = chrono::FixedOffset::east_opt(3 * 3600).unwrap();
    let mut result = graph
        .execute(query("RETURN $d as output").param("d", (time, offset)))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: (chrono::NaiveTime, Option<Offset>) = row.get("output").unwrap();
    assert_eq!(t.0.to_string(), "11:15:30.000000200");
    assert_eq!(t.1, Some(Offset(offset)));
    assert!(result.next().await.unwrap().is_none());
}
}

§Parsing time from result

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    //Parse time without offset
    let mut result = graph
        .execute(query(
            " WITH time({hour:10, minute:15, second:30, nanosecond: 200}) AS t RETURN t",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: (chrono::NaiveTime, Option<Offset>) = row.get("t").unwrap();
    assert_eq!(t.0.to_string(), "10:15:30.000000200");
    assert_eq!(t.1, None);
    assert!(result.next().await.unwrap().is_none());

    //Parse time with timezone information
    let mut result = graph
        .execute(query(
            " WITH time({hour:10, minute:15, second:33, nanosecond: 200, timezone: '+01:00'}) AS t RETURN t",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: (chrono::NaiveTime, Option<Offset>) = row.get("t").unwrap();
    assert_eq!(t.0.to_string(), "10:15:33.000000200");
    assert_eq!(t.1, Some(Offset(chrono::FixedOffset::east_opt(3600).unwrap())));
    assert!(result.next().await.unwrap().is_none());
}
}

§DateTime

  • DateTime captures the date and time with offset
  • NaiveDateTime captures the date time without offset
  • tuple(NaiveDateTime, String) captures the date/time and the time zone id

§DateTime as param

Pass a DateTime as parameter to the query:

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    //send datetime as parameter in the query
    let datetime = chrono::DateTime::parse_from_rfc2822("Tue, 01 Jul 2003 10:52:37 +0200").unwrap();

    let mut result = graph
        .execute(query("RETURN $d as output").param("d", datetime))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: chrono::DateTime<chrono::FixedOffset> = row.get("output").unwrap();
    assert_eq!(t.to_string(), "2003-07-01 10:52:37 +02:00");
    assert!(result.next().await.unwrap().is_none());

    //send NaiveDateTime as parameter in the query
    let localdatetime =
    chrono::NaiveDateTime::parse_from_str("2015-07-01 08:55:59.123", "%Y-%m-%d %H:%M:%S%.f")
        .unwrap();

    let mut result = graph
        .execute(query("RETURN $d as output").param("d", localdatetime))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: chrono::NaiveDateTime = row.get("output").unwrap();
    assert_eq!(t.to_string(), "2015-07-01 08:55:59.123");
    assert!(result.next().await.unwrap().is_none());

    //send NaiveDateTime with timezone id as parameter in the query
    let datetime =
    chrono::NaiveDateTime::parse_from_str("2015-07-03 08:55:59.555", "%Y-%m-%d %H:%M:%S%.f")
        .unwrap();
    let timezone = "Europe/Paris";

    let mut result = graph
        .execute(query("RETURN $d as output").param("d", (datetime, timezone)))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let (time, zone): (chrono::NaiveDateTime, String) = row.get("output").unwrap();
    assert_eq!(time.to_string(), "2015-07-03 08:55:59.555");
    assert_eq!(zone, "Europe/Paris");
    assert!(result.next().await.unwrap().is_none());
}
}

§Parsing DateTime from result

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    //Parse NaiveDateTime from result
    let mut result = graph
        .execute(query(
            "WITH localdatetime('2015-06-24T12:50:35.556') AS t RETURN t",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: chrono::NaiveDateTime = row.get("t").unwrap();
    assert_eq!(t.to_string(), "2015-06-24 12:50:35.556");
    assert!(result.next().await.unwrap().is_none());

    //Parse DateTime from result
    let mut result = graph
        .execute(query(
            "WITH datetime('2015-06-24T12:50:35.777+0100') AS t RETURN t",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let t: chrono::DateTime<chrono::FixedOffset> = row.get("t").unwrap();
    assert_eq!(t.to_string(), "2015-06-24 12:50:35.777 +01:00");
    assert!(result.next().await.unwrap().is_none());

    //Parse NaiveDateTime with zone id from result
    let mut result = graph
        .execute(query(
            "WITH datetime({ year:1984, month:11, day:11, hour:12, minute:31, second:14, nanosecond: 645876123, timezone:'Europe/Stockholm' }) AS d return d",
        ))
        .await
        .unwrap();
    let row = result.next().await.unwrap().unwrap();
    let (datetime, zone_id): (chrono::NaiveDateTime, String) = row.get("d").unwrap();
    assert_eq!(datetime.to_string(), "1984-11-11 12:31:14.645876123");
    assert_eq!(zone_id, "Europe/Stockholm");
    assert!(result.next().await.unwrap().is_none());
}
}

§Path

use neo4rs::*;

#[tokio::main]
async fn main() {
   let uri = "127.0.0.1:7687";
   let user = "neo4j";
   let pass = "neo";
   let graph = Graph::new(uri, user, pass).await.unwrap();

{ 
    let name = uuid::Uuid::new_v4().to_string();
    graph
        .run(
            query("CREATE (p:Person { name: $name })-[r:WORKS_AT]->(n:Company { name: 'Neo'})")
                .param("name", name.clone()),
        )
        .await
        .unwrap();

    let mut result = graph
        .execute(
            query("MATCH p = (person:Person { name: $name })-[r:WORKS_AT]->(c:Company) RETURN p")
                .param("name", name),
        )
        .await
        .unwrap();

    let row = result.next().await.unwrap().unwrap();
    let path: Path = row.get("p").unwrap();
    assert_eq!(path.indices().len(), 2);
    assert_eq!(path.nodes().len(), 2);
    assert_eq!(path.rels().len(), 1);
    assert!(result.next().await.unwrap().is_none());
}
}

Macros§

cenum

Structs§

BoltBoolean
BoltBytes
BoltDate
BoltDateTime
BoltDateTimeZoneId
BoltDuration
BoltFloat
BoltInteger
BoltList
BoltLocalDateTime
BoltLocalTime
BoltMap
BoltNode
BoltNull
BoltPath
BoltPoint2D
BoltPoint3D
BoltRelation
BoltString
BoltTime
BoltUnboundedRelation
ClientCertificate
Config
The configuration used to connect to the database, see crate::Graph::connect.
ConfigBuilder
A builder to override default configurations and build the Config.
Database
Newtype for the name of the database. Stores the name as an Arc<str> to avoid cloning the name around.
EndNodeId
Newtype to extract the end node id of a relationship during deserialization.
Graph
A neo4j database abstraction. This type can be cloned and shared across threads, internal resources are reference-counted.
Id
Newtype to extract the node id or relationship id during deserialization.
Indices
Newtype to extract the indices of a path during deserialization.
Keys
Newtype to extract the node property keys during deserialization.
Labels
Newtype to extract the node labels during deserialization.
Neo4jError
Node
Snapshot of a node within a graph database
Nodes
Newtype to extract the nodes of a path during deserialization.
Offset
Newtype to extract the offset info of times during deserialization.
Path
Alternating sequence of nodes and relationships
Point2D
Represents a single location in 2-dimensional space
Point3D
Represents a single location in 3-dimensional space
Query
Abstracts a cypher query that is sent to neo4j server.
Relation
Snapshot of a relationship within a graph database
Relationships
Newtype to extract the relationships of a path during deserialization.
Row
Represents a row returned as a result of executing a query.
RowStream
An abstraction over a stream of rows, this is returned as a result of crate::Txn::execute.
StartNodeId
Newtype to extract the start node id of a relationship during deserialization.
Timezone
Newtype to extract the timezone info of datetimes during deserialization.
Txn
A handle which is used to control a transaction, created as a result of crate::Graph::start_txn
Type
Newtype to extract the relationship type during deserialization.
UnboundedRelation
Relationship detail without start or end node information

Enums§

BoltType
DeError
Error
Neo4jClientErrorKind
Neo4jErrorKind
Neo4jSecurityErrorKind
Version

Functions§

query
Returns a Query which provides methods like Query::param to add parameters to the query
unexpected

Type Aliases§

Result