IndexedDB - What is Key, keyPath and indexName? - javascript

I am coming from MySQL and I am used to the conventional database table scheme. I am having trouble understanding IndexedDB and some of its terminology. I looked up these definitions in the documentation:
Key
A data value by which stored values are organized and retrieved in the object store.
indexName
The name of the index to create.
keyPath
The key path for the index to use.
Basically, Key is like a Primary Key in MySQL, right? Is indexName the same thing as a column? And I don't understand what a keyPath is.
Can someone explain these for me? Thank you again for you patience :).

Yes, key is like a primary key in SQL. But others seem to be missing an example explaining the main part of your question, and that is the distinction between indexName and keyPath. Per the Mozilla page on creating an index,
indexName
The name of the index to create. Note that it is possible to create an index with an empty name.
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.
The indexName is what you use to access that index. Indexes are used to search that "column" in the database.
The keyPath is the actual name of the "column." See other questions and answers for what forms a keyPath may take.
Note that "column" is not technically correct, but I'm using it because that's what you used.
For example, suppose your data has the column hours and you want to be able to search your database on that column. When creating your database you would create an index for that column:
objectStore.createIndex(indexName, keyPath, { unique: false });
where indexName can be anything you want, let's say hoursColumn, and keyPath would be hours.
objectStore.createIndex("hoursColumn", "hours", { unique: false });
unique: false just means that other rows of data may have the same value for hours.
I can write data to the objectStore as follows:
db.transaction(storeName, "readwrite").objectStore(storeName).add({hours: 20, minutes: 30});
So to search your data on the column hours, you could write:
var data = db.transaction(storeName).objectStore(storeName).index("hoursColumn").get(20)
and the result would be the first row of data where hours = 20, e.g. {hours: 20, minutes: 30}
So to summarize, indexName is just what you call the index you created that you want to search, and keyPath is the actual name of the stored data on which you want to search.

Indexes are a way to make it possible to query data in the indexeddb database. As you know objects are stored into the objectstores. These objectstore don't have a schema like you have in an normal SQL database.
An index exists out of 3 important properties:
indexName: The indexname is just a name you provide to the index. You will need this name if you want to use the index to query data.
keyPath: This defines which property of the object you want to address in your index. For example: you have an object
{ foo: "bar" }
and you want to query on the foo property, "foo" will be your keypath. The keypath can even go further. You can access nested properties
{ foo: { bar: "bla" } }
If you want to query the bar property the keypath will be "foo.bar"
key: The keys are the values inside the keypath. As you mentioned this key is unique for the index, but this doens't mean this value must be unique over all your objects in the objectstore.
The indexes in the indexeddb work like this:
When you create an index, it creates a new objectstore in which the object will be stored. Instead of storing these object bosed on primary key they are stored based on the values present in the keypath. This means that for a single key in an index you can have multiple objects. So if you start querying an index, it will filter on the keys and return the values that are present in these keys.
Hope this makes indexes clear for you.

