8 个版本
0.1.9 | 2024 年 5 月 15 日 |
---|---|
0.1.8 | 2024 年 5 月 7 日 |
0.1.6 | 2024 年 4 月 28 日 |
#400 在 解析实现
275KB
7K SLoC
大普特
大普特正在积极开发中,任何生产使用都可能遇到破坏性更改。如果您有任何建议或问题,请打开问题或 PR。
大普特(数据包)是一个 Rust 对象,允许使用 serde
包进行动态数据的序列化和反序列化。大普特允许您使用类似 jq
的语法遍历数据结构。该项目的目的是作为一个流处理引擎的数据包,该引擎处理非结构化数据,尽管它足够通用,可用于许多其他用途。
示例
use dapt::Dapt;
fn main() {
let data = r#"
{
"name": "John Doe",
"age": 30,
"phones": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
]
}
// use serde_json to parse the data into a Dapt object
let d: Dapt = serde_json::from_str(data).unwrap();
// get just the phones numbers
let name = d.get("phones[].number").unwrap();
// write the data back out
println!("{}", serde_json::to_string_pretty(&name).unwrap();
}
// output will be:
// [
// "212 555-1234",
// "646 555-4567"
// ]
使用 cargo run --example simple
运行它
匹配策略
大普特由两个关键部分组成
- 一个字节数组,用于以二进制格式存储数据
- 一个索引向量,指向该二进制数据中的位置
这意味着大普特数据包可以同时指向多个大普特数据包中的位置。作为用户,您可以使用 get
方法遍历大普特数据包。此方法遍历数据包,返回一个新的数据包,具有新的索引,这些索引与您提供的路径匹配。在此过程中不会复制底层二进制数据,从而减少堆分配。
大普特路径由用 .
分隔的节点组成。路径的每个部分都会检查文档以找到所有匹配的值。让我们通过上一个示例来更好地了解大普特的工作方式。我们的路径是 phones[].number
。首先,大普特使用节点 phone
沿着文档向下遍历
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
]
接下来,大普特使用 []
遍历数组。数组节点允许您指定索引,但由于我们没有指定,大普特数据包现在指向数组中的两个索引。
{
"type": "home",
"number": "212 555-1234"
}
{
"type": "office",
"number": "646 555-4567"
}
现在当使用 number
节点时,它将返回两个对象的 number
字段。这是因为我们指向的每个位置都会使用相同的节点进行遍历。
"212 555-1234"
"646 555-4567"
当我们让serde进行序列化时,dapt意识到它指向多个位置,并将序列化为它的指针数组。这些值不必是相同类型,请查看mixed
示例,了解dapt如何处理这种情况。
可用路径节点
- 字段字面量:例如
host.name
这是最基本的节点,它匹配与确切名称相匹配的字段。 - 数组:例如
hosts[0].name
匹配数组中的所有索引。索引是可选的,如果提供,则只匹配具有该索引的项,否则匹配所有项 - 数组通配符:例如
*.name
匹配映射的所有子节点。只深达一层 - 递归:例如
~.name
递归向下搜索匹配的节点。任何节点都可以跟随递归节点,唯一的要求是该节点有一些子节点 - 正则表达式:例如
/^host.*/.name
匹配所有匹配正则表达式的字段。正则表达式是Rust正则表达式,并与字段名称进行匹配 - 第一个:例如
host.{name,ip.*}
匹配返回值的第一个节点。匹配的每个子节点都是完整路径。如果子节点可能匹配多个值,它仍只匹配第一个值。 - 多个:例如
host.(name|ip.*)
匹配所有指定的路径
查询功能
dapt具有在query
模块中定义的查询功能。您可以使用Select
结构来聚合数据,使用Filter
结构来过滤数据,或者使用Query
过滤器来利用类似SQL的查询实现。
查询
我本可以首先向您介绍过滤器和聚合,但让我们展示一下我们能够做什么。假设您有类似以下结构的数据进入
{
"tickets_purchased": "3",
"state": "NY",
"name": "John Doe",
"purchase_date": "2021-01-01",
}
然后您可以编写如下查询
SELECT
sum("tickets_purchased") as "total.tickets",
count() as "total.purchases",
"name"
WHERE
"state" IN ['NY', 'CA']
HAVING "total_tickets" > 20
GROUP BY "name"
ORDER BY "total_tickets" DESC
TOP 3
此查询将返回类似以下内容
[
{
"total": {
"tickets": 30,
"purchases": 10
},
"name": "John Doe"
},
{
"total": {
"tickets": 25,
"purchases": 5
},
"name": "Jane Doe"
},
{
"total": {
"tickets": 20,
"purchases": 5
},
"name": "John Smith"
}
]
创建查询和收集数据接口非常简单
use dapt::query::Query;
use dapt::Dapt;
fn main() {
let q = Query::new("SELECT sum(\"tickets_purchased\") as \"total.tickets\", count() as \"total.purchases\", \"name\" WHERE \"state\" IN ['NY', 'CA'] HAVING \"total.tickets\" > 20 GROUP BY \"name\" ORDER BY \"total.tickets\" DESC TOP 3");
// load up some data
let data: Vec<Dapt> = vec![
serde_json::from_str(r#"{"tickets_purchased": "3", "state": "NY", "name": "John Doe", "purchase_date": "2021-01-01"}"#).unwrap(),
serde_json::from_str(r#"{"tickets_purchased": "30", "state": "NY", "name": "John Doe", "purchase_date": "2021-01-01"}"#).unwrap(),
serde_json::from_str(r#"{"tickets_purchased": "25", "state": "NY", "name": "Jane Doe", "purchase_date": "2021-01-01"}"#).unwrap(),
serde_json::from_str(r#"{"tickets_purchased": "20", "state": "NY", "name": "John Smith", "purchase_date": "2021-01-01"}"#).unwrap(),
];
for d in data {
q.process(d);
}
let results = q.results();
}
依赖关系
~5-9MB
~160K SLoC