Mostrando entradas con la etiqueta Bases de Datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Bases de Datos. Mostrar todas las entradas

miércoles, 6 de mayo de 2020

Utiliza bases de datos en tus proyectos web sin servidores

EFEMem Web Database
¿Imaginas poder usar una base de datos en tu web de forma exclusiva y sin necesidad de hacer llamadas remotas, como una API?
EFEMem DB es una base de datos NoSQL ultra ligera, rápida, sencilla y eficiente, basada en clave-valor, que se ejecuta enteramente en memoria. Está enteramente escrita en JavaScript, y se puede ejecutar directamente en el front-end, a través de páginas web, aplicaciones PWA (Progressive Web Applications) o aplicaciones móviles híbridas (desarrolladas con Ionic, React Native, Nativescript, Xamarin, etc).

Casos de uso

¿Cuáles son los escenarios ideales para usar una base de datos en un proyecto web?. He aquí algunos casos:
  • Pérdida conexión. Tu aplicación puede seguir funcionando cuando no tienes conectividad con el servidor. Cuando recuperes la conexión, puedes volcar toda la información recopilada al servidor. Esta característica asegura la alta disponibilidad de tu aplicación.
  • Juegos HTML. Los datos de las partidas se guardan en cada navegador o dispositivo.
  • Datos de sesión de usuario
  • Datos exclusivos de la aplicación web
  • Datos de configuración. Permite cambios "en caliente", eliminando la característica de datos estáticos.
  • Monitorización de datos. Puedes, por ejemplo, conectar tus dispositivos IoT a tu web, y almacenar un rango fijo de datos cíclicos (por ejemplo, las últimas 24 horas).
  • Datos caché de uso frecuente
  • Datos maestros


Características principales

Los puntos fuertes de EFEMem DB son los siguientes:
  • NoSQL. Olvídate de la complejidad de relaciones de tablas o esquemas estrictos. Utiliza pares de clave-valor.
  • Acceso ultra-rápido de lectura y escritura (unos pocos nanosegundos)
  • No requiere de instalación en un servidor ni de un canal de sockets. Es una librería JavaScript que trabaja directamente con los datos.
  • Configuración en caliente. Puedes cambiar la configuración sin reiniciar el motor de base de datos.
  • Modo de datos reciclados. Mediante este modo se puede configurar un máximo de claves. Cuando se alcanza este límite, el siguiente dato se guardará tras eliminar, automáticamente, la cave más antigua.
  • Datos con tiempo de vida. Puedes guardar un dato que expire en un tiempo concreto (expresado en segundos).
  • Comandos poderosos. Puedes gestionar tus datos mediante unos pocos y sencillos comandos.
  • Persistencia. Puedes guardar los datos de forma permanente. Para ello, utiliza localStorage y restaurarlo al iniciar la base de datos o cuando quieras.
  • Espacios. Puedes organizar tus claves mediante espacios. Esto permite tener claves con el mismo nombre y distinto valor, en diferentes espacios. También ayuda a localizar e identificar las claves.
  • Patrones de nombre. Puedes buscar y acceder de forma eficiente a multitud de espacios y claves utilizando patrones de nombre, en lugar de acceder uno a uno.
  • Riqueza de tipos de datos. Puedes usar números enteros o reales, cadenas de texto, valores booleanos, arrays u objetos JSON


Comandos básicos

Lo mejor para entender cómo funciona EFEMem DB es el ejemplo.

Importar la librería

Lo primero es descargar la librería efememdb.js, que contiene todo el motor de la base de datos. Se encuentra en el repositorio Github: https://github.com/efememdb/EFEMemDB
Una vez descargado, en nuestro código HTML, importamos la librería:
<SCRIPT src="[/path/]efememdb.js"></SCRIPT>


/ Una vez importado, el motor de EFEMem DB entra en funcionamiento. Si hubo datos persistidos, automáticamente se cargarán estos para su uso.

Guardar datos con set()

Comencemos con el comando set(). Este comando guarda una clave y su valor:
<SCRIPT> var result = efemem.set("saludo", "Hola Mundo"); </SCRIPT>


Si no se especifica un nombre de espacio, asumirá el espacio 'public' por defecto.
Si el espacio y la clave no existen, se creará esta nueva clave en dicho espacio. Si ya existía previamente el espacio y la clave, el valor de ésta se actualizará con el nuevo valor.
El resultado de este comando sería el siguiente:
{   "ok":true,   "cmd":"set(key, value[,space[,due]])",   "data":{     "key":"saludo",     "space":"public",     "value":"Hola Mundo",     "due":"9999-12-31T22:59:59.000Z" },   "msg":"Key 'saludo' saved successfully in space 'public'",   "affected":1,   "time":"< 1 ms" }


Si se produjese un error, la respuesta de cualquier comando de EFEMem DB sería la siguiente:
{   "ok":false,   "msg":"[mensaje de error]", }


Recuperar datos con get()

El comando get() permite recuperar datos a través de la clave:
<SCRIPT> result = efemem.get("saludo"); </SCRIPT>


Si no se especifica un nombre de espacio, por defecto todos los espacios existentes. Lo mismo ocurre si no se especifica un nombre de clave. El comando get() (al igual que otros comandos), utilizan patrones de nombre para referirse a un espacio o a una clave. El nombre que se pase, se tomará como "parte del nombre". Es decir, que la cadena de caracteres especificada se utilizará como parte del nombre, ya sea al principio, en el medio o al final, muy similar a los patrones o comodines utilizados en un sistema operativo. Si se especifica un patrón 'al', es como si se especificara '*al*', con lo que buscaría cualquier nombre que empiece por 'al', termine en 'al' o que contenga 'al' en cualquier parte del nombre.
El resultado de este comando retorna un array o lista con todas las claves encontradas:
{   "ok":true,   "cmd":"get(key[,space])",   "data":[     {"key":"public~saludo","value":"Hola Mundo"}   ],   "msg":"1 values found",   "affected":1,   "time":"1 ms" }


Lista de comandos

Los comandos set() y get() son los comandos más básicos y utilizados de EFEMem DB. Pero tienes a tu disposición un conjunto de comandos que te ayudarán a gestionar de forma eficiente tu base de datos:
  • copy(): Copia claves de un espacio a otro
  • delete(): Elimina claves en los espacios indicados
  • get(): Recupera claves y valores de los espacios indicados
  • getConfig(): Recupera el valor de un parámetro de configuración
  • info(): Muestra la información sobre el uso de EFEMemDB
  • keys(): Retorna las claves definidas en los espacios indicados
  • memory(): Muestra la memoria utilizada por las claves de los espacios indicados
  • move(): Mueve claves de un espacio a otro
  • persist(): Guarda o persiste los datos actuales
  • rename(): Cambia el nombre de una clave
  • restore(): Carga o recupera los datos persistidos
  • set(): Guarda una clave en un espacio
  • setConfig(): Crea o modifica un parámetro de configuración
  • spaceInfo(): Muestra la información de un espacio dado
  • spaces(): Retorna la lista de espacios utilizados


Enlaces de interés

Imagen original en pxfuel: https://www.pxfuel.com/en/free-photo-elhvj

miércoles, 14 de agosto de 2019

Encriptación de columnas en PostgreSQL

PostgreSQL es una base de datos relacional ideal para diferentes escenarios, con una larga historia desde 1982 en el corazón de la Universidad de Berkeley hasta nuestros días. Entre sus características más notables destacan su alta concurrencia, la variedad de tipos de datos nativos, tratamiento de datos geométricos, soporte a transacciones distribuidas, drivers para multitud de lenguajes de programación, alta seguridad o su disponibilidad en entornos cloud como Azure o AWS.

Dentro de la materia de Seguridad, encontramos artefactos para implementar la encriptación mediante diferentes opciones (consultar la documentación oficial). Una de estas formas de encriptación la encontramos a nivel de columna, la cual podemos implementar a través de una extensión llamada pgcrypto. Para usar esta extensión, debemos crearla dentro de la base de datos en uso mediante el comando:

CREATE EXTENSION pgcrypto;


Ahora tenemos a nuestra disposición nuevas funciones para incorporar en el lenguaje SQL, a nivel de columna.

La primera función se llama PGP_SYM_ENCRYPT(), y permite encriptar un texto dado, mediante al algoritmo especificado. La siguiente sintaxis encripta mi nombre mediante el algoritmo AES-KEY:

PGP_SYM_ENCRYPT('Rafael','AES_KEY')

