Je commence le développement d'une application Web avec des exigences de stockage de base de données hors ligne. Pour faire court, l'application devrait pouvoir fonctionner sur:
La question est donc de savoir quelle technologie choisir: IndexedDB ou Web SQL Database?
En ce qui concerne la base de données Web SQL, d'une part, elle est prête à être utilisée dans l'un des scénarios ci-dessus. D'autre part, Mozilla a déclaré que Firefox ne l'implémenterait jamais, et selon le HTML5 projet de travail la spécification a atteint une impasse:
Cette spécification a atteint une impasse: tous les implémenteurs intéressés ont utilisé le même backend SQL (Sqlite), mais nous avons besoin de plusieurs implémentations indépendantes pour procéder le long d'un chemin de normalisation. Jusqu'à ce qu'un autre implémenteur s'intéresse à l'implémentation de cette spécification, la description du dialecte SQL a été laissée comme une simple référence à Sqlite, ce qui n'est pas acceptable pour une norme. Si vous êtes un implémenteur intéressé à implémenter un backend SQL indépendant, veuillez contacter l'éditeur afin qu'il puisse écrire une spécification pour le dialecte, permettant ainsi à cette spécification d'avancer.
IndexedDB est l'alternative préconisée par Mozilla, mais il ne viendra que dans Firefox 4. Microsoft est intéressé et Chrome le supportera également. Je ne sais rien des plans d'Apple concernant IndexedDB.
Je suis personnellement enclin à choisir Web SQL Database, mais juste parce que je suis habitué à SQLite, j'aime la puissance et l'expressivité de SQL, et je comprends le modèle relationnel. IndexedDB, pour moi, est une incertitude.
Cela dit, j'ai peur de parier sur le mauvais cheval. Est-il sûr de supposer que la prise en charge de Web SQL Database continuera d'exister, même si IndexedDB devient la norme?
(Une note sur CouchDB: la voyez-vous également comme une alternative?)
Étant donné que seul WebSQL prend en charge les trois exigences que vous avez répertoriées, votre choix ne devrait-il pas être simple? Vous n'avez aucune idée de la feuille de route de développement pour Safari ou Android, alors utilisez ce dont vous disposez.
Eh bien, comme pour tout ce qui concerne l'informatique, le jeu est "l'abstraction".
Si vous pouvez trouver une couche adéquate qui fonctionne à la fois sur un magasin SQL et un magasin de clés/valeurs, alors, idéalement, vous êtes isolé du problème et pouvez prendre en charge l'implémentation appropriée sur le navigateur particulier. Si votre modèle de données et vos modèles d'accès ne correspondent pas au plus petit dénominateur commun (c'est-à-dire un magasin k/v), cela résout à peu près votre problème.
Si vous pouvez utiliser l'un ou l'autre magasin, travaillez sur une couche d'accès décente et abordez le problème dans cette direction.
Attention, ce n'est pas parce que vous avez un magasin k/v en arrière-plan que vous devez modéliser vos données uniquement comme un modèle k/v. Essentiellement, tout un DB est sur le backend est un magasin k/v. Si vous n'avez pas une quantité démente de données, vous pouvez faire beaucoup de choses. Avec une grande quantité de données, les cercles que vous devrez peut-être parcourir peuvent vous coûter des performances que vous pourriez ne pas voir avec une plus petite quantité de données. Tout dépend.
Ma recommandation est de opter pour IndexDB, car il y a IndexDB Polyfill disponible.
Tous les navigateurs prenant en charge WebSQL peuvent prendre en charge IndexDB API de cette façon. L'inverse serait très difficile à implémenter, donc si vous voulez atteindre tous les navigateurs qui connaissent certaines API DB, IndexDB est le meilleur choix aujourd'hui.
Remarque: Même si cette question est ancienne, elle est toujours pertinente, donc je pense que les réponses à cette question méritent une mise à jour. Et désolé pour la solution de lien uniquement, j'ai donc ajouté uniquement des liens vers des destinations généralement durables: W3C et GitHub
Votre base de données a-t-elle besoin de bien au-delà des magasins de clés/valeur? Sinon, j'ai trouvé un certain nombre de packages javascript pour l'abstraction de la base de données basée sur un navigateur local. Un tel package est jStore:
http://code.google.com/p/jquery-jstore/
Je l'ai récemment utilisé pour ajouter un stockage de clé/valeur local. Il est bien documenté et le temps d'intégration était négligeable - il prend en charge une gamme de backends de stockage, y compris le stockage local flash, via son API.
CouchDB est une excellente solution - pour un problème qui ne correspond pas tout à fait au vôtre. Découvrez couchone mobile . Pas uniquement pour les "applications Web", mais il peut fournir une base de données avec laquelle vous pouvez exécuter, si vous avez une certaine flexibilité avec les spécifications.
Avec votre exigence donnée de Safari sur iOS, il n'y a pas d'autre alternative que WebSQL. WebSQL est pris en charge dans d'autres navigateurs mobiles comme Opera et Blackberry. Je ne pense pas qu'ils supprimeront le support WebSQL même s'ils ont IndexedDB. Ils sont en quelque sorte complémentaires.
D'un autre côté, sur la guerre du stockage par navigateur, IndexedDB gagne pour de bon. IE et FF auront seulement IndexedDB. Le fait ironique est que FF implémente IndexedDB sur Sqlite.
Ce que je voudrais dire, c'est que IndexedDB est plus qu'un simple magasin de valeurs clés. Il a un index et une transaction. Ces deux seuls font presque toutes les fonctionnalités de la requête SQL, y compris la jointure, conditionnelle et le tri. Ce n'est pas évident au premier abord à cause de son API asynchrone.
Les performances d'IndexedDB sont meilleures que WebSQL. C'est plus sûr. Il est plus flexible pour le cas d'utilisation javascript. Enfin, il est plus facile à utiliser.
Pour illustrer le cas, je vais utiliser le code Sudo de ma bibliothèque , mais vous pouvez utiliser directement l'API IndexedDB:
Le magasin 'people' a un champ d'index 'nom' et un champ d'indexation 'hobby'. En JSON,
people = {
name: 'Foo Bar',
email: '[email protected]'
hobby: ['camping', 'swimming']};
Pour récupérer le nom de "personnes" dont le passe-temps est "camping".
var req = db.keys('people', 'hobby', IDBKeyRange.only('camping'));
req.done(function(campers) {
db.keys('people', campers, 'name').done(function(names) {
console.log(names);
});
});
Ce qui est intéressant avec ce code, c'est qu'il n'y a pas de sérialisation. C'est donc très rapide.
L'exemple suivant illustre la requête du graphique d'amitié. friendship
le magasin d'objets n'a qu'un seul champ indexé répertorié friend_list
. Il utilise la clé de magasin d'objets de personnes comme clé primaire hors ligne. people
le magasin d'objets possède de nombreux attributs, parmi lesquels le champ location
. La requête consiste à trouver une liste d'amis qui connaissent me
et other_guy
et situé à "Singapour".
var q1 = new ydn.db.Iterator('friendship', 'friend_list', IDBKeyRange.only(me));
var q2 = new dn.db.Iterator('friendship', 'friend_list', IDBKeyRange.only(other_guy));
// if location is not indexed, a filtered value query is used.
var q3 = new ydn.db.Iterator('people', new ydn.db.Expression(['"location"', "'Singapore'", '=']));
// if location is indexed, an index query is used.
// var q3 = new ydn.db.Iterator('people', 'location', IDBKeyRange.only('Singapore'));
var current_loop = 2; // start from inner loop
var join_algo = function(keys, index_keys) {
var advancement = [];
advancement[keys.length - 1] = null;
var has_adv = false;
for (var i = 0; i < keys.length; i++) {
if (!goog.isDef(keys[i])) {
// completed iterator
if (i != 0) {
advancement[i] = false; // request to restart the iteration
advancement[i - 1] = true; // advance outer iterator
current_loop = i - 1;
} // i == 0 means we are done.
has_adv = true;
break;
}
}
if (!has_adv) {
// continue looping current
advancement[current_loop] = true;
}
return advancement;
}
var result = db.scan([q3, q1, q2], join_algo);
result.done(function(keys, index_keys, values) {
console.log(values); // should get desire list of friends
});
Encore une fois, cette requête de jointure n'est qu'une analyse des clés et donc très rapide. Par défaut, scan
utilise un algorithme de fusion triée pour trouver les clés correspondantes, mais montre ici un algorithme de jointure en boucle imbriquée naïf. La jointure de table est donc possible, mais vous devez coder l'algorithme de jointure. Mais les algorithmes plus récents comme la fusion en zigzag sont plus rapides que possible avec Sqlite car toutes les entrées sont triées, les curseurs peuvent bien avancer et, plus important encore, le processus de jointure peut exploiter les connaissances externes qui ne sont pas dans la base de données. Avec SQL, l'opération de jointure est opaque.
Autre que cela, IndexedDB peut être utilisé des techniques telles que le streaming et le traitement de carte/réduire.
Je réponds à cela en 2016 (5 ans après que vous ayez posé cette question) et tout ce qui concerne le la dépréciation de WebSQL existe toujours . IndexedDB d'autre part, bénéficie du support de tous les principaux fournisseurs de navigateurs .
Donc, pour tous ceux qui peuvent se retrouver ici face à la même décision à prendre, allez avec IndexedDB.
Cependant, comme l'ont laissé entendre d'autres personnes ici, une telle décision ne doit pas nécessairement être prise; on peut simplement choisir (ou créer) une bibliothèque qui utilise la base de données disponible sur une machine cliente.
BakedGoods diffère de ces bibliothèques déjà suggérées ici de plusieurs façons; de manière plus pertinente, il permet de spécifier explicitement le ou les types de stockage à utiliser, ce qui permet au développeur d'introduire d'autres facteurs (tels que les caractéristiques de performance) dans le processus de prise de décision.
Avec elle, la conduite d'opérations de stockage dans n'importe quel type de base de données est prise en charge est une question de ...
... en spécifiant les options d'opération appropriées et les configurations équivalentes pour les deux types de base de données:
//If the operation is a set(), and the referenced structures
//don't exist, they will be created automatically.
var webSQLOptionsObj = {
databaseName: "Example_DB",
databaseDisplayName: "Example DB",
databaseVersion: "",
estimatedDatabaseSize: 1024 * 1024,
tableData: {
name: "Main",
keyColumnName: "lastName",
columnDefinitions: "(lastName TEXT PRIMARY KEY, firstName TEXT)"
},
tableIndexDataArray: [name: "First_Name_Index", columnNames: "(firstName)"]
};
var indexedDBOptionsObj = {
databaseName: "Example_DB",
databaseVersion: 1,
objectStoreData: {
name: "Main",
keyPath: lastName,
autoIncrement: false
},
objectStoreIndexDataArray: [
{name: "First_Name_Index", keyPath: "firstName", unique: false, multiEntry: false}
],
};
var optionsObj = {
conductDisjointly: false,
webSQL: webSQLOptionsObj,
indexedDB: indexedDBOptionsObj
};
... et conduite de l'opération:
bakedGoods.set({
data: [
{value: {lastName: "Obama", firstName: "Barack"}},
{value: {lastName: "Biden", firstName: "Joe"}}
],
storageTypes: ["indexedDB", "webSQL"],
options: optionsObj,
complete: function(byStorageTypeStoredItemRangeDataObj, byStorageTypeErrorObj){}
});
Son interface simple et sa prise en charge inégalée des installations de stockage se font au prix d'un manque de prise en charge pour certaines configurations spécifiques aux installations de stockage. Par exemple, il ne prend pas en charge la conduite des opérations de stockage dans les tables WebSQL avec des clés primaires à plusieurs colonnes.
Donc, si vous faites un usage intensif de ces types de fonctionnalités, vous voudrez peut-être chercher ailleurs.
Oh, et dans un souci de transparence totale, BakedGoods est maintenu par le vôtre vraiment :).