I am updating my answer here having re-read the topic of Keys and Indexes. Coming from a MySQL background they are indeed confusing with IndexedDB.
(Please see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB#gloss_outofline_key)
Indexes
Typically these occur on elements within your "row" - so for example if you had a row:
{Hello:"World",Foo:"Bar} in your database you could set an index (of any name) on say Hello. This would be like your indexes in MySQL.
Keys
So where Indexes can have multiple, Keys must (as the earlier answer mentions) be unique like MySQL Primary keys. Why this gets confusing is that you can also filter/search your data by Keys as well as Indexes. (see https://developer.mozilla.org/en-US/docs/Web/API/IDBKeyRange) The difference appears that Key searches appear to be more Ranges (like A-F or > 5 AND < 10) rather than specific values.
There is a very interesting explanation here about keys:
https://golb.hplar.ch/2017/09/A-closer-look-at-IndexedDB.html#primary-keys
Basically it explains that there are 4 types of keys within IndexedDB:
out-of-line : you provide the key yourself on each insert
out-of-line auto generated : primary key (i.e. AutoIncrement) number generated automatically
inline : the contents of field form the index (this is typical to say a MySQL database but must then be unique)
inline auto generated: hidden field (visible in console) added and is accessible through code but not related directly to the object data.
The difference between 2 and 4 is that 2 doesn't get a defined Name, whereas 4 (like a Primary Key in MySQL) does. You could then update (but not insert) on the ID field you've named in 4.
On an aside I believe its called a Keypath because you can call field.subproperty which is quite nice. Please also make sure your keys are spelt correctly! Obvious mistake.

It was also hard for me to understand the difference, but at the end I found out and why most of the time we set them the same name.
In short, the indexName is how we want to the "field" or "column" (index) will be named in our "table" (Object Store) and the keyPath is the property of the stored object that contains the value of the field. Therefore, is logical that they have the same name.
For example, you have a table with following fields:
name
age
They both are the indexName of each field, but you have to tell IndexedDB which property (keyPath) contains the value of each field.
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("age", "age", { unique: false });

Key = Row Item
indexName = Column name
keyPath = Column name

Related

array data insertion in mysql and prevent duplicate in node js [duplicate]

I've searched around but didn't find if it's possible.
I've this MySQL query:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
Field id has a "unique index", so there can't be two of them. Now if the same id is already present in the database, I'd like to update it. But do I really have to specify all these field again, like:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8
Or:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)
I've specified everything already in the insert...
A extra note, I'd like to use the work around to get the ID to!
id=LAST_INSERT_ID(id)
I hope somebody can tell me what the most efficient way is.
The UPDATE statement is given so that older fields can be updated to new value. If your older values are the same as your new ones, why would you need to update it in any case?
For eg. if your columns a to g are already set as 2 to 8; there would be no need to re-update it.
Alternatively, you can use:
INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY
UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;
To get the id from LAST_INSERT_ID; you need to specify the backend app you're using for the same.
For LuaSQL, a conn:getlastautoid() fetches the value.
There is a MySQL specific extension to SQL that may be what you want - REPLACE INTO
However it does not work quite the same as 'ON DUPLICATE UPDATE'
It deletes the old row that clashes with the new row and then inserts the new row. So long as you don't have a primary key on the table that would be fine, but if you do, then if any other table references that primary key
You can't reference the values in the old rows so you can't do an equivalent of
INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4)
ON DUPLICATE KEY UPDATE
id=1, a=2, b=3, c=c + 1;
I'd like to use the work around to get the ID to!
That should work — last_insert_id() should have the correct value so long as your primary key is auto-incrementing.
However as I said, if you actually use that primary key in other tables, REPLACE INTO probably won't be acceptable to you, as it deletes the old row that clashed via the unique key.
Someone else suggested before you can reduce some typing by doing:
INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
There is no other way, I have to specify everything twice. First for the insert, second in the update case.
Here is a solution to your problem:
I've tried to solve problem like yours & I want to suggest to test from simple aspect.
Follow these steps: Learn from simple solution.
Step 1: Create a table schema using this SQL Query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
Step 2: Create an index of two columns to prevent duplicate data using following SQL Query:
ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);
or, Create an index of two column from GUI as follows:
Step 3: Update if exist, insert if not using following queries:
INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';
INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';
Just in case you are able to utilize a scripting language to prepare your SQL queries, you could reuse field=value pairs by using SET instead of (a,b,c) VALUES(a,b,c).
An example with PHP:
$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";
Example table:
CREATE TABLE IF NOT EXISTS `tester` (
`a` int(11) NOT NULL,
`b` varchar(50) NOT NULL,
`c` text NOT NULL,
UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
I know it's late, but i hope someone will be helped of this answer
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
You can read the tutorial below here :
https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/
http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/
You may want to consider using REPLACE INTO syntax, but be warned, upon duplicate PRIMARY / UNIQUE key, it DELETES the row and INSERTS a new one.
You won't need to re-specify all the fields. However, you should consider the possible performance reduction (depends on your table design).
Caveats:
If you have AUTO_INCREMENT primary key, it will be given a new one
Indexes will probably need to be updated
With MySQL v8.0.19 and above you can do this:
mysql doc
INSERT INTO mytable(fielda, fieldb, fieldc)
VALUES("2022-01-01", 97, "hello")
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=newfielda,
fieldb=newfieldb,
fieldc=newfieldc;
SIDENOTE: Also if you want a conditional in the on duplicate key update part there is a twist in MySQL. If you update fielda as the first argument and include it inside the IF clause for fieldb it will already be updated to the new value! Move it to the end or alike. Let's say fielda is a date like in the example and you want to update only if the date is newer than the previous:
INSERT INTO mytable(fielda, fieldb)
VALUES("2022-01-01", 97)
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfielda,fielda),
fieldb=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfieldb,fieldb);
in this case fieldb would never be updated because of the <! you need to move the update of fielda below it or check with <= or =...!
INSERT INTO mytable(fielda, fieldb)
VALUES("2022-01-01", 97)
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfielda,fielda),
fieldb=IF(fielda=STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfieldb,fieldb);
This works as expected with using = since fielda is already updated to its new value before reaching the if clause of fieldb... Personally i like <= the most in such a case if you ever rearrange the statement...
you can use insert ignore for such case, it will ignore if it gets duplicate records
INSERT IGNORE
... ; -- without ON DUPLICATE KEY

React how to address a key from string name