La segunda función se llama PGP_SYM_DECRYPT(), y desencripta la columna encriptada mediante el algoritmo especificado:

PGP_SYM_DECRYPT(nombre_columna::bytea, 'AES_KEY')


Ejemplo

Vamos a poner en práctica lo anterior. En primer lugar vamos a crear una tabla de usuarios, en donde almacenaremos los nombres de usuarios y su contraseña, la cual vamos a encriptar para evitar robo de contraseñas:

CREATE TABLE usuarios(nombre VARCHAR(50), password VARCHAR(255));

INSERT INTO usuarios (nombre, password) VALUES ('Rafael', PGP_SYM_ENCRYPT('mypassword','AES_KEY'));

Si se lista la información, veremos que la contraseña está encriptada:

SELECT nombre, password FROM usuarios;

Para ver los datos desencriptados, utilizaremos la siguiente sentencia:

SELECT nombre, pgp_sym_decrypt(password::bytea,'AES_KEY') FROM usuarios;

La siguiente sentencia cambia la contraseña, encriptando la misma:

UPDATE usuarios SET password=(PGP_SYM_ENCRYPT('newpassword', 'AES_KEY'));

Podemos hacer una búsqueda comparando el valor en plano con el valor desencriptado de la columna encriptada. En nuestro ejemplo, cuando hacemos un login, hemos de comparar el nombre y la contraseña. La siguiente sentencia resuelve el problema:

SELECT COUNT(*) AS coincidencias FROM usuarios WHERE nombre = 'Rafael' AND PGP_SYM_DECRYPT(password::bytea, 'AES_KEY') = 'newpassword';


Ver también




jueves, 21 de enero de 2016

5 consejos para combatir el Síndrome de Infórgenes


Resulta anecdótico que el mundo que creamos con la tecnología termine adoptando símiles de la vida real o de nuestra cultura: virus, troyanos, zombies, etc.

Quisiera hablar en este artículo sobre algo que he denominado el "Síndrome de Infórgenes" (por buscar una similitud con el síndrome de Diógenes), el cual afecta a más del 75% de la sociedad y de las empresas de todo el mundo, y que se caracteriza por la acumulación constante e innecesaria de información basura.

Durante las dos últimas décadas hemos vivido una explosión descomunal de la información, gracias a internet y a una creciente y económica tecnología. ¿El resultado? Toneladas de información que se almacenan con poca o sin ninguna utilidad. Es decir, generamos, recogemos y almacenamos basura informativa.


Invito a cualquier lector a hacer una revisión de su disco duro, de sus pendrives, de su correo electrónico, de sus aplicaciones en la nube (Evernote, Pocket, Dropbox, Instagram...). Invito a cualquier empresa a revisar la información que almacena en sus servidores y en sus equipos de escritorio: ¿cuánto tiempo ha pasado desde la última vez que utilizaron dicha información? ¿Qué valor o utilidad reales tiene realmente dicha información? El hecho de que hayamos sustituido nuestros viejos archivadores físicos por pequeñas unidades de almacenamiento o invisibles repositorios en la nube, nos crea la ilusión de que apenas consumimos espacio de información. La ilusión del reducido espacio físico, unido a la ingente capacidad de estos medios de almacenamiento, nos lleva a creer que no estamos malgastando nada. Pero a la hora de buscar algo necesario, todo lo que no es necesario nos hará difícil esta labor, lo que al final se traduce en malgastar tiempo, esfuerzo y dinero. Es como vivir en una habitación en la cual vamos acumulando, día tras día, objetos y enseres que se apilan y se olvidan.  Al final, lastramos nuestra propia vida con esos objetos que no son útiles o que no son aprovechados correctamente.

La información nos puede esclavizar y perjudicar si no hacemos un uso útil y óptimo de la misma.

Mis consejos para lidiar, en términos generales, con el Síndrome de Infórgenes son los siguientes:
  1. Antes de guardar información, evaluar si esa información será útil en el futuro.  Esta sencilla decisión evitará acumular mucha información innecesariamente. Hay que discernir entre lo que es interesante de lo que es importante, entre lo que es posible de lo que es realmente.
  2. Clasificar y organizar bien toda la información. Esto facilitará encontrar rápida y eficientemente la información que queremos y evitará el estrés de bucear perdidos en un mar de información. 
  3. Compartir información en un repositorio común. En el caso de una organización, comunidad o empresa, nos permite tener un punto único para la información. Compartimos información, accedemos a información de otros y además, nos ayuda a identificar la redundancia de información, evitando repetir el trabajo innecesariamente.
  4. Revisión y limpieza periódicas de la información. Si hay información que no has usado durante mucho tiempo, y que es poco probable que vuelvas a necesitar, elimínala. Si hay información que no tiene utilidad real para ti o para los demás, elimínala. Si no quieres deshacerte de ella, muévela a un repositorio de respaldo (backup), pero quítala de tus sistemas habituales. Si guardas documentos de hace más de uno o dos años, es muy posible que en ese momento te fueron útiles, pero desde entonces no los has necesitado, pero tienes la duda de si en el futuro podrían ser necesarios. Cópialos en un NAS, en un servidor de almacenamiento, en un disco duro externo, en un repositorio en la nube... pero en tu sistema de trabajo quítalos para trabajar día a dia con lo que realmente necesitas en cada momento. No vivas con la preocupación constante de que no los quieres perder. Los tendrás siempre disponibles en caso de que sean necesarios, pero tu espacio de trabajo, en el día a día, estará liberado de información poco útil en ese momento, a favor de la información que realmente necesitas ahora.
  5. En el caso de una empresa, recomiendo un tener plan y un sistema para hacer resúmenes o registrar estados de la información importante a partir del detalle de la información. Un buen ejemplo es generar informes diarios, semanales, mensuales, trimestrales y anuales a partir de toda la información de la empresa. Esta información resumida es la realmente importante para tomar decisiones operativas, estructurales, organizativas y estratégicas. Ocupa muy poco y contiene la esencia de la acividad y el estado de la empresa en el mismo momento en que se genera (foto), contemplando también la evolución y la tendencia usando un histórico. El detalle de la información sobre la que se ha construido, se puede mover a un repositorio para consultas puntuales, y así liberar los sistemas de trabajo del día a día. Para llevar a cabo esta propuesta, es muy útil un sistema de Business Intelligence, el cual, aunque pueda parecer un gasto importante, con el tiempo es una inversión muy amortizable y beneficiosa, no sólo en dinero, si no también en esfuerzo, tiempo y recursos.
Imagen: WikiCommons: https://commons.wikimedia.org/wiki/File:Monta%C3%B1a_de_basura_junto_al_lago_Managua.jpg
Montaña de Basura junto al lago Managua

martes, 20 de enero de 2015

Sentencia SELECT y procedimientos almacenados en MySQL

Los procedimientos almacenados son un recurso muy útil y socorrido, pues permiten realizar complejas operaciones sobre las bases de datos sin necesidad de que intervenga el código de las aplicaciones. Por otra parte, permiten realizar ETLs simples dentro de la base de datos, es decir, recopilar información de diferentes consultas y generar un resultado resumido, producto de filtrados y operaciones matemáticas y de información.

A veces, necesitamos obtener el resultado de un procedimiento almacenado en una consulta SELECT. Un ejemplo de ello puede ser en herramientas como Pentaho, donde el diseñador de informes utiliza una consulta para genera el informe.

La forma más sencilla es cuando el resultado es un único valor (un campo y un registro). Para ello, en lugar de un procedimiento almacenado se define una función, que retorne un único valor. En tal caso, para hacer la SELECT, simplemente definimos un campo como resultado de la llamada a dicha función:

SELECT nombre_funcion([parametros]);

Sin embargo, si deseamos obtener más valores, ya sea en un único registro o en varios, obtendremos un error por parte de MySQL, ya que esta sintaxis de SELECT se refiere a un único valor.

Una posibilidad es utilizar un procedimiento almacenado, y obtener los diferentes valores a través de parámetros de tipo OUT. Esto funciona bien, pero el problema radica en que aplicaciones como Pentaho, requieren obtener los datos en una única y simple consulta SELECT. En este caso, necesitamos dos pasos: la llamada al procedimiento (para obtenerlos valores en los parámetros OUT) y la SELECT que obtiene dichos valores:

CALL nombre_procedimiento(@param1, @param2...);
SELECT @param1, param2...;

No se puede obtener de otra manera. Si utilizamos una expresión como la siguiente retornará un error:

