17 个稳定版本 (3 个主要版本)

3.5.0 2024年2月14日
3.4.0 2023年9月17日
3.3.0 2023年5月16日
3.2.0 2023年2月22日
0.3.0 2019年9月8日

#104数据库接口

Download history 2/week @ 2024-03-09 102/week @ 2024-03-30 21/week @ 2024-04-06

每月91 次下载

Apache-2.0

105KB
2K SLoC

mokuroku

概述

这个 Rust 包旨在在 RocksDB 键值存储之上提供二级索引,类似于 PouchDBLevelDB 所做的工作。您的应用程序将提供 Document 特质的实现,以适应数据库中存储的各种数据类型,而这个库将调用这些 Document 实例的映射函数以生成索引键值对。

该库的行为与 PouchDB 类似,但 API 更适合该语言。与 PouchDB 不同,该库不对数据库记录的格式施加任何约束。因此,库依赖于应用程序提供反序列化记录和调用带有二级键和可选值的 emit() 函数的功能。为了避免不必要的反序列化,每当应用程序在 Database 实例上调用 put() 函数时,库将使用每个定义的索引名称调用 Document.map()

分类

该库确实做什么:由该库管理的索引是“独立的”,这意味着它们不是嵌入在数据库文件中(例如区域图或布隆过滤器)。此外,索引以懒惰的方式更新,这意味着更改是追加的,而不是在更新时积极合并。这个描述很好地概括了整体性能。

在这里,二级索引中的每个条目都是一个由(二级键+主键)组成的复合键。二级查找是对二级键的前缀搜索,可以使用索引表的常规范围搜索来实现。在这里,写入和压缩比“懒惰”更快,但二级属性查找可能较慢,因为它需要在索引表上执行范围扫描。

与简单的复合索引相比,这个库允许应用程序在辅助键中发出一个值,而不是通常在辅助索引中为null分配的值。

欢迎贡献

尽管作者阅读了一些相关的研究论文,但他在数据库技术方面并不算专家。同样,他的Rust技能可能也并不那么令人印象深刻。如果您想贡献,请尽请随意。提前感谢。

构建和测试

先决条件

  • Rust稳定版(2018版)

构建和测试

以下命令将构建库并运行测试。

$ cargo clean
$ cargo build
$ cargo test

Apple M1 支持

rust-rocksdb 的 0.16 版本开始,此库支持从 macOS 平台上的 2.5.0 版本开始使用 ARM64 目标构建。

示例

请参阅 examples/tagged.rs 中的完整示例,它创建了一个RocksDB数据库,添加了一些记录,建立了一个辅助索引,并使用特定的键值查询该索引。

cargo run --example tagged

演示数字索引的示例,examples/numdex.rs,利用了base32hex的位排序顺序保留功能,这也使得数字键对于辅助索引的复合键格式来说是“安全”的。在 numdex 索引中的键是UTC毫秒数,查询是在特定日期范围内更新的资产。索引键和查询键都必须进行编码,并且将数字按大端顺序排列会有所帮助。

cargo run --example numdex

快速示例

此代码片段是从上述示例中提取的。它展示了打开数据库、添加记录和查询索引的最基本用法。生成索引键和值的函数示例在 examples/tagged.rs 示例代码中。

let db_path = "my_database";
let views = vec!["tags".to_owned()];
let dbase = Database::open_default(Path::new(db_path), views, Box::new(mapper)).unwrap();
let documents = [
    Asset {
        key: String::from("asset/blackcat"),
        location: String::from("hawaii"),
        tags: vec![
            String::from("cat"),
            String::from("black"),
            String::from("tail"),
        ],
    },
    // ...
];
for document in documents.iter() {
    let key = document.key.as_bytes();
    let _ = dbase.put(&key, document);
}

// querying the "tags" index for keyword "cat"
let result = dbase.query_by_key("tags", b"cat");
let iter = result.unwrap();
let results: Vec<QueryResult> = iter.collect();
for result in results {
    let doc_id = str::from_utf8(&result.doc_id).unwrap().to_owned();
    println!("query result key: {:}", doc_id);
}

功能

可选功能

Mokuroku 支持几个可选功能,以减轻与其他流行crate使用此crate的负担。

  • anyhow:启用自动将 anyhow::Error 转换为 mokuroku::Error
  • serde_cbor:启用自动将 serde_cbor::Error 转换为 mokuroku::Error

性能功能

  • multi-threaded-cf:传递给 rocksdb,以允许从多个线程并发创建和删除列族。

设计

术语

关于本项目使用的术语的简要说明。您可能会在这里和那里看到“视图”一词。这是 CouchDB 和 PouchDB 在其文档中称为索引的术语。鉴于这个crate试图以类似的方式运行,使用相同的术语似乎是自然而然的。函数名 emit 也来自 CouchDB 的“map/reduce” API,与其他任何术语一样合理。

用法

应用程序将使用 mokuroku::Database 结构体代替 rocksb::DB,因为这个 crate 将创建和管理那个 DB 实例。由于这个 crate 管理次要索引,应用程序调用 put() 函数在 Database 上,而不是直接调用 DB。对于那些不会影响任何索引的操作,应用程序可以自由地使用 Database.db() 函数来获取对 DB 的直接引用。

在启动时,应用程序将创建一个 Database 实例并提供三个参数。

  1. 数据库文件的路径,就像使用 DB::open()
  2. 要传递给 Document.map() 的索引名称集合
  3. 一个类型为 mokuroku::ByteMapper 的封装函数

索引名称集合是库每次调用 Database.put() 时将更新的索引。也就是说,传递给 put() 调用的 Document 实现将使用提供的每个索引名称调用其 map()。并非每个 Document 都会为每个索引发出索引键/值对。实际上,没有任何要求发出任何内容,这完全取决于应用程序。同样,单个映射调用可能会发出多个值,这在 tagged 示例中有演示。

当库需要重建索引时需要 ByteMapper。由于库将读取默认列族中的每个记录,它不知道如何将记录反序列化到适当的 Document 实现中。因此,应用程序必须提供此函数来识别和反序列化记录,然后发出索引键/值对。

在设置数据库之后,应用程序可能希望对每个命名索引调用数据库上的 query()。这将导致库在缺失时构建索引,这将提高后续对 query() 的调用响应时间。

数据模型

应用程序定义数据库记录格式;这个库不对键或值的格式施加任何限制。这也是为什么与 PouchDB 等相比,使用稍微复杂的原因之一。

该库在单独的列族中维护二级索引,列族名称以mrview-开头,以避免与应用程序可能创建的任何列族发生冲突。例如,如果应用程序创建一个名为tags的索引,那么该库将创建一个名为mrview-tags的列族,并用传递给Document.map()和应用程序定义的ByteMapper实现的Emitter提供的值填充它。

应用程序输出的索引键不需要是唯一的。库将附加数据记录主键以确保没有索引条目会覆盖另一个(两个键由一个空字节分隔;如果您需要更改此设置,请使用Database.separator()函数)。应用程序可以为索引条目发出一个可选值,其格式完全由应用程序决定。

参考文献

在设计该库时,以下论文按照发表顺序被引用,其中第二篇是最相关的。

依赖关系

~27MB
~541K SLoC