DEV Community

Cover image for Capítulo 2 - Modelos de Datos y Lenguajes de Consulta
Pablo Arango Ramirez
Pablo Arango Ramirez

Posted on • Edited on

Capítulo 2 - Modelos de Datos y Lenguajes de Consulta

Crédito de la imagen: Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017

Crédito de la imagen: Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017, Capítulo 2.

Tipos de modelos de datos

  • Jerárquico (El modelo inicial) → Desarrollado en los 1950s por IBM. No se adaptaba bien a las relaciones many-to-many. Modelo Jerárquico
  • Relacional (SQL) → Solución al modelo jerárquico. Poco flexible
  • Documental → Datos que vienen en documentos autocontenidos (JSON). Poco soporte de relación entre documentos
  • Modelo de grafos → Nodos y ejes

El Modelo Relacional

  • Propuesto por Edgar Codd en 1970
  • Los datos se organizan en relaciones (tablas) donde cada relación es una colección no ordenada de tuplas (filas)
  • Casos de uso originales
    • Procesamiento transaccional → operaciones de movimiento de dinero en un banco, reservas de aerolineas
    • Procesamiento analítico en lotes → reportes, payroll, analítica
  • Sigue el paradigma de schema-on-write (Esquema al escribir)
    • El esquema de los datos es explicito y el motor de BD se asegura que todos los datos escritos a la BD sean consistentes con el esquema predefinido.

NoSQL

  • Surgió con un hashtag de Twitter en el 2009
  • def. Not ONLY SQL
  • Sigue el paradigma de schema-on-read (Esquema al leer)
  • Surgió debido a estos factores:
    • Necesidad de mayor escalabilidad que las BDs relacionales.
    • Preferencia por software open-source gratis envés de productos de BDs relacionales empresariales.
    • Operaciones de consultas especializadas que no están bien soportadas por el modelo relacional.
    • Frustración con las restricciones de los esquemas relacionales.
    • Necesidad de un modelo más dinámico y expresivo

Problema: Objetos vs Relaciones → Desajuste por Impedancia (Impedance mismatch)

  • Impedance Mismatch: Problema de incongruencia entre el modelo relacional y los objetos en el código de la aplicación. Es el conjunto de dificultades técnicas y conceptuales que surgen cuando se almacenan objetos de programación en bases de datos con modelos relacionales
    • Si los objetos en la app se almacenan en una BD con modelo relacional (SQL), se necesita una capa de traducción entre el código de la app y la modelo de la BD
  • Solución → Mapeo Objeto-Relacional (ORM: Object-relational mapping)
    • Frameworks de ORM
    • Reducen el código boilerplate requerido para esa capa de transición
  • Ejemplos: Apache OpenJPA, SQLAlchemy, TypeORM

Expresiones en distintos Modelos

  • Perfil de Linkedin
    • Modelo Relacional

Image description
Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017.

  • Modelo Documental NoSQL (JSON)
        {
            "user_id": 251,
            "first_name": "pedro",
            "positions": [
                {"job_title": "founder", "organization": "EmpresaA"},
                {"job_title": "ceo", "organization": "EmpresaB"}
            ],
        ...
        }
Enter fullscreen mode Exit fullscreen mode

Con el modelo JSON, la estructura de árbol implícita en el perfil de Linkedin, se vuelve explícita en el modelo de datos

Versiones iniciales de una aplicación pueden encajar bien en un modelo documental JOIN-Free, pero los datos tienen una tendencia a volverse más interconectados a medida que surgen nuevas funcionalidades

  • En algún punto va a necesitar many-to-many y por ende utilizar joins
  • Ejemplo Linkedin → Representar la organización donde trabaja la persona como una entidad.

Image description
Crédito de la imagen: Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017, Capítulo 2.

Debate: ¿Cómo representar de mejor forma las relaciones de las entidades en una base de datos?

Es una discusión más vieja que los esquemas NoSQL. Viene desde los primeros sistemas de bases de datos computacionales.

  • Caso: La IMS de IBM utilizaba el modelo jerárquico. Este modelo no soportaba JOINs. En los 60s, los desarrolladores tenían que decidir entre duplicar datos (Normalizar) o resolver manualmente las referencias de un record a otro. IMS de IBM. Este es el mismo problema que tienen los desarrolladores hoy en día con las BDs documentales.

La solución a esto en su entonces fue el modelo relacional de SQL. El modelo relacional permite poner todos los datos al descubierto con una lista de tuplas (tabla). El optimizador de consultas en una BD relacional automáticamente decide que partes de la consulta ejecutar y en qué orden hacerlo.

