使用多个条件查询

37

我最近惊讶地发现 HTML5 不再支持 WebSQL,而是使用 IndexedDB 替代它。

我想知道是否有任何方法可以在 IndexedDB 中进行查询或搜索,类似于我如何使用 SQL 来搜索满足多个条件的条目。

我已经看到可以使用 KeyRange 搜索单个条件的 IndexedDB。但是,我似乎找不到任何一种方式可以搜索两个或更多数据列,而不需要从数据库中获取所有数据并使用 for 循环完成。

我知道这是一个新功能,仍在浏览器中实现,但我正在研究我可以使用的不同方法来开始我的项目。

谢谢!


P.S. 我尝试使用 LocalStorage 存储一个 JSON 编码的数组,并使用 for 循环来筛选我的条件。然而,我的表格有 4000 多个条目,循环遍历所有这些条目需要超过(可能)100 毫秒,这太长了。如果我使用 IndexedDB,除非我可以进行多列查询,否则我不知道如何加快速度。 - jthereliable
1
简而言之:您需要在要查询的属性上创建索引,然后在事务中获取index,然后调用openCursor方法。我可以提供一个示例,但可能需要一天左右的时间... - robertc
谢谢您的回复。我尝试过这样做,但是我无法找到如何查询多行索引以执行类似于SQL WHERE语句中的多列比较的方法。您有什么建议吗? - jthereliable
我会在接下来的几天里尝试解决一个例子并发布一个正确的答案。 - robertc
@jthereliable 不确定你是如何得到这么低的时间的。我刚试图查找包含一系列字符的元素。该集合包含25,000多个元素。过滤集合所需的时间仅在3-7毫秒之间。我猜测,你是在搜索之前立即从本地存储加载的。这很糟糕,因为当你从LocalStorage加载时,每次都必须解析JSON(假设你正在存储为json)。 - Goblinlord
显示剩余2条评论
5个回答

40

查看这个答案,它比我在这里给出的答案更详细。store.createIndex和IDBKeyRange方法的关键路径参数可以是一个数组。因此,举个简单的例子:

// In onupgradeneeded
var store = db.createObjectStore('mystore');
store.createIndex('myindex', ['prop1','prop2'], {unique:false});

// In your query section
var transaction = db.transaction('mystore','readonly');
var store = transaction.objectStore('mystore');
var index = store.index('myindex');
// Select only those records where prop1=value1 and prop2=value2
var request = index.openCursor(IDBKeyRange.only([value1, value2]));
// Select the first matching record
var request = index.get(IDBKeyRange.only([value1, value2]));

1
createIndex方法的选项中有第二个参数:multiEntry,它告诉indexedDB是否应该在数组中的每个元素上创建索引或整个数组上创建索引。更多信息请参见:http://www.w3.org/TR/IndexedDB/#widl-IDBObjectStore-createIndex-IDBIndex-DOMString-name-DOMString-sequence-DOMString--keyPath-IDBIndexParameters-optionalParameters - Micha Roon
1
使用 "index.getAll([value1, value2])"。注意:value1和value2不能为未定义,否则会出现错误 - 参数不是有效的键。 - Dan Alboteanu
如果有人正在检查@MichaRoon的链接,使用此锚点可以减少滚动量:https://www.w3.org/TR/IndexedDB/#index-construct - stackprotector

8

假设你的SQL查询是这样的:

SELECT * FROM TableName WHERE Column1 = 'value1' AND Column2 = 'value2'

JsStore库中的等效查询:

var Connection = new JsStore.Instance("YourDbName");
Connection.select({
    From: "YourTableName"
    Where: {
        Column1: 'value1',
        Column2: 'value2'
    },
    OnSuccess:function (results){
        console.log(results);
    },
    OnError:function (error) {
        console.log(error);
    }
});

现在,如果你想知道 JsStore 是什么,让我告诉你它是一个用于简化 IndexedDB 查询的库。点击这里 了解更多关于JsStore的内容。

1
我晚了几年,但我想指出Josh的答案是基于所有条件中的“列”都是索引的keyPath的前提下才有效。
如果任何一个“列”存在于索引的keyPath之外,那么您将不得不在示例迭代器遍历的每个条目上测试涉及它们的条件。因此,如果您正在处理此类查询,或者您的索引不是unique,则需要准备编写一些迭代代码!
无论如何,如果您可以将查询表示为布尔表达式,我建议您查看BakedGoods
对于这些类型的操作,除非执行严格相等的查询(x ===? y,假设x是对象存储或索引键),否则它将始终在焦点对象存储上打开游标,但它将节省您编写自己的游标迭代代码的麻烦:
bakedGoods.getAll({
    filter: "keyObj > 5 && valueObj.someProperty !== 'someValue'",
    storageTypes: ["indexedDB"],
    complete: function(byStorageTypeResultDataObj, byStorageTypeErrorObj){}
});

为了完全透明,BakedGoods由维护。


1

是的,在索引上打开连续键范围就是 IndexedDB 中的基本操作。在 IndexedDB 中无法测试多个条件,必须在游标循环中完成。

如果您找到解决方案,请告诉我。

顺便说一下,我认为循环游标可能比 Sqlite 更快,需要更少的内存。


1

我在回答这个问题时提到了一些关于查询关系的建议,可能会有所帮助:

IndexedDB中的概念问题(关系等)

至于同时查询多个字段,似乎在IndexedDB中没有本地方法可以实现(我可能错了;我还是新手),但你可以创建一个辅助函数,为每个字段使用单独的游标,并迭代它们以查看哪些记录符合所有条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接