【问题标题】:Weird behavior of IDBKeyRange with a compound index具有复合索引的 IDBKeyRange 的奇怪行为
【发布时间】:2014-10-04 19:55:03
【问题描述】:

Here's my test case on JS Bin.

假设您有一个 IndexedDB 对象存储。您的对象非常简单,具有两个属性“a”和“b”。这两个属性都包含整数。并且因为您希望能够在对象存储中查询“a”和“b”的特定值,所以您创建了一个复合索引。以下是此描述之后的一些代码:

var db;

request = indexedDB.open("test", 1);
request.onerror = function (event) { console.log(event); };

request.onupgradeneeded = function (event) {
  var db = event.target.result;
  db.onerror = function (event) { console.log(event); };

  var store = db.createObjectStore("store", {keyPath: "id", autoIncrement: true});
  store.createIndex("a, b", ["a", "b"], {unique: true});

  store.add({a: 0, b: 0});
  store.add({a: 1, b: 0});
  store.add({a: 1, b: 1});
};

request.onsuccess = function (event) {
  db = request.result;
  db.onerror = function (event) { console.log(event); };
};

如果您想从数据库中检索特定对象,这可以正常工作。所以如果你运行这个:

db.transaction("store").objectStore("store").index("a, b").get([1, 0]).onsuccess = function (event) { console.log(event.target.result); };

输出是:

Object {a: 1, b: 0, psid: 2} 

太棒了。伟大的。因此,现在您要检索一系列值。比如说,你想将“b”固定为 0,同时让“a”为 0 或 1。现在奇怪的事情开始发生了。试试这个代码:

console.log("[a, b] bound from [0, 0] to [1, 0]");
db.transaction("store").objectStore("store").index("a, b").openCursor(IDBKeyRange.bound([0, 0], [1, 0])).onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    console.log(cursor.value);
    cursor.continue();
  } else {
    console.log("[a, b] bound from [0, 0] to [2, 0]");
    db.transaction("store").objectStore("store").index("a, b").openCursor(IDBKeyRange.bound([0, 0], [2, 0])).onsuccess = function (event) {
      var cursor = event.target.result;
      if (cursor) {
        console.log(cursor.value);
        cursor.continue();
      }
    };
  }
};

它产生这个输出:

[a, b] bound from [0, 0] to [1, 0]
Object {a: 0, b: 0, id: 1}
Object {a: 1, b: 0, id: 2}
[a, b] bound from [0, 0] to [2, 0]
Object {a: 0, b: 0, id: 1}
Object {a: 1, b: 0, id: 2}
Object {a: 1, b: 1, id: 3}

[0, 0] 和 [1, 0] 之间的边界似乎可以正常工作。但是 [0, 0] 和 [2, 0] 之间的边界没有!它还返回 [0, 1] 作为结果。为什么 [1, 0] 可以作为上限而 [2, 0] 不能?我觉得我要么犯了一个非常愚蠢的错误,要么从根本上误解了什么。

如果你错过了,here is the link to this on JS Bin again

为 Josh 编辑:我最初只使用 bound(x,y) 进行了尝试,这与 bound(x,y,false,false) 相同。将其更改为bound(x,y,true,true) 可以看到here。输出是

[a, b] bound from [0, 0] to [1, 0]
[a, b] bound from [0, 0] to [2, 0]
Object {a: 1, b: 0, id: 2}
Object {a: 1, b: 1, id: 3}

对于这些东西的意义,我仍然没有很好的直观理解。

【问题讨论】:

    标签: javascript indexeddb


    【解决方案1】:

    我试图在the other answer 中解释这一点,但基本上这是因为 indexedDB 规范中定义的短循环数组排序。您正在使用范围查询,并且匹配项都在第一个范围内。如果满足第一个范围条件,则其余范围的操作无关紧要。

    没有解决办法。您可以创建单独的索引(但会遇到爆炸性索引问题)。您可以改为为每个对象创建预先计算的字段。换句话说,不要使用复合索引,而是在对象的这个特别派生的隐藏字段上使用单个索引,每次在 indexedDB 中添加/更新对象时都会更新该隐藏字段。

    【讨论】:

    • 谢谢,我还没有看到你的其他帖子。我想我大部分都明白。但是我仍然不明白为什么使用 [1, 0] 的上限与使用 [2, 0] 的上限不同,因为 1 已经 >= 第一个索引中的任何值。我的意思是,我可以看到它是由您在另一篇帖子中粘贴的规范部分定义的,但 1
    • 只是好奇,您是尝试 IDBKeyRange.bound(x,y,true,true) 还是只是 IDBKeyRange.bound(x,y,false,false) ?
    • 旁注:如果使用 IDBKeyRange.only,则无需在语句中指定,只需使用store.openCursor(keypath) 和/或store.get(keypath)。 indexedDB 将隐式将表达式视为与store.openCursor(IDBKeyRange.only(keypath)) 相同。
    • 好吧,我也对“1
    • 我确定不是 JS 问题,而是 IDB 问题。聪明的@KyawTun(他发布了另一个答案)可能是唯一真正了解这些东西的人,我希望他能启发我们。
    【解决方案2】:

    您的索引不适用于查询b 的修复值。使用复合索引时,认为复合索引有两个部分,prefix and postfix。前缀键路径是您要过滤的路径。后缀是您要订购的那个。

    在您的情况下,您希望按b 过滤并按a 排序,因此索引应为['b', 'a']。由于前缀和后缀 keyPath 可以有多个字段,因此可以使用 IndexedDB 完成非常复杂的查询。

    【讨论】:

    • 我确定你是对的,但你从哪里得到这个?我在 IDB 规范或其他任何地方都找不到这些东西。
    • 还有一个很好的解释为什么将上限设置为[1, 0] 与将其设置为[2, 0] 不同,因为如果我理解正确,它应该进行
    猜你喜欢
    • 2016-09-08
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多