UNPKG

gs-idb-pro

Version:

> A full-featured wrapper for browser IndexedDB

345 lines (286 loc) 9.7 kB
# gs-idb-pro > A full-featured wrapper for browser IndexedDB <details> <summary>中文</summary> > 一款对于浏览器indexedDB的全功能封装 > 包含: 查询、游标、分页、事务、关联、数据库版本处理、配置管理……等功能 ## 安装 ```shell npx yarn gs-idb-pro ``` ## 使用 ### 1. 基础使用 #### 1. 基本CRUD ```ts import {dbStore} from 'gs-idb-pro' //在默认数据库中创建名为`user`的`IDBObjectStore`,默认`keyPath`为`id`,`autoIncrement`为`true` const userStore = dbStore('user') // 在 `userStore` 中创建名为`name`的索引 const nameIndex = userStore.index('name'); // 在 `userStore` 中创建名为`age`的索引 const ageIndex = userStore.index('age'); // 通过 `add` 方法向 `userStore` 中添加数据,默认会记录添加时间与修改时间(可配置) await userStore.add({name: '张三', age: 18, id: 1}) await userStore.add({name: '李四', age: 19, id: 2}) await userStore.add({name: '王五', age: 20, id: 3}) await userStore.add({name: '赵六', age: 20, id: 4}) // 通过 `all` 方法获取 `userStore` 中的所有数据 console.log(await userStore.all()) // 通过 `get` 方法获取 `userStore` 中 `id` 为 1 的数据 console.log(await userStore.get(1)) // 通过 `get` 方法获取 `userStore` 中 `name`索引值为 '张三' 的数据 console.log(await nameIndex.get('张三')) // 通过 `get` 方法获取 `age`索引值为 20 的数据 console.log(await ageIndex.get(20)) // 通过 `delete` 方法删除 `userStore` 中 `id` 为 1 的数据 await userStore.delete(1) // 获取分页数据第一页(添加时间降序,前2条) const page1 = await userStore.page({size:2},1) console.log(page1) // 获取下一页分页数据(最后1条数据) console.log(await userStore.nextPage(page1.info)) ``` #### 2. 保存配置信息 > 将某一个`IDBObjectStore`当作一个键值对数据库使用 > 可用于保存常见配置信息 > 特别是文件句柄(`FileSystemHandle`)在目前的浏览器环境中的只能使用`indexedDB`保存 ```ts import {dbMap} from 'gs-idb-pro' /** * 创建一个无`keyPath`的`IDBObjectStore` */ const settings = dbMap('settings'); await new Promise(( (resolve, reject)=> { const btn = document.createElement('button'); btn.innerText = 'Click me to test.' document.body.append(btn) btn.onclick = async ()=>{ try{ // 设置 工作目录 await settings.set('workDir', await showDirectoryPicker()); resolve(); } catch (e) { reject(e) } } })) // 读取 工作目录 的设置 console.log(await settings.get('workDir')) ``` #### 3. 事务与批处理 ```ts // 批量读取,过程中不会重复关闭打开数据库 const result = await userStore.batchRead(async (openedStore) => { return [ await openedStore.get(1), await openedStore.filter({lte: 10, gt: 3}) ] }) // 多次读写操作, 如果中途出错,将回滚所有写入 await userStore.batchWrite(async (openedStore)=>{ // 代码 }) ``` #### 4. 关联 ```ts import {asyncMap } from 'gs-base' import {dbMap} from 'gs-idb-pro' //创建 `user` 与 `role` 的关联存储 const stores = dbStores(['user','role']) // 执行关联读取 const result = await stores.read((userStore, roleStore) => { return asyncMap(userStore.filter(), async (user) => ({...user, role: await roleStore.get(user.roleId)})) }) ``` ### 2. 更多 #### 1. Schema定义 ```ts import {IDbPro} from 'gs-idb-pro' //配置默认数据库名称 IDbPro.defaultDb.schema.name = 'db-name' //配置默认数据库版本 IDbPro.defaultDb.schema.version = 2 //配置将来创建`IDBObjectStore`时的默认值 IDbPro.defaultDb.schema.storeTemplate = { keyPath: 'id', autoIncrement: true, addedTimeField: false, updatedTimeField: 'updated_at', softDeletedField: true } // 创建带默认值、无`keyPath`的`IDBObjectStore` dbMap('map', [ ['v1', 'k1'], ['v2', 'k2'], // …… ]) // 详细创建多索引和默认值的`IDBObjectStore` dbStore({ name: 'user', indexSchemas: [ {name: 'name_age', keyPath: ['name', 'age']}, {name: 'tags', keyPath: 'tags', multiEntry: true}, ], defaultData: [ {name: '张三', age: 18, tags: ['年轻', '贪玩']}, ] }, {name: 'name', unique: true}) // 初始化 Schema (赋予默认值和转换详细信息,仅用于调试配置信息,非调试时不需要手工调用 ) IDbPro.defaultDb.initSchema(); //向控制台输出当前配置结果 IDbPro.defaultDb.traceSchema() ``` #### 2. 多数据库 > 由于浏览器限制,不能同时打开多个数据库 > 所以没有特殊情况不建议创建多个数据库 ```ts const db1 = new IDbPro('db1') const db2 = new IDbPro({name:'db2',version:2}) db1.store('db1-store-1') db1.store('db1-store-2') db2.store('db2-store-1') db2.store('db2-store-2') ``` </details> <details> <summary>English</summary> > A full-featured wrapper for browser IndexedDB > Includes: query, cursor, pagination, transactions, relations, DB version handling, config management, etc. ## Installation ```shell npx yarn gs-idb-pro ``` ## Usage ### 1. Basic Usage #### 1. Basic CRUD ```ts import {dbStore} from 'gs-idb-pro' // Create an `IDBObjectStore` named `user` in the default DB, with `keyPath`=`id` and `autoIncrement`=true const userStore = dbStore('user') // Create an index named `name` in `userStore` const nameIndex = userStore.index('name'); // Create an index named `age` in `userStore` const ageIndex = userStore.index('age'); // Add data using `add` (creation & update timestamps are added by default, configurable) await userStore.add({name: 'John Smith', age: 18, id: 1}) await userStore.add({name: 'Alice Brown', age: 19, id: 2}) await userStore.add({name: 'Michael Lee', age: 20, id: 3}) await userStore.add({name: 'Sarah Johnson', age: 20, id: 4}) // Get all data from `userStore` console.log(await userStore.all()) // Get data with `id` = 1 console.log(await userStore.get(1)) // Get data by index `name` = 'John Smith' console.log(await nameIndex.get('John Smith')) // Get data by index `age` = 20 console.log(await ageIndex.get(20)) // Delete data with `id` = 1 await userStore.delete(1) // Get first page (size 2, sorted by added time desc) const page1 = await userStore.page({size:2},1) console.log(page1) // Get next page (remaining data) console.log(await userStore.nextPage(page1.info)) ``` #### 2. Storing Config Data > Use an IDBObjectStore as a simple key-value DB > Suitable for storing common configuration > Especially FileSystemHandle must be stored in indexedDB in browsers ```ts import {dbMap} from 'gs-idb-pro' /** * Create an `IDBObjectStore` without `keyPath` */ const settings = dbMap('settings'); await new Promise((resolve, reject)=> { const btn = document.createElement('button'); btn.innerText = 'Click me to test.' document.body.append(btn) btn.onclick = async ()=>{ try{ // Set working directory await settings.set('workDir', await showDirectoryPicker()); resolve(); } catch (e) { reject(e) } } }) // Read working directory setting console.log(await settings.get('workDir')) ``` #### 3. Transactions & Batch ```ts // Batch read, DB won't reopen repeatedly const result = await userStore.batchRead(async (openedStore) => { return [ await openedStore.get(1), await openedStore.filter({lte: 10, gt: 3}) ] }) // Multiple read/write in a single transaction; all writes will rollback if error occurs await userStore.batchWrite(async (openedStore)=>{ // code }) ``` #### 4. Relations ```ts import {asyncMap } from 'gs-base' import {dbMap, dbStores} from 'gs-idb-pro' // Create related stores `user` and `role` const stores = dbStores(['user','role']) // Perform relational read const result = await stores.read((userStore, roleStore) => { return asyncMap(userStore.filter(), async (user) => ({...user, role: await roleStore.get(user.roleId)})) }) ``` ### 2. Advanced #### 1. Schema Definition ```ts import {IDbPro} from 'gs-idb-pro' // Set default DB name IDbPro.defaultDb.schema.name = 'db-name' // Set default DB version IDbPro.defaultDb.schema.version = 2 // Default schema template for future stores IDbPro.defaultDb.schema.storeTemplate = { keyPath: 'id', autoIncrement: true, addedTimeField: false, updatedTimeField: 'updated_at', softDeletedField: true } // Create a key-value style store with defaults dbMap('map', [ ['v1', 'k1'], ['v2', 'k2'], // ... ]) // Create store with multiple indexes and default data dbStore({ name: 'user', indexSchemas: [ {name: 'name_age', keyPath: ['name', 'age']}, {name: 'tags', keyPath: 'tags', multiEntry: true}, ], defaultData: [ {name: 'John Smith', age: 18, tags: ['young', 'playful']}, ] }, {name: 'name', unique: true}) // Initialize schema (for debugging, not required normally) IDbPro.defaultDb.initSchema(); // Trace schema in console IDbPro.defaultDb.traceSchema() ``` #### 2. Multiple Databases > Due to browser limits, you can't open multiple DBs at once. > Avoid multiple DBs unless necessary. ```ts const db1 = new IDbPro('db1') const db2 = new IDbPro({name:'db2',version:2}) db1.store('db1-store-1') db1.store('db1-store-2') db2.store('db2-store-1') db2.store('db2-store-2') ``` </details>