persistence.js is a asynchronous Javascript object-relational
mapper library. It can be used both in the web browser and on
the server using node.js. It currently
supports 4 types of data stores:
There is also an experimental support for Qt 4.7 Declarative UI framework (QML) which is an extension to JavaScript.
For browser use, persistence.js has no dependencies on any other
frameworks, other than the Google Gears initialization
script, in case you
want to enable Gears support.
There are a few persistence.js plug-ins available that add functionality:
persistence.search.js, adds simple full-text search capabilities,
see docs/search.md for more information.persistence.migrations.js, supports data migrations (changes to
the database schema), see docs/migrations.md for more information.persistence.sync.js, supports database synchronization with a
remote server, see docs/sync.md for more information.jquery.persistence.js, adds jQuery integration, including
jQuery-mobile ajax request interception and re-routing to persistencejs,
see docs/jquery.md for more information and demo/jquerymobile for a
simple demo.In browsers, Javascript and the web page's rendering engine share a single thread. The result of this is that only one thing can happen at a time. If a database query would be performed synchronously, like in many other programming environments like Java and PHP the browser would freeze from the moment the query was issued until the results came back. Therefore, many APIs in Javascript are defined as asynchronous APIs, which mean that they do not block when an "expensive" computation is performed, but instead provide the call with a function that will be invoked once the result is known. In the meantime, the browser can perform other duties.
For instance, a synchronous database call call would look as follows:
var results = db.query("SELECT * FROM Table");
for(...) { ... }
The execution of the first statement could take half a second, during which the browser doesn't do anything else. By contrast, the asynchronous version looks as follows:
db.query("SELECT * FROM Table", function(results) {
for(...) { ... }
});
Note that there will be a delay between the db.query call and the
result being available and that while the database is processing the
query, the execution of the Javascript continues. To make this clear,
consider the following program:
db.query("SELECT * FROM Table", function(results) {
console.log("hello");
});
console.log("world");
Although one could assume this would print "hello", followed by "world", the result will likely be that "world" is printed before "hello", because "hello" is only printed when the results from the query are available. This is a tricky thing about asynchronous programming that a Javascript developer will have to get used to.
localStorage (e.g. Firefox)(The following is being worked on:)
Internet Explorer is likely not supported (untested) because it
lacks __defineGetter__ and __defineSetter__ support, which
persistence.js uses heavily. This may change in IE 9.
bower:bower install persistence
Add a <script> to your index.html:
lib/persistence.js needs to be added, as well as any data stores you want to use. Note that the mysql and
websql stores both depend on the sql store. A typical setup requires you to add at least
lib/persistence.js, lib/persistence.store.sql.js and lib/persistence.store.websql.js as follows:
<script src="https://github.com/coresmart/persistencejs/raw/0.3.0/bower_components/persistencejs/lib/persistence.js"></script>
<script src="https://github.com/coresmart/persistencejs/raw/0.3.0/bower_components/persistencejs/lib/persistence.store.sql.js"></script>
<script src="https://github.com/coresmart/persistencejs/raw/0.3.0/bower_components/persistencejs/lib/persistence.store.websql.js"></script>
If you want to use the in-memory store (in combination with
localStorage) you also need the persistence.store.memory.js
included.
Using directly from source:
git clone git://github.com/zefhemel/persistencejs.git
Copy directories you will need following almost the same instructions above.
You need to explicitly configure the data store you want to use, configuration of the data store is store-specific. The WebSQL store (which includes Google Gears support) is configured as follows:
persistence.store.websql.config(persistence, 'yourdbname', 'A database description', 5 * 1024 * 1024);
The first argument is always supposed to be persistence. The second
in your database name (it will create it if it does not already exist,
the third is a description for you database, the last argument is the
maximum size of your database in bytes (5MB in this example).
The in-memory store is offered as a fallback for browsers that do not support any of the other supported stores (e.g. WebSQL or Gears). In principal, it only keeps data in memory, which means that navigating away from the page (including a reload or tab close) will result in the loss of all data.
A way around this is using the persistence.saveToLocalStorage and
persistence.loadFromLocalStorage functions that can save the entire
database to the localStorage, which
is persisted indefinitely (similar to WebSQL).
If you're going to use the in-memory store, you can configure it as follows:
persistence.store.memory.config(persistence);
Then, if desired, current data can be loaded from the localStorage using:
persistence.loadFromLocalStorage(function() {
alert("All data loaded!");
});
And saved using:
persistence.saveToLocalStorage(function() {
alert("All data saved!");
});
Drawbacks of the in-memory store:
localStorage.A data model is declared using persistence.define. The following two
definitions define a Task and Category entity with a few simple
properties. The property types are based on SQLite
types, specifically supported
types are (but any SQLite type is supported):
TEXT: for textual data INT: for numeric valuesBOOL: for boolean values (true or false)DATE: for date/time value (with precision of 1 second)JSON: a special type that can be used to store arbitrary
JSON data. Note that this data can not be used
to filter or sort in any sensible way. If internal changes are made to a JSON
property, persistence.js may not register them. Therefore, a manual
call to anObj.markDirty('jsonPropertyName') is required before calling
persistence.flush.Example use:
var Task = persistence.define('Task', {
name: "TEXT",
description: "TEXT",
done: "BOOL"
});
var Category = persistence.define('Category', {
name: "TEXT",
metaData: "JSON"
});
var Tag = persistence.define('Tag', {
name: "TEXT"
});
The returned values are constructor functions and can be used to create new instances of these entities later.
It is possible to create indexes on one or more columns using
EntityName.index, for instance:
Task.index('done');
Task.index(['done', 'name']);
These indexes can also be used to impose unique constraints :
Task.index(['done', 'name'],{unique:true});
Relationships between entities are defined using the constructor
function's hasMany call:
// This defines a one-to-many relationship:
Category.hasMany('tasks', Task, 'category');
// These two definitions define a many-to-many relationship
Task.hasMany('tags', Tag, 'tasks');
Tag.hasMany('tasks', Task, 'tags');
The first statement defines a tasks relationship on category objects
containing a QueryCollection (see the section on query collections
later) of Tasks, it also defines an inverse relationship on Task
objects with the name category. The last two statements define a
many-to-many relationships between Task and Tag. Task gets a
tags property (a QueryCollection) containing all its tags and vice
versa, Tag gets a tasks property containing all of its tasks.
The defined entity definitions are synchronized (activated) with the
database using a persistence.schemaSync call, which takes a callback
function (with a newly created transaction as an argument), that is called
when the schema synchronization has completed, the callback is
optional.
persistence.schemaSync();
// or
persistence.schemaSync(function(tx) {
// tx is the transaction object of the transaction that was
// automatically started
});
There is also a migrations plugin you can check out, documentation can be found in persistence.migrations.docs.md file.
You can also define mix-ins and apply them to entities of the model.
A mix-in definition is similar to an entity definition, except using
defineMixin rather than just define. For example:
var Annotatable = persistence.defineMixin('Annotatable', {
lastAnnotated: "DATE"
});
You can define relationships between mix-in and entities. For example:
// A normal entity
var Note = persistence.define('Note', {
text: "TEXT"
});
// relationship between a mix-in and a normal entity
Annotatable.hasMany('notes', Note, 'annotated');
Once you have defined a mix-in, you can apply it to any entity of your model,
with the Entity.is(mixin) method. For example:
Project.is(Annotatable);
Task.is(Annotatable);
Now, your Project and Task entities have an additional lastAnnotated property.
They also have a one to many relationship called notes to the Note entity.
And you can also traverse the reverse relationship from a Note to its annotated object.
Note that annotated is a polymorphic relationship as it may yield either a Project
or a Task (or any other entity which is `Annotatable').
Note: Prefetch is not allowed (yet) on a relationship that targets a mixin. In the example above
you cannot prefetch the annotated relationship when querying the Note entity.
Notes: this feature is very experimental at this stage. It needs more testing. Support for "is a" relationships (classical inheritance) is also in the works.
New objects can be instantiated with the constructor functions. Optionally, an object with initial property values can be passed as well, or the properties may be set later:
var task = new Task();
var category = new Category({name: "My category"});
category.metaData = {rating: 5};
var tag = new Tag();
tag.name = "work";
Many-to-one relationships are accessed using their specified name, e.g.: task.category = category;
One-to-many and many-to-many relationships are access and manipulated
through the QueryCollection API that will be discussed later:
task.tags.add(tag);
tasks.tags.remove(tag);
tasks.tags.list(tx, function(allTags) { console.log(allTags); });
Similar to hibernate, persistence.js
uses a tracking mechanism to determine which objects' changes have to
be persisted to the database. All objects retrieved from the database
are automatically tracked for changes. New entities can be tracked to
be persisted using the persistence.add function:
var c = new Category({name: "Main category"});
persistence.add(c);
for ( var i = 0; i < 5; i++) {
var t = new Task();
t.name = 'Task ' + i;
t.done = i % 2 == 0;
t.category = c;
persistence.add(t);
}
Objects can also be removed from the database:
persistence.remove(c);
All changes made to tracked objects can be flushed to the database by
using persistence.flush, which takes a transaction object and
callback function as arguments. A new transaction can be started using
persistence.transaction:
persistence.transact
$ claude mcp add persistencejs \
-- python -m otcore.mcp_server <graph>