SELECT @param1, param2... FROM (CALL nombre_procedimiento(@param1, param2...));

¿Cómo solucionar este problema?

Voy a plantear una propuesta que a mi me ha servido y funciona bien. Se trata de utilizar una función que retorna un valor (físico) que contenga todos los valores (lógicos). Es decir, retornamos una cadena de texto que contiene todos los valores separados por un token. Posteriormente, cuando se recupere con el SELECT, se procederá a extraer todos los valores contenidos, como si fueran diferentes campos de una consulta.

En mi caso, necesitaba extraer diferentes valores que eran el resultado de diferentes operaciones matemáticas, y que resumían, de forma ejecutiva, una serie de ventas clasificadas por diferentes criterios. Todos los datos eran numéricos, y el tamaño máximo era de 10 cifras (sin decimales). Por tanto, monté una cadena de texto en la que concatené diferentes cadenas de 10 caracteres, que eran los números alineados a la derecha y rellenos con espacios por la izquierda. Entre valor y valor añadí un carácter a modo de token (un pipe o un punto y coma, por ejemplo). Por tanto, el valor devuelto por la función MySQL sería algo como esto:

     34550|    340575

A la hora de hacer el SELECT, podemos extraer cada uno de los valores como campos independientes, con el tipo de dato correspondiente (en este caso, numérico entero sin signo):

SELECT
CAST(SUBSTRING(funcion() FROM 1 FOR 10) AS SIGNED) AS campo1,
CAST(SUBSTRING(funcion() FROM 12 FOR 10) AS SIGNED) AS campo2;

lunes, 29 de noviembre de 2010

JSON y ODBMS

Las bases de datos orientadas a objetos son filosóficamente más diferentes de lo que uno podría esperar.

Tenemos una representación estandarizada documento/objeto - JSON es actualmente popular para almacenamientos orientados a documentos, quizá ODL en ODBMS. Lo bueno de JSON es que, al menos para programadores Web, es una tecnología que ya utilizan y con la que están familiarizados. No añadimos nada nuevo qué aprender.

En un documento, realmente estamos pensando en "documentos", no en objetos. Los objetos tiene métodos, esquemas predefinidos, jerarquías e herencias. Esto no está presente en una base de datos de documentos; el código no es parte de la base de datos.

Mientras que pueden existir relaciones entre documentos, los punteros entre documentos están desenfatizados. En el almacenamiento de documentos no persisten "grafos" de objetos - ésta no es una base de datos de grafos (las bases de datos de grafos son una nueva categoría NoSQL - ¿qué diferencia hay entre una base de datos de grafos y una ODBMS? Una pregunta interesante)

El diseño de esquema es importante en las bases de datos de documentos. No podemos pensar en términos de "solamente persiste lo que yo trabajo con memoria RAM desde mi programa". Todavía definimos un esquema. Este esquema puede variar con respecto a un "código de esquema" interno de la aplicación. Por ejemplo, en MongoDB tenemos colecciones (análogas a las tablas) de documentos JSON, y una declaración explícita de índices sobre campos específicos de la colección. Podemos pensar que esta aproximación tiene algunos méritos - un desacoplamiento de datos y de código. El código tiende a cambiar rápidamente.

Embeber es un concepto importante en el almacenamiento de documentos. Es mucho más común anidar datos dentro de documentos que tener referencias entre documentos.

¿Por qué desenfatizar las relaciones? Por un par de razones. La primera, con grafos arbitrarios de objetos, es difícil procesar el grafo desde un cliente sin muchos tiempos de espera cliente/servidor. Un objetivo con bases de datos de documentos es mantener el paradigma cliente/servidor y mantener código predispuesto para el cliente (no obstante con algunas excepciones como map/reduce). Segundo, un objetivo clave en el espacio "NoSQL" es la escalabilidad horizontal. Los grafos arbitrarios de objetos dificultarían la partición entre servidores de una manera que garantice el rendimiento.

Fuente: http://blog.10gen.com/post/437029788/json-db-vs-odbms

Análisis entre modelo relacional y modelo documental en bases de datos

El origen de las bases de datos va ligado a la necesidad de organizar la cada vez más exigente cantidad de datos que se requieren. En los inicios, la información estaba delegada a los ficheros, como meros depósitos de información y a los que se accedía a bajo nivel para las operaciones más elementales de lectura y escritura. Con el tiempo, la información requerida se hacía mayor, y la complejidad de gestionarla también creció. Se comenzó a aplicar técnicas como índices para accesos más rápidos y para la ordenación de la misma. Pero las aplicaciones debían tratar los ficheros de forma artesanal y personalizada.

Surgieron sistemas que automatizaban las operaciones complejas de gestión de los datos, como los índices, así como las primeras relaciones entre campos clave entre dos o más tablas. Pioneros en estos sistemas fueron (por citar los más conocidos) fueron dBASE y Clipper, pero también empezaban a gestarse grandes bases de datos profesionales, como Oracle o DB2. Estamos hablando de los años 80.

Los dispositivos de almacenamiento eran limitados, así como la capacidad de los sistemas operativos en almacenar y direccionar información, aunque suficientes para la época. Es por ello que la optimización del espacio fuera un un factor vital, y que el diseño de las tablas contemplase el acotar el almacenamiento de cada campo, tener en cuenta el tamaño de cada fila, y prever el tamaño total de los ficheros junto con la previsión de crecimiento de los mismos.


Bases de datos relacionales

El modelo relacional surge ante estas circunstancias, postulado por Frank Codd en 1970. La normalización de los datos permite organizar la información separando en tablas la información más pequeña posible e independiente, para evitar la repetición de la misma. Para acceder a toda la información, las tablas se relacionan unas y otras mediante campos clave comunes, que suelen ser números con un id único.

Este modelo de almacenamiento es organizado y óptimo, define unas reglas que evitan la redundancia de la información y facilitan la consistencia de los datos. Este sistema de ingeniería de la información es el que ha regido las bases de datos durante los últimos 40 años.

Para trabajar con este modelo de almacenamiento, surgió el lenguaje SQL, que facilitó el entendimiento de las relaciones a través de una sintaxis sencilla y asimilable.

Este modelo de almacenamiento tiene las siguientes ventajas:
- Sistema de normalización óptimo en almacenamiento. Idóneo para sistemas con requisitos muy claros y poco dado a cambios.
- Sistema fuertemente estructurado e interrelacionado, formando un esquema.
- Sistema unánimente aceptado y estándar, utilizado en más del 90% de las aplicaciones
- La mayor parte de fabricantes de bases de datos utilizan el SQL estándar en sus productos, si bien puede haber alguna pequeña variación fácilmente subsanable.
- Las migraciones entre bases de datos de diferentes fabricantes no son excesivamente impactantes
- Las consultas SQL tienen funciones agregadas de cálculo asociadas a columnas en los resultados.
- Los sistemas de bases de datos relacionales asumen el trabajo de las complejidades de la integridad referencial, velando por la consistencia de la información y liberando al desarrollador de estas tareas para que se centre solamente en las consultas.
- Multitud de herramientas productivas construídas en torno a este modelo: business intelligence, datamining, ETL, administración, monitorización, etc.
- Eficiente para gestionar datos para automatizar actividades.

Sin embargo, también conlleva algunos inconvenientes:
- La normalización en entidades atómicas no es natural y tiende a complicar su entendimiento y los desarrollos.
- Basar la identificación de los registros y relacionar tablas por códigos numéricos es críptico y poco natural.
- La información, tal y como está almacenada, difiere mucho de cómo es gestionada, y requiere un esfuerzo importante de adaptaciones.
- Sistema rígido y estricto poco tolerante a fallos y que penaliza los cambios.
- La adición de entidades de relación y de campos clave a una tabla ya existente implica complejidad e impacta en los desarrollos actuales.
- Sistema que gana en complejidad a medida que crece. La escalabilidad de la estructura se torna condicionada.
- El desarrollo de las aplicaciones requiere de capacitación para conocer el modelo entidad-relación.
- El almacenamiento de toda la información requiere dividir la información en entidades atómicas y tratar éstas por separado en varios procesos.
- Las consultas requieren tracear la información separada y reunirla. Las joins suelen ser complejas y pesadas, y a veces no son suficientes, requiriendo varios procesos de consulta adicionales.
- La gestión automatizada de la integridad referencial supone una penalización de tiempo de accesos (lectura y escritura) a la información.
- La gestión automatizada de la integridad referencial no evita que el desarrollador preste atención a errores de integridad y su causa. El desarrollador debe asegurar previamente la integridad y controlar si el sistema le informa de alguna violación de reglas de integridad. Por ejemplo, la creación de una categoría de productos. El sistema informará si se está tratando de crear una categoría ya existente, y el programa debería capturar este aviso para advertir al usuario sobre la causa del problema. Otra forma (sin integridad automatizada) sería consultar previamente cuántas categorías existen con ese nombre. Si es mayor de cero advertir al usuario, y si no guardar la nueva categoría. El trabajo en ambos casos (con y sin integridad automatizada) no difiere mucho.
- La información de cada tabla está limitada a una estructura fija de campos o columnas, sean o no necesarios, y tengan o no valores. Si no se utiliza una columna, ésta se almacena de todas formas en cada fila.
- Poco eficiente a la hora de explotar grandes volúmenes de información textual (maneja mejor datos que información).
- Los principales fabricantes de sistemas de bases de datos relacionales las desarrollan de forma propietaria.


