javascript - tutorial - Problemas conceptuales con IndexedDB(relaciones etc.)
web sql database html5 example (1)
Estoy escribiendo una tesis sobre habilidades offline de aplicaciones web. Mi tarea es mostrar las posibilidades del almacenamiento sin conexión a través de una aplicación web con una base de datos relacional del lado del servidor y el tráfico Ajax / JSON entre el cliente y el servidor. Mi primera implementación usó un enfoque con localStorage, guardando cada respuesta Ajax como valor con la URL de solicitud como clave. La aplicación funciona bien. Sin embargo, en el siguiente paso, quiero (es decir, la tesis requiere) implementar una versión más avanzada con una base de datos del lado del cliente. Dado que el servidor mantiene una base de datos relacional, la base de datos Web SQL habría sido la opción intuitiva. Pero, como sabemos, el estándar está en desuso y no quiero usar una tecnología cuyo futuro sea incierto. Por lo tanto, quiero usar IndexedDB para implementar la lógica de base de datos del lado del cliente. Desafortunadamente, después de leer una gran cantidad de material en la web que, en su mayoría, sigue haciendo mucho rasguño en la superficie (aplicaciones de notas pendientes, etc.), todavía no sé cómo proceder.
Mi tarea parece bastante sencilla: implementar la base de datos del lado del servidor en el cliente con IndexedDB para replicar todos los datos que una vez se obtuvieron del servidor . Los problemas, que hacen esto mucho menos sencillo, son:
- La base de datos del lado del servidor es relacional, IndexedDB está (más o menos) orientada a objetos
- No hay una forma intuitiva de sincronizar las bases de datos del lado del cliente y del servidor
- No hay una forma intuitiva de implementar las relaciones en IndexedDB que se implementan con claves externas y JOIN en el servidor
En este momento, tengo un concepto en mente que realmente temo comenzar a implementar. Pensé en crear un almacén de objetos para cada tabla en la base de datos del servidor y programar manualmente los objetos de relaciones en diferentes almacenes de objetos. En mi solicitud, que, en resumen, gestiona los cursos de una universidad, tendría 7 tiendas de objetos.
Quiero demostrar mi idea con un ejemplo para una respuesta JSON del servidor (/ * estos son comentarios * /):
{ "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 */
}}
El algoritmo para almacenar los datos con IndexedDB almacenaría cada objeto que se aplica a un almacén de objetos en el almacén de objetos apropiado y reemplazaría los objetos con referencias a estos objetos. Por ejemplo, el objeto del curso anterior se vería como el siguiente en el ''curso'' del almacén de objetos:
{ "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 */
}}
El algoritmo para recuperar datos con IndexedDB haría lo siguiente (tengo un patrón recursivo vagamente en mente):
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
Es claramente visible que esta implementación sería realmente complicada, especialmente debido a la naturaleza asíncrona de IndexedDB. También daría lugar a muchas transacciones diferentes en la base de datos solo para recuperar un objeto del curso y el rendimiento sufriría mucho (de todas formas no sé cómo será el rendimiento de las transacciones de IndexedDB).
¿Cómo podría hacerlo mejor y más simple?
Ya miré estos hilos que representan problemas similares: link1 , link2 . No veo ninguna solución más simple en estos. Además, prefiero evitar el uso de un marco de envoltorio IndexedDB debido a varias razones.
También podría imaginar que estoy totalmente en el camino equivocado con IndexedDB para mi problema.
Editar:
Finalmente terminé siguiendo mi enfoque para almacenar las referencias en los propios objetos en el IndexedDB. Esto puede dar lugar a algunos problemas de rendimiento en casos de grandes cantidades de datos con muchas referencias. Sin embargo, si se usa inteligentemente, en la mayoría de los casos se pueden evitar enormes cantidades de iteraciones y visitas a la base de datos, y no es necesario almacenar un esquema de base de datos complejo en la memoria o en el IndexedDB en sí.
En general, debo decir que me da la impresión de que estoy malinterpretando la idea dinámica y directa con IndexedDB como una base de datos sin esquemas de alguna manera. Pero como sea, implementé todo en JavaScript, funciona bien y no hay ninguna posibilidad de inconsistencias.
Yo mismo soy nuevo en IndexedDB pero también he estado pensando mucho sobre cómo usaría IndexedDB para propósitos como este. Lo primero que sugeriría, si aún no lo ha hecho, es observar cómo funcionan otras bases de datos de valores clave / documentos (CouchDB, MongoDB, etc.), ya que es esencialmente el tipo de base de datos que es IndexedDB.
Hay varios enfoques diferentes para manejar las relaciones en una base de datos de documentos ... como para sincronizar con su base de datos relacional del servidor, probablemente necesitará crear algún tipo de mapeo personalizado porque algunos de los enfoques de las relaciones que tendrían sentido para IndexedDB no se asignará muy limpiamente a una base de datos relacional. Sin embargo, creo que la configuración de este mapeo es definitivamente factible, y el problema más grande es cómo manejar las relaciones en IndexedDB, así que eso es en lo que me centraré aquí ...
En cuanto a su solución propuesta, creo que realmente podría funcionar bien, y podría escribir una biblioteca de consulta simple que ayudó a consolidar el código de plomería (más sobre esto más adelante). Las tiendas de valor-clave están diseñadas para ser muy eficientes en la búsqueda de elementos por clave, por lo que hacerlo para cada objeto relacionado podría no ser tan ineficiente como crees ... sin embargo, se me ocurrió otra idea que hace un mejor uso de los índices. ..
Primero, para mi solución propuesta, necesitaría almacenar los metadatos del "almacén de objetos" en algún lugar que no sea dentro del objeto "de referencia" ... no es necesario que se almacene en IndexedDB; puedes usar un esquema en memoria para eso:
var schema = {
Course: {
fields: [id, title],
relationships: {
lecturers: {objectstore: ''lecturer''},
semester: {objectstore: ''semester''},
}
},
Lecturer: { ... }
...
};
(Por cierto, su ejemplo JSON tiene un error ... no puede tener más de una clave llamada "referencia"; tendría que ser una matriz de "referencias").
Esto lo libera para almacenar los valores de ID directamente en los campos de relación, de modo que pueda crear índices en ellos (he usado los prefijos de letras para mayor claridad, aunque en realidad todos estos probablemente tengan un ID de 1, ya que los valores de ID no es necesario ser único en todas las tiendas):
var course1 = {
id:''C1'',
lecturers:[''L1''],
semester:1
};
var lecturer1 = {
id:''L1'',
courses:[''C1'']
}
var semester1 = {
id:''S1'',
courses:[''C1'']
}
Por supuesto, debe tener cuidado de que todas las operaciones de almacenamiento / recuperación se realicen a través de las funciones de acceso a datos (por ejemplo, insert (), update (), delete ()) que fueron lo suficientemente inteligentes como para garantizar que las relaciones siempre se actualizaron correctamente en ambos. termina ... en realidad, puede que no lo necesite dependiendo de cómo planee consultar los datos, pero parece una buena idea, ya que a veces es posible que solo desee obtener los ID de los objetos relacionados (para consultarlos más tarde o no). ) en lugar de recuperarlos realmente.
Digamos que tiene un índice en el campo "cursos" en la tienda del profesor. Utilizando un índice, puede buscar todos los profesores asociados con un ID de curso en particular de una sola vez:
lecturerStore.index("courses").get("C1").onsuccess = …
Para ese ejemplo, no importa mucho porque los cursos generalmente solo tendrán 1 a 2 profesores, pero considere cómo podría usarse un índice para buscar de manera eficiente todos los cursos en un semestre en particular:
coursesStore.index("semester").get("S1").onsuccess = …
Tenga en cuenta que en el ejemplo del profesor (una relación de muchos a muchos), el índice debe especificarse como "multientry", lo que significa que si tiene un campo cuyo valor es una matriz, cada elemento de la matriz se agregará a El índice. (Consulte https://developer.mozilla.org/en/IndexedDB/IDBObjectStore#createIndex ... No estoy seguro de la compatibilidad del navegador con esto).
Y estoy seguro de que también podría hacer otras cosas inteligentes con la indexación, utilizando los cursores y IDBKeyRange para ayudar a realizar algún tipo de operación de "combinación". Para obtener ideas, consulte este enlace, que muestra las formas de manejar las relaciones en CouchDB:
http://wiki.apache.org/couchdb/EntityRelationship
Ese enlace también menciona el uso de documentos incrustados, que es algo que definitivamente debe considerar, no todos los objetos necesariamente deben tener su propio almacén de objetos, especialmente para las relaciones de "agregación".
(Por cierto, no estoy seguro de lo útil que sería para usted ya que no proporciona muchas consultas), pero alguien ha implementado una base de datos similar a CouchDB sobre IndexedDB: https://github.com/mikeal/pouchdb )
Además de los índices, la implementación de un mecanismo de almacenamiento en caché probablemente ayudaría mucho también.
Ahora, en cuanto a la simplificación del proceso de consulta, sé que mencionó que no quería usar una biblioteca de envoltura ... pero tuve una idea acerca de una API conveniente que podría crearse, que aceptaría un objeto como este:
//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 }
}
No estoy seguro de lo difícil que sería implementar, pero definitivamente parece que haría la vida más fácil.
Ya he caminado el tiempo suficiente, pero quería mencionar una última cosa, y es que también he estado pensando en tomar prestadas algunas ideas de las bases de datos de gráficos, ya que son mucho mejores en el manejo de relaciones que las bases de datos de documentos, y creo que sería posible implementar una base de datos de gráficos en la parte superior de IndexedDB, pero aún no estoy seguro de lo práctico que sería.
¡Buena suerte!