671 lines
39 KiB
HTML
671 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>API 文档 — MetonaSqlark v0.1.14</title>
|
||
<style>
|
||
:root {
|
||
--bg:#0a0a0f; --surface:#14141f; --surface2:#1a1a2e; --border:#2a2a45;
|
||
--primary:#6366f1; --primary-glow:#818cf8; --accent:#06b6d4; --accent2:#ec4899;
|
||
--text:#e2e8f0; --text2:#94a3b8; --gradient:linear-gradient(135deg,#6366f1 0%,#06b6d4 50%,#ec4899 100%);
|
||
--radius:14px;
|
||
}
|
||
* { margin:0; padding:0; box-sizing:border-box; }
|
||
body { font-family:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.7; }
|
||
header {
|
||
position:fixed; top:0; left:0; right:0; z-index:100; backdrop-filter:blur(20px);
|
||
background:rgba(10,10,15,0.85); border-bottom:1px solid var(--border); padding:16px 32px;
|
||
}
|
||
header .inner { max-width:1400px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; }
|
||
.logo { font-size:1.3rem; font-weight:800; display:flex; align-items:center; gap:10px; }
|
||
.logo .icon { width:32px; height:32px; border-radius:8px; background:var(--gradient); display:flex; align-items:center; justify-content:center; font-size:1rem; font-weight:900; color:#fff; }
|
||
.logo span { background:var(--gradient); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
|
||
nav { display:flex; gap:28px; }
|
||
nav a { color:var(--text2); text-decoration:none; font-weight:500; font-size:0.92rem; transition:.2s; }
|
||
nav a:hover,.nav-active { color:var(--text); }
|
||
.layout { display:flex; max-width:1400px; margin:0 auto; padding-top:80px; min-height:100vh; }
|
||
.sidebar {
|
||
width:260px; min-width:260px; padding:32px 24px; position:sticky; top:80px; height:calc(100vh - 80px);
|
||
overflow-y:auto; border-right:1px solid var(--border); background:var(--bg);
|
||
}
|
||
.sidebar h4 { font-size:0.75rem; text-transform:uppercase; letter-spacing:1px; color:var(--text2); margin:20px 0 8px; }
|
||
.sidebar h4:first-child { margin-top:0; }
|
||
.sidebar a { display:block; padding:7px 12px; border-radius:6px; color:var(--text2); text-decoration:none; font-size:0.9rem; transition:.15s; }
|
||
.sidebar a:hover,.sidebar a.active { color:var(--text); background:var(--surface2); }
|
||
.sidebar a.active { border-left:3px solid var(--primary); }
|
||
.content { flex:1; padding:32px 48px 80px; max-width:950px; }
|
||
.content h2 { font-size:1.8rem; font-weight:800; margin:48px 0 16px; padding-top:24px; border-top:1px solid var(--border); }
|
||
.content h2:first-of-type { margin-top:0; padding-top:0; border-top:none; }
|
||
.content h3 { font-size:1.25rem; font-weight:700; margin:32px 0 12px; color:var(--primary-glow); }
|
||
.content p { color:var(--text2); margin-bottom:16px; }
|
||
.content code {
|
||
background:var(--surface2); padding:2px 8px; border-radius:5px; font-family:'JetBrains Mono',monospace;
|
||
font-size:0.88rem; color:var(--accent);
|
||
}
|
||
.content pre {
|
||
background:var(--surface2); border:1px solid var(--border); border-radius:var(--radius);
|
||
padding:20px 24px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:0.85rem;
|
||
line-height:1.65; margin:16px 0; color:#e2e8f0;
|
||
}
|
||
.content pre .k { color:#c084fc; } .content pre .s { color:#34d399; }
|
||
.content pre .f { color:#60a5fa; } .content pre .c { color:#64748b; }
|
||
.content pre .n { color:#fbbf24; } .content pre .t { color:#f472b6; }
|
||
.content table { width:100%; border-collapse:collapse; margin:16px 0; }
|
||
.content th,.content td { text-align:left; padding:12px 16px; border-bottom:1px solid var(--border); font-size:0.9rem; }
|
||
.content th { color:var(--text); font-weight:600; background:var(--surface); }
|
||
.content td { color:var(--text2); }
|
||
.content td code { font-size:0.82rem; }
|
||
::-webkit-scrollbar { width:6px; } ::-webkit-scrollbar-track { background:transparent; } ::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="inner">
|
||
<div class="logo"><div class="icon">◈</div><span>MetonaSqlark</span></div>
|
||
<nav>
|
||
<a href="index.html">首页</a>
|
||
<a href="docs.html" class="nav-active">文档</a>
|
||
<a href="demo.html">演示</a>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="layout">
|
||
<aside class="sidebar">
|
||
<h4>快速开始</h4>
|
||
<a href="#install" class="active">安装</a>
|
||
<a href="#create">创建数据库</a>
|
||
<a href="#define-table">定义表</a>
|
||
<h4>查询 API</h4>
|
||
<a href="#sql-query">SQL 查询</a>
|
||
<a href="#query-builder">Query Builder</a>
|
||
<a href="#join">JOIN 查询</a>
|
||
<a href="#groupby">GROUP BY & 聚合</a>
|
||
<a href="#where-ops">WHERE 操作符</a>
|
||
<h4>高级特性</h4>
|
||
<a href="#transaction">事务 & 回滚</a>
|
||
<a href="#subquery">子查询</a>
|
||
<a href="#foreign-key">外键级联</a>
|
||
<a href="#connection-pool">连接池</a>
|
||
<a href="#migration">数据迁移</a>
|
||
<a href="#export">导入导出</a>
|
||
<a href="#plugin">插件 & 钩子</a>
|
||
<a href="#subscribe">发布订阅</a>
|
||
<h4>框架集成</h4>
|
||
<a href="#react">React 集成</a>
|
||
<a href="#vue">Vue 集成</a>
|
||
<h4>类型参考</h4>
|
||
<a href="#types">TypeScript 类型</a>
|
||
<a href="#config">完整配置项</a>
|
||
<a href="#engine">存储引擎</a>
|
||
<a href="#errors">错误处理</a>
|
||
</aside>
|
||
|
||
<main class="content">
|
||
|
||
<h2 id="install">📦 安装</h2>
|
||
<p>MetonaSqlark 支持多种引入方式,覆盖 npm、CDN、ESM、CJS 所有常见场景。</p>
|
||
|
||
<h3>npm 安装(推荐)</h3>
|
||
<pre><span class="c"># 配置 Gitea Registry(一次性)</span>
|
||
npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/
|
||
|
||
<span class="c"># 安装</span>
|
||
npm install @metona-team/metona-sqlark</pre>
|
||
|
||
<h3>CDN / UMD</h3>
|
||
<pre><span class="c"><!-- UMD 格式,暴露 window.MetonaSqlark 和 window.MeSqlark --></span>
|
||
<script src=<span class="s">"https://git.metona.cn/.../metona-sqlark.min.js"</span>></script>
|
||
<script>
|
||
<span class="k">const</span> db = <span class="k">await</span> window.<span class="f">MetonaSqlark</span>.create({...});
|
||
<span class="c">// 或 window.MeSqlark.create(...) — 完全等价</span>
|
||
</script></pre>
|
||
|
||
<h3>ESM</h3>
|
||
<pre><span class="k">import</span> { MetonaSqlark, MeSqlark } <span class="k">from</span> <span class="s">'@metona-team/metona-sqlark'</span>;
|
||
<span class="c">// MeSqlark 是 MetonaSqlark 的别名,行为完全一致</span></pre>
|
||
|
||
<h3>CJS</h3>
|
||
<pre><span class="k">const</span> { MetonaSqlark } = <span class="f">require</span>(<span class="s">'@metona-team/metona-sqlark'</span>);</pre>
|
||
|
||
<p>输出文件说明:</p>
|
||
<table>
|
||
<tr><th>文件</th><th>格式</th><th>用途</th></tr>
|
||
<tr><td><code>metona-sqlark.js</code></td><td>UMD</td><td>浏览器开发版(含 sourcemap)</td></tr>
|
||
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(~42KB / ~10KB gzip)</td></tr>
|
||
<tr><td><code>metona-sqlark.esm.js</code></td><td>ES Module</td><td>现代打包工具 / 浏览器 ESM</td></tr>
|
||
<tr><td><code>metona-sqlark.cjs.js</code></td><td>CommonJS</td><td>Node.js require()</td></tr>
|
||
<tr><td><code>metona-sqlark.d.ts</code></td><td>TypeScript 声明</td><td>类型提示</td></tr>
|
||
</table>
|
||
|
||
<h2 id="create">🏗 创建数据库</h2>
|
||
<p><code>MetonaSqlark.create(config)</code> — 工厂函数,自动创建并初始化数据库实例。</p>
|
||
|
||
<pre><span class="k">const</span> db = <span class="k">await</span> <span class="f">MetonaSqlark.create</span>({
|
||
<span class="s">name</span>: <span class="s">'my-app'</span>,
|
||
<span class="s">mode</span>: <span class="s">'hybrid'</span>, <span class="c">// 'memory' | 'disk' | 'hybrid'</span>
|
||
<span class="s">diskEngine</span>: <span class="s">'indexeddb'</span>, <span class="c">// 'indexeddb' | 'opfs'(仅 disk/hybrid 生效)</span>
|
||
<span class="s">version</span>: <span class="n">1</span>,
|
||
<span class="s">plugins</span>: [], <span class="c">// MetonaPlugin[]</span>
|
||
<span class="s">onReady</span>: (db) => {}, <span class="c">// 就绪回调</span>
|
||
<span class="s">onError</span>: (err) => {}, <span class="c">// 错误回调</span>
|
||
});
|
||
|
||
<span class="c">// 也可手动实例化</span>
|
||
<span class="k">const</span> db2 = <span class="k">new</span> <span class="f">MetonaSqlark</span>(config);
|
||
<span class="k">await</span> db2.<span class="f">init</span>();
|
||
|
||
<span class="c">// 检查状态</span>
|
||
db.<span class="f">isReady</span>(); <span class="c">// true</span>
|
||
|
||
<span class="c">// 关闭</span>
|
||
<span class="k">await</span> db.<span class="f">close</span>();</pre>
|
||
|
||
<h2 id="define-table">📋 定义表</h2>
|
||
<p>使用 <code>db.defineTable(name, columns)</code> 定义表结构。</p>
|
||
|
||
<pre><span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'users'</span>, {
|
||
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
|
||
<span class="s">name</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">required</span>: <span class="k">true</span> },
|
||
<span class="s">email</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">unique</span>: <span class="k">true</span>, <span class="s">index</span>: <span class="k">true</span> },
|
||
<span class="s">age</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">default</span>: <span class="n">0</span>, <span class="s">min</span>: <span class="n">0</span>, <span class="s">max</span>: <span class="n">150</span> },
|
||
<span class="s">active</span>: { <span class="s">type</span>: <span class="s">'boolean'</span>, <span class="s">default</span>: <span class="k">true</span> },
|
||
<span class="s">birthday</span>: { <span class="s">type</span>: <span class="s">'date'</span> },
|
||
<span class="s">meta</span>: { <span class="s">type</span>: <span class="s">'json'</span> },
|
||
<span class="s">dept_id</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">references</span>: <span class="s">'departments.id'</span> },
|
||
});
|
||
|
||
<span class="c">// 表操作</span>
|
||
<span class="k">await</span> db.<span class="f">getTableNames</span>(); <span class="c">// ['users']</span>
|
||
<span class="k">await</span> db.<span class="f">dropTable</span>(<span class="s">'users'</span>); <span class="c">// 删除表</span></pre>
|
||
|
||
<table>
|
||
<tr><th>ColumnDef 属性</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>type</code></td><td><code>'string'|'number'|'boolean'|'date'|'json'</code></td><td>数据类型</td></tr>
|
||
<tr><td><code>primaryKey</code></td><td><code>boolean</code></td><td>主键(每表至少一个)</td></tr>
|
||
<tr><td><code>required</code></td><td><code>boolean</code></td><td>是否必填</td></tr>
|
||
<tr><td><code>unique</code></td><td><code>boolean</code></td><td>唯一约束(自动建索引)</td></tr>
|
||
<tr><td><code>index</code></td><td><code>boolean</code></td><td>创建哈希索引,O(1) 加速查询</td></tr>
|
||
<tr><td><code>default</code></td><td><code>unknown</code></td><td>默认值</td></tr>
|
||
<tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr>
|
||
<tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr>
|
||
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr>
|
||
<tr><td><code>onDelete</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>删除级联 🆕</td></tr>
|
||
<tr><td><code>onUpdate</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>更新级联 🆕</td></tr>
|
||
</table>
|
||
|
||
<h2 id="sql-query">🔍 SQL 查询</h2>
|
||
<p><code>db.query(sql)</code> — 执行标准 SQL 字符串,返回查询结果。</p>
|
||
|
||
<h3>完整 SQL 语法支持</h3>
|
||
<pre><span class="c">// SELECT — 核心查询</span>
|
||
<span class="k">const</span> rows = <span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||
WHERE age > 18
|
||
ORDER BY name ASC
|
||
LIMIT 10 OFFSET 0`</span>);
|
||
|
||
<span class="c">// INSERT — 插入数据</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users (id, name, age) VALUES ('1', 'Alice', 30)"</span>);
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users VALUES ('2', 'Bob', 25)"</span>);
|
||
<span class="c">// 支持多行插入</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users VALUES ('3', 'C'), ('4', 'D')"</span>);
|
||
|
||
<span class="c">// UPDATE — 更新数据</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"UPDATE users SET age = 31, active = true WHERE id = '1'"</span>);
|
||
|
||
<span class="c">// DELETE — 删除数据</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);
|
||
|
||
<span class="c">// DDL — 表结构操作</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE products (
|
||
id STRING PRIMARY KEY,
|
||
name STRING NOT NULL,
|
||
price NUMBER DEFAULT 0
|
||
)`</span>);
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'DROP TABLE products'</span>);</pre>
|
||
|
||
<h3>条件表达式</h3>
|
||
<pre><span class="c">// 比较运算符</span>
|
||
<span class="s">`WHERE age > 18 AND name LIKE 'A%'`</span>
|
||
<span class="s">`WHERE salary >= 5000 OR dept = 'Engineering'`</span>
|
||
|
||
<span class="c">// IN / NOT IN</span>
|
||
<span class="s">`WHERE dept IN ('Engineering', 'Sales')`</span>
|
||
|
||
<span class="c">// NULL 检查</span>
|
||
<span class="s">`WHERE email IS NULL`</span>
|
||
<span class="s">`WHERE email IS NOT NULL`</span>
|
||
|
||
<span class="c">// NOT 取反</span>
|
||
<span class="s">`WHERE NOT (age < 18 OR age > 65)`</span>
|
||
|
||
<span class="c">// 嵌套条件</span>
|
||
<span class="s">`WHERE (age > 18 AND active = true) OR role = 'admin'`</span></pre>
|
||
|
||
<h2 id="query-builder">⛓ Query Builder</h2>
|
||
<p>链式 API,TypeScript 友好,享受 IDE 自动补全。</p>
|
||
|
||
<h3>SELECT</h3>
|
||
<pre><span class="k">const</span> users = db.<span class="f">table</span>(<span class="s">'users'</span>);
|
||
|
||
<span class="c">// 全量查询</span>
|
||
<span class="k">await</span> users.<span class="f">select</span>().<span class="f">execute</span>();
|
||
|
||
<span class="c">// 指定列 + 条件 + 排序 + 分页</span>
|
||
<span class="k">const</span> result = <span class="k">await</span> users
|
||
.<span class="f">select</span>([<span class="s">'name'</span>, <span class="s">'age'</span>, <span class="s">'email'</span>])
|
||
.<span class="f">where</span>({
|
||
<span class="s">age</span>: { <span class="s">$gt</span>: <span class="n">18</span> },
|
||
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span> },
|
||
})
|
||
.<span class="f">orderBy</span>(<span class="s">'age'</span>, <span class="s">'desc'</span>)
|
||
.<span class="f">limit</span>(<span class="n">10</span>)
|
||
.<span class="f">offset</span>(<span class="n">0</span>)
|
||
.<span class="f">execute</span>();</pre>
|
||
|
||
<h3>INSERT</h3>
|
||
<pre><span class="c">// 单行插入 — 返回主键值</span>
|
||
<span class="k">const</span> pk = <span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span>, <span class="s">age</span>: <span class="n">30</span> });
|
||
|
||
<span class="c">// 批量插入 — 返回主键值数组</span>
|
||
<span class="k">const</span> pks = <span class="k">await</span> users.<span class="f">insertMany</span>([
|
||
{ <span class="s">id</span>: <span class="s">'2'</span>, <span class="s">name</span>: <span class="s">'Bob'</span>, <span class="s">age</span>: <span class="n">25</span> },
|
||
{ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span>, <span class="s">age</span>: <span class="n">35</span> },
|
||
]);</pre>
|
||
|
||
<h3>UPDATE / DELETE</h3>
|
||
<pre><span class="c">// 更新 — 返回影响行数</span>
|
||
<span class="k">const</span> updated = <span class="k">await</span> users
|
||
.<span class="f">update</span>({ <span class="s">age</span>: <span class="n">31</span>, <span class="s">active</span>: <span class="k">false</span> })
|
||
.<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> })
|
||
.<span class="f">execute</span>(); <span class="c">// 1</span>
|
||
|
||
<span class="c">// 删除 — 返回影响行数</span>
|
||
<span class="k">const</span> deleted = <span class="k">await</span> users
|
||
.<span class="f">delete</span>()
|
||
.<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> })
|
||
.<span class="f">execute</span>(); <span class="c">// 1</span>
|
||
|
||
<span class="c">// 全表删除</span>
|
||
<span class="k">await</span> users.<span class="f">delete</span>().<span class="f">execute</span>();</pre>
|
||
|
||
<h2 id="join">🔗 JOIN 查询</h2>
|
||
<p>支持 INNER / LEFT / RIGHT / CROSS JOIN,SQL 和 Query Builder 两种方式。</p>
|
||
|
||
<pre><span class="c">// SQL 方式 — 支持表别名</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT u.name, d.name AS dept_name
|
||
FROM users u
|
||
INNER JOIN departments d ON u.dept_id = d.id
|
||
LEFT JOIN orders o ON u.id = o.user_id
|
||
WHERE d.name = 'Engineering'
|
||
ORDER BY u.name`</span>);
|
||
|
||
<span class="c">// Query Builder 方式</span>
|
||
<span class="k">await</span> db.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">select</span>()
|
||
.<span class="f">as</span>(<span class="s">'u'</span>)
|
||
.<span class="f">innerJoin</span>(<span class="s">'departments'</span>, { <span class="s">'u.dept_id'</span>: { <span class="s">$col</span>: <span class="s">'d.id'</span> } }, <span class="s">'d'</span>)
|
||
.<span class="f">leftJoin</span>(<span class="s">'orders'</span>, { <span class="s">'u.id'</span>: { <span class="s">$col</span>: <span class="s">'o.user_id'</span> } }, <span class="s">'o'</span>)
|
||
.<span class="f">execute</span>();
|
||
|
||
<span class="c">// CROSS JOIN — 笛卡尔积</span>
|
||
.<span class="f">crossJoin</span>(<span class="s">'metadata'</span>, <span class="s">'m'</span>)</pre>
|
||
|
||
<h2 id="groupby">📊 GROUP BY & 聚合</h2>
|
||
<p>五大聚合函数 + GROUP BY + HAVING + DISTINCT,完整的数据分析能力。</p>
|
||
|
||
<pre><span class="c">// GROUP BY + 聚合</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT dept, COUNT(*) as cnt, SUM(salary) as total,
|
||
AVG(salary) as avg_sal, MIN(age), MAX(age)
|
||
FROM employees
|
||
GROUP BY dept
|
||
HAVING COUNT(*) > 1
|
||
ORDER BY total DESC
|
||
LIMIT 5`</span>);
|
||
|
||
<span class="c">// DISTINCT 去重</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT DISTINCT dept FROM employees'</span>);
|
||
|
||
<span class="c">// 聚合函数别名</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT COUNT(*) AS total_users, AVG(age) AS avg_age FROM users'</span>);</pre>
|
||
|
||
<h2 id="where-ops">🎯 WHERE 操作符</h2>
|
||
<p>Query Builder 使用 <code>$</code> 前缀操作符,支持逻辑组合。</p>
|
||
|
||
<table>
|
||
<tr><th>操作符</th><th>含义</th><th>示例</th></tr>
|
||
<tr><td>直接值 / <code>$eq</code></td><td>等于</td><td><code>{ age: 30 }</code> / <code>{ age: { $eq: 30 } }</code></td></tr>
|
||
<tr><td><code>$ne</code></td><td>不等于</td><td><code>{ age: { $ne: 30 } }</code></td></tr>
|
||
<tr><td><code>$gt</code> / <code>$gte</code></td><td>大于 / 大于等于</td><td><code>{ age: { $gt: 18 } }</code></td></tr>
|
||
<tr><td><code>$lt</code> / <code>$lte</code></td><td>小于 / 小于等于</td><td><code>{ age: { $lt: 65 } }</code></td></tr>
|
||
<tr><td><code>$in</code> / <code>$nin</code></td><td>在列表中 / 不在</td><td><code>{ dept: { $in: ['IT','HR'] } }</code></td></tr>
|
||
<tr><td><code>$like</code></td><td>模糊匹配</td><td><code>{ name: { $like: 'A%' } }</code></td></tr>
|
||
<tr><td><code>$and</code></td><td>逻辑与</td><td><code>{ $and: [{...}, {...}] }</code></td></tr>
|
||
<tr><td><code>$or</code></td><td>逻辑或</td><td><code>{ $or: [{...}, {...}] }</code></td></tr>
|
||
<tr><td><code>$not</code></td><td>逻辑非</td><td><code>{ age: { $not: { $gt: 18 } } }</code></td></tr>
|
||
<tr><td><code>$col</code></td><td>列引用(JOIN ON)</td><td><code>{ 'a.id': { $col: 'b.a_id' } }</code></td></tr>
|
||
</table>
|
||
|
||
<pre><span class="c">// 复合条件</span>
|
||
.<span class="f">where</span>({
|
||
<span class="s">$or</span>: [
|
||
{ <span class="s">age</span>: { <span class="s">$lt</span>: <span class="n">18</span> } },
|
||
{ <span class="s">age</span>: { <span class="s">$gt</span>: <span class="n">65</span> } },
|
||
],
|
||
<span class="s">active</span>: <span class="k">true</span>,
|
||
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> },
|
||
})</pre>
|
||
|
||
<h2 id="transaction">🔒 事务 & 回滚</h2>
|
||
<p>v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销。</p>
|
||
|
||
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
|
||
<span class="c">// trx.table() 获取事务内表操作对象</span>
|
||
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> });
|
||
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> });
|
||
<span class="c">// ✅ 全部成功 → 自动 commit</span>
|
||
<span class="c">// ❌ 任何一步失败 → 自动 rollback,数据恢复原状</span>
|
||
<span class="c">// Memory 引擎:快照回滚 | IndexedDB 引擎:延迟写入</span>
|
||
});</pre>
|
||
|
||
<h2 id="subquery">🔍 子查询</h2>
|
||
<p>v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。</p>
|
||
|
||
<pre><span class="c">// IN 子查询 — 查询有高额订单的用户</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
|
||
|
||
<span class="c">// 标量子查询 — 查询年龄等于平均年龄的用户</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||
WHERE age = (SELECT AVG(age) FROM users)`</span>);
|
||
|
||
<span class="c">// NOT IN 子查询</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||
WHERE id NOT IN (SELECT user_id FROM orders)`</span>);</pre>
|
||
|
||
<h2 id="foreign-key">🔗 外键级联</h2>
|
||
<p>v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。</p>
|
||
|
||
<pre><span class="c">// 定义时指定外键 + 级联策略</span>
|
||
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'orders'</span>, {
|
||
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
|
||
<span class="s">user_id</span>: {
|
||
<span class="s">type</span>: <span class="s">'string'</span>,
|
||
<span class="s">references</span>: <span class="s">'users.id'</span>,
|
||
<span class="s">onDelete</span>: <span class="s">'CASCADE'</span>, <span class="c">// 删除用户时级联删除订单</span>
|
||
<span class="s">onUpdate</span>: <span class="s">'RESTRICT'</span>, <span class="c">// 禁止更新被引用的用户 ID</span>
|
||
},
|
||
<span class="s">amount</span>: { <span class="s">type</span>: <span class="s">'number'</span> },
|
||
});
|
||
|
||
<span class="c">// SQL DDL 同样支持</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE orders (
|
||
id STRING PRIMARY KEY,
|
||
user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
|
||
amount NUMBER
|
||
)`</span>);
|
||
|
||
<span class="c">// 删除用户 → 其所有订单自动删除</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);</pre>
|
||
|
||
<table>
|
||
<tr><th>级联选项</th><th>行为</th></tr>
|
||
<tr><td><code>CASCADE</code></td><td>级联删除/更新子表中的匹配行</td></tr>
|
||
<tr><td><code>SET NULL</code></td><td>将子表中的外键列设为 NULL</td></tr>
|
||
<tr><td><code>RESTRICT</code></td><td>禁止操作(默认行为)</td></tr>
|
||
</table>
|
||
|
||
<h2 id="connection-pool">🏊 连接池</h2>
|
||
<p>v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。</p>
|
||
|
||
<pre><span class="c">// connect() — 获取或创建实例(单例复用)</span>
|
||
<span class="k">const</span> db1 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span>, <span class="s">mode</span>: <span class="s">'hybrid'</span> });
|
||
<span class="k">const</span> db2 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span> });
|
||
<span class="c">// db1 === db2 — 复用已有实例,避免重复 open IndexedDB</span>
|
||
|
||
<span class="c">// disconnect() — 释放连接(引用计数 -1)</span>
|
||
<span class="k">await</span> db2.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 2 → 1</span>
|
||
<span class="k">await</span> db1.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 1 → 0,自动 close()</span>
|
||
|
||
<span class="c">// disconnectAll() — 强制关闭所有连接</span>
|
||
<span class="k">await</span> <span class="f">MetonaSqlark.disconnectAll</span>();
|
||
|
||
<span class="c">// getActiveConnections() — 查看活跃连接</span>
|
||
<span class="f">MetonaSqlark.getActiveConnections</span>(); <span class="c">// ['my-app']</span></pre>
|
||
|
||
<h2 id="migration">🔄 数据迁移</h2>
|
||
<p>按版本号管理表结构变更。</p>
|
||
|
||
<pre><span class="c">// 注册迁移</span>
|
||
db.<span class="f">addMigration</span>(<span class="n">2</span>, <span class="k">async</span> (db) => {
|
||
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'products'</span>, {
|
||
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
|
||
<span class="s">name</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">required</span>: <span class="k">true</span> },
|
||
});
|
||
});
|
||
|
||
db.<span class="f">addMigration</span>(<span class="n">3</span>, <span class="k">async</span> (db) => {
|
||
<span class="c">// 添加新列、数据迁移等</span>
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"UPDATE users SET role = 'user' WHERE role IS NULL"</span>);
|
||
});
|
||
|
||
<span class="c">// 执行迁移到目标版本</span>
|
||
<span class="k">await</span> db.<span class="f">migrateTo</span>(<span class="n">3</span>); <span class="c">// 依次执行 v2, v3 的迁移函数</span></pre>
|
||
|
||
<h2 id="export">📤 导入导出</h2>
|
||
<pre><span class="c">// 导出单表 — 返回 JSON 数组</span>
|
||
<span class="k">const</span> userData = <span class="k">await</span> db.<span class="f">exportTable</span>(<span class="s">'users'</span>);
|
||
<span class="c">// [{ id: '1', name: 'Alice', ... }, ...]</span>
|
||
|
||
<span class="c">// 导出全库 — 返回 { tableName: rows[] }</span>
|
||
<span class="k">const</span> allData = <span class="k">await</span> db.<span class="f">exportAll</span>();
|
||
<span class="c">// { users: [...], orders: [...], products: [...] }</span>
|
||
|
||
<span class="c">// 导入数据 — 返回主键列表</span>
|
||
<span class="k">const</span> pks = <span class="k">await</span> db.<span class="f">importTable</span>(<span class="s">'users'</span>, userData);</pre>
|
||
|
||
<h2 id="plugin">🧩 插件 & 钩子</h2>
|
||
<p>14 种生命周期钩子,支持插件机制。</p>
|
||
|
||
<table>
|
||
<tr><th>钩子名称</th><th>触发时机</th><th>参数</th></tr>
|
||
<tr><td><code>beforeCreateTable</code></td><td>创建表前</td><td>schema</td></tr>
|
||
<tr><td><code>afterCreateTable</code></td><td>创建表后</td><td>schema</td></tr>
|
||
<tr><td><code>beforeDropTable</code></td><td>删除表前</td><td>tableName</td></tr>
|
||
<tr><td><code>afterDropTable</code></td><td>删除表后</td><td>tableName</td></tr>
|
||
<tr><td><code>beforeInsert</code></td><td>插入前</td><td>rows[]</td></tr>
|
||
<tr><td><code>afterInsert</code></td><td>插入后</td><td>rows[]</td></tr>
|
||
<tr><td><code>beforeUpdate</code></td><td>更新前</td><td>query, updates</td></tr>
|
||
<tr><td><code>afterUpdate</code></td><td>更新后</td><td>query, updates, count</td></tr>
|
||
<tr><td><code>beforeDelete</code></td><td>删除前</td><td>query</td></tr>
|
||
<tr><td><code>afterDelete</code></td><td>删除后</td><td>query, count</td></tr>
|
||
<tr><td><code>beforeQuery</code></td><td>SQL 查询前</td><td>sql</td></tr>
|
||
<tr><td><code>afterQuery</code></td><td>SQL 查询后</td><td>sql, result</td></tr>
|
||
<tr><td><code>beforeTransaction</code></td><td>事务开始前</td><td>-</td></tr>
|
||
<tr><td><code>afterTransaction</code></td><td>事务完成后</td><td>-</td></tr>
|
||
</table>
|
||
|
||
<pre><span class="c">// 注册钩子</span>
|
||
db.<span class="f">on</span>(<span class="s">'beforeInsert'</span>, <span class="k">async</span> (row) => {
|
||
<span class="f">console.log</span>(<span class="s">'即将插入:'</span>, row);
|
||
<span class="c">// 可在此校验、转换数据</span>
|
||
});
|
||
|
||
db.<span class="f">on</span>(<span class="s">'afterQuery'</span>, <span class="k">async</span> (sql, result) => {
|
||
<span class="f">console.log</span>(<span class="s">`查询完成 [</span><span class="k">${sql}</span><span class="s">] → </span><span class="k">${(result as any[]).length}</span><span class="s"> 行`</span>);
|
||
});
|
||
|
||
<span class="c">// 注册自定义插件</span>
|
||
<span class="k">const</span> loggerPlugin = {
|
||
<span class="s">name</span>: <span class="s">'logger'</span>,
|
||
<span class="s">version</span>: <span class="s">'1.0.0'</span>,
|
||
<span class="s">description</span>: <span class="s">'记录所有数据库操作'</span>,
|
||
<span class="s">priority</span>: <span class="n">100</span>,
|
||
<span class="f">install</span>(db) {
|
||
db.<span class="f">on</span>(<span class="s">'beforeQuery'</span>, (sql) => <span class="f">console.log</span>(<span class="s">'SQL:'</span>, sql));
|
||
},
|
||
<span class="f">destroy</span>() { <span class="c">/* 清理 */</span> },
|
||
};
|
||
|
||
<span class="c">// 在 create 配置中注册</span>
|
||
<span class="k">const</span> db = <span class="k">await</span> <span class="f">MetonaSqlark.create</span>({
|
||
<span class="s">name</span>: <span class="s">'my-app'</span>,
|
||
<span class="s">plugins</span>: [loggerPlugin],
|
||
});</pre>
|
||
|
||
<h2 id="subscribe">📡 发布订阅</h2>
|
||
<pre><span class="c">// 订阅表变更</span>
|
||
<span class="k">const</span> unsubscribe = db.<span class="f">subscribe</span>(<span class="s">'users'</span>, (event) => {
|
||
<span class="c">// event.type: 'insert' | 'update' | 'delete'</span>
|
||
<span class="c">// event.row: 被操作的行数据</span>
|
||
<span class="f">console.log</span>(<span class="s">`users 表 </span><span class="k">${event.type}</span><span class="s">`</span>, event.row);
|
||
});
|
||
|
||
<span class="c">// 手动触发变更</span>
|
||
db.<span class="f">emit</span>(<span class="s">'users'</span>, { <span class="s">type</span>: <span class="s">'insert'</span>, <span class="s">row</span>: { <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span> } });
|
||
|
||
<span class="c">// 取消订阅</span>
|
||
<span class="f">unsubscribe</span>();</pre>
|
||
|
||
<h2 id="react">⚛️ React 集成</h2>
|
||
<pre><span class="k">import</span> { useQuery, useTable, useDatabase } <span class="k">from</span> <span class="s">'@metona-team/metona-sqlark/react'</span>;
|
||
<span class="k">import</span> { db } <span class="k">from</span> <span class="s">'./db'</span>;
|
||
|
||
<span class="k">function</span> <span class="f">UserList</span>() {
|
||
<span class="c">// 执行 SQL 查询,自动响应 db 变化</span>
|
||
<span class="k">const</span> { data, loading, error, refresh } = <span class="f">useQuery</span>(
|
||
db,
|
||
<span class="s">'SELECT * FROM users WHERE age > 18'</span>,
|
||
[<span class="c">/* deps */</span>]
|
||
);
|
||
|
||
<span class="k">if</span> (loading) <span class="k">return</span> <div>Loading...</div>;
|
||
<span class="k">if</span> (error) <span class="k">return</span> <div>Error: {error.message}</div>;
|
||
|
||
<span class="k">return</span> (
|
||
<div>
|
||
{data.<span class="f">map</span>(u => <div key={u.id}>{u.name} ({u.age})</div>)}
|
||
<button onClick={refresh}>刷新</button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
<span class="c">// 便捷 hook — 查询整张表</span>
|
||
<span class="k">const</span> { data, loading, refresh } = <span class="f">useTable</span>(db, <span class="s">'users'</span>);
|
||
|
||
<span class="c">// 管理数据库生命周期</span>
|
||
<span class="k">function</span> <span class="f">App</span>() {
|
||
<span class="k">const</span> { db, ready, error } = <span class="f">useDatabase</span>({
|
||
<span class="s">name</span>: <span class="s">'my-app'</span>,
|
||
<span class="s">mode</span>: <span class="s">'hybrid'</span>,
|
||
});
|
||
<span class="k">if</span> (!ready) <span class="k">return</span> <div>Initializing...</div>;
|
||
<span class="k">return</span> <UserList />;
|
||
}</pre>
|
||
|
||
<h2 id="vue">🟢 Vue 集成</h2>
|
||
<pre><span class="k">import</span> { useSqlarkQuery, useSqlarkTable, useSqlarkDatabase }
|
||
<span class="k">from</span> <span class="s">'@metona-team/metona-sqlark/vue'</span>;
|
||
<span class="k">import</span> { db } <span class="k">from</span> <span class="s">'./db'</span>;
|
||
|
||
<span class="c">// useSqlarkQuery — 执行 SQL 查询</span>
|
||
<span class="k">const</span> { data, loading, error, refresh } = <span class="f">useSqlarkQuery</span>(
|
||
db,
|
||
<span class="s">'SELECT * FROM users WHERE age > 18'</span>
|
||
);
|
||
|
||
<span class="c">// useSqlarkTable — 获取全表数据</span>
|
||
<span class="k">const</span> { data, loading, refresh } = <span class="f">useSqlarkTable</span>(db, <span class="s">'users'</span>);
|
||
|
||
<span class="c">// useSqlarkDatabase — 管理数据库生命周期</span>
|
||
<span class="k">const</span> { db, ready, error } = <span class="f">useSqlarkDatabase</span>({
|
||
<span class="s">name</span>: <span class="s">'my-app'</span>,
|
||
<span class="s">mode</span>: <span class="s">'hybrid'</span>,
|
||
});</pre>
|
||
|
||
<h2 id="types">🔷 TypeScript 泛型</h2>
|
||
<pre><span class="k">interface</span> <span class="t">User</span> {
|
||
<span class="s">id</span>: <span class="t">string</span>;
|
||
<span class="s">name</span>: <span class="t">string</span>;
|
||
<span class="s">age</span>: <span class="t">number</span>;
|
||
<span class="s">email</span>?: <span class="t">string</span>;
|
||
}
|
||
|
||
<span class="c">// 泛型表操作 — 类型安全的 insert/select</span>
|
||
<span class="k">const</span> users = db.<span class="f">table</span><<span class="t">User</span>>(<span class="s">'users'</span>);
|
||
|
||
<span class="c">// ✅ 类型检查通过</span>
|
||
<span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span>, <span class="s">age</span>: <span class="n">30</span> });
|
||
|
||
<span class="c">// ❌ TypeScript 报错:缺少 name</span>
|
||
<span class="c">// await users.insert({ id: '2', age: 25 });</span></pre>
|
||
|
||
<h2 id="config">⚙️ 完整配置项</h2>
|
||
|
||
<table>
|
||
<tr><th>属性</th><th>类型</th><th>默认值</th><th>说明</th></tr>
|
||
<tr><td><code>name</code></td><td><code>string</code></td><td><code>'metona-sqlark'</code></td><td>数据库名称(必填)</td></tr>
|
||
<tr><td><code>mode</code></td><td><code>'memory'|'disk'|'hybrid'</code></td><td><code>'hybrid'</code></td><td>存储模式</td></tr>
|
||
<tr><td><code>diskEngine</code></td><td><code>'indexeddb'|'opfs'</code></td><td><code>'indexeddb'</code></td><td>磁盘引擎类型</td></tr>
|
||
<tr><td><code>version</code></td><td><code>number</code></td><td><code>1</code></td><td>数据库版本号</td></tr>
|
||
<tr><td><code>plugins</code></td><td><code>MetonaPlugin[]</code></td><td><code>[]</code></td><td>初始插件列表</td></tr>
|
||
<tr><td><code>onReady</code></td><td><code>(db) => void</code></td><td>-</td><td>初始化完成回调</td></tr>
|
||
<tr><td><code>onError</code></td><td><code>(err) => void</code></td><td>-</td><td>错误回调</td></tr>
|
||
</table>
|
||
|
||
<h2 id="engine">💾 存储引擎</h2>
|
||
|
||
<table>
|
||
<tr><th>引擎</th><th>模式</th><th>持久化</th><th>性能</th><th>适用场景</th></tr>
|
||
<tr><td><code>MemoryEngine</code></td><td>memory</td><td>❌ 否</td><td>⚡ 极快</td><td>临时数据、缓存、测试</td></tr>
|
||
<tr><td><code>IndexedDBEngine</code></td><td>disk</td><td>✅ 是</td><td>🚀 快</td><td>通用持久化,兼容性最好</td></tr>
|
||
<tr><td><code>OPFSEngine</code></td><td>disk</td><td>✅ 是</td><td>🚀 快</td><td>现代浏览器,文件级存储</td></tr>
|
||
<tr><td><code>HybridEngine</code></td><td>hybrid</td><td>✅ 是</td><td>⚡ 极快</td><td>生产推荐,读写均走内存</td></tr>
|
||
</table>
|
||
|
||
<p>Hybrid 引擎采用 <strong>write-through</strong> 策略:所有写操作同时写入内存和磁盘,所有读操作直接从内存返回,启动时从磁盘加载数据到内存。</p>
|
||
|
||
<h2 id="errors">⚠️ 错误处理</h2>
|
||
<p>所有错误抛出 <code>DatabaseError</code> 实例。</p>
|
||
|
||
<pre><span class="k">try</span> {
|
||
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT * FROM nonexistent'</span>);
|
||
} <span class="k">catch</span> (err) {
|
||
<span class="k">if</span> (err <span class="k">instanceof</span> <span class="t">DatabaseError</span>) {
|
||
<span class="f">console.log</span>(err.message); <span class="c">// 'Table "nonexistent" does not exist'</span>
|
||
<span class="f">console.log</span>(err.code); <span class="c">// 'TABLE_NOT_FOUND'</span>
|
||
<span class="f">console.log</span>(err.details); <span class="c">// 附加信息</span>
|
||
}
|
||
}</pre>
|
||
|
||
<table>
|
||
<tr><th>错误码</th><th>触发场景</th></tr>
|
||
<tr><td><code>TABLE_NOT_FOUND</code></td><td>表不存在</td></tr>
|
||
<tr><td><code>TABLE_EXISTS</code></td><td>表已存在</td></tr>
|
||
<tr><td><code>DUPLICATE_KEY</code></td><td>主键重复</td></tr>
|
||
<tr><td><code>UNIQUE_VIOLATION</code></td><td>唯一约束冲突</td></tr>
|
||
<tr><td><code>VALIDATION_ERROR</code></td><td>数据校验失败</td></tr>
|
||
<tr><td><code>TYPE_ERROR</code></td><td>字段类型错误</td></tr>
|
||
<tr><td><code>SCHEMA_ERROR</code></td><td>表结构定义错误</td></tr>
|
||
<tr><td><code>DB_NOT_READY</code></td><td>数据库未初始化</td></tr>
|
||
<tr><td><code>PARSE_ERROR</code></td><td>SQL 语法错误</td></tr>
|
||
<tr><td><code>TRANSACTION_ERROR</code></td><td>事务执行失败</td></tr>
|
||
<tr><td><code>COMPILE_ERROR</code></td><td>编译 AST 到查询计划失败</td></tr>
|
||
<tr><td><code>CONFIG_ERROR</code></td><td>配置错误</td></tr>
|
||
</table>
|
||
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
document.querySelectorAll('.sidebar a').forEach(a => {
|
||
a.addEventListener('click', function() {
|
||
document.querySelectorAll('.sidebar a').forEach(x => x.classList.remove('active'));
|
||
this.classList.add('active');
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|