2

I am trying to wrap my head around IndexedDB.

I created an object store that uses a key-generator with no key-path.

var objectStore = db.createObjectStore("domains", {autoIncrement: true });
objectStore.createIndex("domain", "domain", { unique: true, multiEntry: true });

In order to access my records, I created an index that references my object store. The index's key path is the domain property of my records.

Here is an example of what one record in my object store might look like:

{ domain: ["stackexchange",
           "stackoverflow",
           "superuser",
           "askubuntu",
           "serverfault"], 

  bold          : ["<strong>",1], 
  italic        : ["<em>",1],
  strikethrough : ["<del>",1],
  superscript   : ["<sup>",1],
  subscript     : ["<sub>",1],
  heading1      : ["<h1>",1],
  heading2      : ["<h2>",1],
  heading3      : ["<h3>",1],
  blockquote    : ["<blockquote>",1],
  code          : ["<code>",1],
  newline       : ["<br>",2],
  horizontal    : ["<hr>",2]
},

Each entry appearing in the domain property is guaranteed to be unique among the set consisting of the union of every domain key's value out of every record of my object store. I.E. You won't find any of these domains anywhere else in my object store.

I need to let user's edit the objects in the object store. Provided with a single domain, I can retrieve the associated object like this:

var DBOpenRequest= window.indexedDB.open(db_name, db_version);
DBOpenRequest.onsuccess= function(event){   
    var db= DBOpenRequest.result;
    var domains=db.transaction('domains', 'readwrite').objectStore('domains');

    var query = domains.index("domain").get("stackoverflow");   
};

Now, here is the part where I am confused. Once the user has changed the properties that they want to edit/added new properties as they please, I need to save their changes by replacing the object retrieved above with the new object that the user created. (or modify the original object and save the changes).

I think I need to use IDBObjectStore.put() to do this.

The put() method takes two parameters, one required and one optional:

value
    The value to be stored.
key
    The key to use to identify the record. If unspecified, it results to null.

The second parameter is presumably optional for the instances in which you are using inline keys. I am using out-of-line keys that were generated with a key generator.

How can I get the (auto-generated, out-of-line) key of the object that I retrieved through my index, so I can communicate to the object store that I want to modify that pre-existing object and not create a new one?

Is there another way I should be going about this?

Luke
  • 5,567
  • 4
  • 37
  • 66

2 Answers2

1

Below is the text from IndexedDB specs for indexes.

Each index also has a unique flag. When this flag is set to true, the index enforces that no two records in the index has the same key. If a record in the index's referenced object store is attempted to be inserted or modified such that evaluating the index's key path on the records new value yields a result which already exists in the index, then the attempted modification to the object store fails.

So, "put()" cannot be used if a unique index is created on that property. So, instead you can create a primary key i.e. use keyPath while creating object store.

var objectStore = db.createObjectStore("domains", {keyPath: "domain"});

Check below 2 URLs which will help you get more insight that it is not possible to perform an update if a unique index exists on it.

hagrawal7777
  • 14,103
  • 5
  • 40
  • 70
  • To clarify, the implications of your answer are that any object store that has an index which references it and has its(the index's) `unique` flag set to `true` can not have its existing objects modified at all? – Luke Jun 01 '15 at 15:24
  • Precisely. I have did a lot of research and made POC samples, and it is not possible to perform update if an index is present on it. Same is very clear from the W3C's note on indexes, I provided in my answer. Also, you can read other 2 URL. For solution part - use keyPath. – hagrawal7777 Jun 01 '15 at 15:31
  • If I use the array as my key path, as you have suggested, does that mean I can provide a single element of the array to an `IDBObjectStore.get()` call? Or do I have to provide an entire array to use for comparison? – Luke Jun 01 '15 at 22:46
  • You can use a single element for search even if you use an array as keyPath while creating object store. so, for `var objectStore = db.createObjectStore("domains", {keyPath: ["domain1", "domain2"]});` you can search on domain1 as well as domain2. Only catch when using array is key path is that it behaves like a composite primary key which means domain1 and domain2 together will represent a unique entry, hence you can still insert 2 same values for domain1 and domain2. – hagrawal7777 Jun 02 '15 at 06:28
  • So, since its technically the entire array that behaves like the key, it would be perfectly legal to have two objects in the same store that each happen to have an element `"domain1"` If this was the case, and I passed `"domain1"` as the parameter to a get call, would it just return the first entry it runs into? – Luke Jun 02 '15 at 22:54
  • Yes, thats for true if you are trying to retrieve the results using `IDBObjectStore.get()` or `IDBIndex.get()`. If you want to get all the duplicate values then open a cursor and iterate over it. Open the cursor something like `var keyRange = IDBKeyRange.only(<>); cursorHandler = indexHandler.openCursor(keyRange);` – hagrawal7777 Jun 03 '15 at 10:27
0

To future readers. As an alternative to using the keypath:

Rather than using IDBObjectStore.put() you can iterate over the objects in the object store with IDBObjectStore.openCursor()

Once you hit the object you are looking for, you can either use:

Luke
  • 5,567
  • 4
  • 37
  • 66