#query-builder #neo4j #cypher #graph-database #flexible #intuitive #node

neo4j_cypher

Neo4j 和 Cypher 的灵活直观查询构建器

3 个不稳定版本

0.2.2 2024年3月17日
0.2.1 2022年8月26日
0.1.4 2022年8月25日

#253数据库接口

BSD-2-Clause

58KB
1K SLoC

Neo4j 查询构建器

Licenses Licenses X

Neo4j 和 Cypher 的灵活直观查询构建器。用 Rust 编写查询就像在 Cypher 中编写一样。

安装

neo4j_cypher = { version = "0.2", features=["derive"] }

使用方法

容器属性

  • #[cypher(rename = "...")]

    当您想在代码中使用名称 A,但在 Neo4j 中希望将其保存为名为 B 的节点结构时,可以使用此属性。

字段属性

  • #[cypher(rename = "...")]

    重命名属性也可以用作字段属性。其工作原理与上述描述类似。应在您想自动更改使用 Neo4j 实体属性对象构建查询时字段名称时使用。

  • #[cypher(skip)]

    当您想隐藏在构建查询时的一些结构字段时,请使用此属性。

  • #[cypher(label)]

    如果您想将字段值用作节点的标签,则可以使用此类属性。建议使用枚举作为此类字段的值。

  • #[cypher(default)]

    如果字段类型是某种类型的 Option<T>,您可以使用此属性,并且当字段值为 None 时,将为该类型设置默认值。

    注意:对于使用简单属性 default 的类型,必须实现 trait Default 的强制实现。

  • #[cypher(default = "...")]

    如果为整个类型设置默认值不适用于您,则可以使用单个字段的默认值。默认值应始终指定为字符串,但在生成查询时将转换为所需类型。

实体

  • 节点

    创建节点

    let node = Node::new("n", "Profile", None, None);
    

    但您可以将 derive 属性设置到结构 #[derive(Debug, Clone, CypQueSet)] 上,节点将自动生成!

  • 关系

    创建关系

    use neo4j_cypher::entity::Relation;
    
    let rel = Relation::new(a1.node("n1"), a2.node("n2"), "SUBSCRIBE", None);
    

    a1a2中,它们是具有CypQue继承宏的结构体。

当然,你不仅可以指定一个Props对象或Label的向量,而不是使用None

模板

neo4j_cypher = { version = "...", features=[ "derive", "templates" ] }

不使用模板的请求示例

use neo4j_cypher::query::match_query::CompOper;

let query = Query::init().r#match(&a1.node("n1").into(), false)
    .r#where("name", CompOper::Equal, PropType::str("admin"))
    .r#match(&a2.node("n2").into(), false)
    .r#where("name", CompOper::Equal, PropType::str("dev"))
    .return_many(vec!["n1", "n2"])
    .finalize();

使用模板的请求示例

To include templates, you must add `templates` to the **features** dependency sections.
let q = Query::init()
    .r#match(&a1.node("n1").into(), false)
    .where_eq_str("name", "admin")
    .r#match(&a2.node("n2").into(), false)
    .where_eq_str("name", "dev")
    .return_many(vec!["n1", "n2"])
    .finalize();

示例

节点

use std::fmt::Display;

use neo4j_cypher::query::match_query::CompOper;
use neo4j_cypher::CypQue;

/// Example of access levels in the system
#[derive(Debug, Clone)]
enum Perm {
    Admin,
    User,
}

/// An example of a structure that should be converted into a Neo4j node.
/// 
/// In your code you use name Account and the Neo4j node label will be Profile.
/// Field `username` will be renamed;
/// Field `secret` will be hidden;
/// Field `perm` will be used as second a node label;
/// Field `level` and `friends` will used a default value if they will be None;
#[derive(Debug, Clone, CypQue)]
#[cypher(rename = "Profile")]
struct Account {
    #[cypher(rename = "name")]
    username: String,
    password: String,
    age: i32,
    status: Option<String>,
    online: bool,
    #[cypher(skip)]
    secret: u8,
    #[cypher(label)]
    perm: Perm,
    #[cypher(default = "5")]
    level: Option<u8>,
    #[cypher(default = "['Bob', 'Tom']")]
    friends: Option<Vec<String>>,
}

fn main() {
    // Init some example struct
    let data = Account {
        username: String::from("mi1fhunter"),
        password: String::from("1234f4321"),
        age: 32,
        status: None,
        online: false,
        secret: 1,
        perm: Perm::User,
        level: None,
        friends: Some(vec![
            "Bob".to_string(),
            "Tom".to_string(),
            "Sam".to_string(),
        ]),
    };

    // Let's build some query
    let query = Query::init()
        .create(vec![&a1.node("n").into()])
        .r#return("n")
        .finalize();

    println!("{}", query);
}

因此,查询构建器会自动为您生成这样的查询

CREATE (n:Profile { password: '1234f4321',level: 5,name: 'mi1fhunter',age: 32,friends: ['Bob','Tom','Sam'],online: false })
SET n:User
RETURN n

创建匹配查询的示例

let query = Query::init()
    .r#match(&a1.node("n1").into(), false)
    .where_eq_str("name", "admin")       
    .r#match(&a2.node("n2").into(), false)
    .where_eq_str("name", "dev")
    .return_many(vec!["n1", "n2"])
    .finalize();

结果将如下所示

MATCH (n1:Profile) WHERE n1.name = 'admin'
MATCH (n2:Profile) WHERE n2.name = 'dev'
RETURN n1,n2

如果您需要返回某个属性的值或获取另一个var的名称,您可以写成这样

let query = Query::init()
    .create(vec![&model.node("n").into()])
    .r#return_field("n", "age")
    .finalize();

结果将是

CREATE (n:Profile { name: 'admin',friends: ['Bob','Tom','Sam'],password: '1234f4321',online: false,level: 5,age: 32 })
SET n:User
RETURN n.age

let query = Query::init()
    .create(vec![&model.node("n").into()])
    .r#return("n")
    .r#as("node")
    .finalize();

结果

CREATE (n:Profile { age: 32,uname: 'mi1fhunter',online: false,level: 5,friends: ['Bob','Tom','Sam'],password: '1234f4321' })
SET n:User
RETURN n AS node

关系

let rel1 = Entity::rel("n1", "n2", "SUBSCRIBE", None);
let rel2 = Entity::rel("n2", "n1", "SUBSCRIBE", None);

let query = Query::init()
    .r#match(&a1.node("n1").into(), false)
    .where_eq_str("name", "admin")
    .r#match(&a2.node("n2").into(), false)
    .where_eq_str("name", "dev")
    .return_many(vec!["n1", "n2"])
    .create(vec![&rel1.into(), &rel2.into()])
    .finalize();

结果

MATCH (n1:Profile) WHERE n1.age = 1 AND n1.level = 10 
MATCH (n1:Profile) WHERE n1.age = 10
CREATE (n1)-[:SUBSCRIBE]->(n2),
        (n2)-[:SUBSCRIBE]->(n1)

依赖关系

~240KB