在IndexedDB中使用复合索引时如何查询单列数据
问题描述
我有一个IndexedDB数据库,其中包含基于['name', 'surname']两个字段的复合索引。如何查询所有name=='Will'的行?
ChatGPT给出了如下示例代码,但无法正常工作(返回空数组):
const getDb = async () => await openDB("myDB", 1, { upgrade(db, _oldVersion, _newVersion, _transaction) { var roleStore = db.createObjectStore("myStore", {autoIncrement: true}); roleStore.createIndex('compoundIndex', ['name', 'surname'], { unique: true, }); }, }); const fetchName = async(name) => { const db = await getDb(); return await db .transaction("myStore", 'readonly') .objectStore("myStore") .index('compoundIndex') .getAll(IDBKeyRange.only([name, ''])); } console.log(fetchName("Will")); // Returns empty array!
是否存在无需获取所有值再使用JS的.filter()函数,就能查询所有匹配name=='Will'行的简便方法?
解决方案
问题根源
原代码使用IDBKeyRange.only([name, '']),这只会精确匹配name为Will且surname为空字符串的条目,自然无法返回其他surname不为空的Will相关数据。
正确实现
利用复合索引的排序特性,使用IDBKeyRange.bound构造范围查询:起始键设为[name, ''],结束键设为[name, '\uffff'](\uffff是Unicode编码中的最大字符,能覆盖所有可能的surname值),这样就能匹配所有name为目标值的条目。
修正后的查询函数如下:
const fetchName = async(name) => { const db = await getDb(); // 构造匹配所有name为目标值的键范围 const range = IDBKeyRange.bound( [name, ''], [name, '\uffff'], false, // 不排除起始键 false // 不排除结束键 ); return await db .transaction("myStore", 'readonly') .objectStore("myStore") .index('compoundIndex') .getAll(range); }
验证示例(可选)
若要验证功能,可添加插入测试数据的函数:
const insertData = async(data) => { const db = await getDb(); const tx = db.transaction("myStore", 'readwrite'); await tx.objectStore("myStore").add(data); await tx.done; } // 插入测试数据 await insertData({name: 'Will', surname: 'Smith'}); await insertData({name: 'Will', surname: 'Johnson'}); await insertData({name: 'John', surname: 'Doe'}); // 查询验证 console.log(await fetchName("Will")); // 返回两条Will相关的数据
内容的提问来源于stack exchange,提问作者Ander
相关产品推荐
相关产品推荐

