#grpc-client #database-client #graphql-client #query-response #grpc #dgraph #client-server

دگراف-تونیک

یک مشتری async/sync برای دیتابیس Dgraph ساخته شده با بسته Tonic

29 روانشتاب

0.11.0 14 مارس 2023
0.10.2 21 اکتبر 2022
0.10.0 30 دسامبر 2021
0.9.0 19 ژانویه 2021
0.7.0 24 ژوئیه 2020

#1236 در Database interfaces

Download history 32/week @ 2024-03-13 4/week @ 2024-03-20 42/week @ 2024-03-27 62/week @ 2024-04-03 10/week @ 2024-04-10 7/week @ 2024-04-17 16/week @ 2024-04-24 13/week @ 2024-05-01 15/week @ 2024-05-08 5/week @ 2024-05-15 9/week @ 2024-05-22 17/week @ 2024-05-29 31/week @ 2024-06-05 16/week @ 2024-06-12 6/week @ 2024-06-19 14/week @ 2024-06-26

77 دانلود در ماه

MIT مجوز

320KB
5.5K SLoC

یک مشتری async/sync rust برای DB دگراف

Build Status Latest Version Docs Coverage Status

یک مشتری async/sync rust برای دگراف که با استفاده از gRPC با سرور ارتباط برقرار می‌کند و با استفاده از Tonic ساخته شده.

قبل از استفاده از این مشتری، توصیه می‌شود که به tour.dgraph.io و docs.dgraph.io بروید تا بفهمید چگونه دگراف را اجرا و با آن کار کنید.

جداول محتوای

نصب

dgraph-tonic در crates.io موجود است. وابستگی زیر را به Cargo.toml خود اضافه کنید.

[dependencies]
dgraph-tonic = "0.11"

ویژگی پیش‌فرض dgraph-1-1 است.

تمام ویژگی‌های موجود می‌توانند با

[dependencies]
dgraph-tonic = {version = "0.11", features = ["all"]}

اگر می‌خواهید از Dgraph v1.0.x استفاده کنید، این وابستگی را اضافه کنید

[dependencies]
dgraph-tonic = { version = "0.11", features = ["dgraph-1-0"], default-features = false }

ویژگی‌های پشتیبانی شده

  • acl: مشتری با احراز هویت را فعال کنید.
  • all: ویژگی‌های tls، acl و sync را با dgraph-1-1 فعال کنید
  • dgraph-1-0: مشتری برای Dgraph v1.0.x فعال شود
  • dgraph-1-1: مشتری برای Dgraph v1.1.x و v20.03.x فعال شود
  • dgraph-21-03: مشتری برای Dgraph v21.03.x فعال شود
  • slash-ql: مشتری برای سرویس Slash GraphQL فعال شود
  • tls: مشتری TlsClient امن را فعال کنید
  • sync: مشتری همزمان را فعال کنید

نسخه‌های پشتیبانی شده

در função از نسخه Dgraph که به آن متصل می‌شوید، باید ویژگی متفاوتی از این مشتری استفاده کنید (dgraph-1-0 نسخه پیش‌فرض است).

نسخه Dgraph ویژگی
1.0.X dgraph-1-0
1.1.X dgraph-1-1
1.2.X dgraph-1-1
20.03.X dgraph-1-1
21.03.X dgraph-21-03

注意:只有从 *dgraph-1-0dgraph-1-1 的 API 破坏出现在函数 MutatedTxn.mutate() 中。该函数在 dgraph-1-0 中返回 Assigned 类型,但在 dgraph-1-1 中返回 Response 类型。

استفاده از مشتری

ایجاد یک مشتری

Client 对象可以通过传递一个端点 URI 或端点 URI 向量来初始化。连接到同一集群中的多个 Dgraph 服务器可以实现更好的工作负载分布。

以下代码片段仅展示了使用单个端点的情况。

use dgraph_tonic::Client;

fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
}

或者您可以使用多个端点初始化新的客户端。不能使用空端点向量创建客户端。

use dgraph_tonic::Client;

fn main() {
  let client = Client::new(vec!["http://127.0.0.1:9080","http://127.0.0.1:19080"]).expect("Dgraph client");
}

客户端还可以使用自定义的 端点配置。

use dgraph_tonic::{Endpoint, EndpointConfig, Client};

use std::time::Duration;

#[derive(Debug, Default)]
struct EndpointWithTimeout {}

