#surrealdb #id #surreal #db #surrealql

surrealdb_id

Id和关系型数据库为SurrealDB

3个版本

0.1.2 2024年3月18日
0.1.1 2024年3月17日
0.1.0 2024年3月17日

#448编码

Download history 129/week @ 2024-03-11 204/week @ 2024-03-18 3/week @ 2024-03-25 51/week @ 2024-04-01 2/week @ 2024-04-22 3/week @ 2024-05-20 3/week @ 2024-05-27 4/week @ 2024-06-03 150/week @ 2024-06-10 5/week @ 2024-06-24

每月下载 159次
2 crate 中使用

MIT 许可证

21KB
243

Surrealdb-id

描述

使用SurrealDB很棒,但与Rust一起使用可能会很困难,我在创建表示记录ID关联语句的结构时遇到了挑战。请看我的解决方案,我想与您分享,也许它也会帮到您。

减少编写SurrealQL的工作量

在深入探讨之前,请允许我介绍我简化编写SurrealQL过程的解决方案。此外,为了充分利用其潜力,我强烈推荐探索另一个名为surreal-derive-plus的库。

您可以在这里找到它: https://crates.io/crates/surreal-derive-plus

当它们结合在一起时的示例

let user: RecordId = RecordId::from(("user", "Devlog"));
let blogPost: RecordId = RecordId::from(("blogPost", "How to use surrealdb"));
let discussion = Discussion { content: "Hello I really want to know more".to_string(), created_at: Default::default() };
let relation = discussion.relate(user, blogPost)

assert_eq!(
    surreal_quote!("#relate(&relation)"),
    "RELATE user:Devlog -> discuss -> blogPost:⟨How to use surrealdb⟩ SET content = 'Hello I really want to know more', created_at = '1970-01-01T00:00:00Z'"
);

链接

链接可以是recordid

最简单的方式

let user_link: Link<User> = Link::<User>::from(("user", "devlog"));

从实现From<T> for RecordId类型的任何类型

struct UserId {
    display_name: String
}

impl From<UserId> for RecordId {
    fn from(value: UserId) -> Self {
        RecordId::from(("user", value.display_name.as_str()))
    }
}

let user_link: Link<User> = Link::<User>::from(UserId {display_name: "Devlog"});

这种方法与之前提到的使用From相比,提供了更高的类型安全性,因为它在使用链接时限制了用户的具体参数。

impl NewLink<User, String> for Link<User> {
    fn new(params: String) -> Link<User> {
        Link::<User>::from(UserId { display_name: params })
    }
}

// Or with multiple params
//impl NewLink<User, (String, DateTime)> for Link<User> {
//  TOOD: Impl here
//}

let user_link = Link::<User>::new(("Devlog"));
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
    name: String
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Friend {
    me: Link<User>,
    my_friend: Link<User>
}

// Every record must be impl Into<RecordId>
impl Into<RecordId> for Friend {
    fn into(self) -> RecordId {
        RecordId::from(("friend", self.me.name.as_str()))
    }
}

// If the id needed to be type safe, create another struct
pub struct UserId {
    pub display_name: String
}

// Implement From<UserId> to RecordId
// so that we can use it with Link::<User>::from(UserId)
impl From<UserId> for RecordId {
    fn from(value: UserId) -> Self {
        RecordId::from(("user", value.display_name.as_str()))
    }
}

// Or with more constraint
// by forcing Link::User::new() can only be trigger with UserId
impl NewLink<User, UserId> for Link<User> {
    fn new(params: UserId) -> Link<User> {
        Link::from(("user", params.display_name.as_str()))
    }
}

#[tokio::test]
pub async fn test() -> surrealdb::Result<()> {
    let db = Surreal::new::<Mem>(()).await?;
    db.use_ns("test").use_db("test").await?;
    let user = User { name: "Devlog".to_string() };
    db.create(Resource::RecordId(user.clone().into())).content(&user).await?;
    let friend = Friend {
        me: Link::<User>::new(UserId { display_name: "Devlog".to_string() }), // `::new` will have more constrained to make sure only UserId can be passed
        my_friend: Link::<User>::from(UserId { display_name: "TienDang".to_string() }) // while `::from` will work with any E where RecordId: From<E>
    };

    let friend: Option<Friend> = db.query(
        "SELECT * FROM (CREATE friend:Devlog set me=user:Devlog, my_friend=user:TienDang) FETCH me"
    ).await?.take(0)?;

    let friend = friend.unwrap();
    // Right here, the my_friend is only id type
    // and me is object type
    assert_eq!(friend.my_friend.id().id.to_string(), "TienDang".to_owned());
    assert_eq!(friend.me.name, "Devlog".to_owned());
    Ok(())
}