Ok, so I have a database with data sets in it. the application performs a base API call to retrieve that base data set containing all other data sets. I will receive a string variable with the name of the key I need to access so let's say const addon = "Book". However, I don't know the key name beforehand. So the following code works but I need to somehow not hard code the key parameter but rather use the string value incoming from the const addon. I am not quite sure how to do this please point me to the right documentation or explain how to achieve the wanted result.
const columns = levelOne.Book && Object.keys(levelOne.Book);
However, as the incoming param might not be "Book" but anything else it will only work for this case. It is guaranteed that there is a key-value pair where the key bears the name stored in the string value of addon.
You can use a variable as the key. For example, levelOne[variable] where variable is the string that you want to use as the key.
Also, you can get the keys through Object.keys(levelOne) and then you can set variable value from the keys array.

Access element of dictionary in pug/jade

If I have a dictionary like:
x = {"A" : 1, "B" : 2 }
And I pass this dictionary into the pug file, how can I access a specific key value without iterating through all the elements in the dictionary?
For ex. x[A]
The only way I can think of right now is to iterate through the elements:
for key, val in x
And then have an if conditional in there that displays val when key equals A.
Am I missing something obvious here?
If you passes x dictionary to your .pug file you can simply show the key 1 of this dictionary using ${x.A}, same as in normal javascript.
Be sure to add back-tick between the expression.
every Javascript object is an associative array which is the most general sort of array you can invent - sometimes this is called a hash or map structure or a dictionary object.
An associative array is simply a set of key value pairs.
The value is stored in association with its key and if you provide the key the array will return the value.
This is all an associative array is and the name comes from the association between the key and the value. The key is a sort of generalized address that can be used to retrieve the stored value.
For example:
array={key1: 'value1',key2:'value2'};
creates an object called array with two keys and two values which in this case happen to be two constant strings.
Notice that the value stored can be any JavaScript object and in this example it is probably better to think of storing two string objects rather two string literals.
The key can be either an identifier, a string or a number but more about the difference in the key type as we progress.
You can retrieve a value via it key using array notation:
console.log(array['key2']);
Which displays the string value2. If you try and access a key that doesn't exist then you get the result undefined.
As the associative array is used to as the basis of the JavaScript object there is an alternative way to access a value that makes the key look like a property. That is you can access the value using "property syntax" as in:
console.log(array.key2);
So in jade/pug if you pass the x it will show you the similar result like in js. In your case it would be something like that:
${x["A"]} or ${x.A}

How can I get the key as well as the value when using db.js to query IndexedDB?

I have an IndexedDB of changes. I add an item like this, and then log the result to check the key has been created successfully:
_this._idb.add('steps', step).done(function (items) {
var item = items[0];
_logger.log("ADDED STEP", { id: item.__id__, step: item }, "CT");
});
The output from this is as expected:
...as you can see, the id has been added to the object when it is stored.
However, when I query the db to getback a list of objects, using this code:
this._idb.steps.query('timestamp').bound(start, end).execute().done(function (results) {
_logger.log("Results", results, "CT");
}
I don't get the id as part of the object that is returned:
... and the lack of id makes updating and deleting impossible.
How can I get the id of the item when I query indexed db using db.js - or am I approaching this in the wrong way, and is there something else I should be doing?
(Note: I'm using TypeScript to compile the JS, but I don't think that's especially relevant to this question)
This is expected behaviour, you're only going to get the __id__ property if you don't define a keyPath in your db schema.
Because there's no keyPath defined the value is not associated with it in indexeddb, it's only added to the resulting object after it has been added, because at that point in time we know the auto-incremented value that IndexedDB has assigned to it.
Since the value isn't really part of the object I don't have any way to assign it to the object when it comes out during a query, maybe I could use the position in the array but that's more likely to be wrong than right.
If you want the ID to be persisted against the object then you need to define a keyPath as part of the object store schema and the property will be added to the resulting object and available and it will be on the object returned from a query.
Disclaimer - I wrote db.js
Looking at the source, __id__ is only defined when your keyPath is null in the add() method. From what I'm seeing, you'll never see this in a query() response.
In IDB null keyPaths are allowed only when using auto-incrementing ("out-of-line") keys. So if you're getting the object back, it should have an auto-incrementing key on it or some other keyPath.
The __ prefix in JavaScript usually means the developer intended it to be a "private" property. I'm guessing this is for internal use and you shouldn't be counting on this in your application code.
Consider using explicit, so-called "in-line" keys on your object store.
The goal of db.js is easy and simple to use. Your is advanced use case.

Store information in object, sort by key + search object later

I'm having a hard time figuring out this.
So, I am making a site where I need to store the horizontal position of elements in a object with the ID as value. Then I need to sort the object by the key. Later I have to loop though the object and find the closest elements (both before and after).
I am struggling with storing the information. I have it like a variabel.
Is it correct to store it like this:
myObj.x = tempId;
Next, how do I sort the keys? It should sort as a number, not text because I had problem with the sort doing 14, 1500, 800.
And last, how can I loop the object and maintain access to both key and value? If I do for (var i in obj) I loose the key-value.

Categories

Resources