Estas decisiones ya no las toma un desarrollador
- “Solo se construye un optimizador de consultas una vez. Todas las apps que utilicen la BD se benefician de el”

Las BDs documentales se devolvieron al modelo jerárquico → almacena records embebidos (many-to-one) en el mismo record, no en otra tabla

  • Cuando se trata de relaciones many-to-many, el modelo documental y el modelo relacional no son muy distintos. En ambos casos, el ítem relacionado se referencia mediante un identificador único. Este identificador se utiliza como llave foránea en el otro ítem en el modelo relacional. En el modelo documental se usa como referencia a otro documento.

Modelo Relacional vs Modelo Documental

  • Documental
    • Flexibilidad de esquema
    • Mejor localidad de datos → Capacidad para mover computo (objetos) a datos almacenados
    • Poco soporte de JOINS
  • Relacional
    • Buen soporte de joins
    • Buen soporte de relaciones many-to-one y many-to-many
    • Poca flexibilidad de esquema

¿Convergencia entre el modelo relacional y documental?

  • Postgres ya soporta documentos JSON como tipo de datos desde la versión 9. Documentación Postgres
  • Hay drivers de Mongo que automáticamente resuelven las referencias externas de documentos. Resuelve el problema desde el lado del cliente.

Parece ser que las BDs relacionales y documentales se están volviendo cada vez más parecidas. Esto es algo bueno porque los modelos de datos se complementan entre sí. Si una BD soporta datos que son document-like y también soporta consultas relacionales, las aplicaciones pueden utilizar la combinación de ambas funcionalidades que mejor se ajuste a sus necesidades.

Regla de pulgar:
Use documentales cuando

  • Tiene datos con relaciones one-to-many.
  • Necesita que un árbol de datos embebidos se cargue en la aplicación desde el inicio.
  • Estructura de datos tipo documento.

Use relacionales cuando

  • Necesita muy buen soporte de joins
  • Tiene relaciones many-to-many

Lenguajes de Consulta

  • Dos tipos de lenguajes: Declarativo e Imperativo

    • Declarativo → SQL

      • Le digo a la máquina que datos y transformaciones quiero realizar frente a los datos, pero no le digo como realizar la consulta

        SELECT * FROM animals WHERE family = "sharks"
        
    • Imperativo → Lenguajes de programación (Java, python, JS)

      • Le digo a la máquina, como ejecutar la consulta

        function getSharks(){
            var sharks = [];
            for (var i=0; i<animals.length; i++){
                if(animals[i].family === "sharks"){
                    sharks.push(animals[i]);
                }
            }
        return sharks;
        }
        

La misma consulta se puede expresar de forma genérica con algebra relacional: sharks = σ_family="Sharks" (animals)

SQL Provee mejor soporte para algunos tipos de consultas

Consultas tipo Map Reduce (Documentales) → Ej: Obtener número de tiburones por mes
SQL

SELECT date_trunc('month', observation_timestamp) AS observation_month, 1
                   sum(num_animals) AS total_animals
            FROM observations
            WHERE family = 'Sharks'
            GROUP BY observation_month;
Enter fullscreen mode Exit fullscreen mode

Documental (Mongo)

db.observations.mapReduce(
                function map() { 2
                    var year  = this.observationTimestamp.getFullYear();
                    var month = this.observationTimestamp.getMonth() + 1;
                    emit(year + "-" + month, this.numAnimals); 3
                },
                function reduce(key, values) { 4
                    return Array.sum(values); 5
                },
                {
                    query: { family: "Sharks" }, 1
                    out: "monthlySharkReport" 6
                }
            );
Enter fullscreen mode Exit fullscreen mode

Modelo de Grafos

def. Base de datos que almacena los datos como relaciones entre nodos conectados por ejes

Image description
Imagen: https://www.larkinfolab.nl/partners/neo4j/

Ejemplo: Representar el matrimonio de dos personas

Image description

Estructura→ ejemplo Neo4J

  • Cada vertice (Nodo) consiste de
    • ID
    • Ejes hacia afuera
    • Ejes hacia adentro
    • propiedades llave-valor
  • Cada eje consiste de
    • Id
    • Vertice origen
    • Vertice destino
    • etiqueta
    • Propiedades llave - valor