impl EndpointConfig for EndpointWithTimeout {
    fn configure_endpoint(&self, endpoint: Endpoint) -> Endpoint {
        endpoint.timeout(Duration::from_secs(5))
    }
}

fn main() {
  let endpoint_config = EndpointWithTimeout::default();
  let client = Client::new_with_endpoint_config("http://127.0.0.1:19080",endpoint_config).expect("Dgraph client");
}

ایجاد یک مشتری TLS

此外,tls 功能中提供了安全的 TLS 客户端。

[dependencies]
dgraph-tonic = { version = "0.11", features = ["tls"] }
use dgraph_tonic::TlsClient;

#[tokio::main]
async fn main() {
  let server_root_ca_cert = tokio::fs::read("path/to/ca.crt").await.expect("CA cert");
  let client_cert = tokio::fs::read("path/to/client.crt").await.expect("Client cert");
  let client_key = tokio::fs::read("path/to/ca.key").await.expect("Client key");
  let client = TlsClient::new(
    vec!["http://192.168.0.10:19080", "http://192.168.0.11:19080"],
    server_root_ca_cert,
    client_cert,
    client_key)
  .expect("Dgraph TLS client");
}

所有证书都必须是 PEM 格式。

多租户

多租户 环境中,dgraph-tonic 提供了一个新的方法 login_into_namespace(),这将允许用户登录到特定的命名空间。

为了创建一个客户端,并使其登录到命名空间 123

use dgraph_tonic::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
   let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
   let logged = client.login_into_namespace("groot", "password", 123).await.expect("Logged into namespace");
   Ok(())
}

在上面的示例中,客户端使用用户名 groot 和密码 password 登录到命名空间 123。登录后,客户端可以执行命名空间 123groot 用户允许的所有操作。

ایجاد یک مشتری Slash GraphQL

如果您的 Slash GraphQL 端点是 https://app.eu-central-1.aws.cloud.dgraph.io/graphql,则 gRPC 客户端的连接端点是 http://app.grpc.eu-central-1.aws.cloud.dgraph.io:443

客户端在 slash-ql 功能中可用

[dependencies]
dgraph-tonic = { version = "0.11", features = ["slash-ql"] }
use dgraph_tonic::TlsClient;

#[tokio::main]
async fn main() {
  let client = TlsClient::for_slash_ql(
    "http://app.grpc.eu-central-1.aws.cloud.dgraph.io:443",
    "API_KEY")
  .expect("Slash GraphQL client");
}

ایجاد یک مشتری Sync

此外,带有 sync 功能的同步客户端(Tls、Acl)可在 dgraph_tonic::sync 模块中使用

[dependencies]
dgraph-tonic = { version = "0.11", features = ["sync"] }
use dgraph_tonic::sync::{Mutate, Client};

fn main() {
  let p = Person {
    uid:  "_:alice".into(),
    name: "Alice".into(),
  };
  let mut mu = Mutation::new();
  mu.set_set_json(&p).expect("JSON");
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_mutated_txn();
  let response = txn.mutate(mu).expect("Mutated");
  txn.commit().expect("Transaction is commited");
}

所有同步客户端都支持与异步版本相同的功能。

تغییر دیتابیس

要设置模式,创建一个 Operation 实例并使用 Alter 端点。

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let op = Operation {
    schema: "name: string @index(exact) .".into(),
    ..Default::default()
  };
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let response = client.alter(op).await.expect("Schema set");
  //you can set schema directly with
  client.set_schema("name: string @index(exact) .").await.expect("Schema is not updated");
}

从 Dgraph 版本 20.03.0 开始,索引可以在后台计算。更多详情请参阅 此处

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let op = Operation {
    schema: "name: string @index(exact) .".into(),
    ..Default::default()
  };
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let response = client.alter(op).await.expect("Schema set");
  //you can set schema directly with
  client.set_schema_in_background("name: string @index(exact) .").await.expect("Schema is not updated");
}

Operation 还包含其他字段,包括 DropAttrDropAll。如果希望丢弃所有数据并从头开始,而不需要关闭实例,则 DropAll 很有用。用于删除与谓词相关的所有数据的 DropAttr

如果您想删除数据库中的所有数据,可以使用

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  client.drop_all().await.expect("Data not dropped");
}

ایجاد یک تراکنش