Bases de datos documentales

Las bases de datos documentales, a diferencia de las bases de datos relacionales, están libres de esquemas y de estructuras. Por tanto, la información no se divide en columnas fijas y filas, si no en documentos que pueden tener libertad de información, como cualquier cantidad de campos o de tipos de campos, e incluso de contener en el propio campo una lista de valores, un documento o una colección de documentos. La información almacenada en un documento está formado por pares de clave y valor.

La gran ventaja de no poseer esquema (y la consiguiente rigidez de estructuras y relaciones), es que son muy eficientes al trabajar con grandes cantidades de documentos.

Las principales ventajas de una base documental son las siguientes:
- Libre de esquemas. Al no estar sujeta a estructuras ni relaciones, los cambios de diseño son fáciles de adoptar y con un impacto mínimo.
- Al no tener relaciones se facilita la replicación
- Los sistemas de bases de datos documentales ganan rapidez dedicándose más en exclusiva al almacenamiento y la recuperación de la información (no consumen tiempos en la integridad o en la revisión de las reglas o constraints).
- Poseen un lenguaje de consulta natural.
- Sencillez y potencia en escalabilidad.
- Los campos vacíos no se añaden al documento, optimizando el espacio de almacenamiento.
- Cada documento (o “fila”) puede tener estructuras diferentes (más o menos campos, tipos diferentes de datos, etc.)
- Un documento es un objeto que normalmente es mapeado mediante JSON o XML
- Posibilidad en embeber documentos y colecciones dentro de documentos. Esto solventaría joins entre documentos, aunque agregaría complejidad a la estructura del documento.
Los datos almacenados no necesitan adaptaciones por las aplicaciones (tal como se almacenan se pueden gestionar).
- Muy eficiente a la hora de explotar grandes volúmenes de información textual (maneja mejor información que datos).
- Las migraciones entre bases de datos diferentes tienen muy poco impacto, pues comparten el formato JSON o XML.
- La mayor parte de los fabricantes de sistemas de bases de datos documentales, la desarrollan como open source.
- El auge de la Web está mirando este modelo de datos como clave en sus negocios, por su mayor capacidad de escalabilidad, rendimiento y alta disponibilidad, que los actuales sistemas de bases de datos relacionales.

Los principales inconvenientes serían:
- El sistema de base de datos no contempla realizar joins entre documentos.
- Más peso por parte de las aplicaciones para asumir la integridad y la consistencia.
- No posee funciones agregadas para realizar cálculos sobre la información de las consultas.
- Redundancia de la información. Penaliza el espacio de almacenamiento en aras de una mayor rapidez de acceso y de claridad de la información.
- Menos eficiente para automatizar actividades.
- Aunque este modelo de información es anterior al relacional, aún tiene una aceptación muy baja (menos de un 10% del total de aplicaciones funcionando en el mundo).
- No hay muchas herramientas de productividad en torno a este tipo de bases de datos, como Business Intelligence, monitorización, etc.
- Aunque es posible migrar un sistema relacional a un sistema documental, la potencia de la integridad referencial ha de ser restituida por código en las aplicaciones. No merece la pena esto, y sí una restrospectiva en la que se adapte los conceptos a documentos teniendo siempre en mente las ventajas que podría ganarse en la migración.

Safe Creative #1003275854193

martes, 19 de octubre de 2010

Conceptos básicos de Python y SQLite

En el presente post vamos a aprender los conceptos básicos para desarrollar código en Python utilizando la base de datos SQLite. Se presupone que el lector conoce los conceptos básicos de Python y de SQL, y que ya tiene instalado en su máquina tanto el intérprete de Python como SQLite.

Librería pysqlite

Lo primero que debemos hacer es descargar e instalar la librería pysqlite desde su página web: http://code.google.com/p/pysqlite

En el caso de tener Windows, descargamos un archivo ejecutable (.exe) y lo ejecutamos, instalándolo como cualquier aplicación Windows.

En el caso de tener Linux, descargamos el archivo comprimido (.tar.gz), y lo descomprimimos. Una vez descomprimido, instalamos la librería mediante el siguiente comando:

$ python setup.py build
$ python setup.py install


Conexión a la base de datos

Para realizar una conexión a una base de datos SQLite, ésta debe estar arrancada:

$ sqlite3 mibasedatos.bbdd

Para realizar una conexión a una base de datos, importar la librería:

from pysqlite2 import dbapi2 as sqlite

A continuación se establece la conexión indicando el archivo de base de datos:

con = sqlite.connect('mibasedatos.bbdd')


Consultas de datos

Para todas las operaciones con la base de datos, hay que crear un cursor a partir de la conexión:

cur = con.cursor()

A partir del cursor, se pueden ejecutar las operaciones que se requieran, como la consulta de datos:

cur.execute('select campo from tabla')

Al ejecutar la consulta, el cursor apunta antes del primer registro o fila. Los siguientes comandos avanzan hasta el siguiente registro:

cur.next() # Avance
cur.fetchone() # Avance y recupera registro

Para obtener de una vez todos los registros del cursor:

cur = con.cursor()
cur.execute('select * from tabla')
result = cur.fetchall()

En la variable result se almacenará una lista o secuencia de tuplas, cada una de las cuales corresponde a un registro, y el valor de cada uno de los campos se encuentra separado por comas:

print result
[(0, u'valor1', 87), (1, u'valor2', 32), (2, u'valor3', 38)]

En este ejemplo, el resultado da 3 registros con tres columnas o campos, de los cuales, el primero y el último son numéricos, y el segundo es alfanumérico.

print result[1] # registro 2
(1, u'valor2', 32)
print result[2][1] # campo 2 del registro 3
valor3

Recorrer el resultado resulta muy sencillo, utilizando un iterador sobre la secuencia:

cursor = con.cursor()
cursor.execute('SELECT id, nombre, nick, password, rol FROM usuarios')
for fila in cursor:
   print 'id:', fila[0]
   print 'Nombre:', fila[1]
   print 'Nick:', fila[2]
   print 'Password:', fila[3]
   print 'Rol:', fila[4]
   print '*'*30


Actualizaciones

Por actualización se entiende toda aquella operación que realiza algún cambio, lo que implica algún tipo de escritura. Existen varios tipos de escritura o modificaciones en la base de datos. El primero afectaría únicamente a los datos de las tablas, lo que se consigue mediante las sentencias UPDATE, INSERT y DELETE de SQL. Otro afectaría a la estructura de la base de datos, pudiendo crear, modificar o eliminar tablas o índices.


Actualización de datos en tablas

Para todas ellas, se utilizará un cursor y la función execute(), tal y como vimos anteriormente:

from pysqlite2 import dbapi2 as sqlite
con = sqlite.connect("ticube.bbdd")
cur = con.cursor()
cur.execute("insert into tbconfig (parametro, valor, descripcion) values ('PARAM1', '10', 'Parametro 1')")
cur.lastrowid

En este ejemplo se inserta un registro en una tabla. La última línea retorna el número de la última fila afectada en el cursor. Si sólo se realizó esta operación de escritura, pero no afecta a ningún registro, no retornará nada. Si todo fue bien, retornará (en este caso) 1.

El cursor acumulará operaciones de escritura, a medida que se vayan ejecutando. Pero estas operaciones no se hacen efectivas físicamente en la base de datos. Ello se debe a que se encuentran en una transacción.

