Problèmes conceptuels avec IndexedDB (relations, etc.))

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 par le biais d'une application web avec une base de données relationnelle côté serveur et trafic Ajax/JSON entre le client et le serveur. Ma première implémentation a utilisé une approche avec localStorage, sauvegardant chaque réponse Ajax comme valeur avec L'URL de la requête comme clé. L'application fonctionne très bien. Dans la prochaine étape cependant, je veux (c.-à-d. la thèse nécessite) mettre en œuvre une version plus avancée avec une base de données côté client. Puisque le serveur maintient une base de données relationnelle, la base de données SQL Web aurait été le choix intuitif. Mais, comme nous le savons, la norme est obsolète, et je ne veux pas utiliser une technologie dont l'avenir 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 la plupart du temps très gratter la surface (todo-notes applications etc.), Je ne sais toujours pas comment procéder.

ma tâche semble assez simple: implémenter la base de données côté serveur sur le client avec IndexedDB pour répliquer toutes les données qui ont été récupérées à partir du serveur . Les problèmes qui rendent cela beaucoup moins simple sont:

  • la base de données côté serveur est relationnelle, IndexedDB est (plus ou moins) orientée objet
  • il n'y a pas de moyen intuitif de synchroniser côté client et Côté Serveur bases de données
  • il n'y a pas de façon intuitive d'implémenter les relations dans IndexedDB qui sont implémentées avec des clés étrangères et se joint au serveur

en ce moment, j'ai un concept en tête 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 les objets de relations dans les magasins d'objets différents manuellement. Dans mon application, qui, en bref, gère les cours d'une université, j'aurais 7 magasins d'objets.

je veux démontrer mon idée avec un exemple de 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 de stocke les données avec IndexedDB stocke chaque objet qui s'applique à un stock d'objet dans le stock d'objet approprié et remplace les objets par des références à ces objets. Par exemple, le cours ci-dessus objet ressemblerait à ce qui suit dans le magasin 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 extraire des données avec IndexedDB ferait alors ce qui suit (j'ai vaguement un modèle récursif à 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 clair que cette implémentation serait très lourde, surtout en raison de la nature asynchrone de IndexedDB. Il en résulterait également de nombreuses transactions différentes à la base de données juste pour récupérer un objet de cours et la performance souffrirait beaucoup (Je ne sais pas vraiment à quoi ressemble la performance des transactions IndexedDB de toute façon).

Comment pourrais-je faire cela mieux et plus simple?

j'ai déjà regardé ces fils qui représentent des problèmes similaires: link1 , link2 . Je ne vois pas de solutions plus simples. De plus, je préfère éviter d'utiliser un Le cadre d'enrubannage IndexedDB pour plusieurs raisons.

je pourrais aussi imaginer que je suis totalement sur la mauvaise voie avec IndexedDB pour mon problème.

Edit:

j'ai finalement poursuivi mon approche pour stocker les références dans les objets eux-mêmes dans la base de données indexée. Cela peut entraîner des problèmes de performances dans le cas de grandes quantités de données avec de nombreuses références. Si utilisé intelligemment, cependant, énorme dans la plupart des cas, il est possible d'éviter un grand nombre d'itérations et d'occurrences dans la base de données, et il n'est pas nécessaire de stocker un schéma de base de données complexe en mémoire ou dans la base de données indexée elle-même.

en général, je dois dire, que j'ai l'impression que J'interprète mal L'idée dynamique et droite avec IndexedDB comme une base de données sans schéma d'une certaine manière. Mais quoi qu'il en soit, J'ai tout implémenté dans JavaScript, ça marche très bien et il n'y a aucune possibilité d'incohérence.

28
demandé sur Cœur 2012-01-01 23:21:17

1 réponses

Je suis nouveau à IndexedDB moi-même mais j'ai moi aussi beaucoup réfléchi à la façon dont J'utiliserais IndexedDB pour des buts comme celui-ci. La première chose que je suggère, si vous ne l'avez pas déjà fait, est de regarder comment D'autres bases de données de valeurs clés / documents (CouchDB, MongoDB, etc.) travail, puisque c'est essentiellement le type de base de données que IndexedDB est.