事务使用 Rust 中的 Typestate 模式 来建模。Typestate 模式是一种 API 设计模式,它将对象运行时状态的信息编码在其编译时类型中。此原则允许我们在编译时识别某些类型错误,例如在只读事务中进行修改。事务类型包括

  • 默认:可以转换为 ReadOnly、BestEffort、Mutated。可以执行 queryquery_with_vars 操作。
  • ReadOnly:由于它们可以绕过通常的共识协议,因此可以提高读取速度。可以执行 queryquery_with_vars 操作,这些操作在 Query 特性中定义。
  • BestEffort:只读查询可以可选地设置为尽力而为。使用此标志将要求 Dgraph Alpha 尽力从内存中获取时间戳,以减少对 Zero 的出站请求次数。这可能在不需要可线性化读取的读取密集型工作中提供改进的延迟。可以执行 queryquery_with_vars 操作。
  • 已更改:可以执行默认事务的所有操作并可以修改数据库中的数据。只能从默认事务创建。

客户端为事务提供了几个工厂方法。这些操作不会产生网络开销。

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let txn = client.new_txn();
  let read_only = client.new_read_only_txn();
  let best_effort = client.new_best_effort_txn();
  let mutated = client.new_mutated_txn();
}

对于已更改的事务,必须在丢弃事务变量之前始终调用 txn.dicard().await?txn.commit().await? 函数。

由于事务的内部状态取决于使用的变体(plain、tls、plain + acl、tls + acl x 只读、已更改),您应该在结构中为事务参数使用特质。

struct MyTxn<Q: Query, M: Mutate> {
    readonly_txn: Q,
    mutated_txn: M,
   
}

或作为函数的返回参数

fn my_query_operation() -> impl Query {
    //your code
}

fn my_mutation_operation() -> impl Mutate {
    //your code
}

如果您无法使用泛型或特质对象,则可以使用预定义的已导出事务类型:Txn、TxnReadOnly、TxnBestEffort、TxnMutated、TxnTls、TxnTlsReadOnly、TxnTlsBestEffort、TxnTlsMutated、TxnAcl、TxnAclReadOnly、TxnAclBestEffort、TxnAclMutated、TxnAclTls、TxnAclTlsReadOnly、TxnAclTlsBestEffort、TxnAclTlsMutated

اجرای یک تغییر

txn.mutate(mu).await? 运行一个变更。它接受一个 Mutation 对象。您可以使用 JSON 或 RDF N-Quad 格式设置数据。存在针对 JSON 格式的辅助函数(mu.set_set_json(), mu.set_delete_json())。所有变更操作都在 Mutate 特质中定义。

示例

use dgraph_tonic::{Mutate, Client};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Default, Debug)]
struct Person {
  uid: String,
  name: String,
}

#[tokio::main]
async fn main() {
  let p = Person {
    uid:  "_:alice".into(),
    name: "Alice".into(),
  };
  let mut mu = Mutation::new();
  mu.set_set_json(&p).expect("JSON");
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_mutated_txn();
  let response = txn.mutate(mu).await.expect("Mutated");
  txn.commit().await.expect("Transaction is commited");
}

注意:从 dgraph-1-0dgraph-1-1 的 API 破坏仅发生在函数 MutatedTxn.mutate() 中。在 dgraph-1-0 中,此函数返回一个 Assigned 类型,但在 dgraph-1-1 中返回一个 Response 类型。

有时,您只想提交一个变更,而不查询其他任何内容。在这种情况下,您可以使用 txn.mutate_and_commit_now(mu) 来指示必须立即提交变更。在这种情况下正在消耗事务对象。

dgraph-1-0 中,可以将 Mutation::with_ignored_index_conflict() 应用于 Mutation 对象,以不在索引上运行冲突检测,这会减少事务冲突和终止的数量。然而,这将以潜在的不一致更新操作为代价。此标志仅在 dgraph-1-0 中可用。

如何从响应中获取分配的 UIDs

您可以通过以下方式指定返回 uid 的自己的键:指定自己的返回 uid 键

use dgraph_tonic::{Mutate, Client};
use serde::{Serialize, Deserialize};
use serde_json::json;

#[tokio::main]
async fn main() {
  let p = json!({
    "uid": "_:diggy",
    "name": "diggy",
    "food": "pizza"
  });
  let mut mu = Mutation::new();
  mu.set_set_json(&p).expect("JSON");
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_mutated_txn();
  let response = txn.mutate(mu).await.expect("Mutated");
  txn.commit().await.expect("Transaction is commited");
  println!("{:?}", response.uids);
}