Una transacción resulta muy útil cuando hay un encadenamiento de operaciones de escritura que tienen dependencias unas de otras. Cuando se completan todas las operaciones de escritura, si no se ha producido ningún tipo de error, se puede procesar toda la transacción indicando a la conexión de la base de datos que perpetre definitivamente la transacción con todas las operaciones realizadas:

con.commit()

Si se produjera algún error durante la transacción, podrían abortarse todas las operaciones de escritura indicadas en el cursor para evitar inconsistencias de datos. Para ello, una vez se detecte algún error o inconsistencia, se indicaría a la conexión de la base de datos que dé marcha atrás, cancelando todas las operaciones realizadas:

con.rollback()



Actualización de la estructura de la BBDD

También es posible acceder y modificar la estructura de la base de datos, pudiendo crear nuevas tablas, añadir campos a las tablas existentes, crear o borrar índices, etc.

El proceder es idéntico a como hemos visto antes, a través del cursor. El siguiente ejemplo crea una sencilla tabla en nuestra base de datos:

cursor.execute("CREATE TABLE configuracion (parametro VARCHAR(30) NOT NULL, valor VARCHAR(30))")

A diferencia de la actualización de datos en las tablas, este tipo de actualizaciones se realiza directamente, y no requiere de transacciones.


Consultas parametrizadas

Imaginemos que se pretende hacer una carga masiva de datos de una tabla. En lugar de tantas sentencias INSERT como conjuntos de valores (cosa inviable si los datos son dinámicos), se pueden recoger éstos de otra consulta o de un fichero, y asignar dichos valores a unas variables y utilizar el valor de éstas en una única sentencia INSERT:

from pysqlite2 import dbapi2 as sqlite
con = sqlite.connect("ticube.bbdd")
cursor = con.cursor()
parametros=['UNO','DOS',’TRES’]
valores=['1','2','3']
max=len(parámetros)
for i in range(0,max):
   cursor.execute("INSERT INTO configuracion (parametro, valor) VALUES (?, ?)", (parametros[i], valores[i]))
con.commit()

En este ejemplo se insertarán 3 registros en la tabla configuracion, obtenidos de dos secuencias (parametros y valores). El símbolo ? (interrogación), reserva el valor que va a tomar de la lista de parámetros que se especifican a continuación, en el mismo orden en que se enumeran.

Esta capacidad se puede aplicar también a otras consultas de actualización (como UPDATE o DELETE) o a consultas de datos (SELECT).



Cerrar siempre

SQLite es una base de datos muy versátil, ligera y práctica. Ello se consigue sacrificando una parte esencial en entornos distribuídos: la concurrencia. Así es: SQLite no es concurrente. Por ello, para evitar problemas e inconsistencias por la concurrencia, deberemos siempre cerrar todos los cursores y conexiones una vez han sido utilizados y no los utilicemos en cada módulo. Ello libera la conexión para nuevas operaciones.

El método close() se encarga de esta operación:

cursor.close()
con.close()

El orden de apertura y cierre es importante:

1) Abrir conexión
2) Abrir cursor
3) Operaciones
4) …
5) Cerrar cursor
6) Cerrar conexión

Safe Creative #1010197608377

martes, 28 de septiembre de 2010

Primeros pasos con Python y MongoDB

Una de las mejores características de MongoDB es la multitud de drivers existentes para casi cualquier lenguaje de programación (ver Sección Drivers). Para los que amamos Python, tenemos un driver llamado pymongo, y que está soportado por 10gen, la empresa creadora de MongoDB. El site oficial de este driver lo encontramos en: http://api.mongodb.org/python/1.8.1%2B/index.html

Utilizar Python junto a MongoDB es muy sencillo, pues su sintaxis es muy similar a la de la consola mongo.

Instalación
La instalación de pymongo es sencilla. En Linux, procederemos a ejecutar el siguiente comando:

$ easy_install pymongo

O bien, descargamos el código fuente y lo instalamos desde la consola de Python:

$ git clone git://github.com/mongodb/mongo-python-driver.git pymongo
$ cd pymongo/
$ python setup.py install


Abrir conexión
La conexión se realiza de la siguiente manera:

>>> from pymongo import Connection
>>> conn = Connection() # Conexion local por defecto
>>> conn = Connection('miServidor', 30500) # Conexion remota a puerto 30500


Usar base de datos
Para obtener un objeto que referencia a la base de datos:

>>> db = conn.miBBDD # metodo 1
>>> db = conn['miBBDD'] # metodo 2



Usar una colección
Para obtener la colección con la que trabajar:

>>> coll = db.miColeccion # metodo 1
>>> coll = db['miColeccion'] # metodo 2



Documentos
Los documentos se mapean en Python como un diccionario:

>>> noticia = {"autor": "Rafael Hernamperez",
...   "cuerpo": "Python y MongoDB",
...   "etiquetas": ["Tutorial","Python","MongoDB"]}


Algunos tipos de datos especiales requieren de alguna librería específica, como datetime:

>>> import datetime
>>> noticia = {"autor": "Rafael Hernamperez",
...   "cuerpo": "Python y MongoDB",
...   "etiquetas": ["Tutorial","Python","MongoDB"],
...   "fecha": datetime.datetime.utcnow()}



Insertar documentos
Para insertar un documento:

>>> coll = db.noticias # Coleccion "noticias"
>>> coll.insert(noticia)



Recuperar un documento
Para recuperar un único documento simple (el primero):

>>> coll.find_one({"autor": "Rafael Hernamperez"})


Recuperar varios documentos
Para recuperar todos los documentos de una colección:

>>> for noticia in coll.find():
...    print noticia


Para recuperar todos los documentos (noticias, en este caso) de un autor concreto:

>>> for noticia in coll.find({"autor": "Rafael Hernamperez"})
...    print noticia



Contar documentos
Para contar todos los documentos de una colección:

>>> coll.count()

Para contar todos los documentos de un autor concreto:

>>> coll.find({"autor": "Rafael Hernamperez"}).count()


Consultas de rango
Recupera un determinado rango de documentos, delimitados por condiciones. En este caso, recupera aquellas noticias anteriores a una determinada fecha. El resultado se ordenará por autor:

>>> fecha = datetime.datetime(2010, 9, 28, 0, 0) # 2010/09/38 00:00h
... for noticia in coll.find({"fecha": {"$lt":fecha}}).sort(autor):
...    print noticia



Indexación
Para indexar la colección "noticias", por fecha (de la más reciente a la más antigua) y por autor (en orden alfabético):

>>> from pymongo import ASCENDING, DESCENDING
>>> coll.create_index([("fecha", DESCENDING), ("autor", ASCENDING)])

viernes, 2 de julio de 2010

¿Tantas bases de datos NoSQL?

Cuando hay alternativas a los sistemas tradicionales, a veces superan las expectativas y uno cabe preguntarse, ¿para qué tanto? ¿en qué casos me viene bien qué? Quizá esto despierte más dudas que antes, y uno ya no sepa qué decidir, o incluso volver a lo tradicional. Por otra parte, se percibe proactividad y creatividad por parte de comunidades de software libre y de algunas empresas, proponiendo alternativas muy interesantes.

A continuación se expone una lista de 64 bases de datos NoSQL. No están todas las que hay, pero sí las más relevantes y populares.

Familias de columnas
Hadoop/HBase
Cassandra
HyperTable
Cloudera
SciDB


Documento
CouchDB
MongoDB
TerraStore
ThruDB
OrientDB
RavenDB


Clave/Valor
Amazon SimpleDB
Azure Table Storage
Riak
Chordless
Redis
Scalaris
Tokio Cabinet
GT.M
Scalien
Berkeley DB
MemcacheDB
HamsterDB
Membase
NorthScale
Mnesia
LightCloud
Pincaster
GenieDB


Clave Valor eventualmente consistente
Amazon Dynamo
Voldemort
Dynomite
KAI


Bases de datos por grafos
Neo4J
Sones
InfoGrid
HyperGraphDB
AllegroGraph
BigData
DEX
OpenLink Virtuoso
Infinite Graph
VertexDB


Bases de datos de objetos
db4o
Versant
PERST
ZODB
NEO


Bases de datos en Grid
GigaSpaces
Hazelcast
Joafip


Bases de datos XML
Mark Logic Server
EMC Documentum xDB
Tamino
eXist
Sedna
Xindice
Berkeley DB XML


Bases de datos multivalor
U2
OpenInsight


Otros tipos de base de datos
IBM Lotus/Domino
ISIS Family
Prevayler
Yserial



