#json #query #in-memory #nosql #unit-testing #json-object

memquery

MemQuery 是一个简单的库,用于创建、查询和更新以 JSON 对象表示的内存文档,并使用类似 MongoDB 的操作进行查询

2 个版本

0.1.1 2021 年 5 月 1 日
0.1.0 2021 年 4 月 27 日

#2137数据库接口

MIT 许可证

115KB
2K SLoC

Rust 1K SLoC // 0.0% comments Python 686 SLoC // 0.1% comments Go 396 SLoC // 0.0% comments

MemQuery

memquery License

MemQuery 是一个简单的库,用于创建、查询和更新以 JSON 对象表示的内存文档,并使用类似 MongoDB 的操作进行查询。

这不是一个数据库,也不是试图进行任何优化。它是为了单元测试或需要小型内存文档存储的简单项目而设计的。

该库使用支持 tokio 的异步 API。该库还有一个同步 API,可以通过 sync 功能标志启用。

示例用法

创建数据库

use memquery::{doc, errors::Error, memdb::MemDb, query};
let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;

创建集合

memdb.create_collection("TestCollection").await;

获取集合句柄

let coll = memdb.collection("TestCollection").await?;

插入文档

coll.insert(doc!({ "name": "Tom", "age": 25 })).await?;

查找文档

let docs = coll.find(query!({"name": "Tom", "age": 25})).await?;

assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["name"], "Tom");

逻辑查询操作符

$and

let docs = coll
    .find(query!({ "$and": [{ "name": "Bob" }, { "age": 20 }] }))
    .await?;

$or

let docs = coll
    .find(query!({ "$or": [{ "name": "Bob" }, { "age": 30 }] }))
    .await?;

比较查询操作符

$eq

按字段比较

let docs = coll.find(query!({ "qty": { "$eq": 20 } })).await?;

在嵌入式文档中比较

let docs = coll.find(query!({ "item.name": { "$eq": "ab" } })).await?;

您还可以比较数组与嵌入式数组

coll
    .insert(doc!({ "item": { "name": "ab", "code": "123" }, "qty": 15, "tags": [ "A", "B", "C" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "cd", "code": "123" }, "qty": 20, "tags": [ "B" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "ij", "code": "456" }, "qty": 25, "tags": [ "A", "B" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "xy", "code": "456" }, "qty": 30, "tags": [ "B", "A" ] }))
    .await?;
  coll
    .insert(
      doc!({ "item": { "name": "mn", "code": "000" }, "qty": 20, "tags": [ [ "A", "B" ], "C" ] }),
    )
    .await?;

  let docs = coll
    .find(query!({ "tags": { "$eq": [ "A", "B" ] } }))
    .await?;

  assert_eq!(docs.len(), 2);
  assert_eq!(docs[0]["item"]["name"], "ij");
  assert_eq!(docs[1]["item"]["name"], "mn");

或嵌入式数组中的值

coll
    .insert(doc!({ "item": { "name": "ab", "code": "123" }, "qty": 15, "tags": [ "A", "B", "C" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "cd", "code": "123" }, "qty": 20, "tags": [ "B" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "ij", "code": "456" }, "qty": 25, "tags": [ "A", "B" ] }))
    .await?;
  coll
    .insert(doc!({ "item": { "name": "xy", "code": "456" }, "qty": 30, "tags": [ "B", "A" ] }))
    .await?;
  coll
    .insert(
      doc!({ "item": { "name": "mn", "code": "000" }, "qty": 20, "tags": [ [ "A", "B" ], "C" ] }),
    )
    .await?;

  let docs = coll.find(query!({ "tags": { "$eq": "B" } })).await?;

  assert_eq!(docs.len(), 4);
  assert_eq!(docs[0]["item"]["name"], "ab");
  assert_eq!(docs[1]["item"]["name"], "cd");
  assert_eq!(docs[2]["item"]["name"], "ij");
  assert_eq!(docs[3]["item"]["name"], "xy");

$gt

let docs = coll.find(query!({ "qty": { "$gt": 20 } })).await?;

$gte

let docs = coll.find(query!({ "qty": { "$gte": 20 } })).await?;

$lt

let docs = coll.find(query!({ "qty": { "$lt": 20 } })).await?;

$lte

let docs = coll.find(query!({ "qty": { "$lte": 20 } })).await?;

查找所有文档

let docs = coll.find(query!({})).await?;

更新文档

这显示了如何使用 find_and_update API 的示例。

通过替换整个文档来更新文档

