- 关系型数据库
- 数据库
- 前端
【免费下载链接】lovefield
Lovefield is a relational database for web apps. Written in JavaScript, works cross-browser. Provides SQL-like APIs that are fast, safe, and easy to use.
Lovefield 是一个面向 Web 应用的关系型查询引擎(relational query engine),以 JavaScript 编写、跨浏览器运行,提供类 SQL 的 API。本文是 Lovefield 官方规范(docs/spec/)的开篇导读,系统梳理其设计目标、基本假设、十项核心需求、两种使用工作流(Grab-and-use 与 Closure 编译器高级优化流程)以及 API 风格约定。读完本文,你将掌握 Lovefield 的设计边界(数据量、SQL 子集、存储方式)、如何从零接入并使用其 Schema Builder 建库,以及为何所有异步 API 均基于 Promise、DDL 与 DML 的同步/异步分工。
1. 设计目标:为 Web 应用提供关系型查询引擎
Lovefield 的定位非常明确:为 Web 应用提供关系型查询引擎(relational query engine for web apps)。它不是传统意义上的"数据库",而是一个运行在浏览器里的查询引擎层——数据持久化委托给浏览器已有的存储技术(如 IndexedDB),查询、索引、约束、事务则由 Lovefield 自身实现。
这一目标决定了它与其他方案的本质区别:相对于纯前端数据容器(如简单的数组或对象存储),Lovefield 提供结构化的表(Table)、列(Column)、索引(Index)、约束(Constraint)与查询计划(Query Plan);相对于服务端数据库,它运行在浏览器环境中,受限于浏览器存储能力与单机资源。
2. 基本假设:Lovefield 适用的场景边界
Lovefield 规范在开篇即明确了三条基本假设,这些假设划定了它的适用范围:
- 数据集规模:Lovefield 面向"数据集小于 X(当前上限为 2GB),但大到足以需要一个结构化查询引擎"的数据库。也就是说,它服务于中等规模的数据集——小到不需要服务端数据库,大到纯内存数组难以高效管理。
- SQL 子集:Lovefield 只提供 SQL-03 标准的一个有限子集(相关语法 BNF 可参考 SQL-2003-2),并非完整的 SQL 实现。这意味着 JOIN、GROUP BY、聚合等能力是"够用但受限"的,例如多列
ROLLUP、CUBE、HAVING均不支持。 - 数据安全性责任:Lovefield 使用现有存储技术(如 IndexedDB)在需要时持久化数据。开发者有责任确保任何从 Lovefield 查询引擎访问的数据都被视为"unsafe",并在发送回服务器之前以某种方式进行净化(sanitize)。
第 3 条假设尤其值得注意:Lovefield 不会替你做输入校验或 XSS 防护,它只是数据的存取与查询层,安全边界由使用者自己把控。
3. 十项核心需求:Lovefield 的设计约束清单
规范列出了 Lovefield 必须满足的十项需求,它们直接塑造了库的架构形态,也与仓库中的源码结构一一对应:
| # | 需求 | 对实现的影响(对应仓库证据) |
|---|---|---|
| 1 | SQL 类关系查询引擎,覆盖 WebSQL 支持的大部分用例 | 查询构建器与执行引擎,见 lib/query/、lib/proc/ |
| 2 | 兼容 Closure compiler:生成的代码与库本身都必须可被其编译 | 全库使用 Closure 风格注解与goog.provide/goog.require,如 lib/schema/builder.js |
| 3 | 兼容 Chrome Apps v2,仅需存储访问权限 | 这直接排除了"持久化 JavaScript 函数再 eval"的自定义索引方案(见下文 Schema 一节) |
| 4 | 可即插即用(Drop-in library) | 单文件分发即可使用 |
| 5 | 可作组件使用 | 提供lf.schema、lf.query、lf.proc等分层命名空间 |
| 6 | 可被多种 JS 框架使用:Closure、jQuery、Polymer、AngularJS 等 | 仓库demos/下提供了 jQuery 演示、Polymer 演示、Angular 演示 等 |
| 7 | 复制式部署:用户只需把压缩后的 JS 文件拷进项目即可使用 | 即下面"Basic Flow"中的 Grab and use |
| 8 | 使用本库不得产生任何副作用 | API 全部收敛在lf命名空间内,不污染全局 |
| 9 | 跨浏览器:兼容 Chrome、Firefox、Internet Explorer、Safari | 通过goog.Promise等 polyfill 机制实现,见下文 API 风格 |
| 10 | 支持低端设备(低 CPU、低内存,如 HP Chromebook 11) | 性能与内存占用是索引、结果对象设计的重要考量 |
需求 2 与需求 3 是理解 Lovefield 技术选型的两把钥匙:Closure 编译器兼容意味着所有代码都带有完整类型注解(这也是仓库源码几乎每个函数都有@param、@return、@constructor、@export注解的原因);Chrome Apps v2 约束则限定了"不能持久化并执行 JavaScript 函数",进而决定了自定义索引的不可能(详见 Schema 一节)。
4. 两种设计工作流:Basic Flow 与 Advanced Flow
4.1 Basic Flow:Grab and use
最基本的使用方式就是"拿来就用"(Grab and use)——复制分发好的压缩 JS 文件到项目,调用lf.schema.create()建库、connect()连接、然后用查询构建器执行 CRUD。仓库中的 todo 演示 就是这种开箱即用流程的完整示例,也是规范指定的 Quick Start 入口。
这种流程对应的就是动态 Schema 创建:直接用 JavaScript API 声明表结构。例如(代码取自 docs/spec/01_schema.md):
// Begin schema creation. var schemaBuilder = lf.schema.create('crdb', 1); schemaBuilder.createTable('Asset'). addColumn('id', lf.Type.STRING). addColumn('asset', lf.Type.STRING). addColumn('timestamp', lf.Type.INTEGER). addPrimaryKey(['id']); // Schema is defined, now connect to the database instance. schemaBuilder.connect().then( function(db) { // Schema is not mutable once the connection to DB has established. });从源码看,lf.schema.create(dbName, dbVersion)实际是lf.schema.Builder的工厂函数(lib/schema/builder.js#L414-L416),而connect()内部会完成数据库初始化、版本匹配检测、必要时执行升级,最终返回一个实现了lf.Database接口的连接对象(lib/schema/builder.js#L257-L285)。
4.2 Advanced Flow:面向 Closure 编译器高级优化
当项目整体使用 Closure 编译器做高级优化(advanced optimization)编译打包时,推荐采用静态 Schema 流程:
- 在 YAML 文件中创建表结构——用声明式文件描述 Schema;
- 使用 SPAC(Schema Parser And Code-Generator,Schema 解析器与代码生成器)解析 YAML 并生成 JavaScript 源码——生成带完整 Closure 类型注解的类与函数;
- 在代码中使用生成的类/函数——建库、建表对象、构造查询条件都直接引用生成的符号;
- 用 Closure 编译器把一切编译合并——Lovefield 核心库与你的代码一起编译。
这条流程的收益在 docs/spec/07_spac.md 中有明确量化:通过 SPAC + Closure 编译,Lovefield 的代码体积可从约 200KB(minimized)降至约 70KB,同时获得更严格的类型检查。代价是 Lovefield 需要被编译进使用者的代码中,而不是单独分发。
SPAC 工具本体位于仓库 spac/ 目录,包括解析器(spac/parser.js)、代码生成器(spac/codegen.js)与命令行入口(spac/lovefield-spac)。它生成的代码包含:一个用于创建数据库实例的静态函数、一个可用于构造查询条件的数据库 Schema 类、以及一个可用于创建查询的数据库实例类。
两种流程的本质区别在于 Schema 的定义方式:动态(JavaScript API)vs 静态(YAML + 代码生成)。两者功能等价,选择依据是是否走 Closure 高级优化编译。
5. API 风格:三大铁律
规范明确了 Lovefield 全部 API 必须遵守的三条风格约定,理解它们等于拿到了阅读整个仓库源码的钥匙。
5.1 Google JS 风格 + Closure 注解
所有 API 与源码必须遵循 Google JavaScript Style Guide,并且必须通过 Closure 编译器编译。这意味着所有 API 与源码都带 Closure 风格注解(@constructor、@export、@param、@return等)。
一个重要的细节是:Closure 将Promise注解为IThenable。Lovefield 使用goog.Promise实现跨浏览器的 Promise 支持,因此继承了IThenable注解——这正是为不支持原生 Promise 的浏览器(如 Internet Explorer 10)提供 polyfill 的基础。
5.2 所有异步 API 均基于 Promise
所有异步 API 都是 Promise 化的。你几乎看不到回调风格 API:connect()、exec()、createTransaction()的begin()/attach()/commit()/rollback()全部返回 Promise 或接受 then 链。这带来一致的异步编程模型:
db.select().from(item).exec().then(function(rows) { // 处理查询结果 });5.3 DDL 同步、DML 异步
- 所有 DDL(数据定义语言)API 是同步的:
lf.schema.create()、createTable()、addColumn()、addPrimaryKey()等 Schema 构建调用都是同步执行的——这符合"Schema 一旦连接便不可变"的设计。 - 所有 DML(数据操作语言)API 是异步的:
insert()、update()、delete()、select()的exec()都是异步的,返回 Promise。
这条分工从 lib/schema/builder.js 中可见一斑:createTable()直接同步返回TableBuilder并注册表构建器(L293-L303),而connect()则返回IThenable<!lf.proc.Database>(L257)。
6. 深入:Schema Builder 的状态机与校验逻辑
理解lf.schema.Builder的源码实现,有助于把上文的工作流落到实处。从 lib/schema/builder.js 可以看到:
- Builder 是有状态对象:包含
building(构建中)与finalized(已定型)两种状态。Schema 只能在构建状态下修改;一旦通过connect()或getSchema()触发finalize_(),Builder 不再接受任何调用(createTable()会抛出异常 535 "Schema is already finalized")。 createTable()返回 TableBuilder 并支持链式调用:addColumn、addPrimaryKey、addForeignKey、addUnique、addNullable、addIndex、persistentIndex全部返回this,形成流畅的链式声明语法(见 lib/schema/table_builder.js)。- finalize 阶段执行完整的外键校验:
finalize_()内部依次执行checkForeignKeyValidity_()(外键引用的表/列是否存在、类型是否匹配、被引用列是否唯一)、checkForeignKeyChain_()(禁止外键链式引用同一列)、checkFkCycle_()(基于 DFS 检测外键环,异常 533)。也就是说,非法 Schema 会在connect()之前就被拦截。
TableBuilder 内部还会校验:主键列不可同时是外键子列(异常 543)、主键索引不可与显式索引重复(异常 544)、主键列不可标记为 nullable(异常 545)、autoIncrement 只能用于单列整数主键(异常 504/505)、ARRAY_BUFFER与OBJECT类型不可被索引(异常 509)等。
7. 延伸阅读:规范文档地图
本导读对应的完整规范位于 docs/spec/ 目录,推荐按以下顺序阅读:
| 文档 | 主题 |
|---|---|
| 01_schema.md | Schema 定义:命名规则、列类型、约束、索引、静态 Schema |
| 02_data_store.md | 数据存储:IndexedDB / Memory / Firebase / WebSQL 四种存储 |
| 03_life_of_db.md | 数据库生命周期:初始化、多进程连接、升级、导入导出 |
| 04_query.md | 查询:SELECT/INSERT/UPDATE/DELETE 构建器、参数化查询、观察者 |
| 05_transaction.md | 事务:隐式/显式事务、执行计划、表级锁并发控制 |
| 07_spac.md | SPAC:YAML 静态 Schema 与代码生成 |
| 08_referential_integrity.md | 引用完整性:外键 action 与 timing 的详细语义 |
| 99_postfix.md | 附录:实验性特性(如 Bundle Mode) |
此外,设计文档位于 docs/dd/,其中 04_query_engine.md 深入讲解查询引擎的物理计划生成与各类 pass(如 push-down selections、index range scan),适合想了解查询引擎内部的读者;docs/dev_setup.md 与 docs/running_tests.md 则介绍了本地开发环境与测试运行方式。
结语
Lovefield 的设计文档以其十项需求勾勒出一个清晰的工程画像:一个面向中等规模数据、以 SQL 子集为接口、以浏览器存储为后端、以 Closure 编译器为工程基线的前端关系型查询引擎。无论是开箱即用的动态 Schema,还是面向编译优化的 SPAC 静态 Schema,其 API 风格始终如一:同步的 DDL、Promise 化的 DML、收敛于lf命名空间、零全局副作用。以此为导读,你可以顺着规范目录逐章深入,也可以在仓库源码中验证文档所述的每一项设计决策。
- 关系型数据库
- 数据库
- 前端
【免费下载链接】lovefield
Lovefield is a relational database for web apps. Written in JavaScript, works cross-browser. Provides SQL-like APIs that are fast, safe, and easy to use.
相关推荐
支撑AI工作流的关系模型:sim数据库设计详解
支撑AI工作流的关系模型:sim数据库设计详解 数据库设计是AI工作流平台的核心支撑,sim项目通过精心设计的关系模型实现了工作流定义、执行状态和用户数据的高效
人工智能AI AgentAgent 工作流工作流自动化AI 应用后端前端桌面应用CLIPhoenix Evals 设计规范指南:代码风格、Docstring 与 Evaluator 开发工作流
Phoenix Evals 设计规范指南:代码风格、Docstring 与 Evaluator 开发工作流 本文以 Phoenix Evals 包(仓库路径 p
可观测性AI 评测LLMOpsAI 应用人工智能SonataAdminBundle 路由系统解析:自动生成与自定义路由的完美结合
SonataAdminBundle 路由系统解析:自动生成与自定义路由的完美结合 SonataAdminBundle 作为 Symfony 生态中强大的后台管理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考