如何更新IndexedDB中数组内的对象?及put方法相关疑问
问题解答
一、更新子项isHappy的最优实现方式
当前方案的效率瓶颈在于把children作为父对象的嵌套数组存储,每次更新子项都需要完整读取整个父对象、遍历数组修改,再把整个父对象写回数据库,数据量越大开销越高。
最优方案是拆分数据结构,将子项单独创建一个独立的对象仓库,通过新增的parentSsn字段和父对象建立关联。这样更新子项时,直接操作子仓库的对应条目即可,无需读取和修改父对象。
1. 调整数据库结构(版本升级)
修改onupgradeneeded逻辑,新增子项的对象仓库:
const request = indexedDB.open(nameOfDB, 2); // 版本号升级为2 request.onerror = (event) => { console.error(`Database error: ${event.target.errorCode}`); }; request.onupgradeneeded = (event) => { const db = event.target.result; // 父对象仓库(已存在则跳过创建) if (!db.objectStoreNames.contains(nameOfObjectStore)) { db.createObjectStore(nameOfObjectStore, { keyPath: "ssn" }); } // 新增子项对象仓库,用`name+parentSsn`作为唯一键 const childStore = db.createObjectStore('children', { keyPath: ['name', 'parentSsn'] }); // 建立索引,方便通过父对象ssn快速查询所有子项 childStore.createIndex('by_parentSsn', 'parentSsn', { unique: false }); };
2. 迁移旧数据(首次调整结构时执行)
如果已有旧数据,需要把原父对象中的children迁移到新仓库:
// 示例:迁移Alex的子项数据 const transaction = db.transaction([nameOfObjectStore, 'children'], 'readwrite'); const personStore = transaction.objectStore(nameOfObjectStore); const childStore = transaction.objectStore('children'); personStore.get(123).onsuccess = (e) => { const person = e.target.result; person.children.forEach(child => { childStore.put({ ...child, parentSsn: person.ssn }); }); // 可选:移除父对象中的children字段,减少存储体积 delete person.children; personStore.put(person); };
3. 直接更新子项的isHappy属性
现在更新Emma的isHappy只需操作子仓库,无需处理父对象:
const request = indexedDB.open(nameOfDB, 2); request.onerror = (event) => { console.error(`Database error: ${event.target.errorCode}`); }; request.onsuccess = (event) => { const db = event.target.result; const childStore = db.transaction('children', 'readwrite').objectStore('children'); // 通过唯一键(name+parentSsn)获取子项并更新 const getChildRequest = childStore.get(['Emma', 123]); getChildRequest.onsuccess = (e) => { const child = e.target.result; if (child) { child.isHappy = false; childStore.put(child).onsuccess = () => { console.log('successfully updated'); }; } }; };
如果需要批量查询某父对象的所有子项,可使用之前创建的索引:
const index = childStore.index('by_parentSsn'); const cursorRequest = index.openCursor(123); cursorRequest.onsuccess = (e) => { const cursor = e.target.result; if (cursor) { // 处理每个子项 console.log(cursor.value); cursor.continue(); } };
二、关于put()方法的行为
IndexedDB的put()方法是重写整个对象:当传入对象的keyPath值与仓库中已有条目匹配时,会用新对象完全替换旧对象,而非仅更新修改的字段。
这也是嵌套数组存储效率低的核心原因——哪怕只改子项的一个字段,也要把整个大父对象重新写入数据库。拆分存储后,每次只需写入单个子项对象,数据量大幅减少,效率自然提升。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

