Problèmes conceptuels avec IndexedDB (relations etc.)

Bonsoir aux gens!

J'écris une thèse sur les capacités hors ligne des applications Web. Ma tâche est de montrer les possibilités de stockage hors ligne via une application Web avec une base de données relationnelle côté serveur et le trafic Ajax / JSON entre client et serveur. Ma première implémentation a utilisé une approche avec localStorage, enregistrant chaque réponse Ajax comme valeur avec l'URL de demande comme clé. L'application fonctionne très bien. Cependant, à l'étape suivante, je veux (c'est-à-dire que la thèse requiert) implémente une version plus avancée avec une base de données côté client. Étant donné que le serveur maintient une base de données relationnelle, la base de données Web SQL aurait été le choix intuitif. Mais, comme nous le savons, la norme est obsolète et je ne veux pas utiliser une technologie dont le futur est incertain. Ainsi, je veux utiliser IndexedDB pour implémenter la logique de base de données côté client. Malheureusement, après avoir lu beaucoup de matériel sur le web qui garde surtout beaucoup de surface (applications tout-notes, etc.), je ne sais toujours pas comment procéder.

Ma tâche semble plutôt simple: implémentez la base de données côté serveur sur le client avec IndexedDB pour répliquer toutes les données qu'une fois achetées depuis le serveur . Les problèmes, qui rendent cela beaucoup moins simple, sont les suivants:

  • La base de données côté serveur est relationnelle, IndexedDB est (plus ou moins) orienté objet
  • Il n'existe aucun moyen intuitif de synchroniser les bases de données client et serveur
  • Il n'existe pas de façon intuitive d'implémenter les relations dans IndexedDB qui sont implémentées avec des clés étrangères et des JOIN sur le serveur

À l'heure actuelle, j'ai un concept à l'esprit que j'ai vraiment peur de commencer à mettre en œuvre. J'ai pensé à créer un magasin d'objets pour chaque table dans la base de données du serveur et à programmer manuellement les objets de relations dans différents magasins d'objets. Dans ma demande, qui, en somme, gère les cours d'une université, j'aurais 7 magasins d'objets.

Je souhaite démontrer mon idée avec un exemple pour une réponse JSON du serveur (/ * ce sont des commentaires * /):

{ "course": { /* course object */ "id":1, "lecturer": { "id":"1", /* lecturer object with many attributes */ }, "semester": { "id":"1", /* semester object with many attributes */ }, /* more references and attributes */ }} 

L'algorithme pour stocker les données avec IndexedDB stockerait chaque objet qui s'applique à un magasin d'objets dans le magasin d'objet approprié et remplacer les objets par des références à ces objets. Par exemple, l'objet de cours ci-dessus ressemblerait à ce qui suit dans le cours de l'objet: «course»:

 { "course": { /* course object */ "id":1, "lecturer": { "reference": { /* reference to the lecturer in the object store 'lecturer' */ "objectstore":"lecturer", "id":"1" } }, "semester": { "reference": { /* reference to the semester in the object store 'semester' */ "objectstore":"semester", "id":"1" } } /* more references and attributes */ }} 

L'algorithme pour récupérer des données avec IndexedDB fera alors ce qui suit (j'ai un motif récursif vaguement à l'esprit):

 Retrieve the course object with id=1 from the object store 'course' For each reference object in the retrieved course object, do Retrieve the object with id=reference.id from the object store reference.objectstore Replace the reference object with the retrieved object 

Il est clairement visible que cette implémentation serait vraiment encombrante, surtout en raison de la nature asynchrone de IndexedDB. Cela entraînerait également de nombreuses transactions différentes sur la base de données pour récupérer un objet de cours et les performances en souffriraient beaucoup (je ne sais pas vraiment comment sont actuellement les performances des transactions IndexedDB).

Y a-t-il quelqu'un qui sait plus sur IndexedDB que moi et a une idée de comment je pourrais le faire mieux et plus simple?

J'ai déjà examiné ces fils qui représentent des problèmes similaires: link1 , link2 . Je ne vois aucune solution plus simple à cela. En outre, je préférerais éviter d'utiliser un cadre encadré IndexedDB pour plusieurs raisons.

Toute aide est très appréciée, car je me sens bien coincé avec ma thèse à ce moment-là. Je pourrais également imaginer que je suis totalement sur la mauvaise voie avec IndexedDB pour mon problème. Dans ce cas, ce qui serait dû au manque d'expertise, je m'excuse à l'avance.

Cheers, Felix

Modifier:

J'ai fini par poursuivre ma démarche pour stocker les références dans les objets eux-mêmes dans IndexedDB. Cela peut entraîner des problèmes de performance dans les cas de grandes quantités de données avec de nombreuses références. Si on l'utilise de manière intelligente, cependant, des quantités énormes d'itérations et de résultats de bases de données peuvent être évitées dans la plupart des cas, et il n'est pas nécessaire de stocker un schéma de base de données complexe dans la mémoire ou dans le IndexedDB lui-même.

En général, je dois dire que j'ai l'impression que je mal interprète l'idée dynamique et directe avec IndexedDB comme une base de données de schemaless d'une certaine manière. Mais quoi que ce soit, j'ai implémenté tout en JavaScript, ça marche bien et il n'y a aucune chance d'incohérences.

Si vous le souhaitez, les idées que j'ai écrites d'une manière plus abstraite peuvent être trouvées ici . Veuillez noter que l'anglais n'est pas la langue maternelle et que le texte n'a pas encore été corrigé. Des erreurs ont été incorporées! Si quelqu'un veut voir le code JavaScript correspondant, laissez-moi savoir.

Je suis nouveau dans IndexedDB moi-même, mais moi aussi, j'ai beaucoup pensé à la façon dont j'utiliserais IndexedDB à des fins comme celle-ci. La première chose que je suggère, si vous ne l'avez pas déjà fait, c'est de voir comment fonctionnent d'autres bases de données clés / document (CouchDB, MongoDB, etc.), car c'est essentiellement le type de base de données que IndexedDB est.

Il existe plusieurs approches différentes pour traiter les relations dans une base de données de documents … en ce qui concerne la synchronisation avec votre base de données relationnelle du serveur, vous devrez probablement créer une sorte de mappage personnalisé car certaines approches des relations qui seraient logiques pour IndexedDB Ne va pas mapper très proprement à une base de données relationnelle. Cependant, je pense que la mise en place d'un tel cartographie est définitivement possible, et le problème majeur est de gérer les relations dans IndexedDB, alors c'est sur quoi je me concentrerai ici …

En ce qui concerne votre solution proposée, je pense que cela pourrait bien fonctionner, et vous pouvez écrire une bibliothèque d'interrogation simple qui a contribué à consolider le code de la plomberie (plus à ce sujet ci-dessous). Les magasins à valeur nominale sont conçus pour être très efficaces pour rechercher des éléments par clé, ce qui fait pour chaque objet lié peut-être pas aussi inefficace que vous le pensez … cependant, j'ai eu une autre idée qui permet de mieux utiliser les index. ..

Tout d'abord, pour ma solution proposée, vous devriez stocker les métadonnées "objectstore" quelque part autrement que dans l'objet "référence" lui-même … il ne faut pas nécessairement qu'il soit nécessaire de l'enregistrer dans IndexedDB; Vous pouvez simplement utiliser un schéma en mémoire pour cela:

 var schema = { Course: { fields: [id, title], relationships: { lecturers: {objectstore: 'lecturer'}, semester: {objectstore: 'semester'}, } }, Lecturer: { ... } ... }; 

(En passant, votre exemple JSON a une erreur … vous ne pouvez pas avoir plus d'une touche appelée «référence» – il faudrait qu'il s'agisse d'un «référentiel».)

Cela vous libère pour stocker les valeurs d'ID directement dans les champs de relation, afin que vous puissiez créer des index sur eux (j'ai utilisé des préfixes de lettres pour plus de clarté même si, dans la réalité, tout cela aurait probablement un ID de 1, car les valeurs d'ID N'ont pas besoin d'être uniques dans les magasins):

 var course1 = { id:'C1', lecturers:['L1'], semester:1 }; var lecturer1 = { id:'L1', courses:['C1'] } var semester1 = { id:'S1', courses:['C1'] } 

Vous devriez bien sûr faire attention à toutes les opérations de stockage / extraction effectuées par le biais de fonctions d'accès aux données (p. Ex. Insertion (), mise à jour (), suppression ()) qui étaient suffisamment intelligentes pour s'assurer que les relations étaient toujours mises à jour correctement sur les deux Se termine … en fait, vous ne devrez peut-être pas cela selon la façon dont vous envisagez d'interroger les données, mais cela semble être une bonne idée, car vous pourriez parfois souhaiter obtenir les identifiants des objets associés (à rechercher plus tard, ou non ) Plutôt que de les récupérer.

Disons que vous avez un index sur le champ "cours" dans le magasin de conférenciers. À l'aide d'un index, vous pouvez consulter tous les conférenciers associés à un identifiant de cours particulier d'un seul coup:

 lecturerStore.index("courses").get("C1").onsuccess = … 

Pour cet exemple, cela n'a pas d'importance, car les cours auront généralement seulement 1 à 2 conférenciers, mais considérons comment un indice pourrait être utilisé pour rechercher efficacement tous les cours d'un semestre particulier:

 coursesStore.index("semester").get("S1").onsuccess = … 

Notez que dans l'exemple du conférencier (une relation de plusieurs-à-plusieurs), l'index devrait être spécifié comme "multientry", ce qui signifie que si vous avez un champ dont la valeur est un tableau, chaque élément du tableau sera ajouté à l'index. (Voir https://developer.mozilla.org/fr/IndexedDB/IDBObjectStore#createIndex … Je ne suis pas sûr de ce qu'est le support du navigateur.)