Fuente: NoSQL Databases

martes, 22 de junio de 2010

lunes, 21 de junio de 2010

H2: una base de datos matona

En esta ocasión vamos a darle una oportunidad a una base de datos muy poco conocida, pero que tiene un potencial bastante grande. H2 es una base de datos relacional, que poco tiene que envidiar a sus hermanas mayores, aunque hay que tener en cuenta que en entornos de alta disponibilidad, con una una operatividad crítica, será recomendable una base de datos más especializada, como MySQL, PostgreSQL, SQL Server u Oracle.

Está escrita íntegramente en Java. Pero este aspecto no nos tiene que echar para atrás, pues es muy rápida gracias a su optimización basada en costes que, en muchos aspectos, supera a las bases de datos más populares. Además, al estar escrito en Java, se ejecuta sin problemas en cualquier sistema operativo.

Lo primero que sorprende es su tamaño, apenas 5MB, en el que el fichero jar apenas ocupa 1MB. En estos 5MB se incluye también un servidor de aplicaciones con una consola web muy rica para poder administrar la base de datos, y poder ejecutar consultas. Dicha consola incluye autocompletado y ayuda completa.

H2 opera en dos modos: embebido y “en memoria”. El modo embebido es el tradicional, en el que el servidor gestiona los datos en archivos, teniendo una pequeña parte en memoria. El modo “en memoria”, aloja todos los datos en memoria, con lo que se consigue una velocidad increíble en el acceso a los datos. También es posible combinar los dos modos.

Al estar escrito en Java puro, la integración con aplicaciones escritas en Java se hace de forma natural y sencilla. Evidentemente, utiliza una API JDBC, pero también soporta ODBC, por lo que puede ser utilizada en otros lenguajes de programación y otras aplicaciones, tales como Office, Business Intelligence, etc.

Otras características a reseñar son:
- Aíslamiento de trasacciones
- Encriptación de la base de datos
- SQL estándar
- Búsqueda de texto completo
- Concurrencia multiversión
- Plan explicativo de la ejecución de operaciones
- Clustering
- Tablas enlazadas
- Tipos de datos definidos por el usuario
- Bloqueo a nivel de fila y de tabla
- Seguridad basada en roles
- Secuencias
- Resultsets actualizables
- Información de esquema
- Columnas computadas
- Funciones agregadas personalizadas
- Limit y Offset
- Tablas temporales
- Alto rendimiento
- Triggers mediante clases Java
- Procedimientos almacenados en Java
- Almacenamiento de objetos grandes (CLOB y BLOB)
- Commit en dos fases
- Acceso desde .NET mediante JDBC (IKVM.NET) o mediante ADO.NET
- ACID
- Ejecución como servicio Windows
- Incluye herramienta de recuperación
- Soporte para conexiones seguras (SSL/TLS)

A todo lo anterior cabe añadir que esta base de datos cuenta con una documentación muy detallada y sencilla, en la que nunca estaremos perdidos.

Los casos de uso más frecuentes son aplicaciones de escritorio y aplicaciones Web en las que no se requiera de una enorme base de datos. Para páginas web en las que imperen las consultas sobre las actualizaciones, también es un escenario ideal. Para aplicaciones pequeñas también es recomendable, en lugar utilizar bases de datos pesadas que consuman muchos recursos innecesariamente.

Enlaces:
Página principal de H2
Tutorial
Descarga

miércoles, 2 de junio de 2010

MongoDB y la NoSQL Summer Madrid 2010

Durante los meses de Junio, Julio, Agosto y Septiembre tendrá lugar charlas sobre las bases de datos NoSQL en ciudades de todo el planeta. En Madrid tendremos la oportunidad de poder compartir momentos interesantes, donde podremos discutir sobre el movimiento NoSQL, conocer más a fondo este escenario y diferentes bases de datos, tales como MongoDB (entre otras).

Aún no tenemos fechas para los eventos, pero os iremos informando en esta comunidad y desde el site:

http://nosqlsummer.org/city/madrid

A medida que vayamos organizando las charlas os informaremos en más detalle de las agendas, de los participantes y de los temas.

Os esperamos!!!

lunes, 24 de mayo de 2010

Primeros pasos en Java y MongoDB - Parte 2

En la entrada anterior abordamos cómo conectar a la base de datos MongoDB y cómo realizar consultas. En el presente post abordaremos cómo modificar datos en una colección.


Inserciones
La operación más sencilla es la inserción, gracias a la cual añadimos un nuevo documento a una colección. Tomemos el siguiente ejemplo extraído del tutorial de Java de MongoDB. Tenemos el siguiente documento:


{
  "name" : "MongoDB",
  "type" : "database",
  "count" : 1,
  "info" : {
    x : 203,
    y : 102
  }
}


El campo info es un documento embebido dentro del documento principal. La forma de añadir este documento a MongoDB mediante código Java, sería la siguiente:


// Conexion por defecto a localhost por el puerto 27017
Mongo con = new Mongo();
DB bbdd = con.getDB("nombreBaseDatos");
DBCollection col = bbdd.getCollection("nombreColeccion");

BasicDBObject doc = new BasicDBObject();

doc.put("name", "MongoDB");
doc.put("type", "database");
doc.put("count", 1);

BasicDBObject info = new BasicDBObject();

info.put("x", 203);
info.put("y", 102);

doc.put("info", info);

col.insert(doc);


En el ejemplo anterior hemos almacenado un único documento. Si lo que se desea es almacenar una lista de documentos de una sola vez, es posible crear un array o una lista de tipo DBOject, y pasar éste como parámetro. El método insert añadirá todos los documentos a la colección.


abstract void insert(DBObject arr[])

abstract foid insert(List list)


Nota: En el caso anterior, el documento contenía pares campo/valor de tipo nativo (cadenas de texto, números) o de tipo documento (embebido). Si el dato es un array, definir un tipo Vector, y asignarlo como valor a un campo.


Actualizaciones
La actualización permite modificar un documento ya existente.

Existe varias maneras de hacerlo. La primera es mediante el método save, cuya sintaxis es la siguiente:


public final void save(DBOject doc)


El funcionamiento de este método es muy simple: "guardar el documento 'doc', sustituyendo si ya existe, o añadiendo si no existe". En el caso de la sustitución, el documento debe tener el campo "_id" y el valor correspondiente del documento a sustituir.

Otra forma de actualizar documentos es mediante el método update, el cual posee varias posibilidades. La más sencilla sería por sustitución de documento:


BasicDBObject oquery = new BasicDBObject();
oquery.put("name", "MongoDB");
oquery.put("type", "database");

BasicDBObject doc = new BasicDBObject();

doc.put("name", "MongoDB");
doc.put("type", "database");
doc.put("count", 3);

BasicDBObject info = new BasicDBObject();

info.put("x", 255);
info.put("y", 64);

doc.put("info", info);

col.update(oquery, doc);


El método update admite varias sintaxis. En la utilizada en el ejemplo anterior, recoge dos parámetros. El primero de ellos (oquery), contiene un documento de filtro, en el que se especifican los campos y valores que deben cumplirse para los documentos a modificar. En este caso, las condiciones eran "cuyo 'name' tenga el valor 'MongoDB' Y cuyo 'type' tenga el valor 'database'". Si hubiese más de un documento que se correspondiera con el resultado del filtro, por defecto, se asumiría el primero.

El segundo parámetro sería un documento completo que sustituirá por completo al documento encontrado.

Otra forma modificar documentos sería a través de operaciones atómicas. Esto significa que se modifica directamente un elemento del documento, en lugar de sustituir el documento completo. El siguiente ejemplo cambiaría el valor del campo count:


BasicDBObject oquery = new BasicDBObject();
oquery.put("name", "MongoDB");
oquery.put("type", "database");

BasicDBObject doc = new BasicDBObject();
BasicDBObject op = new BasicDBObject();
op.put("count", 4);
doc.put("$set", op);

col.update(oquery, doc);


Esto sería similar a la sentencia SQL:

UPDATE col SET count=4 WHERE name='MongoDB' AND type='database'

Existe una lista rica de operaciones atómicas, muy útiles según el caso. Para más información, consulta la siguiente URL:

http://www.mongodb.org/display/DOCSES/Actualizar

El método update permite las siguientes sintaxis:


public void update(
  DBObject query,
  DBOject doc)

public abstract void update(
  DBObject query,
  DBObject doc,
  boolean upsert,
  boolean multi)


