.. _nativas: Bases nativas ************* Bases documentales ================== Las :dfn:`bases de datos documentales` o, más exactamente, las :dfn:`bases de datos orientadas al documento` son bases de datos no relacionales cuya característica principal es que se estructuran sobre la base de un :dfn:`documento`, que no es más que un conjunto de datos semiestructurados. Un archivo |XML| o un archivo |JSON| o |YAML| son un documento. Internamente pueden almacenar su información en distintos formatos, pero lo habitual es que lo hagan en |XML| (como :ref:`BaseX`), |JSON| (como CouchDB_) o |BSON| (como MongoDB_), que es una representación binaria del formato |JSON|. .. Bases documentales (mongoDB) .. _basex: BaseX ===== *BaseX* es un motor de bases de datos |XML| desarrollado en Java_ que permite el acceso y la modificación de datos a través de expresiones :ref:`XQuery `. Las bases de datos pueden crearse a través de un archivo origen |XML| y, una vez creadas, el propio motor se encarga de la concurrencia. Es importante entender que el archivo sirve para crear la base de datos y dotarla de información, pero una vez creada, se almacena independiente al archivo original (en *Linux* dentro de :file:`~/basex/data`) y, por tanto, está dotada de vida propia al margen del archivo, esto es, si se altera, sus cambios no afectarán al archivo; y viceversa. Tiene varias versiones de ejecución: #. Una cómoda versión |GUI| pensada para gestionar las bases de datos y hacer consultas desde la propia aplicación gráfica. #. Una versión |CLI| que permite otro tanto, pero desde la terminal. Esta versión tiene la ventaja de permitir consultas (de lectura, no de manipulación) usando directamente el archivo |XML| sin necesidad de crear ninguna base de datos. Es, por tanto, ideal para comprobar la validez de expresiones *XPath* y *XQuery*. #. Una aplicación dividida en cliente y servidor, cuyo servidor queda escuchando en un puerto y cuyo cliente permite hacerle consultas de modo semejante a la versión |CLI|. #. Una aplicación que crea un servidor |HTTP| al que se le pueden hacer consultas a través de una |API| |REST| (véase `la explicación de su uso en la wiki del proyecto `_). Lamentablemente, esta versión está ausente en `el paquete de Debian `_. Las dos últimas versiones, pues, permiten la consulta remota. En realidad, con saber :ref:`XQuery` (incluida la extensión al lenguaje para la :ref:`actualización de datos `), tenemos prácticamente toda la lección aprendida y sólo nos falta conocer o las particularidades de la interfaz gráfica, si usamos la |GUI|, o las órdenes que permiten manipular las bases de datos (crear, borrar, abrir, cerrar, etc.) en caso de no hacerlo. |GUI| ----- Tiene el aspecto de la imagen: .. image:: files/basexgui.png donde hemos marcado las zonas más interesantes. * En **1** podemos manipular las bases de datos. Por ejemplo, para empezar a hacer consultas lo primero que necesitamos es abrir la base de datos correspondiente, y, si esta no existe, crearla a partir de un archivo |XML|. * **2** permite interpretar el tipo de expresiones definidas al seleccionar un ítem de la lista **3**: - `Command` es una orden propia de *BaseX*. Por ejemplo, si escribiéramos ``CLOSE`` se cerraría la base de datos que está abierta. Y si ``HELP``, obtendríamos un listado de todas las órdenes posibles. - `Find` y `XQuery` permiten escribir expresiones *XPath* y *XQuery*, respectivamente para consultar la base de datos. * **4** permite escribir, cargar y grabar expresiones de consulta más complejas que puedan ocuparnos varias líneas y que, por tanto, resulte engorrosas escribir en **2**. Al pinchar sobre el icono del triángulo verde, la expresión se evaluará. * **5** representa de forma gráfica los datos incluidos en la base de datos y, además, marcará en rojo los elementos extraídos en la consulta. * **6** muestra el resultado de la consulta. .. seealso:: Échele un vistazo a `este vídeo de Cristian González `_ si quiere revisar algo más ilustrativo que esta simple descripción. |CLI| ----- La aplicación |CLI| permite su uso interactivo: .. code-block:: console $ basex > HELP [... La lista y explicación de todas las órdenes ...] > CREATE DATABASE casilleros /ruta/a/casilleros.xml > LIST Name Resources Size Input Path ---------------------------------------------------------- casilleros 1 6989 /ruta/casilleros.xml 1 database(s). > OPEN casilleros > XQUERY //profesor[1]/nombre José > CLOSE > QUIT o pasar argumentos al ejecutable para no entrar en el modo interactivo. Este modo de ejecución permite la consulta directa del archivo |XML| sin ni siquiera haber creado la base de datos: .. code-block:: console $ basex -i /ruta/a/casilleros.xml '//profesor[1]/nombre' José $ basex -i /ruta/a/casilleros.xml -c 'XQUERY //profesor[1]/nombre' José $ basex -i /ruta/a/casilleros.xml una_consulta_compleja.xq .. note:: La opción ``-c`` permite introducir las órdenes propias de *BaseX* que también podemos usar de forma interactiva. Por ejemplo, :code:`-c HELP` nos devolvería un listado de todas las órdenes posibles que podemos introducir en el cajetín **2** de la |GUI|. .. caution:: Directamente sobre el archivo |XML| podremos hacer consultas, pero no podremos alterarlo usando las :ref:`órdenes XQuery para la manipulación de los datos `. Para ello, forzosamente, deberemos crear la base de datos. Cliente/Servidor ---------------- Primero hay que arrancar el servidor para que quede escuchando en el puerto 1984/|TCP|: .. code-block:: console $ basexserver -S BaseX 9.7.2 [Server] Server was started (port: 1984). $ ss -ltn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 50 *:1984 *:* Hecho, lo cual podremos usar el cliente de modo semejante a como usamos la versión |CLI|, aunque no podremos usar directamente un archivo y además tendremos que indicar un usuario y una contraseña con permisos de acceso. Al instalar la aplicación en el sistema, se debió pedir una contraseña que es la contraseña que tiene el usuario administrador (*admin*): .. code-block:: console $ basexclient -Uadmin -Pcontraseña -i casilleros '//profesor[1]/nombre' José Por último, para parar el servidor: .. code-block:: console $ basexserver stop BaseX 9.7.2 [Server] Server was stopped (port: 1984). Servidor |HTTP| --------------- .. todo:: Escribir una breve reseña del servidor |HTTP| y la |API| |REST| de BaseX. .. https://www.youtube.com/watch?v=tgQrfKOmlRw Ejercicios resueltos ==================== Dado el |XML| :ref:`propuesto para representar el negocio de una cadena de restaurantes `, indique cómo alterar el documento para realizar lo siguiente: #. Sustituya la unidad "cc" por "ml". .. dropdown:: Solución propuesta .. code-block:: xquery (: También podría usarse un where para la condición :) for $unidad in //@unidad[. = "cc"] return replace node $unidad with attribute {fn:name($unidad)} {"ml"} #. Cambie el nombre de "El tragón feliz" a "La cueva de Juanchu" .. dropdown:: Solución propuesta .. code-block:: xquery replace node //restaurante/@nombre[. = "El tragón feliz"] with attribute nombre {"La cueva de Juanchu"} #. Añada a cada receta un atributo *ingredientes* cuyo valor sea el número de ingredientes que componen la receta. .. dropdown:: Solución propuesta .. code-block:: xquery for $receta in //receta return insert node attribute ingredientes {count($receta/ingrediente)} into $receta #. Sume **5** a la cantidad de vinagre de todas las recetas. .. dropdown:: Solución propuesta .. code-block:: xquery (: También podría usarse un where para la condición :) for $vinagre in //ingrediente[@nombre = "vinagre"] return replace node $vinagre/@cantidad with attribute cantidad {$vinagre/@cantidad + 5} #. Pase a mayúsculas los nombres de todos los municipios. En *XPath* 3 existe `fn:upper-case() `_: .. dropdown:: Solucion propuesta .. code-block:: xquery for $texto in //municipio/text() return replace node $texto with text {fn:upper-case($texto)} #. Elimine las medias raciones de la carta de todos los restaurantes (y si el plato sólo se servía en ese formato, elimine el plato). .. note:: La solución no es nada trivial, así que le proporcionaremos algunas pistas: * Los platos sin media ración se quedan iguales, los platos con sólo media deben eliminarse por completo y los platos que ofrecen también otros formatos deben modificar el valor de su atributo *tipo* para quitar la media ración. Puede hacerlo por separado (recuerde que puede hacer distintas acciones utilizando una secuencia) o echar mano de :ref:`if `. * La otra dificultad está en eliminar el formato *media* en los platos que se sirven en varios formatos. No tendrá más remedio que valerse de `funciones de XPath 3 `_. Hay al menos dos opciones: sustituir "media" por la cadena vacía o crear una secuencia separando las palabras, filtrar en esta secuencia "media" y, finalmente, volver a reconstruir la cadena. * Respecto al filtrado que se acaba de sugerir, XPath 3 permite aplicar predicados a cualquier secuencia y no exclusivamente a un conjunto de nodos. Por ejemplo, esto filtraría "c", por lo que el resultado sería un a secuencia con los otros tres ítems: .. code-block:: xquery ("a", "b", "c", "d")[. != "c"] .. dropdown:: Solución propuesta .. code-block:: xquery ( for $plato in //plato where $plato/@tipo = "media" return delete node $plato, for $plato in //plato let $tipo := $plato/@tipo where contains($tipo, "media") return (: Separamos los componentes de la cadena, filtramos "media" y volvemos a unirlos :) replace node $tipo with attribute tipo {fn:string-join(fn:tokenize($tipo)[. != "media"], " ")} ) También podemos usar el :ref:`if de XPath ` para evitar hacer dos estructuras |FLWOR|: .. code-block:: xquery for $plato in //plato let $tipo := $plato/@tipo where contains($tipo, "media") return if ($tipo = "media") then delete node $plato else replace node $tipo with attribute tipo {fn:string-join(fn:tokenize($tipo)[. != "media"], " ")} Ejercicios propuestos ===================== .. include:: /99.ejercicios/50.basex.rst :start-line: 5 .. |GUI| replace:: :abbr:`GUI (Graphical User Interface)` .. |CLI| replace:: :abbr:`CLI (Command Line Interface)` .. |HTTP| replace:: :abbr:`HTTP (HyperText Transfer Protocol)` .. |API| replace:: :abbr:`API (Application Programming Interface)` .. |REST| replace:: :abbr:`REST (REpresentational State Transfer)` .. |TCP| replace:: :abbr:`TCP (Transmission Control Protocol)` .. |BSON| replace:: :abbr:`BSON (Binary JSON)` .. |YAML| replace:: :abbr:`YAML (YAML Ain't Markup Language)` .. |FLWOR| replace:: :abbr:`FLWOR (For, Let, Where, Order by, Return)` .. _Java: https://es.wikipedia.org/wiki/Java_(lenguaje_de_programaci%C3%B3n) .. _CouchDB: https://couchdb.apache.org/ .. _MongoDB: https://www.mongodb.com/