5 unstable releases
0.3.1 | Mar 24, 2024 |
---|---|
0.3.0 | Mar 1, 2024 |
0.2.0 | Aug 12, 2023 |
0.1.1 | Aug 7, 2023 |
0.1.0 | Aug 6, 2023 |
#629 in Database interfaces
64KB
1K
SLoC
cypher-dto
A collection of traits and macros for working with Data Transfer Objects (DTOs) in Neo4j.
Brief Overview
use cypher_dto::{Node, Relation};
#[derive(Node)]
struct Person {
name: String
}
#[derive(Relation)]
struct Knows {
since: u16
}
let alice = Person::new("Alice");
let bob = Person::new("Bob");
let knows = Knows::new(2020);
let graph = neo4rs::Graph::new(/*...*/);
let query: neo4rs::Query = alice.create();
graph.execute(query);
let alice = alice.into_builder().name("Allison").build();
let query = alice.update();
graph.execute(query);
let query = knows.create(RelationBound::Match(&alice), RelationBound::Create(&bob));
graph.execute(query);
Examples
Basic usage
use cypher_dto::Node;
use neo4rs::Query;
#[derive(Node)]
struct Person {
id: String, // Inferred to be the only #[id] field.
name: String,
#[name = "zip_code"]
zip: String,
}
assert_eq!(Person::typename(), "Person");
assert_eq!(Person::field_names(), &["id", "name", "zip_code"]);
// For building parameterized cypher queries...
assert_eq!(Person::as_query_fields(), "id: $id, name: $name, zip_code: $zip_code");
assert_eq!(Person::as_query_obj(), "Person { id: $id, name: $name, zip_code: $zip_code }");
let person = Person::new("123", "John", "12345");
// Unitary CRUD operations are provided for convenience.
let query = person.create();
// Equivalent to:
let mut query = Query::new(format!(
"CREATE (n:{})",
Person::as_query_obj()
));
query = person.add_values_to_params(query, None, StampMode::Create);
#[derive(Clone, Debug, PartialEq, Relation)]
struct Knows;
assert_eq!(Knows::typename(), "KNOWS");
Multi valued identifiers
use cypher_dto::Node;
use neo4rs::Query;
#[derive(Node)]
struct Company {
#[id]
name: String,
#[id]
state: String,
phone: String,
}
let company = Company::new("Acme", "CA", "555-555-5555");
let id = company.identifier();
assert_eq!(id.name(), "Acme");
assert_eq!(id.state(), "CA");
assert_eq!(id, CompanyId::new("Acme", "CA"));
assert_eq!(CompanyId::typename(), "Company");
assert_eq!(CompanyId::field_names(), &["name", "state"]);
let query = id.read();
// Equivalent to:
let mut query = Query::new(format!(
"MATCH (n:{}) RETURN n",
CompanyId::as_query_obj()
));
query = id.add_values_to_params(query, None, StampMode::Read);
Builder, new, and getters
-
The generated
::new()
method will accept&str
forString
fields, and&[T]
forVec<T>
fields. -
Doc comments are copied to the getters for the struct, the getter(s) on the
FooId
struct, and the methods on theFooBuilder
struct.
#[derive(Node)]
struct Person {
/// This comment is copied to the getter, the Id getter, and the builder method.
name: String,
}
let p = Person::new("John");
let p = p.into_builder().name("Ferris").build();
assert_eq!(p.name(), "Ferris");
Timestamps
There's built-in support for special timestamp fields: created_at
and updated_at
, created
and updated
, or any single one of those four.
use cypher_dto::timestamps;
#[timestamps]
struct Person {
name: String,
}
// Adds two fields:
// created_at: Option<DateTime<Utc>>,
// updated_at: Option<DateTime<Utc>>,
#[timestamps = "short"]
struct Person {
name: String,
}
// Adds two fields:
// created: Option<DateTime<Utc>>,
// updated: Option<DateTime<Utc>>,
#[timestamps = "updated_at"]
struct Person {
name: String,
}
// Adds one field:
// updated_at: Option<DateTime<Utc>>,
The timestamp fields are treated a little bit differently than other fields:
- They are not parameters in the generated
::new()
method. - They sometimes have hardcoded values in
::to_query_fields()
.- Calling
to_query_fields()
withStampMode::Create
will usedatetime()
in the query instead of$created_at
for example.
- Calling
Option<DateTime<Utc>>
is used instead of DateTime<Utc>
so that the fields can be None
when creating a new instance, before it exists in the database.
For more details about the macro variations, see the cypher-dto-macros crate.
Unitary CRUD operations
This library takes the point of view that non-trivial queries should be managed by hand, but it does provide basic CRUD operations for convenience.
#[derive(Node)]
and #[derive(Relation)]
structs get create()
and update()
methods, while the corresponding FooId
structs get read()
and delete()
methods, all of which return a neo4rs::Query
.
None of those methods even take any arguments, with the exception of creating a relation, which needs to know if the start and end nodes it's between need created or already exist.
use cypher_dto::{Node, Relation};
#[derive(Node)]
Person {
name: String,
}
#[derive(Clone, Debug, PartialEq, Relation)]
struct Knows;
let alice = Person::new("Alice");
let bob = Person::new("Bob");
let knows = Knows; // Relations can have fields and ids too.
let query = knows.create(RelationBound::Create(&alice), RelationBound::Create(&bob));
Node labels
While neo4rs::Node::labels()
can be used to read the current labels of a node from the database, this library provides a way to define the labels a struct should use. Those labels are then used by the built in CRUD operations.
#[derive(Node)]
#[labels("Person", "Employee")]
Person {
name: String,
}
let person = Person::new("Alice");
assert_eq!(person.labels(), &["Person", "Employee"]);
Dependencies
~15–26MB
~456K SLoC