La primera de ellas, es la vista anteriormente. La segunda de ellas, posee cuatro parámetros. Los dos primeros ya los conocemos. Los dos restantes:

- upsert: Si se especifica el valor true, si el filtro se cumple, actualiza los documentos encontrados. Si no se cumple, añade el documento doc a la colección (como un insert). Si se especifica el valor false (por defecto), sólo permite actualizar, no insertar.
- multi: Si se especifica el valor true, si el filtro retorna varios documentos, las actualizaciones se realizarán sobre todos ellos. Si se especifica el valor false (por defecto), las actualizaciones sólo se realizarán sobre el primer documento retornado.

El driver Java de MongoDB ofrece también el método updateMulti, cuya sintaxis es la siguiente:


public void updateMulti(
  DBObject query,
  DBOject doc)


La sintaxis es exactamente igual que la primera del método update. Sin embargo, update solamente actualiza el primer documento encontrado por el filtro. Con updateMulti las actualizaciones se aplicarían a todos los documentos encontrados.


Eliminaciones
Para eliminar documentos, se utiliza el método remove, cuya sintaxis es la siguiente:

public abstract void remove(DBObject query)

El parámetro query es un filtro que determina qué documentos han de coincidir para la eliminación. Dicha eliminación se aplica a todos los documentos que coincidan con el filtro.

martes, 11 de mayo de 2010

Primeros pasos en Java y MongoDB - Parte 1

MongoDB es una base de datos que cuenta con una amplia comunidad, y fruto de ello, son los proyectos que en torno ella se generan, especialmente los drivers, los cuales permiten acceder a la base de datos desde cualquier lenguaje de programación. En este post se realizará una introducción al uso del Driver de Java para Mongo.

Nota:
Se asume que el lector tiene conocimientos sobre programación de Java. En este artículo se omite cualquier estructura de código Java, enfocándose únicamente en las declaraciones y usos concretos del driver de Java para MongoDB.


Descarga del driver de Java

El Driver de Java para Mongo se encuentra en la siguiente URL:

http://www.mongodb.org/display/DOCSES/Centro+de+lenguaje+Java


Documentación sobre el driver

El Driver de Java cuenta con una documentación muy completa, la cual es accesible desde la siguiente URL:

http://www.mongodb.org/display/DOCSES/Centro+de+lenguaje+Java


Conexión al servidor

El primer paso a realizar es importar las clases del driver. Para ello, utilizar la sentencia import de la siguiente manera:

import com.mongodb.*;

Una vez importadas las clases, lo primero es crear una conexión al servidor. Existen varias maneras de hacer ésto:

// Conexion por defecto a localhost por el puerto 27017
Mongo con = new Mongo();
// Conexion al servidor "mongosrv" por el puerto 27017
Mongo con = new Mongo("mongosrv");
// Conexion al servidor "mongosrv" por el puerto 30000
Mongo con = new Mongo("mongosrv", 30000);



Uso de una base de datos

Un servidor de base de datos MongoDB puede tener múltiples bases de datos. Para indicar qué base de datos se desea utilizar, introducir la siguiente sentencia:

DB bbdd = con.getDB("nombreBaseDatos");


Utilizar una colección

Una vez en uso la base de datos, hay que especificar qué colección de entre las existentes en dicha base de datos, queremos utilizar. Para ello, se obtiene la colección a través de la siguiente sentencia:

DBCollection col = bbdd.getCollection("nombreColeccion");


Consultas simples

La consulta más sencilla posible sería la siguiente:

DBObject doc = col.findOne();
System.out.println(doc);


Retornaría el primer documento de la colección, mostrándolo por la consola.

Para obtener más documentos, hay que recuperar un cursor:

DBCursor cur = col.find();
while (cur.hasNext())
  System.out.println(cur.next());


El código anterior recuperaría todos los documentos de la colección y los mostraría por la consola.


Consultas de filtro

Las consultas de filtro permiten seleccionar solamente aquellos documentos que cumplen algún criterio. Por ejemplo, imaginemos que tenemos una colección Agenda, y queremos consultar todos los contactos de Madrid. La consulta sería la siguiente:

...
DBCollection col = bbdd.getCollection("Agenda");

// Se crea el documento de filtro
BasicDBObject filtro = new BasicDBObject();
filtro.put("provincia", "Madrid");
DBCursor cur = col.find(filtro);
while (cur.hasNext())
  System.out.println(cur.next());


Esta consulta sería similar a si lo hiciéramos desde la consola mongo:

db.Agenda.find({"provincia":"Madrid"})

Para ser más concretos y precisos en el filtro, se pueden añadir más condiciones, con las cuales se realizaría una selección AND/Y. Por ejemplo, para extraer los documentos de aquellas personas que se llamen Pedro, Y que vivan en Madrid, Y que tengan vehículo, el filtro quedaría de la siguiente manera:

filtro.put("nombre", "Pedro");
filtro.put("provincia", "Madrid");
filtro.put("vehiculo", true);


Esto equivaldría a:

db.Agenda.find({"nombre":"Madrid", "provincia":"Madrid", "vehiculo":true})

También es posible especificar operadores lógicos para limitar el rango de la consulta. El siguiente ejemplo extaería de la agenda a las personas mayores de edad que pueden trabajar:

filtro.put("edad", new BasicDBObject("$gte", 18));
filtro.put("edad", new BasicDBObject("$lte", 65));


Esto equivaldría a:

db.Agenda.find({"edad":{"$gte":18}, "edad":{"$lte":18}})

Nota: Para más información sobre consultas avanzadas mediante el uso de operadores lógicos, consultar: Consultas Avanzadas o Complejas


Acceso a los valores

Las aplicaciones que acceden a los datos, normalmente lo harán a sus valores, con el fin de realizar operaciones concretas con los mismos: formato, cálculo, comparación, etc.

El siguiente ejemplo valida un usuario y una contraseña, recogiendo el nombre completo, el idioma y la edad:

...
DBCollection col = bbdd.getCollection("Usuarios");

String sUsuario = "usuario";
String sPassword = "password";
String sNombre;
String sIdioma;
Integer iEdad;

BasicDBObject filtro = new BasicDBObject();
filtro.put("usuario", sUsuario);
filtro.put("password", sPassword);
DBObject result = col.findOne(filtro);

if (result==null)
  System.out.println("No se encuentra el usuario");
else {
  sNombre = (String)result.get("nombre");
  sIdioma = (String)result.get("idioma");
  sEdad = (Integer)result.get("edad");
}


Imaginemos ahora que deseamos recoger todas las áreas donde un determinado usuario puede acceder. La estructura de su documento podría ser el siguiente:

{"usuario":"usuario", "areas":["Area 1", "Area 2", "Area 3"]}

En este caso, la clave areas es un array. Para poder leer este array y almacenarlo en un vector interno, se utilizaría el siguiente código:

...
DBCollection col = bbdd.getCollection("Usuarios");

Vector v;

BasicDBObject filtro = new BasicDBObject();
filtro.put("usuario", "usuario");
DBObject result = col.findOne(filtro);

if (result==null)
  System.out.println("No se encuentra el usuario");
else {
  v = new Vector();
  BasicDBList dbl = (BasicDBList)obj.get("areas");
  for (int i=0; i<dbl.size(); i++)
    v.add((String)dbl.get(i));
}


Imaginemos ahora documentos en un array embebido en otro documento:

{"usuario":"usuario", prestado:[{"tipo":"libro", "titulo":"Don Quijote"}, {"tipo":"pelicula", "titulo":"Casablanca"}]}

El acceso a este esquema sería el siguiente:

...
DBCollection col = bbdd.getCollection("Usuarios");

BasicDBObject filtro = new BasicDBObject();
filtro.put("usuario", "usuario");
DBObject result = col.findOne(filtro);

if (result==null)
  System.out.println("No se encuentra el usuario");
else {
  BasicDBList dbl = (BasicDBList)obj.get("prestado");
  for (int i=0; i<dbl.size(); i++) {
    DBObject doc = dbl.get(i);
    System.out.println("Tipo: "+ (String)doc.get("tipo") + " / Titulo: " + (String)doc.get("titulo"));
  }
}

domingo, 28 de marzo de 2010

Análisis entre modelo relacional y modelo documental de bases de datos