Et je suis sûr que vous pourriez faire d'autres choses intelligentes avec l'indexation aussi, en utilisant des curseurs et IDBKeyRange pour aider à faire une sorte d'opération de "jointure". Pour des idées, consultez ce lien, qui démontre les moyens de gérer les relations dans CouchDB:

http://wiki.apache.org/couchdb/EntityRelationship

Ce lien mentionne également l'utilisation de documents intégrés, ce que vous devez certainement considérer – tous les objets ne doivent pas nécessairement avoir leur propre magasin d'objets, en particulier pour les relations d'agrégation.

(En passant, je ne sais pas à quel point cela vous sera utile, car cela ne permet pas d'interroger, mais quelqu'un a effectivement implémenté une base de données CouchDB sur IndexedDB: https: // github .com / mikeal / pouchdb )

En plus des index, la mise en place d'un mécanisme de mise en cache aiderait probablement beaucoup.

Maintenant, en ce qui concerne la simplification du processus d'interrogation, je sais que vous avez mentionné ne pas vouloir utiliser une bibliothèque wrapper … mais j'ai eu une idée d'une API pratique qui pourrait être créée, qui accepterait un objet comme ceci:

 //select all courses taught by 'Professor Wilkins' { from: 'lecturer', //open cursor on lecturer store where: function(lecturer) { return lecturer.name=='Professor Wilkins' }, //evaluate for each item found select: function(lecturer) { return lecturer.courses }, //what to return from previous step //this should be inferred in this case, but just to make it clear... eagerFetch: function(lecturer) { return lecturer.courses } } 

Je ne sais pas combien il serait difficile de mettre en œuvre, mais il semble certainement que cela faciliterait la vie.

J'ai parcouru assez longtemps, mais je voulais mentionner une dernière chose, c'est-à-dire que j'ai pensé à emprunter des idées à partir de bases de données graphiques, car elles sont beaucoup mieux pour gérer les relations que les bases de données de documents, et je pense Il serait possible de mettre en place une base de données graphique sur IndexedDB, je ne suis pas encore sûr de la façon dont elle serait pratique.

Bonne chance!