分配的 uids 返回在响应属性 uids 中。例如 printlns 某样东西

{"diggy": "0xc377"}

اجرای یک پرسش

您可以通过调用 txn.query(q) 来执行一个查询。您需要传递一个 GraphQL+- 查询字符串。如果您想传递一个额外的变量映射,以便在查询中设置,请调用 txn.query_with_vars(q, vars),并将变量映射作为第二个参数。所有查询操作都在 Query 特性中定义。

让我们用变量 $a 运行以下查询

query all($a: string) {
  all(func: eq(name, $a))
  {
    uid
    name
  }
}

Response 提供了一个函数 try_into(),它可以将返回的 JSON 转换为对应的实现了 serde Deserialize 特性的结构体对象。

use dgraph_tonic::{Client, Query};
use serde::Deserialize;

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

#[derive(Deserialize, Debug)]
struct Persons {
  all: Vec<Person>
}

#[tokio::main]
async fn main() {
  let q = r#"query all($a: string) {
    all(func: eq(name, $a)) {
      uid
      name
    }
  }"#;
  let mut vars = HashMap::new();
  vars.insert("$a", "Alice");
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_read_only_txn();
  let response = txn.query_with_vars(q, vars).await.expect("Response");
  let persons: Persons = resp.try_into().except("Persons");
  println!("Persons: {:?}", persons);
}

在运行模式查询时,模式响应位于 ResponseSchema 字段中。

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let q = r#"schema(pred: [name]) {
    type
    index
    reverse
    tokenizer
    list
    count
    upsert
    lang
  }"#;
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_read_only_txn();
  let response = txn.query(q).await.expect("Response");
  println!("{:#?}", response.schema);
}

Stream/Iterator

这些函数在 experimental 功能中可用。

有时您不能一次性从查询中获取所有所需数据,因为响应可能很大,这可能导致 gRPC 错误等。在这种情况下,您可以转换您的查询为流 Stream<Item = Result<T, Error>>(或在同步模式下的迭代器),这将按定义的容量以块的形式查询您的数据。在定义查询方面存在一些限制

  1. 您的查询必须接受 $first: string, $offset: string 输入参数。
  2. 流/迭代器的项必须以 items 名称返回块。

Stream/Iterator 在以下情况下结束

  • 查询返回没有项目时,
  • 查询返回错误,这是从流中返回的最后项目时,
  • 查询响应无法反序列化为 Vec<T>
use std::collections::HashMap;
use failure::Error;
use futures::pin_mut;
use futures::stream::StreamExt;
use dgraph_tonic::{Client, Response, Query};
use serde::Deserialize;

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

#[tokio::main]
async fn main() {
  let query = r#"query stream($first: string, $offset: string, $name: string) {
         items(func: eq(name, $name), first: $first, offset: $offset) {
             uid
             name
         }
     }"#;
  let mut vars = HashMap::new();
  vars.insert("$name", "Alice");
  let client = client().await;
  let stream = client.new_read_only_txn().into_stream_with_vars(query, vars, 100);
  pin_mut!(stream);
  let alices: Vec<Result<Person, Error>> = stream.collect().await;
}

如果您不想指定输入变量,您可以通过调用 client.new_read_only_txn().into_stream(query, 100) 来实现。

同步 只读事务可以通过调用 client.new_read_only_txn().into_iter(query, 100)client.new_read_only_txn().into_iter_with_vars(query, vars, 100) 转换为迭代器。

اجرای یک Upsert: Query + Mutation

dgraph-1-1 起可用。

txn.upsert(查询, 变更) 函数允许您运行由一个查询和一个或多个变更组成的 upsert 操作。查询变量可以使用 txn.upsert_with_vars(查询, 变量, 变更) 定义。如果您想提交 upsert 操作,可以使用 txn.upsert_with_vars_and_commit_now()。在这种情况下,Txn 对象将被消耗。

想了解更多关于 upsert 的信息,我们强烈建议您查阅 https://docs.dgraph.io/mutations/#upsert-block 的文档。

use dgraph_tonic::{Mutate, Client};

