IndexedDB - 如何创建和使用简单和复杂的 IDBIndex keyPaths?

IndexedDB - How to create and use simple and complex IDBIndex keyPaths?

我想创建和使用两个 IndexedDB 索引,一个具有简单的 keyPath,另一个具有复杂的 keyPath。虽然在我试过的每个浏览器中都创建成功,但索引似乎在大多数情况下都没有正确填写。

我正在使用的过程(问题末尾的独立测试用例)是:

  1. 使用 { keyPath: 'id', autoIncrement: true }.

  2. 创建具有单个存储的索引数据库
  3. 创建两个索引:第一个索引是用keyPath: 'id'{ unique: false, multiEntry: false }创建的。第二个索引是用 keyPath: ['id', 'name']{ unique: false, multiEntry: false }.

  4. 创建的
  5. 向商店添加一些 (19) 个对象,所有对象都具有 name 属性。

  6. 对两个索引调用 IDBIndex.count

预期 IDBIndex.count 的两次调用都会 return 商店中存在的对象总数 (19)。根据浏览器的不同,这可能会也可能不会发生:

如果我将索引的创建从 { unique: false, multiEntry: false } 更改为仅 { unique: false },则每个浏览器的行为都保持不变。

如果我将复杂索引的创建从 ['id', 'name'] 更改为 (according to the spec) 等价物 id.name,Safari 会将行为更改为 simple: 19, complex : 0¹,其他不变

如果我将复杂索引的创建从 'id' 索引更改为 ['id'],Safari 仍然 returns 简单:19,复杂:0,每隔一个浏览器 returns 简单:0,复杂:0.

我做错了什么?如何创建多属性索引?

(我目前正在使用以下独立示例进行测试,由于使用了 indexedDB,因此无法在 Whosebug 上运行)

var deleteRequest = indexedDB.deleteDatabase('test-db');
deleteRequest.onerror = deleteRequest.onblocked = deleteRequest.onsuccess = function () {
 console.log('Database deleted', arguments);
 var openRequest = indexedDB.open('test-db');
 openRequest.onerror = function (errorEvent) { console.log('open error', errorEvent); };
 openRequest.onblocked = function (blockedEvent) { console.log('open blocked', blockedEvent); };
 openRequest.onupgradeneeded = function (upgradeEvent) {
  console.log('Database opened');
  var database = upgradeEvent.target.result;
  var store = database.createObjectStore('sochrastic', { keyPath: 'id', autoIncrement: true });
  store.createIndex('index_name', ['id', 'name'], { unique: false, multiEntry: false });
  store.createIndex('index_id', 'id', { unique: false, multiEntry: false });
 };
 openRequest.onsuccess = function (successEvent) {
  console.log('Indices created');
  successEvent.target.result.close();
  var openRequest = indexedDB.open('test-db');
  openRequest.onerror = function (errorEvent) { console.log('open error', errorEvent); };
  openRequest.onblocked = function (blockedEvent) { console.log('open blocked', blockedEvent); };
  openRequest.onsuccess = function (upgradeEvent) {
   console.log('Database opened');
   var database = upgradeEvent.target.result;
   var transaction = database.transaction('sochrastic', 'readwrite');
   var store = transaction.objectStore('sochrastic');
   store.add({ name: 'druk', badjorance: 'animal' });
   store.add({ name: 'druk', badjorance: 'beast' });
   store.add({ name: 'druk', badjorance: 'canal' });
   store.add({ name: 'myke', badjorance: 'dice' });
   store.add({ name: 'myke', badjorance: 'evergreen' });
   store.add({ name: 'myke', badjorance: 'fake' });
   store.add({ name: 'myke', badjorance: 'game' });
   store.add({ name: 'grey', badjorance: 'honor' });
   store.add({ name: 'grey', badjorance: 'incognito' });
   store.add({ name: 'grey', badjorance: 'joke' });
   store.add({ name: 'grey', badjorance: 'key' });
   store.add({ name: 'grey', badjorance: 'lemon' });
   store.add({ name: 'brady', badjorance: 'mast' });
   store.add({ name: 'brady', badjorance: 'nothing' });
   store.add({ name: 'brady', badjorance: 'opera' });
   store.add({ name: 'brady', badjorance: 'pear' });
   store.add({ name: 'brady', badjorance: 'quote' });
   store.add({ name: 'brady', badjorance: 'rodent' });
   store.add({ name: 'brady', badjorance: 'sunk' });

   database.close();
   var openRequest = indexedDB.open('test-db');
   openRequest.onerror = function (errorEvent) { console.log('open error', errorEvent); };
   openRequest.onblocked = function (blockedEvent) { console.log('open blocked', blockedEvent); };
   openRequest.onsuccess = function (successEvent) {
    console.log('Store filled');
    var database = successEvent.target.result;
    var transaction = database.transaction('sochrastic', 'readonly');
    var store = transaction.objectStore('sochrastic');
    var indexName = store.index('index_name');
    var nameCountRequest = indexName.count();
    nameCountRequest.onerror = function (errorEvent) { document.querySelector('#name-id-count').textContent = 'error'; console.log(errorEvent); };
    nameCountRequest.onblocked = function (blockedEvent) { document.querySelector('#name-id-count').textContent = 'blocked'; console.log(blockedEvent); };
    nameCountRequest.onsuccess = function (successEvent) { document.querySelector('#name-id-count').textContent = successEvent.target.result; console.log('name-id', successEvent.target.result); };

    var indexId = store.index('index_id');
    var idCountRequest = indexId.count();
    idCountRequest.onerror = function (errorEvent) { document.querySelector('#id-count').textContent = 'error'; console.log(errorEvent); };
    idCountRequest.onblocked = function (blockedEvent) { document.querySelector('#id-count').textContent = 'blocked'; console.log(blockedEvent); };
    idCountRequest.onsuccess = function (successEvent) { document.querySelector('#id-count').textContent = successEvent.target.result; console.log('id', successEvent.target.result); };
   }
  }
 }
}
<!doctype html>
<html>
<head>
 <meta charset="utf-8"/>
 <title>IndexedDB Indices</title>
</head>
<body>
 <p>['id', 'name'] index result: <span id="name-id-count"></span></p>
 <p>['id'] index result: <span id="id-count"></span></p>
</body>
</html>

IE 和 Edge 根本不支持复杂索引https://developer.microsoft.com/en-us/microsoft-edge/platform/status/indexeddbarraysandmultientrysupport/?q=indexeddb,但您可以在其他浏览器中使用它们。

If I change the creation of the complex index from ['id', 'name'] to the (according to the spec) equivalent id.name, Safari changes behaviour to simple: 19, complex: 0, the others remain the same.

它们并不等同。 id.name 在单个值上创建索引,如 {id: {name: 'value'}}['id', 'name'] 在两个字段上创建复合索引,如 {id: 1, name: 'value'}.

您的索引失败是因为您依赖于自动生成的主键 id。如果您将 id 更改为 badjorance,那么您的计数符合您的预期,至少在 Firefox 和 Chrome 中是这样。 (顺便说一句,即使在您的原始示例中,我也没有看到任何区别,两种浏览器都显示为 0)。

我猜有些浏览器会在将主键添加到对象之前评估 keyPath,而其他浏览器会在之后评估。至于哪种行为是正确的……我想阅读规范已经很晚了,但是除非规范不明确,否则您可能想报告浏览器中的错误。如果我是赌徒,我猜 Safari 是错误的。