近来在个人的rust项目中使用neo4j的graphql作为持久层(工作中的项目用的是graphql ogm,公司小,人手不足,使用rust这类编译缓慢的语言就是自己找班加),起初使用起来还算勉强能接受,但当需要通过关系查询多种节点时,graphql client生成的类型就忽然膨胀了,此时即使使用了graphql client生成的类型亦需要编写十分复杂的query builder(需要对大部分类型实现default,而且query_str需要动态修改返回值,此时用graphql client就基本没有什么意义了,cynic虽然可以用,但该库的序列化derive宏在命名方面存在些许问题,转换命名不够自由,换言之就是和neo4j那边生成的命名风格不符,也没有像graphql client一样的rename选项,加上其官方文档常年不更新,Fragment trait实际使用和文档中所说的参数并不一致,属实令人难以接受),在使用了一段时间的json作为参数(直接使用json宏写json)之后,实在受不了了(可读性差,也完全没有对json进行校验的余地),而此时在下偶然发现了arangodb的一个ogm(/odm)——aragog,于是决定尝试一下arangodb这个数据库,关于这个事情可以查看在下的GitHub
arangodb这个数据库算是一种文档数据库和图数据库的结合,用文档数据库的方式存储节点和关系,但支持绝大部分的图操作(如果仅仅只是试图在该数据库中进行类似关系型数据库的联表查询操作倒是未必需要使用到图),该数据库并未使用sql(关系型数据库使用的查询语言),cypher(图数据库的通用查询语言),亦或者是bson(mongodb的查询语言),而是有一门自己设计的查询语言AQL(ArangoDB query language),这里我们暂且按下不表(第一章暂时不会涉及查询语句的使用)
该数据库的安装并不复杂,加上该数据库本身是开源的,有兴趣的甚至可以直接从源码进行编译(本系列教程主要是图数据库相关,故而这里就不去尝试从源码编译了)

虽然有很高的js含量,但核心部分是由c++实现的,编译的话不出意料应当是CMake常见的那一套mkdir build\ cd build\ cmake .. -{编译器相关配置},或者直接vs打开点击生成即可(windows限定)
安装完成以后,即可直接在8529端口打开该数据库的管理员客户端(安装过程基本全是点击下一步,然后也就设置个管理员账户密码)

使用填写的管理员密码登录

先登录_system创建业务数据库(_system数据库不要拿来用作业务数据库)

创建一个只有访问业务数据库权限的账户(这里我以及创建好了)

创建一个业务数据库(记得username选择刚才创建的用户)

查看一下权限表
以上的操作对于大部分数据库来说都大同小异,想必对于各位来说完全不成问题
dashboard自不必多言,不过是显示一些与数据库performance相关的东西,这些当且仅当项目颇具规模的时候才有详细了解的必要,目前项目尚未投入使用,观察性能监控并无太多意义(如果该系列反响好的话会把这个项目的开发都记录成专栏的形式,并专门为项目做一下性能测试,届时将会对这些数据进行解释和分析,本章节不过是个引子)

由于我们是本地安装,故而该功能无法使用,使用该数据库的云服务将可以正常使用该服务
其他功能除了管理数据库和用户的功能外,均为所有用户的通用功能,这两项功能上文已然有所讲解(该数据库这方面做的非常简洁,并无很多可以讲解的地方),那么我们现在退出管理员账户,用刚才创建的用户登录我们的业务数据库来对剩下的功能进行基本的讲解

如无意外,普通用户是无法访问_system数据库的(除非你在权限表对该用户追加了权限)

普通用户的功能略微少一些
这里在下已经创建了部分collections(AragogConfiguration是aragog的迁移工具创建的,如果各位有使用过类似的工具应当是无需在下多言),具体的可以看这个项目:https://github.com/pathologyenigma/Card-Server-ArangoDB-based-/tree/main(master分支是前言提到的另一个项目)
analyzers是用于搜索的分词功能(这也是我选择该数据库的其中一个原因),后续会具体讲解,
views是视图功能,与关系数据库的视图作用一致,这里就不多赘述了
queries即为查询编辑器,与很多数据库不同的是该数据库提供了一个解释功能

