While the library is still under development, I want to take some time to explain how you can use this framework, and show how easy it gets to work with the Indexed DB. As mentioned before, the library is based on the on the Promises context. This way we can easily handle all the async callbacks on which the Indexed DB API is based, and provide a uniform object to the developer for handling the results and/or errors. And by using the progress callback, we can provide returning multiple records one by one instead of a whole collection in the complete callback. For more information about promises:
Another strength of the library is, that you get the opportunity to let it auto build it’s structure. It’s not necessary to provide the database structure. You can just start writing queries, and the library will take care of the creation of tables and indexes. This way it can easily be used for writing POCs or demo applications. This means when ever you are mentioning a string value in the from method that isn’t known in the object store collection, it will be created for you.
But enough about the theory, let us watch some code.
Setting UP Linq2indexeddb
The first thing you need to do is getting the linq2indexeddb library. You can do this by getting the JS script files from codeplex. The only thing you need to make sure is: the sort.js and where.js file need to be in a “Script” folder under the root of the project. These files are used to preform web worker tasks and are hardcoded referenced for now.
Or VS developers can use Nuget Packages:
- http://www.myget.org/feed/Packages/linq2indexeddb (jQuery version)
- http://www.myget.org/feed/Packages/linq2indexeddbmetro (Win8 version, using WinJS)
Once you have the linq2indexeddb library, the next thing you need to do is adding a reference to it in your page. Note, if you are using the jQuery version, you need to add a reference to the jQuery framework first.
using Linq2indexeddb
Once all the references are added, we can start using the library. The first thing you need to do is creating a instance of the linq2indexeddb object. You can do this by calling the linq2indexeddb method on the window object. This method accepts 3 parameters:
- The first one is the name of the database u want to use.
- The second one is a database configuration object. More on that later on the post.
- The last is an Boolean which allows you to enable logging. By default this is disabled
1: var db = window.linq2indexedDB("dbName", dbConfig, false);
Configuring the database
When opening/creating the database, you can provide a database configuration. Here you can configure the object stores and indexes you want to use. There are several ways to do the configuration, but one thing needs to be provided at all time: the version of the database. This makes sure that the database will have the correct structure we need.
1: var dbConfig = {};
2: dbConfig.version = 1;
A second thing we need to do, is providing a way to define the structure. In the current implementation of the library, there are 4 ways to do this. For now I would advise you to use the definition. This doesn’t require you to write the create/delete statements your self. But if you do want to, you can make use of the promises present in the linq2indexeddb.core to create/delete object stores and indexes.
onupgradeneeded
This a function that gets called when the database needs to get updated to the most recent version you provided in the configuration object. 3 parameters are passed to this function:
- Transaction: on this transaction you can create/delete object stores and indexes
- The current version of the database
- The version the database is upgrading to.
1: dbConfig.onupgradeneeded =
2: function (transaction, oldVersion, newVersion){
3: // Code to upgrade the db structure
4: }
The onupgradeneeded callback is the only one that can’t be used in combination with one of the other ways to define the function
schema
The schema is an object which defines the several versions as a key/value. The key keeps the version it targets and in the value an upgrade function. In this function you can add your code to upgrade the database to the version given in the key. The upgrade function has one parameter:
- Transaction: on this transaction you can create/delete object stores and indexes
1: dbConfig.schema = {
2: 1: function (transaction){
3: // Code to upgrade the db structure to version 1
4: }
5: 2: function (transaction){
6: // Code to upgrade the db structure to version 2
7: }
8: }
Note: If the database needs to upgrade from version 0 to 2, the upgrade function of version 1 gets called first. When the upgrade to version 1 is done, the upgrade function of version 2 gets called.
definition
The definition object is the only way to define your database structure without having to write upgrade code. The object keeps a collection of objects which describe what needs to be added or removed for a version. Each object exists out of the following properties:
- Version: The version where for the definitions are.
- objectStores: Collection of objects that define an object store
- name: the name of the object store
- objectStoreOptions
- autoincrement: defines if the key is handled by the database
- keyPath: the name of the property that keeps the key for the object store
- autoincrement: defines if the key is handled by the database
- remove: indicates if the object store must be removed
- name: the name of the object store
- indexes: Collection of objects that define an index
- objectStoreName: the name of the object store where the index needs to be created on
- propertyName: the name of the property where for we want to add an index
- indexOptions
- unique: defines if the value of this property needs to be unique
- multirow: Defines the way keys are handled that have an array value in the key
- unique: defines if the value of this property needs to be unique
- remove: indicates if the index must be removed
- objectStoreName: the name of the object store where the index needs to be created on
- defaultData: Collection of default data that needs to be added in the version
- objectStoreName: the name of the object store where we want to add the data
- data: the data we want to add
- key: the key of the data
- remove: indicates if the data needs to be removed
- objectStoreName: the name of the object store where we want to add the data
1: dbConfig.definition= [{
2: version: 1,
3: objectStores: [{ name: "ObjectStoreName"
4: , objectStoreOptions: { autoIncrement: false
5: , keyPath: "Id" }
6: }]
7: indexes: [{ objectStoreName: "ObjectStoreName"
8: , propertyName: "PropertyName"
9: , indexOptions: { unique: false, multirow: false }
10: }],
11: defaultData: [{ objectStoreName: ObjectStoreName
12: , data: { Id: 1, Description: "Description1" }
13: , remove: false },
14: { objectStoreName: ObjectStoreName
15: , data: { Id: 2, Description: "Description2" }
16: , remove: false },
17: { objectStoreName: ObjectStoreName
18: , data: { Id: 3, Description: "Description3" }
19: , remove: false }]
20: }];
onversionchange
This a function that gets called when the database needs to get updated to the most recent version you provided in the configuration object. But in contrast to the onupgradeneeded callback, this function can be called multiple times. For example if an database is upgrading from version 0 to version 2, the onversionchange will be called 2 times. Once for version 1 and once for version 2. 2 parameters are passed to the function:
- Transaction: on this transaction you can create/delete object stores and indexes
- The version of the database it is upgrading to.
1: dbConfig.onversionchange =
2: function (transaction, version){
3: // Code to upgrade the db structure
4: }
Querying data
Once the database structure is created, we can start querying on it. On the linq2indexeddb object we have a field linq which holds all the linq functionality. The first method you always need to call is the from method. This method excepts only one parameter, the name of the object store we want to work on. Once we have this we can start inserting, updating, deleting and selecting data from it.
1: // inserting data, key is optional
2: db.linq.from("objectstore").insert({}, key);
3: // updating data, key is optional
4: db.linq.from("objectstore").update({}, key);
5: // removing data
6: db.linq.from("objectstore").remove(id);
7: // clears all data from the object store
8: db.linq.from("objectstore").clear();
9: // gets a single record by his key
10: db.linq.from("objectstore").get(key);
11: // selects all objects
12: db.linq.from("objectstore").select();
But the library also provides ways to filter data. This can be done by calling the where method. This accepts the name of the property we want to filter on as parameter. On this you can call the filter you want to add. For now these are limited to the following:
- equals(value)
- between(value1, value2, value1included, value2included)
- greaterThen(value, valueIncluded)
- smallerThen(value, valueIncluded)
- inArray(arrayOfObjects)
- like(value)
So if we want to select all the objects which have a field called '”property” and have the value “value” we write the following query:
1: db.linq.from("objectstore").where("property").equals("value").select()
If you want to add multiple filters you need to do the following:
1: db.linq.from("objectstore")
2: .where("property").equals("value")
3: .and("anotherproperty").greaterThen(4)
4: .select()
You are also able to sort your data:
- orderBy(propertyName)
- orderByDesc(propertyName)
1: db.linq.orderBy("property").select()
As last, the library also enables you to get only a subset of the properties stored in the objects
1: db.linq.from("objectstore").select(["property1", "property2"])
Conclusion
This was a brief introduction of the functionalities of the linq2indexeddb library. In future post I will go more in-depth into the advanced functionalities like linq2indexeddb.core. If you have any suggestions, bugs, additions,… about the library, feel free to contact me. Hope you enjoy using the library.
This looks very interesting, thanks for your work. I'm going to try using it in a new project I'm starting.
ReplyDeleteFirst minor bug I noticed: greaterThen and smallerThen should be greaterThan and smallerThan (or maybe even lessThan, as it's more commonly phrased).
I have read your blog its very attractive and impressive. I like it your blog.
DeleteLINQ Training in Chennai | Dot Net Online Training
LINQ Training in Chennai | .Net Online Training
Hi,
ReplyDeleteThanks for mentioning it :). I'll try to make the change asap. I'll blame it on my bad english :).
Kristof
hi
ReplyDeletei will use IndexedDB in my Metro App project without use of jQuery.
but this link to download Win 8 Package does not work.
http://www.myget.org/feed/Packages/linq2indexeddbmetro
please help me !
Hi,
ReplyDeleteIf you are using nuget you can just look for Linq2IndexedDB. Then you take the package "Linq2IndexedDB (win8)".
If you are not using nuget, Download the latest version of Linq2IndexedDB from codeplex (http://linq2indexeddb.codeplex.com/releases) and just put the js file in your script map and reference it in your pages.
greetings,
Kristof
Hi,
ReplyDeletei've added a reference to nuget linq2indexedDB Win8 package.
then i wrote a bit code:
var db = window.linq2indexedDB("db1");
when i run the metro app i get an error:
Exception was thrown but not handled in user code at line 20, column 13 in ms-appx://545c9585-83a5-4422-af63-ef9ef9b1e5fe/js/default.js
0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'linq2indexedDB'
File: default.js, line: 20 column: 13
Hi,
ReplyDeleteDid you add a script reference to you linq2indexeddb on your hmtl page? If you did, check if the reference is added before the default.js file.
Greeting
Kristof
yes i had forgotten insert script before default.js
ReplyDeletethat problem resolved but now when i run, my app stopped and the cursor placed on debugger without any specific error !
var terminateAppHandler = function (data) {
debugger;
MSApp.terminateApp(data);
};
If you send me the code, I do want to take a look at it.
ReplyDeleteYou can mail it to kristofdeg at gmail dot com
Hi,
ReplyDeletegreat library and has been very helpful indeed. I am however having an issue when trying to query an the object store. When i am just querying on one property it works fine. When i am querying on two it doesnt seem to do anything. I have the below:
db.linq.from("objs").where("bc")
.equals(bc.toString())
.and("UID")
.equals($.cookie('currentId').toString())
.select().then(function () {
alert("other");
}, handleError, function (data) {
alert("aA");
});
So i am guessing it sint finding anything. But i dont know why because looking at the IndexedDB using chrome seems to work. Any ideas?
So you don't get a complete or an error? Try to configure your linq2indexeddb so it allows debugging. you can do this by passing true as third parameter when you create a linq2indexeddb object
Deletevar db = linq2indexedDB("name", config, true). With the viewer obj you are able to look inside your db an see what data is present and if you go to your console you should see logging appear. (when a db connection is opened, data inserted, data queried, ...).
If you don't seem to find a solution, you can send me an example or your source and I will take a look at it.
Can you maybe provide an example of the object you store in the objs objectstore?
greetings,
Kristof
Hi, I am still working through it but whenever i put in the where clause i am getting the below Javascript error:
DeleteTimestamp: 20/08/2012 8:16:35 AM
Error: Script file not found: /Scripts/Linq2IndexedDB.js
In IIS for some reason it is going back up another level of directory. I will look thru but any other ideas where/what could be causing this?
Thanks in advance.
Sorry to spam you here. As soon as i add the where clause as soon as it hits that line it does a request for the Link2IndexedDB.js in the wrong directory. Any idea what would be causing this call? I noticed this in the Net tab on firebug.
DeleteHi,
DeleteYes that was my guess on your previous post. The library uses web workers to filter and sort the data when ever you use more than 1 filter. This is because the IndexedDB API only supports 1 filter.
Because I use also my linq2indexeddb as webworker, there is a reference to my file added in the linq2IndexedDB.js file. That is why I advise to put this file in the scripts folder. The reference that exists now is "/Scripts/Linq2IndexedDB.js" so it goes and look for a scripts folder on the root.
It is possible to change that, in the linq2indexedDB.prototype.utilities.linq2indexedDBWorkerFileLocation field I keep the location of the webwerker file (reference to my self). If you change this location, either in the linq2indexeddb.js file or in your html files to the location where the linq2indexeddb.js file is located, it should work again. Note make it an absolute reference and start from the root of your site. (start with '/' and don't use '../' becaus then you could get issues when you reference to the linq2indexeddb.js file from an other page on an other location.)
Let me know if this worked out.
greetings Kristof
Thank you so much. This worked for me. I went to:
Deletelinq2indexedDBWorkerFileLocation: "/Scripts/Linq2IndexedDB.js"
and changed it to:
linq2indexedDBWorkerFileLocation: "Scripts/Linq2IndexedDB.js",
It worked a treat! Thanks again.
Hey Kris I was wondering if you had tested this with large amount of data >100K items?
ReplyDeleteHaven't done any stress test yet, should add this to my task list :)
DeleteHi. I have been working with the library a fair bit recently and i was just wondering it it will work on iOS browsers, specifically the iPad?
ReplyDeleteBy default it won't work on the iPad because safari doesn't support indexedDB, but there is a shim on the WebSQL API by nparashuram. (http://nparashuram.com/IndexedDBShim/) This shim enables indexeddb support on WebSQL browsers like safari. As far as I know, this shim should work on the iPad and is compatible with my library.
DeleteHate to be a pain but do you know of any examples of using the shim with Linq2IndexedDB that i can have a look at?
DeleteTry my test project http://linq2indexeddb.kristofdegrave.be this als make use of the shim if necessary.
DeleteHi, Iam getting an error in FF and Safari. If you go to read a table with a "where" clause it is erroring the first time you go to the site. Works fine in chrome. I assume it is because there are no objects in that object store. Is there anyway around this?
ReplyDeleteCan you provide me some more information about the error? This would be usefull to determine the cause.
DeleteI think it's an issue with security. Depending on the FF version, you need to give permission to allow a site to access the indexeddb API.
greetings,
Kristof
Oh and when i look in the tools in chrome the object store items hadnt been created but the top level one had.
DeleteI think possibly could be a timing issue. I have put a "setTimeout" on the first insert statement that was causing the issue and it seems to work ok. Any ideas?
DeleteTiming can be an issue. When working with a database configuration object in linq2indexeddb it is best to call the initialize method. This will make sure the database structure is created or updated before you start working with it. It is best that all code to insert and query is added inside the success handler of that method. For an example take a look at my linq2IndexedDB demo http://linq2indexeddb.kristofdegrave.be
DeleteHi,
ReplyDeleteI am looking at this and have come across a problem i would like some help on if possible. I have a function in javascript to get configuration from the config table i created and populated. So what i want to do is something like below. The problem i am facing is obviously the control is returned to the calling function before the value is set. Is there a way i can get around this so that it get the value back to the calling function?
function GetConfigSettingFromIndexedDb(category, name){
var value = "";
db.linq.from(constPosConfig)
.where("Name").equals(name)
.and("Category").equals(category)
.select().then(function () {
return value;
},
handleError,
function (data) {
value = data.Value;
}
);
}
I think want you want to accomplish is making the async call sync. I'm afraid this isn't possible for now. I have been looking for a solution for this, because I wanted to offer that in my library. If you are using JavaScript 1.7 (I don't know which browsers support this yet) you can use the yield key word to make async calls sync. In all other ways, you will have to work with callbacks.
DeleteHey Kristof , thanks a lot for this article but i have two quesitons
ReplyDelete1) how to show value in a div on my html page?
2) how to store pictues in this data base and show them?
Thanks in advance
Hello Kristof!
ReplyDeleteIt´s possible use order with more than one field?
Thanks.
Nice one, Thanks for your information very nice blog and article, i have some doubts to make it clear, your article shows me very clear enough, thanks for it.
ReplyDeleteDot Net Training in chennai