let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;
let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs_updated = coll
  .find_and_update(
    query!({"name": "Bob"}),
    update!({"nickname": "Bobcat", "voice": "meow"}),
  )
  .await?;

assert_eq!(docs_updated, 1);

let docs = coll.find(query!({"nickname": "Bobcat"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["voice"], "meow");

更新文档中的特定字段

let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs_updated = coll
  .find_and_update(
    query!({"name": "Bob"}),
    update!({"$set": { "name": "Roy", "age": 21, "email": "[email protected]"}}),
  )
  .await?;

assert_eq!(docs_updated, 1);

let docs = coll.find(query!({"name": "Roy"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["age"], 21);
assert_eq!(docs[0]["email"], "[email protected]");

更新文档以删除字段

let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;
let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs_updated = coll
  .find_and_update(
    query!({ "name": "Bob" }),
    update!({ "$set": { "name": "Roy", "age": 21, "email": "[email protected]" }}),
  )
  .await?;

assert_eq!(docs_updated, 1);

let docs = coll.find(query!({"name": "Roy"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["age"], 21);
assert_eq!(docs[0]["email"], "[email protected]");

let docs_updated2 = coll
  .find_and_update(
    query!({ "name": "Roy" }),
    update!({ "$unset": { "email": "" }}),
  )
  .await?;

assert_eq!(docs_updated2, 1);

let docs = coll.find(query!({"name": "Roy"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["age"], 21);
assert_eq!(docs[0]["email"], serde_json::Value::Null);

增加文档中字段的值

let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;
let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs_updated = coll
  .find_and_update(query!({"name": "Bob"}), update!({"$inc": { "age": -5 }}))
  .await?;

assert_eq!(docs_updated, 1);

let docs = coll.find(query!({"name": "Bob"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["age"], 15.0);

乘以文档中字段的值

let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;
let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs_updated = coll
  .find_and_update(query!({"name": "Bob"}), update!({"$mul": { "age": 5}}))
  .await?;

assert_eq!(docs_updated, 1);

let docs = coll.find(query!({"name": "Bob"})).await?;
assert_eq!(docs.len(), 1);
assert_eq!(docs[0]["age"], 100.0);

删除文档

let memdb = MemDb::new();
memdb.create_collection("TestCollection").await;
let coll = memdb.collection("TestCollection").await?;
coll.insert(doc!({ "name": "Rob", "age": 25 })).await?;
coll.insert(doc!({ "name": "Bob", "age": 20 })).await?;
coll.insert(doc!({ "name": "Tom", "age": 30 })).await?;

let docs = coll.find_and_delete(query!({})).await?;
assert_eq!(docs.len(), 3);

let docs_remaining = coll.find(query!({})).await?;
assert_eq!(docs_remaining.len(), 0);

同步 API

要使用同步 API,您需要通过 sync 功能标志启用它。

use memquery::{doc, errors::Error, query, sync_memdb::MemDb};
let memdb = MemDb::new();
  memdb.create_collection("TestCollection");
  let coll = memdb.collection("TestCollection")?;
  coll.insert(
    doc!({ "item": { "name": "ab", "code": 123 }, "qty": 15, "tags": [ "A", "B", "C" ] }),
  )?;
  coll.insert(doc!({ "item": { "name": "cd", "code": 123 }, "qty": 20, "tags": [ "B" ] }))?;
  coll.insert(doc!({ "item": { "name": "ij", "code": 456 }, "qty": 25, "tags": [ "A", "B" ] }))?;
  coll.insert(doc!({ "item": { "name": "xy", "code": 456 }, "qty": 30, "tags": [ "B", "A" ] }))?;
  coll.insert(
    doc!({ "item": { "name": "mn", "code": 000 }, "qty": 20, "tags": [ [ "A", "B" ], "C" ] }),
  )?;

  let docs = coll.find(query!({ "item.code": { "$lte": 123 } }))?;

  assert_eq!(docs.len(), 3);
  assert_eq!(docs[0]["item"]["name"], "ab");
  assert_eq!(docs[1]["item"]["name"], "cd");
  assert_eq!(docs[2]["item"]["name"], "mn");

构建和运行测试

构建 Rust 库

要构建库

  • cargo build

测试 Rust 库

要测试异步 API

  • cargo t

要测试同步 API

  • cargo t --features "sync"

构建 WASM (wsmemquery.wasm)

添加 wasm32-unknown-unknown 目标

  • rustup target add wasm32-unknown-unknown

将 memquery 构建为 WebAssembly

  • cd wasm
  • cargo build --target wasm32-unknown-unknown

依赖项

~3–11MB
~98K SLoC