IDBObjectStore.createIndex() 中 keyPath 的用途是什么?

What's the purpose of keyPath in IDBObjectStore.createIndex()?

我一直在阅读 MDN,但我得到 stuff like:

keyPath
The key path for the index to use. Note that it is possible to create an index with an empty keyPath, and also to pass in a sequence (array) as a keyPath.

嗯,不,s@!t,keyPath 是关键路径。但那是什么?

在所有 examples 中,它与列的名称(或索引,如他们所说)相同:

  objectStore.createIndex("hours", "hours", { unique: false });
  objectStore.createIndex("minutes", "minutes", { unique: false });
  objectStore.createIndex("day", "day", { unique: false });
  objectStore.createIndex("month", "month", { unique: false });
  objectStore.createIndex("year", "year", { unique: false });

They say你可以通过:

我无法想象这有什么用。我完全不明白 keyPath 是什么,我可以用它做什么。有人可以提供示例用法,其中 keyPath 不是列名吗?解释 keyPath 的值对数据库有什么影响?

键路径是您向 indexedDB 指示对象的哪些属性起着特殊作用的方式。类似于您如何向 SQL 数据库指示 table 中的某个列是 table 的主键,或者您如何告诉数据库在一个数据库上创建索引或 table.

中的更多特定列

换句话说,它是 indexedDB 实现在确定应该使用哪个 属性 进行某些计算时应该遵循的路径。例如,在搜索具有给定键的值时。

它是一个路径,而不是一个简单的键,因为它认为对象属性值也可以是对象。换句话说,存在等级制度。例如,{a:{b:1}}。 "path" 到值 1 是 "a.b"。路径是访问值的属性序列。

名称的关键部分表示列起着重要作用。例如,在识别主键 属性 或特定索引 属性.

从某种意义上说,不属于键路径的属性将被忽略,因为 indexedDB 实现只是将整个对象视为一组属性,并且只关注或了解那些属于一部分的属性键路径。

示例可能会有所帮助。如果将键路径与对象存储一起使用,则可以从存储的对象中提取键,而不必在每次 put() 调用时都指定它们。例如,对于只有 ID 和名称的记录,您可以使用记录的 ID 作为对象存储的主键:

store = db.createObjectStore('my_store', {keyPath: 'id'});
store.put({id: 987, name: 'Alice'});
store.put({id: 123, name: 'Bob'});

哪个给你这家店:

key   | value
------+-------------------
123   | {id: 123, name: 'Bob'}
987   | {id: 987, name: 'Alice'}

但是如果你想按名字查找记录,你创建一个索引:

name_index = store.createIndex('index_by_name', 'name');

哪个给你这个索引:

key     | primary key | value
--------+-------------+--------------------------
'Alice' | 987         | {id: 987, name: 'Alice'}
'Bob'   | 123         | {id: 123, name: 'Bob'}

(索引并不真正存储值的副本,仅存储主键。但这种方式更容易可视化。这也解释了如果迭代索引,您将在游标上看到的属性.)

现在您可以按名称查找记录:

req = name_index.get('Alice')

将记录添加到存储时,键路径用于生成索引键。

带有 . 分隔符的关键路径可用于在更复杂的记录中查找。作为数组的键路径可以生成复合键(其中键本身是一个数组)或多个索引条目(如果指定了 multiEntry: true

理解事物的一个好方法是思考您自己将如何设计它。

让我们退后一步,看看一个非常简单的 NoSQL 数据库如何存储数据,然后添加索引。

每个 Store 看起来像一个 JavaScript 对象(或 python 中的字典,C# 中的散列)等...

{
  "1": {"name": "Lara", "age": "20"},
  "2": {"name": "Sara", "age": "22"},
  "3": {"name": "Joe", "age": "22"},
}

这个结构基本上是一个值列表,加上一个 index 来检索值,像这样:

//Raw JS:
var sara = data["2"]
// IndexedDB equivalent:
store.get(2)

这对于直接对象检索来说非常快,但是对于过滤来说很糟糕。如果你想获得特定年龄的人,你需要遍历每个值。

如果你事先知道你会按年龄进行查询,你真的可以通过创建一个按年龄索引值的新对象来加速这样的查询:

{
  "20": [<lara>],
  "22": [<joe>, <sara>],
  ...
}

但是你会如何查询呢?您不能使用默认索引语法(例如 data[22]store.get(22)),因为它们需要键。

一种方法是命名第二个索引,例如by_age_index 并为我们的存储对象提供一种按名称访问该索引的方法,因此您可以这样做:

store.index('by_age_index').get(22) // Returns Joe and Sara

难题的最后一点是告诉索引如何确定哪些记录对应哪个键(因为它必须在记录added/changed/removed)

换句话说,它怎么知道Joe和Sara反对key 22,而Lara反对key 20?

在我们的例子中,我们想要使用每个记录中的字段 age。这就是我们所说的 keyPath 的意思。

因此,在定义索引时,我们指定索引以及名称是有意义的,例如

store.createIndex('by_age_index', 'age');

当然,如果你想像这样访问你的索引:

store.index('age').get(22) // Returns Joe and Sara

然后您需要像这样创建索引:

store.createIndex('age', 'age');

这是大多数人所做的,这就是我们在示例中看到的原因,它给人的印象是第一个参数是 keyPath(而实际上它只是我们为该索引指定的任意名称)让我们不确定是什么第二个参数可能是 for.

我可以这样解释这一切:

The first parameter is the handle by which you access the index on the store, the second parameter is the name of the field on the record by which that index should group its records.

但也许这个纲要也会对其他人有所帮助:-)