JavaScript SDK
每个编程语言版本的 SDK 都基于对应语言的语言特性进行实现,JavaScript 版本的 SDK 同样如此,完全基于 ES6 标准编写,充分利用 JavaScript 灵活的函数式编程能力。因此 UrnaDB JavaScript SDK 在 API 设计上采用 Lambda 函数式风格,将函数作为一等公民,使 API 更加简洁、灵活,并能够充分发挥 JavaScript 的语言特性,为熟悉 JavaScript 和函数式编程的开发者提供自然、友好的编程体验。
🚀 快速开始
Tip
在 JavaScript 工程项目中使用 UrnaDB 数据库,只需引入 UrnaDB 提供的 SDK 依赖 npm 包即可开始使用,通常情况下 JavaScript 项目基于 Node.js 和 npm 构建,并采用标准的 npm 工程结构。
在你的计算机中首先配置好 Node.js 运行环境,再在需要使用到 UrnaDB 的项目下执行以下命令,即可安装 UrnaDB SDK依赖包到当前项目中:
npm install auula/urnadb-js-sdk成功安装 urnadb-js-sdk 依赖包后,开发者只需从 urnadb-js-sdk 模块中导入 UrnaDB 对象,即可创建与远程 UrnaDB 服务器的连接,并通过 SDK 对数据库进行操作,示例代码如下:
// 从 SDK 模块中导入 UrnaDB 对象
import UrnaDB from "urnadb-js-sdk";
// 通过 UrnaDB 静态方法创建 Server 连接对象
const db = UrnaDB.OpenConnection({
host: "192.168.3.20",
port: 2668,
token: "connection-secret-token",
});获取 UrnaDB 数据库连接实例对象后,开发者可以通过 db 连接对象访问 UrnaDB 数据库中不同命名空间所提供的数据模型,并通过对应的 API 对数据库进行操作。下面以查询当前 UrnaDB 数据库运行时状态数据为例,代码示例如下:
// 打开连接远程数据库服务器
const db = UrnaDB.OpenConnection({
host: "192.168.3.20",
port: 2668,
token: "connection-secret-token",
});
// 获取当前的数据库服务器运行状态
const info = await db.serverInfo();
// 远程数据库服务器指标
console.log(`键数量: ${info.keyCount}`);
console.log(`GC 状态: ${info.gcState} (0=初始, 1=执行中, 2=空闲)`);
console.log(`磁盘剩余: ${info.diskFreeSpace}`);
console.log(`磁盘已用: ${info.diskUsedSpace}`);
console.log(`磁盘总量: ${info.diskTotalSpace}`);
console.log(`磁盘使用率: ${info.diskUsagePercent}`);
console.log(`内存可用: ${info.memoryFree}`);
console.log(`内存总量: ${info.memoryTotal}`);
console.log(`数据库存储总量: ${info.totalSpaceUsed}`);开发者在客户端可以通过以上的 API 示例,来获取服务器端数据库的指标数据。
📈 Table 命名空间
Tip
Table 命名空间用于存储结构化数据,其数据组织方式与传统关系型数据库的 Schema 模型最为接近。为了降低开发者的学习和迁移成本,Table 命名空间的 SDK API 采用了与传统关系型数据库 ORM 框架相近的设计,使开发者能够以熟悉的方式完成数据的创建、查询、更新和删除操作。
下面是一个创建一个名为 users 的 Table 命名空间 API 操作示例:
test("should create table", async () => {
// 创建一个名为 users 的命名空间
const result = await db.createTable("users");
// 如果创建成功 result.status 的值为 success
assert.equal(result.status, "success");
});向已有的名为 users 的 Table 命名空间中插入数值 API 操作示例:
test("should insert row data into table", async () => {
// 类似于 SQL 关系数据库中的 INSERT INTO 语句操作
const result = await db.tables("users").put(rows => {
rows
.set("name", "Leon Ding")
.set("age", 26)
.set("address", address => {
address
.put("🌍 nation", "China")
.put("🌆 city", "Shanghai")
.put("📧 zipcode", 2000001);
})
.set("hobbies", ["🏸️ badminton", "🎮 games", "🎵 music"]);
});
assert.equal(result.status, "success");
});根据条件来实现数据记录更新,类似于关系数据库 SQL 的 UPDATE 语句,更新 API 操作示例如下:
test("should update table row data", async () => {
// 更新 name 值为 Leon Ding 的 age 和 address 字段
const result = await db.tables("users").patch(patch => {
patch
.where(where => {
where.eq("name", "Leon Ding");
})
.sets(sets => {
sets
.put("age", 27)
.put("address", address => {
address
.put("🌆 city", "Shanghai")
})
});
});
assert.equal(result.status, "success");
});根据条件删除数据记录类似于 SQL 关系数据库中的 DELETE 语句,删除 API 操作示例如下:
test("should delete table row data", async () => {
// 删除 users 表中字段 name 值为 Leon Ding 的数据记录
const result = await db.tables("users").delete(where => {
where.eq("name", "Leon Ding");
});
assert.equal(result.status, "success");
});根据条件查询数据记录似于 SQL 关系数据库中的 SELECT 语句,查询 API 操作示例如下:
test("should query table row data", async () => {
// 查询 users 表中的 name 字段为 Leon Ding 的数据记录
const result = await db.tables("users").query(where => {
where.eq("name", "Leon Ding");
});
assert.equal(result.status, "success");
assert.equal(result.data[0].address["🌆 city"], "Shanghai");
});Important
在 UrnaDB 的 Table 命名空间提供了类似于关系型数据库 SQL 事务的操作机制,支持在同一个事务中对多个不同 Table 命名空间中的数据记录进行操作,并通过统一的事务提交确保这些操作作为一个整体完成。
下面是 UrnaDB 中的数据库事物处理 API 代码调用示例,该示例会对 users 表进行数据记录插入操作和对插入操作之后的 address 字段进行原子更新操作,代码示例如下:
test("should build a transaction with multiple table", async () => {
const db = UrnaDB.OpenConnection({
host: "192.168.3.20",
port: 2668,
token: "connection-secret-token"
});
// UrnaDB ES6 SDK Transaction Example:
const result = await db.tables().transaction(txns => {
txns
// Enable serializable isolation level for this transaction
.serializable(true)
.put("users", rows => {
rows
.set("name", "Leon Ding")
.set("age", 26)
.set("address", address => {
address
.put("🌍 nation", "China")
.put("🌆 city", "Shanghai")
.put("📧 zipcode", 200001);
})
.set("hobbies", ["🏸️ badminton", "🎮 games", "🎵 music"]);
})
.patch("users", patch => {
patch
.where(where => {
where.eq("name", "Leon Ding");
})
.sets(sets => {
sets
.put("address", address => {
address
.put("🌆 city", "Singapore");
})
});
});
});
assert.equal(result.status, "success");
});这段 UrnaDB SDK 代码在关系型数据库 SQL 中对应的是事务中的 INSERT 和 UPDATE 组合语句,并使用了可串行化隔离级别,该 SDK 代码对应的 SQL 语句示例如下:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO users (name, age, address, hobbies)
VALUES (
'Leon Ding',
26,
'{"🌍 nation": "China", "🌆 city": "Shanghai", "📧 zipcode": 200001}',
'["🏸️ badminton", "🎮 games", "🎵 music"]'
);
UPDATE users
SET address = jsonb_set(
address,
'{🌆 city}',
'"Singapore"'
)
WHERE name = 'Leon Ding';
COMMIT;📂 Document 命名空间
Tip
Document 命名空间的结构类似 MongoDB 中的 Document 结构,其 Document 通常直接映射编程语言中的 class 的一条记录,OOP 面向对象编程中的对象可以直接映射为 Document 记录,类似于传统关系 SQL 数据库中的一组行记录的抽象 Schema 模型。
下面的示例演示如何创建一个名为 user.profile 的 Document 对象,并通过 db.save() 方法将其保存到数据库中,示例代码如下:
test("should save a single document", async () => {
// 创建一条名为 user.profile 的 document 类型的记录
const profile = Document.from("user.profile", {
name: "Leon Ding",
age: 26,
email: "ding_ms@outlook.com"
});
// 通过 save 方法保存 document 记录
const results = await db.save(profile);
});Important
需要注意在整个 UrnaDB SDK 中 db.save() 方法,统一用于将 Document 和 Variant 对象持久化存储到数据库中,不适用于其他的数据类型。
下面的示例演示如何使用 db.save() 方法一次性保存多个 Document 对象,其 db.save() 方法是支持可变参数调用,因此可以在同一次调用中传入多个待保存的对象,并通过返回结果检查各对象的保存状态,代码示例如下:
test("should save multiple documents", async () => {
const user_config = Document.from("user.config", {
theme: "dark",
language: "zh-CN"
});
const user_settings = Document.from("user.settings", {
notifications: true,
autoSave: false
});
// 一次性保存多个 Document 对象
const results = await db.save(user_config, user_settings);
results.forEach(result => {
assert.equal(result.status, "success");
});
});Important
除了用于持久化存储的 db.save() 方法外,Document 对象还提供了一系列内置方法,例如 has()、merge() 和 search() ,帮助开发者便捷地操作和处理 Document 数据,开发者可以根据具体的业务需求,灵活组合这些 API 实现更复杂的业务逻辑。
下面的示例演示如何使用 Document 对象的 merge() 方法合并数据,该方法将传入对象中的字段合并到当前 Document 对象中:如果字段已存在,则更新其值,如果字段不存在,则添加该字段。最后通过 build() 方法获取合并后的数据,并验证各字段的值,代码示例如下:
test("should merge data", () => {
// 创建包含初始字段的 Document 对象
const document = Document.from("user", {
name: "Leon Ding",
age: 25
});
// 合并新数据:覆盖已有的 age 字段,并添加 email 字段
document.merge({
age: 26,
email: "ding_ms@outlook.com"
});
// 获取合并后的数据并验证字段值
const built = document.build();
assert.equal(built.name, "Leon Ding");
assert.equal(built.age, 26);
assert.equal(built.email, "ding_ms@outlook.com");
});下面的示例演示如何使用 Document 对象的 search() 方法查询字段值,该方法支持通过字段名称访问对应的值,也支持使用点分隔路径查询嵌套对象中的字段,当指定字段不存在时方法会返回 undefined 值,代码示例如下:
test("should search field value", () => {
const document = Document.from("userinfo", {
name: "Leon Ding",
age: 26,
address: {
city: "Shanghai",
zipcode: 200001
}
});
// 搜索源 Document 对象某个字段的值
assert.equal(document.search("name"), "Leon Ding");
assert.equal(document.search("age"), 26);
assert.equal(document.search("address.city"), "Shanghai");
assert.equal(document.search("address.zipcode"), 200001);
assert.equal(document.search("nonexistent"), undefined);
});下面的示例演示如何使用 Document 对象的 has() 方法检查指定字段是否存在。该方法支持通过字段名称检查普通字段,也支持使用点分隔路径检查嵌套对象中的字段,当指定字段存在时返回 true 值,否则返回 false 值,代码示例如下:
test("should check if field exists", () => {
const document = Document.from("userinfo", {
name: "Leon Ding",
age: 26,
address: {
city: "Shanghai",
zipcode: 200001
}
});
// 查看某个字段查询是否存在
assert.equal(document.has("name"), true);
assert.equal(document.has("age"), true);
assert.equal(document.has("address.city"), true);
assert.equal(document.has("address.zipcode"), true);
assert.equal(document.has("address.country"), false);
assert.equal(document.has("phone"), false);
});