16个版本

0.2.6 2023年6月11日
0.2.5 2023年6月6日
0.2.4 2023年5月29日
0.1.9 2023年5月21日

#371 in 解析器实现


用于 flinch

Apache-2.0

195KB
4.5K SLoC

FLQL - Flinch查询语言

Rust

FLQL - Flinch查询语言是一种轻量级的查询语言,用于从称为Flinch的内存数据库中检索数据。Flinch是一种实时非结构化文档数据库,能够以JSON格式存储和检索文档。

FLQL是为了简化Flinch中的查询过程而创建的,使开发者更容易从数据库中检索数据。它是一种简单、直观、易于学习的语言。

功能

  • FLQL支持基本的CRUD(创建、读取、更新、删除)操作。
  • 它支持基于多种标准查询文档,包括文本匹配、日期范围和数值范围。
  • 它使用简单直观的语法,易于阅读和编写。
  • FLQL针对性能进行了优化,可以轻松处理大型数据集。

入门

要使用FLQL,您需要在系统上安装并运行Flinch。一旦Flinch启动并运行,您就可以开始使用FLQL查询您的数据。


//Create collection
new({}); 

// TTL
ttl(60).if('').into('');

//Drop collection
drop(''); 

//Check if pointer exists in collection
exists('').into(''); 

//Length of collection
length(''); 

//Update or Insert into collection
put({}).into(''); 

//Conditional Update or Insert into collection
put({}).when('gjson_expression').into(''); 

//Update or Insert into collection to a Pointer
put({}).pointer('').into(''); 

//Get from collection
get.from(''); 

//Conditional Get from collection
get.when('gjson_expression').from(''); 

//Get Pointer from collection
get.pointer('').from(''); 

//Get View from collection
get.view('').from(''); 

//Get Clip from collection
get.clip('').from(''); 

//Delete from collection
delete.from(''); 

//Conditional Delete from collection
delete.when('gjson_expression').from(''); 

//Delete Pointer from collection
delete.pointer('').from(''); 

//Delete View from collection
delete.view('').from(''); 

//Delete Clip from collection
delete.clip('').from(''); 



示例

        use flql::{parse, Flql};
        let commands = vec![
            "new({});",
            "drop('');",
            "exists('').into('');",
            "length('');",
            "put({}).into('');",
            "put({}).when('gjson_expression').into('');",
            "put({}).pointer('').into('');",
            "get.from('');",
            "get.when('gjson_expression').from('');",
            "get.pointer('').from('');",
            "get.view('').from('');",
            "get.clip('').from('');",
            "delete.from('');",
            "delete.when('gjson_expression').from('');",
            "delete.pointer('').from('');",
            "delete.clip('').from('');"
        ];
        for command in commands {
            let chk = parse(command);
            assert!(chk.is_ok(),"{:?}",chk.err());
            if chk.is_ok() {
                let parsed = chk.unwrap();
                match parsed {
                    Flql::New(_) => {}
                    Flql::Drop(_) => {}
                    Flql::Exists(_,_) => {}
                    Flql::Length(_) => {}
                    Flql::Flush(_) => {}
                    Flql::Put(_, _) => {}
                    Flql::PutWhen(_, _, _) => {}
                    Flql::PutPointer(_, _, _) => {}
                    Flql::Search(_,_) => {}
                    Flql::Get(_) => {}
                    Flql::GetWhen(_, _) => {}
                    Flql::GetPointer(_, _) => {}
                    Flql::GetView(_, _) => {}
                    Flql::GetClip(_, _) => {}
                    Flql::GetIndex(_,_) => {}
                    Flql::GetRange(_,_,_,_) => {}
                    Flql::Delete(_) => {}
                    Flql::DeleteWhen(_, _) => {}
                    Flql::DeletePointer(_, _) => {}
                    Flql::DeleteClip(_, _) => {}
                    Flql::None => {}
                }
            }
        }