关系

创建关系

使用IdRelation

它将使用RecordId作为inout的默认类型

let relation: IdRelation<Discussion> = db.query("RELATE user:Devlog -> discuss -> blog:⟨How to use surrealdb⟩").await?.take(0)?;

使用LinkRelation

该关系将inout包裹在一个Link

let relation: LinkRelation<User, Discussion, Blog> = db.query("SELECT * FROM RELATE user:Devlog -> discuss -> blog:⟨How to use surrealdb⟩ FETCH in, out").await?.take(0)?;
relation.r#in.id().id // => Devlog
relation.r#in.id().tb // => user
relation.out.id().id // ⟨How to use surrealdb⟩
relation.out.id().tb // => blog

关系使用

例如,您正在编写博客文章的上下文中,您想要为您的博客创建一个特性讨论

use chrono::{DateTime, Utc};
use serde_derive::{Deserialize, Serialize};
use surrealdb::engine::local::Mem;
use surrealdb::opt::{RecordId, Resource};
use surrealdb::sql::Id;
use surrealdb::Surreal;
use crate::link::Link;
use crate::relation::{IdRelation, LinkRelation};

// Entity 1
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
    name: String
}

// Entity 2
#[derive(Debug, Clone, Serialize, Deserialize)]
struct BlogPost {
    title: String
}

// Relation
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Discussion {
    content: String,
    created_at: DateTime<Utc>
}

impl Into<RecordId> for User {
    fn into(self) -> RecordId {
        ("user", self.name.as_str()).into()
    }
}

impl Into<RecordId> for Discussion {
    fn into(self) -> RecordId {
        ("discussion", Id::Number(self.created_at.timestamp_millis())).into()
    }
}

impl Into<RecordId> for BlogPost {
    fn into(self) -> RecordId {
        ("blog", self.title.as_str()).into()
    }
}

#[tokio::test]
pub async fn test() -> surrealdb::Result<()> {
    let db = Surreal::new::<Mem>(()).await?;
    db.use_ns("test").use_db("test").await?;

    let user = Link::Record(User { name: "Devlog".to_string() });
    let blogPost = Link::Record(BlogPost { title: "How to use surrealdb".to_string() });
    db.create(Resource::RecordId(user.id())).content(&user).await?;
    db.create(Resource::RecordId(blogPost.id())).content(&blogPost).await?;

    let relation: Option<LinkRelation<User, Discussion, BlogPost>> = db.query(
        "SELECT * FROM RELATE user:Devlog->discuss->blog:⟨How to use surrealdb⟩ SET content='Hello I really want to know more', created_at='2020-01-01T00:00:00Z'"
    ).await?.take(0)?;
    let relation = relation.unwrap();

    assert_eq!(relation.r#in.as_ref().unwrap().id().id.to_string(), "Devlog".to_owned());
    assert_eq!(relation.r#in.as_ref().unwrap().id().tb.as_str(), "user");
    assert_eq!(relation.out.as_ref().unwrap().id().id.to_string(), "⟨How to use surrealdb⟩".to_owned());
    assert_eq!(reation.out.as_ref().unwrap().id().tb.as_str(), "blog");

    let relation: Option<LinkRelation<User, Discussion, BlogPost>> = db.query(
        "SELECT * FROM (RELATE user:Devlog->discuss->blog:⟨How to use surrealdb⟩ SET content='Hello I really want to know more', created_at='2020-01-01T00:00:00Z') FETCH in, out"
    ).await?.take(0)?;

    let relation = relation.unwrap();
    assert_eq!(relation.r#in.as_ref().unwrap().name.to_string(), "Devlog".to_owned());
    assert_eq!(relation.out.as_ref().unwrap().title.to_string(), "How to use surrealdb".to_owned());
    Ok(())
}

依赖关系

~37–52MB
~870K ~870K SLoC