El origen de las bases de datos va ligado a la necesidad de organizar la cada vez más exigente cantidad de datos que se requieren. En los inicios, la información estaba delegada a los ficheros, como meros depósitos de información y a los que se accedía a bajo nivel para las operaciones más elementales de lectura y escritura. Con el tiempo, la información requerida se hacía mayor, y la complejidad de gestionarla también creció. Se comenzó a aplicar técnicas como índices para accesos más rápidos y para la ordenación de la misma. Pero las aplicaciones debían tratar los ficheros de forma artesanal y personalizada.

Surgieron sistemas que automatizaban las operaciones complejas de gestión de los datos, como los índices, así como las primeras relaciones entre campos clave entre dos o más tablas. Pioneros en estos sistemas fueron (por citar los más conocidos) fueron dBASE y Clipper, pero también empezaban a gestarse grandes bases de datos profesionales, como Oracle o DB2. Estamos hablando de los años 80.

Los dispositivos de almacenamiento eran limitados, así como la capacidad de los sistemas operativos en almacenar y direccionar información, aunque suficientes para la época. Es por ello que la optimización del espacio fuera un un factor vital, y que el diseño de las tablas contemplase el acotar el almacenamiento de cada campo, tener en cuenta el tamaño de cada fila, y prever el tamaño total de los ficheros junto con la previsión de crecimiento de los mismos.


Bases de datos relacionales

El modelo relacional surge ante estas circunstancias, postulado por Frank Codd en 1970. La normalización de los datos permite organizar la información separando en tablas la información más pequeña posible e independiente, para evitar la repetición de la misma. Para acceder a toda la información, las tablas se relacionan unas y otras mediante campos clave comunes, que suelen ser números con un id único.

Este modelo de almacenamiento es organizado y óptimo, define unas reglas que evitan la redundancia de la información y facilitan la consistencia de los datos. Este sistema de ingeniería de la información es el que ha regido las bases de datos durante los últimos 40 años.

Para trabajar con este modelo de almacenamiento, surgió el lenguaje SQL, que facilitó el entendimiento de las relaciones a través de una sintaxis sencilla y asimilable.

Este modelo de almacenamiento tiene las siguientes ventajas:
- Sistema de normalización óptimo en almacenamiento. Idóneo para sistemas con requisitos muy claros y poco dado a cambios.
- Sistema fuertemente estructurado e interrelacionado, formando un esquema.
- Sistema unánimente aceptado y estándar, utilizado en más del 90% de las aplicaciones
- La mayor parte de fabricantes de bases de datos utilizan el SQL estándar en sus productos, si bien puede haber alguna pequeña variación fácilmente subsanable.
- Las migraciones entre bases de datos de diferentes fabricantes no son excesivamente impactantes
- Las consultas SQL tienen funciones agregadas de cálculo asociadas a columnas en los resultados.
- Los sistemas de bases de datos relacionales asumen el trabajo de las complejidades de la integridad referencial, velando por la consistencia de la información y liberando al desarrollador de estas tareas para que se centre solamente en las consultas.
- Multitud de herramientas productivas construídas en torno a este modelo: business intelligence, datamining, ETL, administración, monitorización, etc.
- Eficiente para gestionar datos para automatizar actividades.

Sin embargo, también conlleva algunos inconvenientes:
- La normalización en entidades atómicas no es natural y tiende a complicar su entendimiento y los desarrollos.
- Basar la identificación de los registros y relacionar tablas por códigos numéricos es críptico y poco natural.
- La información, tal y como está almacenada, difiere mucho de cómo es gestionada, y requiere un esfuerzo importante de adaptaciones.
- Sistema rígido y estricto poco tolerante a fallos y que penaliza los cambios.
- La adición de entidades de relación y de campos clave a una tabla ya existente implica complejidad e impacta en los desarrollos actuales.
- Sistema que gana en complejidad a medida que crece. La escalabilidad de la estructura se torna condicionada.
- El desarrollo de las aplicaciones requiere de capacitación para conocer el modelo entidad-relación.
- El almacenamiento de toda la información requiere dividir la información en entidades atómicas y tratar éstas por separado en varios procesos.
- Las consultas requieren tracear la información separada y reunirla. Las joins suelen ser complejas y pesadas, y a veces no son suficientes, requiriendo varios procesos de consulta adicionales.
- La gestión automatizada de la integridad referencial supone una penalización de tiempo de accesos (lectura y escritura) a la información.
- La gestión automatizada de la integridad referencial no evita que el desarrollador preste atención a errores de integridad y su causa. El desarrollador debe asegurar previamente la integridad y controlar si el sistema le informa de alguna violación de reglas de integridad. Por ejemplo, la creación de una categoría de productos. El sistema informará si se está tratando de crear una categoría ya existente, y el programa debería capturar este aviso para advertir al usuario sobre la causa del problema. Otra forma (sin integridad automatizada) sería consultar previamente cuántas categorías existen con ese nombre. Si es mayor de cero advertir al usuario, y si no guardar la nueva categoría. El trabajo en ambos casos (con y sin integridad automatizada) no difiere mucho.
- La información de cada tabla está limitada a una estructura fija de campos o columnas, sean o no necesarios, y tengan o no valores. Si no se utiliza una columna, ésta se almacena de todas formas en cada fila.
- Poco eficiente a la hora de explotar grandes volúmenes de información textual (maneja mejor datos que información).
- Los principales fabricantes de sistemas de bases de datos relacionales las desarrollan de forma propietaria.


Bases de datos documentales

Las bases de datos documentales, a diferencia de las bases de datos relacionales, están libres de esquemas y de estructuras. Por tanto, la información no se divide en columnas fijas y filas, si no en documentos que pueden tener libertad de información, como cualquier cantidad de campos o de tipos de campos, e incluso de contener en el propio campo una lista de valores, un documento o una colección de documentos. La información almacenada en un documento está formado por pares de clave y valor.

La gran ventaja de no poseer esquema (y la consiguiente rigidez de estructuras y relaciones), es que son muy eficientes al trabajar con grandes cantidades de documentos.

Las principales ventajas de una base documental son las siguientes:
- Libre de esquemas. Al no estar sujeta a estructuras ni relaciones, los cambios de diseño son fáciles de adoptar y con un impacto mínimo.
- Al no tener relaciones se facilita la replicación
- Los sistemas de bases de datos documentales ganan rapidez dedicándose más en exclusiva al almacenamiento y la recuperación de la información (no consumen tiempos en la integridad o en la revisión de las reglas o constraints).
- Poseen un lenguaje de consulta natural.
- Sencillez y potencia en escalabilidad.
- Los campos vacíos no se añaden al documento, optimizando el espacio de almacenamiento.
- Cada documento (o “fila”) puede tener estructuras diferentes (más o menos campos, tipos diferentes de datos, etc.)
- Un documento es un objeto que normalmente es mapeado mediante JSON o XML
- Posibilidad en embeber documentos y colecciones dentro de documentos. Esto solventaría joins entre documentos, aunque agregaría complejidad a la estructura del documento.
Los datos almacenados no necesitan adaptaciones por las aplicaciones (tal como se almacenan se pueden gestionar).
- Muy eficiente a la hora de explotar grandes volúmenes de información textual (maneja mejor información que datos).
- Las migraciones entre bases de datos diferentes tienen muy poco impacto, pues comparten el formato JSON o XML.
- La mayor parte de los fabricantes de sistemas de bases de datos documentales, la desarrollan como open source.
- El auge de la Web está mirando este modelo de datos como clave en sus negocios, por su mayor capacidad de escalabilidad, rendimiento y alta disponibilidad, que los actuales sistemas de bases de datos relacionales.

Los principales inconvenientes serían:
- El sistema de base de datos no contempla realizar joins entre documentos.
- Más peso por parte de las aplicaciones para asumir la integridad y la consistencia.
- No posee funciones agregadas para realizar cálculos sobre la información de las consultas.
- Redundancia de la información. Penaliza el espacio de almacenamiento en aras de una mayor rapidez de acceso y de claridad de la información.
- Menos eficiente para automatizar actividades.
- Aunque este modelo de información es anterior al relacional, aún tiene una aceptación muy baja (menos de un 10% del total de aplicaciones funcionando en el mundo).
- No hay muchas herramientas de productividad en torno a este tipo de bases de datos, como Business Intelligence, monitorización, etc.
- Aunque es posible migrar un sistema relacional a un sistema documental, la potencia de la integridad referencial ha de ser restituida por código en las aplicaciones. No merece la pena esto, y sí una restrospectiva en la que se adapte los conceptos a documentos teniendo siempre en mente las ventajas que podría ganarse en la migración.

Safe Creative #1003275854193