一个简单的查询,想必各位应该是能看懂的吧(该数据库使用==来表示相等)

数据库会将解析以及查询的过程像这样解释出来,如果查询中涉及了拥有index的字段,index也会在解释范围内
graphs,即图功能,该功能与neo4j这样的纯图数据库不同,需要你指定节点以及关系对应的collection,并限定关系可以由哪个类型的节点指向哪个类型的节点(相对neo4j多了不少束缚),同时可以指定顶点collection(该功能在下尚不是很清楚使用场景和具体效果,后续可能会专门出专栏针对该数据库的图功能进行讲解)
services,该功能类似于插件功能,目前可以安装的功能并不多,且本地安装的用户未必能成功安装这些服务,这里就不过多的了解了
logs,即日志功能,并没有什么特别之处
support,并非是使用者对于该项目的支持,而是类似于技术支持这样的东西,即这个支持是开发团队对使用者的支持,另外这里有一个比较有用的功能——rest api,即使你没有任何database driver,你也可以通过这里提供的rest api来直接使用该数据库(你甚至无需了解AQL)
那么接下来,我们来了解一下aragog这个库,该库提供了一个cli用于跑迁移和一些其他操作(但该cli不知为何不支持读取.env),具体可以查看官方文档:https://aragog.rs/book/
该工具默认会在src/db/config目录下生成yaml文件用于管理迁移文件和schema(schema based or entity/record based是一个老生常谈的问题,个人倾向schema based),以下是user collection的迁移文件(项目仓库隐藏了迁移文件,只保留了schema)
up:
- create_collection:
name: User
- create_index:
name: unique_username
collection: User
fields: ["username"]
settings:
type: persistent
unique: true
sparse: false
deduplicate: false
- create_index:
name: unique_email
collection: User
fields:
- email
settings:
type: persistent
unique: true
sparse: false
deduplicate: false
down:
- delete_collection:
name: User 迁移文件无法包含字段定义,但可以定义index,具体的可以查看官方的迁移演示例子:https://gitlab.com/qonfucius/aragog/-/blob/master/aragog_cli/example/example_migration.yaml
字段的定义则由aragog代为管理(注意,arangodb是文本型数据库,数据库本身是不限制字段的)
use aragog::Record;
use serde::{Serialize, Deserialize};
#[derive(Clone, Serialize, Deserialize, Record, Debug)]
pub struct User {
pub username: String,
pub password: String,
pub email: String,
pub registered_at: u64,
} 以下是一个简单的查询User的service,服务于登录功能(Error类型为自定义的错误类型),实现的较为简单(性能并不好),想必各位都能看得懂
pub async fn get_user_by_username_or_email(
account: &String,
db: &DatabaseConnection,
) -> Result<(String, User), Error> {
let res = User::query()
.filter(
Filter::new(Comparison::field("username").equals_str(&account))
.or(Comparison::field("email").equals_str(&account)),
)
.call::<DatabaseConnection, User>(db)
.await
.map_err(|err| match err {
aragog::Error::ArangoError(err) => match err.arango_error {
aragog::error::ArangoError::ArangoDataSourceNotFound => {
return Error::Internal("Table Not exist".to_owned());
}
_ => return Error::Internal(format!("{}", err)),
},
_ => {
return Error::Internal(format!("{}", err));
}
})?;
let mut users = res
.0
.into_iter()
.map(|data| (data.key().clone(), data.record))
.take(1)
.collect::<Vec<(String, User)>>();
if users.len() < 1 {
return Err(Error::NotFound("account".to_owned(), account.clone()));
}
Ok(users.pop().unwrap())
} 上文中的query builder相当于生成了这样的AQL({account}指代传入的account参数的值,并非实际查询时传入的占位符):
for user in User
filter user.username == "{account}" or user.email == "{account}"
return user 以上是arangodb的粗略介绍,以及在下自己项目中的一些使用
arangodb的官方文档相当完善,开源社区也相对活跃,使用体验也是要优于单纯的文本数据库或者图数据库的(主要是AQL的语法相对SQL和cypher来说可读性高了不少)
下一章,我们会开始使用该数据库的图功能