il existe plusieurs approches différentes pour gérer les relations dans une base de données de documents...quant à la synchronisation avec votre base de données relationnelle côté serveur, vous aurez probablement besoin de créer une sorte de mappage personnalisé Parce que certaines des approches aux relations qui seraient sensées pour IndexedDB ne sera pas très clairement mapper à une base de données relationnelle. Cependant, je pense que la mise en place d'un tel mapping est certainement faisable, et la plus grande question Est de savoir comment gérer les relations dans IndexedDB, donc c'est ce sur quoi je vais me concentrer ici...

quant à la solution que vous proposez, je pense qu'elle pourrait bien fonctionner, et vous pouvez écrire une bibliothèque de questions simple qui a aidé à consolider le code de plomberie (plus sur ce ci-dessous). Les magasins Key-value sont construits pour être très efficace à regarder des articles par clé, donc faire ainsi pour chaque objet lié pourrait ne pas être aussi inefficace que vous le pensez...cependant, j'ai eu une autre idée qui fait une meilleure utilisation des index...

tout D'abord, pour la solution que je propose, vous devez stocker les métadonnées" objectstore "ailleurs qu'à l'intérieur de l'objet" reference". il n'a même pas besoin d'être stocké dans IndexedDB du tout; 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: { ... }
    ...
};

(soit dit en passant, votre exemple JSON a une erreur...vous ne pouvez pas avoir plus d'une clé appelée "référence" - cela devrait être un tableau "références".)

cela vous libère pour stocker les valeurs D'ID directement dans les champs de relation, de sorte que vous pouvez créer des index sur eux (j'ai utilisé des préfixes de lettre pour la clarté même bien qu'en réalité, tous ces magasins auraient probablement un ID de 1, puisque les valeurs D'ID n'ont pas besoin d'être uniques d'un magasin à l'autre):

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

var lecturer1 = {
    id:'L1',
    courses:['C1']
}

var semester1 = {
    id:'S1',
    courses:['C1']
}

bien sûr, vous devez faire attention à ce que toutes les opérations de stockage/récupération se produisent par le biais de fonctions d'accès aux données (par exemple insert(), update(), delete ())) qui sont suffisamment intelligentes pour s'assurer que les relations sont toujours mises à jour correctement aux deux extrémités...en fait, vous pouvez ne pas avoir besoin que selon la façon dont vous prévoyez d'interroger les données, mais il cela semble être une bonne idée puisque vous pourriez parfois juste vouloir obtenir les Id des objets liés (pour être regardé plus tard, ou pas) plutôt que de les récupérer réellement.

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

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

pour cet exemple, cela n'a pas beaucoup d'importance parce que les cours ne seront généralement avoir 1 ou 2 professeurs, mais envisager comment un index pourrait être utilisé pour chercher efficacement tous les cours dans un semestre particulier:

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

notez que dans l'exemple de maître de conférences (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/en/IndexedDB/IDBObjectStore#createIndex ...Je ne suis pas sûr du 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 de" join " opération. Pour des idées, consultez ce lien, qui montre comment gérer les relations dans CouchDB:

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

ce lien mentionne également l'utilisation de documents, ce qui est quelque chose que vous devriez certainement considérer -- tous les objets n'ont pas nécessairement besoin d'avoir leur propre magasin d'objets, en particulier pour les relations "d'agrégation".

(soit dit en passant, je ne suis pas sûr de l'utilité que cela vous apporterait puisque cela ne fournit pas grand chose dans la façon de poser une question, mais quelqu'un a en fait implémenté une base de données de type CouchDB en haut de IndexedDB: https://github.com/mikeal/pouchdb )

en plus de index, implémenter un mécanisme de mise en cache aiderait probablement beaucoup aussi.

maintenant, pour simplifier le processus d'interrogation, je sais que vous avez mentionné ne pas vouloir utiliser une bibliothèque de papier d'emballage...mais j'avais une idée d'une API pratique qui pourrait être créée, qui accepterait un objet comme celui-ci:

//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 suis pas sûr à quel point il serait difficile à mettre en œuvre, mais il semble certainement que cela rendrait la vie plus facile.

j'ai divaguait assez longtemps, mais je voulais mentionner une dernière chose, qui est que j'ai également pensé à emprunter quelques idées de bases de données de graphiques, car ils sont beaucoup mieux à la manipulation des relations que les bases de données de documents, et je pense qu'il serait possible d'implémenter une base de données de graphiques sur le dessus de IndexedDB, Je ne suis pas encore sûr comment pratique il serait.

bonne chance!

21
répondu Matt Browne 2012-01-15 00:10:22