Re: [IndexedDB] IDBCursor.update for cursors returned from IDBIndex.openCursor

On Thu, Sep 16, 2010 at 2:15 AM, Jeremy Orlow <jorlow@chromium.org> wrote:
> On Wed, Sep 15, 2010 at 10:45 PM, Jonas Sicking <jonas@sicking.cc> wrote:
>>
>> Heh, I've also been thinking about this exact issue lately. There is a
>> similar question for IDBCursor.delete.
>>
>> On Wed, Sep 15, 2010 at 8:55 AM, Jeremy Orlow <jorlow@chromium.org> wrote:
>> > I think it's clear what IDBCursor does when created from
>> > IDBObjectStore.openCursor or IDBIndex.openObjectCursor: it modifies the
>> > objectStore's value and updates all indexes (or does nothing and returns
>> > an
>> > error if all of that can't be done while satisfying the constraints).
>>
>> Agreed.
>>
>> > But what about IDBCursor.update when created from IDBIndex.openCursor?
>> >  I
>> > see two options: we could modify the value within the objectStore's
>> > value
>> > that corresponds to the objectStore's key path or we could do like above
>> > and
>> > simply modify the objectStore's value.
>>
>> There's also a third option: Throw an exception. Maybe that's what
>> you're referring to by "make this unsupported" below?
>>
>> > More concretely, if we have an object store with a "id" key path and an
>> > index with a "fname" key path, and our index.openCursor() created cursor
>> > is
>> > currently on the {id: 22, fname: "Fred"} value (and thus cursor.key ==
>> > "Fred" and cursor.value == 22), let's say I wanted to change the object
>> > to
>> > be {id: 23, fname: "Fred"}.  In other words, I want id to change from 22
>> > to
>> > 23.  Which of the following should I write?
>> > 1) calling cursor.update(23)   or
>> > 2) calling cursor.update({id: 23, fname: "Fred"})
>> > The former seems to match the behavior of the IDBObjectStore.openCursor
>> > and
>> > IDBIndex.openObjectCursor better (i.e. it modifies the cursor.value).
>> >  The
>> > latter intuitively seems like it'd be more useful.  But to be honest, I
>> > can't think of any use cases for either.  Can anyone else?  If not,
>> > maybe we
>> > should just make this unsupported for now?
>>
>> The only use case I have thought of is wanting to update some set of
>> entries, where the best way to find these entries is through an index.
>> For example updating every entry with a specific shipping-id. You can
>> use IDBIndex.openObjectCursor for this, but that's slower than
>> IDBIndex.openCursor. So in the rare instance when you can make the
>> modification without inspecting the existing value (i.e. you only need
>> to write, read-modify-write), then IDBIndex.openCursor +
>> IDBCursor.update() would be a perf optimization.
>>
>> On the other hand, it might be just as quick to call IDBObjectStore.put().
>>
>> Since the use case if pretty weak (when would you be able to update an
>> entry without first reading the entry), and that you can seemingly get
>> the same performance using IDBObjectStore.put(), I would be fine with
>> making this unsupported.
>>
>> As for IDBCursor.delete(), I can see a somewhat stronger use case
>> there. For example removing all entries with a specific shipping-id or
>> some such. If you can determine which entries should be removed purely
>> on the information in the index, then using IDBIndex.openCursor is
>> definitely faster than IDBIndex.openObjectCursor. So on one hand it
>> would be nice to allow people to use that. On the other hand, I
>> suspect you can get the same performance using IDBObjectStore.delete()
>> and we might want to be consistent with IDBCursor.update().
>>
>> In this case I'm actually leaning towards allowing IDBCursor.delete(),
>> but I could go either way.
>
> Wait a sec.  What are the use cases for non-object cursors anyway?  They
> made perfect sense back when we allowed explicit index management, but now
> they kind of seem like a premature optimization or possibly even dead
> weight.  Maybe we should just remove them altogether?

They are still useful for joins. Consider an objectStore "employees":

{ id: 1, name: "Sven", employed: "1-1-2010" }
{ id: 2, name: "Bert", employed: "5-1-2009" }
{ id: 3, name: "Adam", employed: "6-6-2008" }
And objectStore "sales"

{ seller: 1, candyName: "lollipop", quantity: 5, date: "9-15-2010" }
{ seller: 1, candyName: "swedish fish", quantity: 12, date: "9-15-2010" }
{ seller: 2, candyName: "jelly belly", quantity: 3, date: "9-14-2010" }
{ seller: 3, candyName: "heath bar", quantity: 3, date: "9-13-2010" }
If you want to display the amount of sales per person, sorted by names
of sales person, you could do this by first creating and index for
"employees" with keyPath "name". You'd then use IDBIndex.openCursor to
iterate that index, and for each entry find all entries in the "sales"
objectStore where "seller" matches the cursors .value.

So in this case you don't actually need any data from the "employees"
objectStore, all the data is available in the index. Thus it is
sufficient, and faster, to use openCursor than openObjectCursor.

In general, it's a common optimization to stick enough data in an
index that you don't have to actually look up in the objectStore
itself. This is slightly less commonly doable since we have relatively
simple indexes so far. But still doable as the example above shows.
Once we add support for arrays as keys this will be much more common
as you can then stick arbitrary data into the index by simply adding
additional entries to all key arrays. And even more so once we
(probably in a future version) add support for computed indexes.

/ Jonas

Received on Thursday, 16 September 2010 19:54:14 UTC