¿Qué hay de nuevo en IndexedDB 2.0?

25 febrero, 2017 21:49 por

Esta es una traducción del artículo original publicado en el blog de Hacks.

El borrador de la API de la base de datos IndexedDB 2.0 está casi completo, brindando muchas nuevas APIs para acceso refinado a IndexedDB.

La buena noticia es que todas estas nuevas API están implementadas y disponibles desde Firefox 51 (versión estable actual). En este artículo, compartiremos algunos ejemplos de cómo usar las APIs para acceder a IndexedDB de una mejor manera.

Acceso a IDBObjectStore.name y IDBIndex.name

En la versión previa de la API de IndexedDB, IndexedDB permitía a los desarrolladores actualizar el esquema de la base de datos, agregando/borrando almacenamiento de objetos e índices; pero no era posible cambiar el nombre de los almacenes de objetos e índices existentes.

Esto hacía imposible nombrar apropiadamente los índices y almacenamientos de objetos, cuando el nombre original ya no representa con precisión el nuevo esquema de los objetos a almacenar. Además, era una pérdida de tiempo mover objetos a un nuevo almacén de objetos sólo por renombrarlo.

Por ejemplo, vamos va decir que hay un almacén de objetos llamado “mensajes de texto”, para almacenar los mensajes enviados o recibidos en texto plano de una red móvil. Luego, imagina que al desarrollador le gustaría proveer mensajes de contenido enriquecido, que incluyan archivos adjuntos como imágenes, audio, y video – sería mejor darle a este almacén de objetos un nombre más genérico como “mensajes móviles”. Adicionalmente, imagina que hay un índice en este almacén de objetos llamado “destinatario”, que representa la dirección del destinatario de un mensaje, y ahora, al desarrollador le gustaría soportar múltiples destinatarios de mensajes. Entonces, sería bueno tener un índice renombrado como “destinatarios” (en plural).

let request = indexedDB.open("messageDB", 2);
request.onupgradeneeded = (event) => {
  let txn = event.target.transaction;
  let store = txn.objectStore("mensajes de texto");

  store.name = "mensajes móviles";
  let index = store.index("destinatario");
  index.name = "destinatarios";
};

IDBDatabase.onclose()

Está definido un nuevo gestor de evento onclose, en la interfaz IDBDatabase para permitir a los desarrolladores escuchar el cambio de almacenamiento de la base de datos fuera de los scripts. Tomando a Firefox como un ejemplo, el navegador permite al usuario limpiar el almacenamiento usado por un sólo sitio. Con este gestor, los scripts pueden ser notificados cuando la base de datos se cerró forzosamente fuera de los scripts para proporcionar información más sensible o como una advertencia para el usuario.

let request = indexedDB.open("bookstore");
request.onsuccess = (event) => {
  let db = event.target.result;

  db.onclose = (event) => {
    alert("la base de datos: " + db.name + "fue cerrada fuera del script!");
  };
};

Indexación de clave binaria

Los tipos de datos binarios pueden ser tratados como claves indexadas en la implementación 2.0. Más precisamente, cualquier propiedad de los almacenes de objetos presentados por ArrayBuffer, los array tipados de objetos, y DataView pueden ahora ser indexados. Esto beneficia a los desarrolladores: ahora puedes tener firmas binarias como llaves directamente, sin serializarlas en cadenas de texto o arreglos de objetos como se requería en la versión anterior de la API.

IDBTransaction.objectStoreNames[]

Esta es una nueva propiedad útil le da a los desarrolladores una manera fácil de determinar qué almacenes de objetos están involucrados en la actual transacción:

let request = indexedDB.open("bookstore", 2);
request.onupgradeneeded = (event) => {
  let txn = event.target.transaction; // txn.mode == "versionchange"
  txn.objectStoreNames; // una lista de nombres de los objectstore puede ser accesible en esta transacción
};

IDBObjectStore.getKey(query)

getKey(query) ahora es proporcionado en IDBObjectStore por razones de desempeño y simetría con IDBIndex. Con esta nueva API, no tienes que adoptar una heurística para comprobar si una clave de objeto en un rango específico está disponible en el almacén. Por ejemplo, openCursor(query) podría ser un enfoque alternativo, pero todavía seguirá teniendo el costo de la creación y serialización/deserialización del objeto.

Vamos a asumir que hay un almacén de objetos para el registro de las actividades de red y los campos de marcas de tiempo se han escogido como claves de objetos. Ahora, nos gustaría saber cuándo fue la primera actividad ocurrida en las últimas 24 horas.

let openRequest = indexedDB.open("telemetry");
openRequest.onsuccess = (event) => {
  let db = event.target.result;
  let store = db.transaction("netlogs").objectStore("netlogs");

  let today = new Date();
  let yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);
  let request = store.getKey(IDBKeyRange(yesterday, today));
  request.onsuccess = (event) => {
    let when = event.target.result;
    alert("La primera actividad en las últimas 24 horas ocurrió en " + when);
  };
};