Los grafos son muy buenos para la evolucionabilidad del sistema. Un grafo se puede extender fácilmente para acomodar cambios en las estructuras de datos de la app.

Ejemplo de lenguaje para BDs de grafos → Cypher (Lenguaje de consultas utilizado en Neo4J)
Ejemplo: Insertar el hecho que Idaho queda en USA y que USA queda en América del norte. Insertar que Lucy nació en Idaho

        CREATE
        (NAmerica:Location {name: 'north america', type: 'continent'}),
        (USA:Location {name:'united states', type: 'country'}),
        (Idaho:Location {name: 'idaho', type 'state'}),
        (Lucy: Person {name:'Lucy'}),
        (Idaho) -[:WITHIN] -> (USA) -[:WITHIN] -> (NAmerica)
        (Lucy) -[:BORN_IN] -> (Idaho)
Enter fullscreen mode Exit fullscreen mode

Este modelo permite realizar consultas muy complejas con una sintaxis sencilla.
Ejemplo: Encuentre los nombres de las personas que emigraron de USA a Europa

MATCH
  (person) -[:BORN_IN]->  () -[:WITHIN*0..]-> (us:Location {name:'United States'}),
  (person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (eu:Location {name:'Europe'})
RETURN person.name
    //[:WITHIN*0..] -> follow a within edge, zero or more times

Enter fullscreen mode Exit fullscreen mode

Encuentra todos los vertices (personas) que

  • Tengan un born_in eje a un vertice. Desde ese vértice siga una cadena ejes WITHINs hasta que llegue a una ubicación que sea USA.
  • Ese mismo vertice (persona) tenga un eje lives_in que lleve a un vertices que pertenezca a Europa.

Si fuera a hacer la misma consulta en SQL, sería algo asi

-- in_usa es el set de vértices de todas las ubicaciones en USA
in_usa(vertex_id) AS (
                      SELECT vertex_id FROM vertices WHERE properties->>'name' = 'United States' 1
                    UNION
                      SELECT edges.tail_vertex FROM edges 2
                        JOIN in_usa ON edges.head_vertex = in_usa.vertex_id
                        WHERE edges.label = 'within'
                  ),

-- in_europe es el set de vèrtices de todas las ubicaciones en Europa
                  in_europe(vertex_id) AS (
                      SELECT vertex_id FROM vertices WHERE properties->>'name' = 'Europe' 3
                    UNION
                      SELECT edges.tail_vertex FROM edges
                        JOIN in_europe ON edges.head_vertex = in_europe.vertex_id
                        WHERE edges.label = 'within'
                  ),

-- born_in_usa es el set de vértices de todas las personas que nacieron en USA
                  born_in_usa(vertex_id) AS ( 4
                    SELECT edges.tail_vertex FROM edges
                      JOIN in_usa ON edges.head_vertex = in_usa.vertex_id
                      WHERE edges.label = 'born_in'
                  ),

 -- lives_in_europe es el set de todas las personas que viven en Europa
                  lives_in_europe(vertex_id) AS ( 5
                    SELECT edges.tail_vertex FROM edges
                      JOIN in_europe ON edges.head_vertex = in_europe.vertex_id
                      WHERE edges.label = 'lives_in'
                  )

SELECT vertices.properties->>'name'
                FROM vertices
                -- join para encontrar a las personas que nacieron en USA y viven en Europa
                JOIN born_in_usa     ON vertices.vertex_id = born_in_usa.vertex_id 6
                JOIN lives_in_europe ON vertices.vertex_id = lives_in_europe.vertex_id;
Enter fullscreen mode Exit fullscreen mode

Triple store & SPARQL → Almacenar toda la información en forma de sentencias de tres partes

  1. Sujeto
  2. Predicato
  3. Objeto
  4. ejemplo: Juan Gustan bananos
  5. Modelo “Lucy vive en Londres”

    _:lucy     a       :Person.
    _:lucy     :name   "Lucy".
    _:lucy     :bornIn _:idaho.
    _:idaho    a       :Location.
    _:idaho    :name   "Idaho".
    _:idaho    :type   "state".
    _:idaho    :within _:usa.
    _:usa      a       :Location.
    _:usa      :name   "United States".
    _:usa      :type   "country".
    _:usa      :within _:namerica.
    _:namerica a       :Location.
    _:namerica :name   "North America".
    _:namerica :type   "continent".
    

Nota. → La web3 (web semantica) se basa en ese modelo
https://www.programstrategyhq.com/post/web3-meaning-explained

Top comments (0)