Creating a persistent Dojo Object Store

Posted by filed under Dojo Love, Goodies to go, How to, Storage.

As of version 1.6, dojo comes with the new Dojo Object Store API. This is an awesome thing, as it greatly simplifies the work with data stores in Dojo. Everybody who had to do with the traditional dojo.data API felt it was overly complex and hard to use – this has finally changed now. There are also wrappers from and to the old and new APIs, so that you can do stuff like using your traditional data-aware widgets with a new Object Store. And the goodness doesn’t end here; but more on this later. If you haven’t done so yet, you might want to read the excellent post on the new Dojo Object Stores by Kris Zyp where he explains all the awesomeness he created.


Dojo also comes with two fresh implementations for the API, a non-persistent memory store and a JsonRest store that interacts with a server through RESTful HTTP requests. You can also observe changes made to objects in the store. What is missing is a store that uses a client-side persistent backend, which would be useful for a couple of reasons (e.g., as one commenter in that post asks, to use it as chache store for dojo.store.Cache). As Kris already mentions, it’s a piece of cake to create one, so let’s go ahead and do it!

Choosing the Backend

The post mentions two possible candidates: dojox.storage and StorageJS, both already having a similar API, so making them compliant to the Dojo Object Store API is fairly easy. For this tutorial I choose StorageJS – because it is very lightweight and still provides all we need (and, well, for a couple of other reasons that don’t belong in this post). So, what is the exact API we need to comply to? Check it out in the docs. See the first sentence in that paragraph? “Every method in the API is optional, it’s presence indicating support for that feature.” Wow, that’s nice!

Basic compliance

Let’s start with the methods get(), put(), add() and remove(). That will make our store pretty usable for most store scenarios.

First, create a ‘store’ object we can work with (StorageJS creates a global variable called storage where all it’s methods reside):

‘store’ now contains everything that ‘storage’ does, but we can leave the original ‘storage’ object alone and modify our ‘store’ object if needed. Time for the first method, let’s start with get(). StorageJS works with Strings as keys and values only, so we need to parse what we get from it.

Easy one, now move on to put(). The Object Store API allows user to do stuff like this: store.put({foo: 'bar'}, {id: 3}) as well as store.put({id: 3, foo: 'bar'}). Also, StorageJS only has the method set() instead of put() and add().

Easy, as well – you could write this as a one-liner. Now, add(). It’s close to put, except that it won’t overwrite data that already exists. So we just need to check if something with the given id already exists and throw an error if so.

What about remove()? Nothing to do there, it already has the desired signature and functionality.

That’s it, we’re done with basic compliance. Our ‘store’ object now implements the basic methods of the Dojo Object Store API and can be used by anything that can work with a Dojo Object Store – and it will keep it’s state persistent in the browser.

More compliance? Querying!

There’s one interesting thing in the API: the query() method. Uh, yeah, we want that! Lucky us, Dojo already provides a query engine as well as a method that makes sure there are iterative methods and a total property containing the number of hits available in our result (well, it also abstracts away the differences between sync and async results, but as StorageJS is purely synchronous, this is not important to us). First, define a queryEngine:

So, if we want our store to be able to run queries, all we need to do is to take the store’s data and hand it over to the query engine. We use StorageJS’ getAll() method to acquire the data. It will return an array of objects in the form { key: ‘theKey’, value: ‘theValue’}, but the queryEngine wants an array containing only the objects, so we need to do a conversion.

Now we can query our store for specific data, like so: var results = store.query({ prime: true }); This will give us all objects in the store that have a property called ‘prime’ and have that property set to true.

Hierarchie and Transactions

The API also mentions methods about data hierarchie and transactions, but I’m not going to cover these in this tutorial.

Convenience and Optimization

Currently, every time we run a query, we fetch all data again from our storage engine. So, if you don’t have a massive amount of data and want to query the store a lot, it might be wise to maintain a copy of all data in memory. There are other things you could optimize as well – or you could write your own queryEngine.

For your convenience,  here’s a wrapper that creates a Dojo Object Store like we did above and accepts a useMemory parameter during instantiation that denotes whether a copy of the data should be kept in memory or not. It uses the traditional require/provide instead of the AMD format. If you want to use it, make sure you use the right namespace in the declare() call! (Or, not recommended, copy it in the /dojo/store directory.)

If you want to fool around with it in the console first, you can go here (non-memory-using) or here (memory-using), check out the source and fire up the console and try things out.

Getting StorageJS

The above code uses the features base and getAll, so you could do a specific build of StorageJS with these features or just grab a full build of it for your desired storage engine (the storage-full-[engine].js files). If you don’t know about storage engines, all modern browser support localStorage, so this is the one what you might want.

 

[Comments are automatically closed after 30 days.]

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close