#[tokio::main]
async fn main() {
  let q = r#"
    query {
        user as var(func: eq(email, "[email protected]"))
    }"#;
  let mut mu = Mutation::new();
  mu.set_set_nquads(r#"uid(user) <email> "[email protected]" ."#);
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let txn = client.new_mutated_txn();
  // Upsert: If wrong_email found, update the existing data or else perform a new mutation.
  let response = txn.upsert(q, mu).await.expect("Upserted data");
  tnx.commit().await.expect("Committed");
}

您可以使用一个变更或变更向量来进行 upsert 操作。如果您想提交 upsert 操作,可以使用 txn.upsert_and_commit_now()。在这种情况下,Txn 对象将被消耗。

اجرای یک Upsert موقتانه

dgraph-1-1 起可用。

upsert 块允许使用 @if 指令指定条件变更块。只有当指定的条件为真时,才会执行变更。如果条件为假,则变更将被静默忽略。

更多关于条件 upsert 的信息请参阅 这里

use dgraph_tonic::{Client, Mutate};

#[tokio::main]
async fn main() {
  let q = r#"
    query {
        user as var(func: eq(email, "[email protected]"))
    }"#;

  let mut mu = Mutation::new();
  mu.set_set_nquads(r#"uid(user) <email> "[email protected]" ."#);
  mu.set_cond("@if(eq(len(user), 1))");

  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let txn = client.new_mutated_txn();
  let response = txn.upsert(q, vec![mu]).await.expect("Upserted data");
  tnx.commit().await.expect("Committed");
}

تراکنش را ارسال کنید

可以使用 txn.commit() 方法提交变更事务。如果您的交易仅由对 txn.querytxn.query_with_vars 的调用组成,而没有对 txn.mutate 的调用,则调用 txn.commit 是不必要的。

如果其他并发事务修改了此事务中修改的同一数据,将返回错误。当事务失败时,需要用户自行重试事务。

use dgraph_tonic::{Client, Mutate};

#[tokio::main]
async fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let mut txn = client.new_mutated_txn();
  // Perform some queries and mutations.

  //than commit
  let res = txn.commit().await;
  if res.is_err() {
    // Retry or handle error
  }
}

لیست‌های کنترل دسترسی

此企业级 Dgraph 功能可以通过

[dependencies]
dgraph-tonic = { version = "0.11", features = ["acl"] }

访问控制列表 (ACL) 启用,为存储在 Dgraph 中的数据提供访问保护。当 ACL 功能开启时,客户端必须使用用户名和密码进行身份验证,才能执行任何事务,并且只能访问 ACL 规则允许的数据。

两者,ClientTlsClient 都可以使用 login(user_id,password) 函数登录。原始客户端将被消耗,并返回 AclClient 的实例,该实例可以使用 refresh_login() 函数刷新令牌。

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let logged_in_client = client.login("groot", "password").await.expect("Logged in");
  // use client

  //than refresh token
  logged_in_client.refresh_login().await.except("Refreshed access token");
}

نسخه را بررسی کنید

use dgraph_tonic::Client;

#[tokio::main]
async fn main() {
  let client = Client::new("http://127.0.0.1:19080").expect("Dgraph client");
  let version = client.check_version().await.expect("Version");
  println!("{:#?}", version);
}

输出

Version {
    tag: "v20.03.0",
}

مثال‌ها

  • simple:使用 dgraph-tonic 的快速入门示例。
  • tls:使用 TLS 保护 Dgraph 集群的 dgraph-tonic 示例。

آزمون‌های یکپارچه

测试需要在运行在 localhost:19080 上的 Dgraph 上运行。为了方便起见,在根目录中准备了两个 docker-compose.yaml 文件,具体取决于您要测试的 Dgraph 版本。

docker-compose -f docker-compose-1-X.yaml up -d

由于我们正在与数据库一起工作,因此测试也需要在单个线程中运行,以防止中止。根据您使用的 Dgraph 版本使用功能标志。例如。

cargo test --no-default-features --features dgraph-1-1 -- --test-threads=1

پیشنهادات

欢迎贡献。请随意提出问题,包括功能请求、错误修复和改进。

在创建拉取请求之前,运行以下命令

rustup component add rustfmt
cargo fmt

发布清单

这些必须在Dgraph 1.0和Dgraph 1.1+上执行

  • 运行测试
  • 尝试示例

更新版本并发布crate

  • 更新Cargo.toml中的标签
  • 更新README.md中的标签
  • gittag v0.X.X
  • gitpush origin v0.X.X
  • 在GitHub上编写发布日志
  • cargopublish

ممنون از

eDocu

依赖项

~16–29MB
~511K SLoC