IDBObjectStore.openKeyCursor(rango, direccion);

Como una versión ligera de openCursor() en IDBObjectStore, openKeyCursor() te permite iterar las claves de objetos en un rango especificado sin tener que obtener todo el objeto entero desde la base de datos. Esto reduce el sobre costo de serializacion.

Continuemos con el ejemplo anterior para obtener las marcas de tiempo de las actividades de red que ocurrieron en las últimas 24 horas:

let openRequest = indexedDB.open("telemetry");
openRequest.onsuccess = (event) => {
  let db = event.target.result;
  let store = db.transaction("netlogs").objectStore("netlogs");

  let today = new Date();
  let yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);
  let request = store.openKeyCursor(IDBKeyRange(yesterday, today));
  let timestamps = [];
  request.onsuccess = (event) => {
    let cursor = event.target.result;
    if (!cursor) { return; }
    timestamps.push(cursor.key);
    cursor.continue();
  };
};

getAll/getAllKeys(rango, cantidad) de IDBObjectStore y IDBIndex

En lugar de obtener los datos uno por uno iterando el cursor, getAll() y getAllKeys() nos permiten obtener todos los datos de una sola vez en orden ascendente desde un IDBObjectStore o un IDBIndex. Puedes especificar opcionalmente un rango y una cantidad para limitar el número de resultados en las respuesta. Es muy útil cuando el tamaño total de datos para ser obtenido no es muy grande.

Por ejemplo, si te gustaría obtener las primeras diez actividades en las últimas horas de una sola vez:

let openRequest = indexedDB.open("telemetry");
openRequest.onsuccess = (event) => {
  let db = event.target.result;
  let store = db.transaction("netlogs").objectStore("netlogs");

  let today = new Date();
  let yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);
  let activities = null;
  let request = store.getAll(IDBKeyRange(yesterday, today), 10);
  request.onsuccess = (event) => {
    activities = event.target.result;
  };
};

Nota: Esto es menos útil si quieres obtener datos de una sola vez en orden descendente cuando la cantidad está especificada.

IDBCursor.continuePrimaryKey(clave, clavePrimaria)

En el diseño de IndexedDB, los registros en un árbol IDBIndex están almacenados en orden ascendente de la clave índice seguidos en orden ascendente de la clave del objeto. Con la ayuda de continuePrimaryKey() para un IDBCursor abierto de un IDBIndex, los desarrolladores pueden retomar la iteración de un cursor que fue cerrado en una previa iteración. Sólo especifica la clave índice y la clave primaria grabada anteriormente, en vez de empezar la iteración desde el inicio y comparar la claves primarias una por una.

Por ejemplo, aquí te mostramos cómo puedes retomar una iteración de todos los artículos etiquetados con “javascript” desde tu última visita:

let request = articleStore.index("tag").openCursor();
let count = 0;
let unreadList = [];
request.onsuccess = (event) => {
    let cursor = event.target.result;
    if (!cursor) { return; }
    let lastPrimaryKey = getLastIteratedArticleId();
    if (lastPrimaryKey > cursor.primaryKey) {
      cursor.continuePrimaryKey("javascript", lastPrimaryKey);
      return;
    }
    // actualizar lastIteratedArticleId
    setLastIteratedArticleId(cursor.primaryKey);
    // precargar 5 artículos dentro la lista de no leídos;
    unreadList.push(cursor.value);
    if (++count < 5) {
      cursor.continue();
    }
};

IDBKeyRange.includes(clave)

Este es una nueva función de ayuda para verificar si una clave está en el contexto de una IDBKeyRange:

let openRange = IDBKeyRange.bound(5, 10, true, true);
openRange.includes(5);  // false;
openRange.includes(10); // false;
openRange.includes(7);  // true;

Con los cambios y actualizaciones de 2.0, es posible lograr cosas totalmente nuevas con IndexedDB. Averigua a dónde te llevan estas nuevas capacidades y haznos saber cómo te va.

The following two tabs change content below.

AngelFQC

Web Developer at BeezNest Latino
Ingeniero de Sistemas y Computación. Desarrollador PHP. Mozilla Peru. Chamilo LMS Developer.

Compartir artículo:

Start the discussion at foro.mozilla-hispano.org

  • ¡Participa!

    Firefox Friends »
    Agrega botones de Firefox en tu sitio web y comparte tu amor por Mozilla Firefox.
    Ayuda a otros usuarios en Twitter.
    Colabora con la comunidad »
    En Mozilla lo importante son las personas. Descubre cómo puedes colaborar.

    Boletín Firefox

    Suscríbete al boletín de novedades de Firefox.

  • Descargas

    Descarga los programas de Mozilla.

    Lo más visto

    cc-by-sa