LokiJS 快速上手:JavaScript 嵌入式内存数据库的创建、查询、链式操作与动态视图实战

发布时间:2026/10/7 9:57:30
LokiJS 快速上手:JavaScript 嵌入式内存数据库的创建、查询、链式操作与动态视图实战 数据库后端【免费下载链接】LokiJSjavascript embeddable / in-memory database项目地址https://gitcode.com/gh_mirrors/lo/LokiJS点击查看免费下载本文是一份基于 LokiJS 官方文档OVERVIEW.md与仓库源码的实战入门指南面向需要在浏览器或 Node.js 环境中嵌入一个轻量级内存数据库的开发者。你将学会用十几行代码完成数据库创建、集合管理、文档插入、find/where 查询、Resultset 链式操作、命名 Transform 与 Dynamic View 动态视图并能对照仓库中的可运行示例examples/quickstart-core.js 等在本地直接复现验证。一、LokiJS 是什么从项目定位看核心概念LokiJS 是一个 JavaScript 嵌入式 / 内存数据库javascript embeddable / in-memory database核心实现集中在 src/lokijs.js约 7700 行单文件、零外部依赖。它最大的特点是不需要独立数据库服务进程你把数据库实例当作普通对象在应用进程内创建数据驻留内存写入时通过适配器序列化到磁盘、浏览器 IndexedDB / LocalStorage 或远端存储。官方文档 OVERVIEW.md 明确将其定位为一套由 jsdoc 驱动的、分布式维护的准确文档并用一组Getting Started代码串联了全部核心 API。在开始之前先建立三个贯穿全文的核心概念Loki 实例整个数据库对象对应 Loki 构造函数内部维护collections数组负责集合的增删查与持久化调度Collection集合同一类文档的容器类似关系数据库的表每个文档会被自动分配唯一的$loki主键与meta元信息见 Collection.prototype.insertOne 相关实现Resultset / Dynamic View / Transform三种查询与结果组织方式——一次性查询结果集Resultset、可增量维护的活视图Dynamic View、可命名复用的查询链模板Transform。仓库在 examples 目录提供了与本文每个小节一一对应的可运行示例quickstart-core.js、quickstart-chaining.js、quickstart-transforms.js、quickstart-dynview.js建议边读边跑。二、创建数据库与添加集合官方文档给出的最小启动流程只有两步var db new loki(example.db);var users db.addCollection(users);第一步在内存中创建名为example.db的数据库实例。从源码看Loki(filename, options)构造函数会以filename || loki.db作为默认文件名src/lokijs.js#L903-L905因此文件名是可选的真正把数据落盘需要配合持久化适配器与saveDatabase()/autosave等机制Node.js 环境默认使用LokiFsAdapter见 src/loki-fs-adapter.js 同族的 src/loki-fs-sync-adapter.js 等适配器。第二步在数据库内注册名为users的集合。查看 Loki.prototype.addCollection 的实现可以发现两个值得注意的行为同名集合会被去重如果已存在同名集合直接返回既有引用而不会重复创建集合名可以携带可选的options参数如disableMeta、disableChangesApi、ttl等并且当disableMeta: true与disableChangesApi: false、disableDeltaChangesApi: false或启用ttl同时出现时会抛出异常防止产生无法追踪元信息的集合。与创建对应的还有db.getCollection(name)src/lokijs.js#L1216-L1229——找不到时返回null并发出warning事件这在后面配合持久化做首次运行初始化判断时非常常用。三、插入文档单个与批量官方文档展示了两种插入形态源码实现也正好对应两条路径Collection.prototype.insertusers.insert({ name: Odin, age: 50, address: Asgard }); // alternatively, insert array of documents users.insert([{ name: Thor, age: 35}, { name: Loki, age: 30}]);传入单个普通对象时内部转调insertOnesrc/lokijs.js#L5918为文档生成$loki唯一主键、写入meta含created/revision等、触发pre-insert/insert事件并同步维护唯一索引、二分索引等结构传入数组时走批量插入路径可选bulkInsert静默模式适合初始化阶段一次性灌入大量数据。在实际项目中插入前往往先建好集合上的索引如users.ensureIndex(age)这样后续范围查询会直接命中二分查找效率更高相关性能主题可参考 tutorials/Indexing and Query performance.md 与 spec/generic/binaryidx.spec.js 测试。四、查询文档find、findOne 与 where4.1 查询对象与 find文档的简单 find 查询示例var results users.find({ age: {$gte: 35} }); var odin users.findOne({ name:Odin });find接受一个查询对象Query Object支持隐式等值{ age: 35 }等价于{ age: { $eq: 35 } }比较操作符$eq、$gt、$gte、$lt、$lte、$between等组合逻辑对象键隐式$and也可显式写{ $and: [...] }、$or点路径{ attributes.eyes: 1 }直达嵌套字段数组操作$contains、$size等。findOne与find的区别在于只返回第一条匹配文档无匹配返回null。从源码看Collection.prototype.find就是chain().find(query).data()的语法糖src/lokijs.js#L7086-L7088findOne则通过Resultset.prototype.find的firstOnly参数提前终止遍历src/lokijs.js#L7042-L7046。仓库中的 examples/quickstart-core.js 对这套查询对象做了更完整的演示{ age: { $eq: 1000 } }显式等值、{ age: { $gt: 500 } }范围、{ age: 29, gender: f }隐式与、{ $and: [...] }显式与、{ age: { $between: [20, 40] } }区间、{ attributes.eyes: 1 }嵌套路径、{ items: { $contains: eski } }数组包含、{ items: { $size: 2 } }数组长度以及$loki主键删除等操作可作为完整 API 清单逐条运行对照。4.2 where函数式过滤当查询对象无法表达复杂业务逻辑时用where传入过滤函数var results users.where(function(obj) { return (obj.age 35); });where是纯 JavaScript 函数式过滤src/lokijs.js#L7180-L7182每次执行都会遍历全部文档并调用你的函数因此无法利用索引加速只适合小数据量或查询对象难以表达的临时条件高频路径建议优先用find 索引。五、链式操作Resultset 组合查询官方文档的链式示例var results users.chain().find({ age: {$gte: 35} }).simplesort(name).data();chain()返回一个 Resultset惰性求值的结果集对象内部通过filteredrows记录符合条件的行号后续所有过滤、排序、分页操作都以链式叠加直到调用data()才真正产出结果数组。常见的链式方法包括find(query)/where(fn)叠加过滤条件可连续调用多个find条件取交集simplesort(propname, options)按字段排序第二个参数为true时降序src/lokijs.js#L3209还有基于索引的sort变体limit(n)/offset(pos)分页src/lokijs.js#L3013、src/lokijs.js#L3034update(fn)/remove()对结果集内的所有文档执行批量更新 / 删除无需取回数据src/lokijs.js#L3877data(options)终止链可传{ removeMeta: true }剥离$loki与meta字段或{ forceClones: true }强制返回克隆对象src/lokijs.js#L3795-L3864。examples/quickstart-chaining.js 完整覆盖了以上用法其中几个片段很能说明问题// 按年龄降序取前两名 result users.chain().simplesort(age, true).limit(2).data(); // 混合 find 与 where 过滤器 result users.chain().find({ age: 29 }).where(function(obj) { return obj.gender f }).data(); // 对结果集内文档批量 1 岁无需 data() users.chain().find({ age: { $between: [30, 40] } }) .update(function(obj) { obj.age obj.age1; }); // 去除元信息的浅克隆结果 result users.chain().data({ removeMeta: true });从源码看update在集合启用了克隆/增量变更追踪时会先克隆文档、执行你的函数再回写collection.update以保证变更记录完整这一点在需要 Changes API 追踪数据变更的场景中至关重要。六、命名 Transform把查询链存成存储过程官方文档的 transform 示例users.addTransform(progeny, [ { type: find, value: { age: {$lte: 40} } } ]); var results users.chain(progeny).data();Transform 是查询链的对象化表示把find、where、simplesort、limit、offset、update等步骤按顺序写成一个对象数组用addTransform(name, steps)命名保存后就能像调用存储过程一样通过chain(name)复用Collection.prototype.addTransform。配套 API 还有getTransform、setTransform、removeTransformsrc/lokijs.js#L5336-L5357。Transform 还支持[%lktxp]前缀的参数占位符。例如 examples/quickstart-transforms.js 中的分页模板users.addTransform(paged, [ { type: offset, value: [%lktxp]pageStart }, { type: limit, value: [%lktxp]pageSize } ]); var page 1, pageSize 5, start (page - 1) * pageSize; var result users.chain(paged, { pageStart: start, pageSize: pageSize }).data();调用chain(name, params)/transform(name, params)时传入的参数对象会替换模板中的[%lktxp]xxx占位符替换逻辑见 src/lokijs.js#L72-L76 附近的参数解析辅助函数这让分页热门筛选这类高频查询只需定义一次。七、Dynamic View持续维护的活视图官方文档的动态视图示例var pview users.addDynamicView(progeny); pview.applyFind({ age: {$lte: 40} }); pview.applySimpleSort(name); var results pview.data();Dynamic ViewCollection.prototype.addDynamicView与一次性chain()的最大区别在于增量维护视图在集合上注册后通过applyFind、applyWhere、applySimpleSort、applySort设定过滤与排序规则DynamicView.prototype.applyFind、DynamicView.prototype.applySimpleSort此后每当集合insert/update/remove视图会自动评估新文档是否进入结果集并调整顺序pview.data()拿到的始终是最新结果无需重新全量扫描。这对于展示层需要实时跟随数据变化的场景如下拉列表、实时报表尤其合适。Dynamic View 还支持对当前结果做二次查询——branchResultset()会基于视图现有结果派生一个 Resultset并可选套用 Transform// 在 over 500 视图结果中继续筛选男性 result ov500.branchResultset().find({ gender: m }).data(); // 视图结果 命名 transform 组合成命名抽取 result ov500.branchResultset(paged, { pageStart: start, pageSize: pageSize }).data();对应实现见 DynamicView.prototype.branchResultset完整示例见 examples/quickstart-dynview.js。八、落地实践持久化与自动初始化前面各节都是纯内存操作真实应用中通常还需要把数据保存到磁盘。综合 examples/quickstart-dynview.js 与 examples/quickstart-transforms.js 的用法一个带持久化的最小模板如下const loki require(../src/lokijs.js); var db new loki(app.db, { autoload: true, // 启动时自动加载既有数据库文件 autoloadCallback: databaseInitialize, // 加载完成后回调 autosave: true, // 自动保存 autosaveInterval: 4000 // 每 4 秒保存一次 }); function databaseInitialize() { var users db.getCollection(users); // 首次运行集合不存在则创建 if (!users) { users db.addCollection(users); } // 首次运行创建动态视图applyFind 的过滤条件会随库持久化 if (!users.getDynamicView(over 500)) { let ov500 users.addDynamicView(over 500); ov500.applyFind({ age: { $gte: 500 } }); ov500.applySimpleSort(age, true); } // 首次运行注册命名 transform同样会持久化 if (!users.getTransform(females)) { users.addTransform(females, [{ type: find, value: { gender: f } }]); } if (users.count() 0) { seedData(); // 首次运行灌入种子数据 } runProgramLogic(); }要点与源码佐证autoloadCallback是恢复现场的标准入口动态视图的 find 过滤条件、命名 transform、集合本身都会随数据库 JSON 持久化但applyWhere的函数过滤无法序列化每次加载后都需要在回调里重新调用applyWhere恢复examples/quickstart-dynview.js 中对此有明确注释db.getCollection在集合不存在时返回nullsrc/lokijs.js#L1216-L1229这正被用来区分首次运行与已有数据autosave由定时器驱动Node.js 进程退出前若未到保存间隔可在SIGINT等退出事件中调用db.close()强制落盘db.close()仅在集合标记为 dirty 时执行保存。在浏览器端LokiJS 同样可用只需换成浏览器适配器如 src/loki-indexed-adapter.js、src/loki-localstorage-adapter.js 系列的 IndexedDB / LocalStorage 适配器API 使用方式完全一致。九、结语与延伸阅读至此官方 OVERVIEW.md 展示的全部核心 API——loki实例、addCollection、insert、find/findOne、where、链式chain、命名 Transform、Dynamic View——都已覆盖并且每一处都从 src/lokijs.js 源码与 examples 示例中找到了对应实现与完整用法。如果你想继续深入仓库还提供了更完整的中文可读教程tutorials/Autoupdating Collections.md、tutorials/Collection Transforms.md、tutorials/Query Examples.md、tutorials/Indexing and Query performance.md类级 API 参考docs 目录下由 jsdoc 生成的 Loki.html、Collection.html、Resultset.html、DynamicView.html全套行为测试如 spec/generic/collection.spec.js、spec/generic/dynamicview.spec.js、spec/generic/transforms.spec.js可用来校验你对 API 语义的理解。按官方推荐你也可以直接用npm install lokijs引入依赖当前仓库的 package.json 即记录了 lokijs 的包结构然后在自己的 Node.js 或浏览器项目中复刻本文各小节示例。赞分享数据库后端【免费下载链接】LokiJSjavascript embeddable / in-memory database项目地址https://gitcode.com/gh_mirrors/lo/LokiJS点击查看免费下载相关推荐推荐使用LokiJS超快速的JavaScript内存数据库推荐使用LokiJS超快速的JavaScript内存数据库 如果你正在寻找一个高性能、轻量级且适应多环境的JavaScript数据库解决方案那么LokiJS数据库后端如何用Tesla-Menu解锁Switch隐藏功能从覆盖菜单到系统定制的完整路径如何用Tesla Menu解锁Switch隐藏功能从覆盖菜单到系统定制的完整路径 Tesla Menu是Nintendo Switch平台上的一款革命性覆盖菜嵌入式Cayley 作为 Go 库使用快速上手内存图、路径查询与持久化后端接入实战Cayley 作为 Go 库使用快速上手内存图、路径查询与持久化后端接入实战 本指南基于 Cayley一个开源图数据库的官方库使用文档讲解如何将 Cay图数据库数据库后端上一篇PostHog AI 可观测性成本拆分指南一份覆盖模型、用户、Trace 与缓存经济学的 SQL 配方集下一篇VictoriaMetrics 与 VictoriaLogs 的 OpenTelemetry 接入实战Kubernetes 部署、OTLP 采集与 Go 应用埋点创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询