when函数中,您可以使用来自(https://github.com/tidwall/gjson.rs)的任何表达式来操作数据。

文档来自gjson仓库:本文件旨在通过示例说明GJSON路径的结构。

最终的实现是 github.com/tidwall/gjson
使用GJSON Playground在线尝试语法。

路径结构

一个GJSON路径被设计成一系列通过点字符.分隔的组件。

除了点字符外,还有一些具有特殊意义的字符,包括|#@\*!?

示例

给定以下JSON

{
  "name": {"first": "Tom", "last": "Anderson"},
  "age":37,
  "children": ["Sara","Alex","Jack"],
  "fav.movie": "Deer Hunter",
  "friends": [
    {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
    {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
    {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  ]
}

以下GJSON路径计算出的值如下。

基本

在许多情况下,您只需通过对象名称或数组索引来检索值。

name.last              "Anderson"
name.first             "Tom"
age                    37
children               ["Sara","Alex","Jack"]
children.0             "Sara"
children.1             "Alex"
friends.1              {"first": "Roger", "last": "Craig", "age": 68}
friends.1.first        "Roger"

通配符

键可能包含特殊通配符字符 *?* 将匹配任意零个或多个字符,而 ? 则匹配任意单个字符。

child*.2               "Jack"
c?ildren.0             "Sara"

转义字符

特殊目的字符,如 .*? 可以使用 \ 转义。

fav\.movie             "Deer Hunter"

数组

# 字符允许深入到 JSON 数组。

要获取数组的长度,只需单独使用 # 即可。

friends.#              3
friends.#.age         [44,68,47]

查询

您也可以使用 #(...) 来查询数组中的第一个匹配项,或者使用 #(...)# 来查找所有匹配项。查询支持 ==!=<<=>>= 比较运算符,以及简单的模式匹配运算符 %(像)和 !%(不像是)。

friends.#(last=="Murphy").first     "Dale"
friends.#(last=="Murphy")#.first    ["Dale","Jane"]
friends.#(age>45)#.last             ["Craig","Murphy"]
friends.#(first%"D*").last          "Murphy"
friends.#(first!%"D*").last         "Craig"

要查询数组中的非对象值,可以省略操作符右侧的字符串。

children.#(!%"*a*")                 "Alex"
children.#(%"*a*")#                 ["Sara","Jack"]

允许嵌套查询。

friends.#(nets.#(=="fb"))#.first  >> ["Dale","Roger"]

请注意,在 v1.3.0 之前,查询使用了 #[...] 方括号。这在 v1.3.0 中发生了变化,以避免与新的 多路径 语法混淆。为了向后兼容,#[...] 将继续在下一个主要版本中工作。

~(波浪号)运算符将在比较之前将值转换为布尔值。

例如,使用以下 JSON

{
  "vals": [
    { "a": 1, "b": true },
    { "a": 2, "b": true },
    { "a": 3, "b": false },
    { "a": 4, "b": "0" },
    { "a": 5, "b": 0 },
    { "a": 6, "b": "1" },
    { "a": 7, "b": 1 },
    { "a": 8, "b": "true" },
    { "a": 9, "b": false },
    { "a": 10, "b": null },
    { "a": 11 }
  ]
}

您现在可以查询所有真(或似真)或假(或似假)的值

vals.#(b==~true)#.a    >> [1,2,6,7,8]
vals.#(b==~false)#.a   >> [3,4,5,9,10,11]

最后一个不存在的值被视为 false

点与管道

. 是标准分隔符,但也可以使用 |。在大多数情况下,它们都返回相同的结果。当 |. 不同时,是在 # 后用于 数组查询 的情况下。

这里有一些例子

friends.0.first                     "Dale"
friends|0.first                     "Dale"
friends.0|first                     "Dale"
friends|0|first                     "Dale"
friends|#                           3
friends.#                           3
friends.#(last="Murphy")#           [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
friends.#(last="Murphy")#.first     ["Dale","Jane"]
friends.#(last="Murphy")#|first     <non-existent>
friends.#(last="Murphy")#.0         []
friends.#(last="Murphy")#|0         {"first": "Dale", "last": "Murphy", "age": 44}
friends.#(last="Murphy")#.#         []
friends.#(last="Murphy")#|#         2

让我们分析其中的一些。

路径 friends.#(last="Murphy")# 独自使用的结果是

[{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]

.first 后缀将在返回结果之前处理每个数组元素上的 first 路径。这变成

["Dale","Jane"]

|first 后缀实际上是在之前的结果之后处理 first 路径。由于前一个结果是一个数组而不是一个对象,因此无法处理,因为 first 不存在。

然而,|0 后缀返回

{"first": "Dale", "last": "Murphy", "age": 44}

因为 0 是先前结果的第一索引。

修饰符

修饰符是在 JSON 上执行自定义处理的路径组件。

例如,在上述 JSON 负载中使用内置的 @reverse 修饰符将反转 children 数组

children.@reverse                   ["Jack","Alex","Sara"]
children.@reverse.0                 "Jack"

目前有以下内置修饰符

  • @reverse:反转数组或对象的成员。
  • @ugly:从 JSON 中删除所有空白。
  • @pretty:使 JSON 更易于阅读。
  • @this:返回当前元素。它可以用来检索根元素。
  • @valid:确保 JSON 文档有效。
  • @flatten:扁平化数组。
  • @join:将多个对象连接成一个对象。
  • @keys:返回对象的键数组。
  • @values:返回对象的值数组。
  • @tostr:将 JSON 转换为字符串。包装 JSON 字符串。
  • @fromstr:将字符串转换为 JSON。解包 JSON 字符串。
  • @group:分组对象数组。参见 e4fc67c

修饰符参数

修饰符可以接受一个可选参数。该参数可以是有效的 JSON 负载或只是字符。

例如,@pretty 修饰符接受一个 JSON 对象作为其参数。

@pretty:{"sortKeys":true}

这使得 JSON 更易于阅读并排序所有键。

{
  "age":37,
  "children": ["Sara","Alex","Jack"],
  "fav.movie": "Deer Hunter",
  "friends": [
    {"age": 44, "first": "Dale", "last": "Murphy"},
    {"age": 68, "first": "Roger", "last": "Craig"},
    {"age": 47, "first": "Jane", "last": "Murphy"}
  ],
  "name": {"first": "Tom", "last": "Anderson"}
}

@pretty 的完整选项列表为 sortKeysindentprefixwidth。请参阅 格式化选项 获取更多信息。

多路径

从 v1.3.0 版本开始,GJSON 添加了将多个路径合并在一起以形成新文档的功能。用方括号 [...] 或花括号 {...} 括起来的逗号分隔的路径将分别生成一个新数组或对象。

例如,使用给定的多路径

{name.first,age,"the_murphys":friends.#(last="Murphy")#.first}

这里我们选择了名字、年龄以及姓氏为 "Murphy" 的朋友的名字。

您会注意到可以提供一个可选键,在这种情况下是 "the_murphys",以强制将键分配给值。否则,将使用实际字段的名称,在这种情况下是 "first"。如果无法确定名称,则使用 "_"。

这导致

{"first":"Tom","age":37,"the_murphys":["Dale","Jane"]}

字面量

从 v1.12.0 版本开始,GJSON 添加了对 JSON 文字的支持,这提供了一种构建静态 JSON 块的方法。这当使用 多路径 构建新的 JSON 文档时特别有用。

JSON 文字以感叹号声明字符开头。

例如,使用给定的多路径

{name.first,age,"company":!"Happysoft","employed":!true}

这里我们选择了名字和年龄。然后添加了两个新字段,"company" 和 "employed"。

这导致

{"first":"Tom","age":37,"company":"Happysoft","employed":true}

致谢

没有以下内容,这个库将无法实现

KSQL https://crates.io/crates/ksql

gjson.rs https://github.com/tidwall/gjson.rs

使用库中代码的目的(而不是作为库使用)是添加额外功能并移除不必要的功能。

依赖项

~6MB
~110K SLoC