8 Reglas de transformación


En este capítulo se plantea la transformación de esquemas SQL a esquemas NoSQL, en concreto cada ejemplo parte de un esquema en PostgreSQL y su correspondencia en un esquema de MongoDB.

Para cada tipo de relación que se puede encontrar en un esquema relacional de PostgreSQL se emplea un algoritmo de transformación a MongoDB.

Lo primero que hay que definir es la correspondencia entre elementos de PostgreSQL y MongoDB. En la siguiente tabla se recoge la correlación que existe entre los elementos de PostgreSQL y de MongoDB:

PostgreSQL MongoDB
Base de datos Base de datos
Tabla Colección
Fila o registro Documento
Columna o atributo Campo
Índice Índice

Cabe destacar que MongoDB no tiene una definición de esquemas como lo tienen las bases de datos relacionales: la estructura de las colecciones dependen de los documentos insertados y cada uno pueden diferente estructura dentro de una misma colección. Es por esto que cuando se hable de trasformación o migración a MongoDB no se está migrando solo la estructura, a la vez que se construye la estructura se está haciendo la migración de datos.

Por otro lado, la migración a MongoDB se realizará de los esquemas y datos de PostgreSQL. No se contempla la migración de triggers, procedimientos almacenados, funciones, objetos etc. Únicamente esquemas y datos.

Las reglas de transformación que se van a seguir son las siguientes:

Regla 1

Cada fila de una tabla siempre corresponde a un documento de formato objeto JSON de la colección.

Regla 2

Dado que en MongoDB no se definen tipos ni restricciones cómo NOT NULL, únicamente se utilizará el nombre de la columna cómo nombre del campo en MongoDB y el valor correspondiente cómo valor en el campo.

Regla 3

Una tabla será una colección en la base de datos MongoDB:

  1. Si no tiene claves foráneas.

  2. Si tiene claves foráneas de otras tablas pero sin restricción NOT NULL (con la cardinalidad mínima 0:1 ó 0:N)

  3. Cuando tiene relación con otras tablas pero no tiene claves foráneas.

  4. Cuando tiene una referencia circular con otra tabla ambas tendrán su colección.

  5. Cuando tiene claves foráneas, su propia clave primaria y atributos de relación.

Regla 4

Una tabla no será una colección:

  1. Cuando solo tiene claves foráneas y su propia clave primaria, sin tener atributos de relación.

  2. Cuando tiene la clave foránea de una única tabla.

  3. Cuando es un enumerado.

Regla 5

Las tablas que no tienen su propia colección y tienen una clave foránea se incorporan como un array de objetos JSON dentro de la colección relacionada.

Regla 6

Para el caso de que una tabla se relacione con otra y ambas sean una colección:

  1. Si una de las tablas tiene la clave foránea, se embeberán los índices que le corresponden como un array en la colección referenciada.

  2. Si ninguna de las dos tablas tiene clave foránea, es decir, se relacionan por una tercera tabla:

    1. La tabla que más columnas tenga será la que tenga un array de índices de la otra tabla en su colección.

    2. Si ambas tienen las mismas columnas, la tabla que más filas tenga será la que tenga un array de índices de la otra tabla en su colección.

    3. En caso de coincidir en número de filas y columnas, la tabla que tenga el array de índices será la primera en orden alfabético por nombre de tabla.

Regla 7

Para el caso de que una tabla se relacione con más de una sin claves foráneas, mediante una tabla auxiliar, todas las tablas serán colecciones y todas las colecciones tendrán un array de índices de las colecciones que le corresponden. Con esto se mantiene la integridad referencial a pesar de que haya datos redundantes.


Partiendo de estas reglas se recoge un ejemplo de transformación por cada tipo de relación que puede tener una tabla con otra, partiendo de un esquema de creación de tabla en PostgreSQL y transformándose en su correspondiente esquema de documento dentro de una colección en MongoDB.

Las reglas 1 y 2 se cumplen en cualquier caso y para cualquier tipo de relación.

8.1 Relación binaria


Las relaciones binarias son aquellas que relacionan dos tablas entre sí. En este tipo de relaciones, una tabla se puede relacionar con otra de tres formas diferentes en función de la cardinalidad.

8.1.1 Cardinalidad mínima 1 a 1

Una tabla se relaciona con otra de forma única y una sola vez mediante una relación total. Un registro de la tabla A está asociado con un registro de la tabla B y viceversa.

Relación mínima 1 a 1
Figura.10 - Relación mínima 1 a 1

Este tipo son más bien una particularidad de las relaciones 1 a N. La restricción debe hacerse de forma lógica al realizar las operaciones sobre la base de datos.

Ejemplo de cardinalidad mínima 1 a 1 sin entidades dependientes
Figura.11 - Ejemplo de cardinalidad mínima 1 a 1 sin entidades dependientes

En este ejemplo, un periodista escribe una columna en un periódico y una columna solo es gestionada por un periodista. Ambas tablas existen de forma necesaria entre ellas: no tiene sentido que haya un periodista sin columna y no hay columnas que no tengan un periodista.

Los esquemas en postgreSQL de creación de cada una de las tablas son los siguientes:

CREATE TABLE columna
(
  id bigint NOT NULL,
  periodista bigint NOT NULL,
  titulo character varying(255),
  CONSTRAINT columna_pkey PRIMARY KEY (id),
  CONSTRAINT periodista_fkey FOREIGN KEY (periodista)
      REFERENCES periodista (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE TABLE periodista
(
  id bigint NOT NULL,
  nombre character varying(255),
  apellidos character varying(255),
  fechaAlta timestamp without time zone,
  CONSTRAINT periodista_pkey PRIMARY KEY (id)
)

Como puede observarse, la cardinalidad mínima de 1 es así por la restricción NOT NULL de la clave foránea en la tabla columna.

En este caso se aplica la regla 3.1 a la tabla periodista y la regla 4.2 a la tabla columna: la tabla periodista tendrá una colección y la tabla columna se incorporará como un array de objetos JSON dentro de la colección, siguiendo la regla 5. En este caso ese array será de un solo elemento.

El esquema de la colección periodista es el siguiente:

idPeriodista = ObjectId()
db.createCollection("periodista")
db.periodista.insert(
{
    "_id": idPeriodista,
    "nombre": "periodistaNombreEjemplo",
    "apellido": "periodistaApellidoEjemplo",
    "fechaAlta": {"date": new Date()},
    "columna": [
    {
          "titulo": "columna de ejemplo",
          "fechaPublicacion": {"date": new Date()}
    }
    ]
}
)

8.1.2 Cardinalidad máxima 1 a N

Una tabla se relaciona de forma múltiple con otra de la siguiente forma: Un registro de la tabla A está asociado de forma múltiple con varios registros de la tabla B pero los registros de la tabla B solo se asocian con un registro de la tabla A.

Relación máxima 1 a N
Figura.12 - Relación máxima 1 a N

Este tipo de relaciones permite recoger los casos en los que una tabla debe tener una o más relaciones con otra tabla mediante una relación total.

Ejemplo de cardinalidad máxima 1 a N sin entidades dependientes
Figura.13 - Ejemplo de cardinalidad máxima 1 a N sin entidades dependientes

En este ejemplo, un cliente puede tener cero o varias facturas y una factura solo pertenece a un cliente, de forma que un registro de la tabla factura no tendrá razón de existencia si no es con un cliente asociado.

Los esquemas en postgreSQL de creación de cada una de las tablas son los siguientes:

CREATE TABLE  factura
(
  id bigint NOT NULL,
  cliente bigint NOT NULL,
  descripcion character varying(255),
  fecha timestamp without time zone,
  CONSTRAINT factura_pkey PRIMARY KEY (id),
  CONSTRAINT cliente_fkey FOREIGN KEY (cliente)
      REFERENCES cliente (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE TABLE  cliente
(
  id bigint NOT NULL,
  nombre character varying(255),
  fecha timestamp without time zone,
  CONSTRAINT cliente_pkey PRIMARY KEY (id)
)

Como puede observarse, el esquema de creación de las tablas en postgreSQL es el mismo que el esquema de la cardinalidad 1 a 1, además la cardinalidad mínima de 1 es así por la restricción NOT NULL de la clave foránea en la tabla factura.

En la transformación a MongoDB los pasos a seguir son los mismos que en el caso anterior: se aplica la regla 3.1 a la tabla cliente y la regla 4.2 a la tabla factura: la tabla cliente tendrá una colección y la tabla factura se incorporará como un array de objetos JSON dentro de la colección, siguiendo la regla 5. En este caso ese array será de cero o más elementos.

idCliente = ObjectId()
db.createCollection("cliente")
db.cliente.insert(
{
    "_id": idCliente,
    "nombre": "cliente de ejemplo con facturas",
    "fechaAlta": { "date": new Date() },
    "factura": [
    {
        "descripcion": "factura de ejemplo 1", 
        "fecha": { "date": new Date() }
    },
    {
          "descripcion": "factura de ejemplo 2", 
          "fecha": { "date": new Date() }
    }
    ],        
}
)

8.1.3 Cardinalidad máxima N a N

Una tabla se relaciona de forma múltiple con otra de la siguiente forma: Una tabla A está asociada varias veces con una tabla B y una tabla B está asociada varias veces con una tabla A.

Relación máxima N a N
Figura.14 - Relación máxima N a N

Estas tablas se relacionan con una relación parcial ya que ambas tendrán existencia de forma independiente antes de ser relacionadas. Este tipo de relación no puede recogerse de forma simple en un esquema relacional como en los dos casos anteriores. Para poder representar este tipo de relación se genera una tercera tabla que contendrá las claves foráneas de la tabla A y de la tabla B.

Ejemplo de cardinalidad máxima N a N sin entidades dependientes
Figura.15 - Ejemplo de cardinalidad máxima N a N sin entidades dependientes

En este ejemplo un alumno cursa una o más asignaturas y una asignatura tiene uno o más alumnos.

Para recoger esta relación se genera la tabla alumnoAsignatura que contiene las claves foráneas de alumno y asignatura. Las tablas alumno y asignatura no tendrán ninguna relación entre sí más allá de la tabla que las relaciona.

El esquema en PostgreSQL de creación de las tablas es el siguiente:

CREATE TABLE alumno
(
  id bigint NOT NULL,
  nombre character varying(255),
  fechaAlta timestamp without time zone,
  CONSTRAINT alumno_pkey PRIMARY KEY (id)
)
CREATE TABLE asignatura
(
  id bigint NOT NULL,
  nombre character varying(255),
  fechaInicio timestamp without time zone,
  fechaFin timestamp without time zone,
  CONSTRAINT asignatura_pkey PRIMARY KEY (id)
)
CREATE TABLE alumnoasignatura
(
  id bigint NOT NULL,
  alumno bigint NOT NULL,
  asignatura bigint NOT NULL,
  CONSTRAINT alumnoasignatura_pkey PRIMARY KEY (id),
  CONSTRAINT alumno_fkey FOREIGN KEY (alumno)
      REFERENCES alumno (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT asignatura_fkey FOREIGN KEY (asignatura)
      REFERENCES asignatura (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

Como puede observarse, la tabla alumnoasignatura es la que contiene las claves foráneas que hacen referencia a la tabla alumno y a la tabla asignatura.

En este caso se aplica la regla 4.1 a la tabla alumnoasignatura ya que solo contiene claves foráneas y la regla 3.3 para las tablas alumno y asignatura porque ninguna de las dos tiene clave foránea pero hay una tabla que las relaciona, de forma que ambas tendrán su propia colección.

A la hora de elegir que colección tendrá el array de índices se aplica la regla 6.2.i, la tabla asignatura tiene más columnas de forma que tendrá un array de índices a alumno.

En el caso de alumnos con asignatura, el esquema sería el siguiente:

idAlumno1 = ObjectId()
idAlumno2 = ObjectId()
db.createCollection("alumno")
db.alumno.insert(
{
  "_id": idAlumno1,
  "nombre": "alumno uno",
  "fechaAlta ": { "date": new Date() }
},
{
  "_id": idAlumno2,
  "nombre": "alumno dos",
  "fechaAlta ": { "date": new Date() }
}
)
idAsignatura = ObjectId()
db.createCollection("asignatura")
db.asignatura.insert(
{
  "_id": idAsignatura,
  "nombre": "asignatura de ejemplo",
  "fechaInicio": { "date": new Date() },
  "fechaFin": { "date": new Date() },
  "alumno": [idAlumno1, idAlumno2]
}
)

Cardinalidad máxima N a N con atributos en la relación

Este caso es el mismo que el anterior: las tablas se relacionan de forma parcial tendiendo existencia de forma independiente y se genera una tercera tabla que las relaciona. La particularidad de este caso es que en esa tercera tabla hay atributos que corresponden a la relación y se encuentran dentro de esta tercera tabla al pasar al esquema.

Ejemplo de cardinalidad máxima N a N sin entidades dependientes y con atributos en la relación
Figura.16 - Ejemplo de cardinalidad máxima N a N sin entidades dependientes y con atributos en la relación

En este ejemplo un cliente alquila cero o más películas y una película puede ser alquilada por 0 o más clientes.
Para recoger esta relación se genera la tabla clientePelicula que contiene las claves foráneas de cliente y película. Como en el caso anterior, ninguna de las dos tablas tienen ninguna relación con otra tabla.

Además de todo esto, la relación tiene atributos. En el paso a tabla de estas entidades, la tercera tabla generada tendrá los atributos, de forma que los esquemas de creación de tablas en PostgreSQL son los siguientes:

CREATE TABLE cliente
(
  id bigint NOT NULL,
  nombre character varying(255),
  fechaAlta timestamp without time zone,
  CONSTRAINT cliente_pkey PRIMARY KEY (id)
)
CREATE TABLE pelicula
(
  id bigint NOT NULL,
  titulo character varying(255)
  CONSTRAINT titulo_pkey PRIMARY KEY (id)
)
CREATE TABLE clientepelicula
(
  id bigint NOT NULL,
  cliente bigint NOT NULL,
  pelicula bigint NOT NULL,
  fecha_alquiler timestamp without time zone NOT NULL,
  fecha_devolucion timestamp without time zone,
  CONSTRAINT alumnoasignatura_pkey PRIMARY KEY (id),
  CONSTRAINT cliente_fkey FOREIGN KEY (cliente)
      REFERENCES cliente (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
      CONSTRAINT pelicula_fkey FOREIGN KEY (pelicula)
      REFERENCES pelicula (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

Para este caso se aplica la regla 3.3 para las tablas cliente y pelicula, de forma común al caso anterior. En este caso la tabla clientepelicula tiene más atributos aparte de las claves foráneas por lo que siguiendo la regla 3.5, la tabla clientepelicula tendrá su propia colección.

A la hora de elegir que colección tendrá el array de índices se aplica la regla 6.1, la colección película tendrá un array de índices a clientepelicula y lo mismo para la colección cliente.

Los esquemas de creación en MongoDB son los siguientes:

idCliente = ObjectId()
idPelicula = ObjectId()
idClientePelicula = ObjectId()
db.createCollection("cliente")
db.cliente.insert(
{
  "_id": idCliente,
  "nombre": "cliente uno",
  "fechaAlta ": { "date": new Date() },
  "clientepelicula": [idClientePelicula]
}
)
db.createCollection("pelicula")
db.pelicula.insert(
{
  "_id": idPelicula,
  "titulo": "jumanji",
  "clientepelicula": [idClientePelicula]
}
)
db.createCollection("clientepelicula")
db.clientepelicula.insert(
{
  "_id": idClientePelicula,
  "cliente": [idCliente],
  "pelicula": [idPelicula],
  "fecha_alquiler ": { "date": new Date() },
  "fecha_devolucion ": { "date": new Date() }
}
)

8.2 Relación unaria o reflexiva


Una relación unaria o reflexiva es aquella que relaciona una tabla consigo misma. Este tipo de relación se utiliza para evitar la duplicidad de información, dado que su modelización binaria sería tener dos tablas con los mismos datos en ambas. Existen dos formas de relacionar estas tablas, que se exponen y explican a continuación.

8.1.2 Cardinalidad máxima 1 a N

En las relaciones unarias la cardinalidad 1 a N se representa poniendo la clave foránea en la misma tabla, de forma que un registro de la tabla se relaciona con uno o más registros de esa misma tabla.

Relación máxima 1 a N reflexiva
Figura.17 - Relación máxima 1 a N reflexiva

La misma tabla puede tener 0 o más relaciones consigo misma mediante una relación parcial, tal y como se recoge en el siguiente ejemplo

Ejemplo de cardinalidad máxima 1 a N reflexiva sin entidades dependientes
Figura.18 - Ejemplo de cardinalidad máxima 1 a N reflexiva sin entidades dependientes

Un mensaje puede tener cero o más mensajes hijo asociados y un mensaje hijo solo puede tener un padre. Es un ejemplo típico de relación en un foro o tablón de anuncios en el que un mensaje puede tener múltiples respuestas pero una respuesta solo pertenece a un mensaje raíz.

El esquema PostgreSQL de creación de esta tabla es el siguiente:

CREATE TABLE mensaje
(
  id bigint NOT NULL,
  asunto character varying(255),
  fecha timestamp without time zone,
  texto text,
  mensaje bigint,
  CONSTRAINT mensaje_pkey PRIMARY KEY (id),
  CONSTRAINT mensaje_fkey FOREIGN KEY (mensaje)
      REFERENCES mensaje (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

Como puede verse, la tabla mensaje tiene una clave foránea relacionada con la tabla mensaje mediante el atributo mensajepadre, pero un mensaje no tiene que tener un padre por lo que no tiene la restricción NOT NULL.

Los pasos a realizar para la transformación a MongoDB son: se aplica la regla 3.2 a la tabla mensaje ya que no tiene una relación de forma obligatoria con otros mensajes. Además cada mensaje que tenga una relación con otro mensaje será incorporado como un objeto JSON en el documento del mensaje padre, siguiendo con la regla 5.

El esquema es el siguiente:

idMensaje = ObjectId()
db.createCollection("mensaje")
db.mensaje.insert(
{
  "_id": idMensaje,
  "asunto": "asunto del padre",
  "fecha": { "date": new Date() },
  "texto": "este es el texto del mensaje padre",
  "mensaje": [
  {
    "asunto": "asunto del hijo 1", 
    "fecha": { "date": new Date() }, 
    "texto": "este es el texto del mensaje hijo 1"
  },
  {
    "asunto": "asunto del hijo 2",
    "fecha": { "date": new Date() }, 
    "texto": "este es el texto del mensaje hijo 2"
  }
  ]
}
)

8.1.3 Cardinalidad máxima N a N

Este tipo de cardinalidad dentro de las relaciones unarias o reflexivas no es muy común. La estructura es muy similar a las relaciones binarias de cardinalidad N a N con la diferencia de que la tabla implicada en las relaciones unarias es la misma.

Al igual que en la relación binaria, la relación es parcial y es necesario apoyarse en una tabla auxiliar que recoja la relación de muchos a muchos, tal y como se recoge en el siguiente ejemplo.

Relación máxima N a N sin entidades dependientes
Figura.19 - Relación máxima N a N sin entidades dependientes

En este ejemplo, una persona puede tener de 0 a N amigos y un amigo a su vez puede tener un listado de amigos, de forma que de esta relación se deriva la necesidad de utilizar una tabla auxiliar que recoja el listado de relaciones entre personas que son amigos.

El esquema de creación de tablas en PostgreSQL es el siguiente:

CREATE TABLE persona
(
  id bigint NOT NULL,
  nombre character varying(255),
  fechaAlta timestamp without time zone,
  CONSTRAINT persona_pkey PRIMARY KEY (id)
)
CREATE TABLE personaamigo
(
  id bigint NOT NULL,
  persona1 bigint,
  persona2 bigint,
  CONSTRAINT personaamigo_pkey PRIMARY KEY (id),
  CONSTRAINT persona1_fkey FOREIGN KEY (persona1)
      REFERENCES persona (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT persona2_fkey FOREIGN KEY (persona2)
      REFERENCES persona (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

Debido a la complejidad en este tipo de relaciones y dado que no son un tipo de relación muy común, estos casos no estarán contempladas en el trabajo y no se realizará la transformación a MongoDB.

8.3 Relación N-aria


Las relaciones n-arias son aquellas que involucran a tres o más tablas relacionadas. La estructura final es muy similar a las relaciones binarias con cardinalidad N a N ya que en las relaciones n-arias es necesario utilizar una nueva tabla que incluya las relaciones.

Las cardinalidades de este tipo de relación deben ser N a N en todos los posibles casos, ya que de no ser así se considera que puede ser reducida a una relación binaria. Siguiendo el criterio del director de este proyecto de fin de grado, no tengo en cuenta asociaciones en las que las cardinalidades máximas no sean N a N.

8.3.1 Cardinalidad máxima N a N a N

En esta cardinalidad una tabla se relaciona de forma múltiple con dos o más tablas que también están relacionadas entre ellas.

Relación máxima N a N a N
Figura.20 - Relación máxima N a N a N

Al igual que para las relaciones binarias la relación es parcial y para poder recoger este tipo de cardinalidad es necesario utilizar una tabla auxiliar que contendrá las claves foráneas de las tablas A, B y C.

Ejemplo de cardinalidad máxima N a N a N sin entidades dependientes
Figura.21 - Ejemplo de cardinalidad máxima N a N a N sin entidades dependientes

En este ejemplo un estudiante puede cursar 1 o más semestres y en cada semestre puede cursar una o más asignaturas. De la misma forma, una asignatura puede tener uno o más estudiantes y puede ser impartida en uno o más semestres. Cada semestre tiene una o más asignaturas con uno o más alumnos por asignatura.

Los esquemas de creación de tablas en PostgreSQL son los siguientes:

CREATE TABLE estudiante
(
  id bigint NOT NULL,
  nombre character varying(255),
  apellidos character varying(255),
  CONSTRAINT estudiante_pkey PRIMARY KEY (id)
)
CREATE TABLE semestre
(
  id bigint NOT NULL,
  numero bigint,
  fechaInicio timestamp,
  fechaFin timestamp,
  CONSTRAINT semestre_pkey PRIMARY KEY (id)
)
CREATE TABLE asignatura
(
  id bigint NOT NULL,
  nombre character varying(255),
  CONSTRAINT asignatura_pkey PRIMARY KEY (id)
)

Y para recoger las relaciones es necesaria la siguiente tabla, que contiene las relaciones. Tiene la siguiente estructura:

CREATE TABLE evaluacion
(
  id bigint NOT NULL,
  estudiante bigint,
  semestre bigint,
  asignatura bigint,
  CONSTRAINT evaluacion_pkey PRIMARY KEY (id),
  CONSTRAINT estudiante_fkey FOREIGN KEY (estudiante)
      REFERENCES estudiante (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT semestre_fkey FOREIGN KEY (semestre)
      REFERENCES semestre (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT asignatura_fkey FOREIGN KEY (asignatura)
      REFERENCES asignatura (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

La transformación a MongoDB se realizará de forma muy similar al proceso seguido en las relaciones binarias de tipo N a N: la tabla evaluación no tendrá una colección siguiendo la regla 4.1, las otras tres tablas tendrán su propia colección y los índices que le corresponden serán incorporados como un array siguiendo la regla 7.

La estructura de los documentos será la siguiente:

idEstudiante1 = ObjectId()
idEstudiante2 = ObjectId()
idSemestre1 = ObjectId()
idSemestre2 = ObjectId()
idAsignatura1 = ObjectId()
idAsignatura2 = ObjectId()
idAsignatura3 = ObjectId()
db.createCollection("semestre")
db.semestre.insert(
{
  "_id": idSemestre1,
  "nombre": "semestre uno",
  "evaluacion": [
  {
      "asignatura": [idAsignatura1], 
      "estudiante": [idEstudiante1,idEstudiante2]
  },
  {
      "asignatura": [idAsignatura2], 
      "estudiante": [idEstudiante1,idEstudiante2]
  }
  ]
},
{
  "_id": idSemestre2,
  "nombre": "semestre dos",
  "evaluacion": [
  {
      "asignatura": [idAsignatura2], 
      "estudiante": [idEstudiante1,idEstudiante2]
  },
  {
      "asignatura": [idAsignatura3], 
      "estudiante": [idEstudiante1,idEstudiante2]
  }
  ] 
}
)
db.createCollection("estudiante")
db.estudiante.insert(
{
  "_id": idEstudiante1,
  "nombre": "estudiante uno",
  "evaluacion": [
    {
      "semestre": [idSemestre1], 
      "asignatura": [idAsignatura1,idAsignatura2]
    },
    {
      "semestre": [idSemestre2], 
      "asignatura": [idAsignatura2,idAsignatura3]
    }
  ]
},
{
  "_id": idEstudiante2,
  "nombre": "estudiante dos",
  "evaluacion": [
    {
      "semestre": [idSemestre1], 
      "asignatura": [idAsignatura1,idAsignatura2]
    },
    {
      "semestre": [idSemestre2], 
      "asignatura": [idAsignatura2,idAsignatura3]
    }
  ]
}
)
db.createCollection("asignatura")
db.asignatura.insert(
{
  "_id": idAsignatura1,
  "nombre": "algebra",
  "evaluacion": [
    {
      "semestre": [idSemestre1], 
      "estudiante": [idEstudiante1, idEstudiante2]
    }
  ]
},
{
  "_id": idAsignatura2,
  "nombre": "fisica",
  "evaluacion": [
    {
      "semestre": [idSemestre1], 
      "estudiante": [idEstudiante1, idEstudiante2]
    },
    {
      "semestre": [idSemestre2], 
      "estudiante": [idEstudiante1, idEstudiante2]
    }
  ]

},
{
  "_id": idAsignatura3,
  "nombre": "analisis matematico",
  "evaluacion": [
    {
      "semestre": [idSemestre2], 
      "estudiante": [idEstudiante1, idEstudiante2]
    }
  ]

}
)

8.4 Casos especiales


Estos casos hay que estudiarlos de forma independiente debido a la estructura de su relación. Uno de los casos estudiados es la relación binaria con cardinalidad de tipo 0:1 ó 0:N en el que la restricción de participación es parcial y ambas tablas tienen existencia propia e independiente aunque en algún momento podrán relacionarse.

Por otro lado, también se estudia el caso de los tipos enumerados, que son recursos de apoyo para el resto de tablas.

8.4.1 Cardinalidad mínima 0 a 1 ó 0 a N

En este tipo de relación, una tabla se relaciona con otra de forma única y cero o una vez. Un registro de la tabla A está asociado con un registro de la tabla B y viceversa.

Relación mínima 0 a 1
Figura.22 - Relación mínima 0 a 1

En este tipo de relación, una tabla se relaciona con otra de forma única y cero o más veces. Un registro de la tabla A está asociado con cero o más registros de la tabla B y la tabla B está asociado a un único registro de la tabla A.

Relación mínima 0 a N
Figura.23 - Relación mínima 0 a N

La particularidad de estas relaciones está en que no necesariamente un registro de la tabla A está obligatoriamente relacionado con uno o más registros de la tabla B, lo que implica que ambas tablas podrían tener existencia por sí mismas de forma independiente, pero solo si tienen relación con otras tablas.

Este tipo de casos puede verse con el siguiente ejemplo.

Ejemplo de cardinalidad mínima 0 a 1 con entidades dependientes
Figura.24 - Ejemplo de cardinalidad mínima 0 a 1 con entidades dependientes

En este ejemplo pueden verse los casos de relación mínima 1 a 1, 0 a 1 y un ejemplo de referencia circular entre la tabla profesor y el departamento.

Un profesor enseña en una y solo una asignatura y una asignatura debe tener un único profesor.

Por otro lado, un departamento tiene que tener un profesor que será el jefe del mismo y un profesor puede ser jefe de cero o un departamento. Además un profesor debe pertenecer obligatoriamente a un departamento y un departamento debe tener uno o más profesores.

Los esquemas en postgreSQL de creación de cada una de las tablas son los siguientes:

CREATE TABLE profesor
(
  id bigint NOT NULL,
  nombre character varying (255),
  apellidos character varying (255),
  departamento bigint NOT NULL,
  fechaAlta timestamp without timezone,
  CONSTRAINT profesor _pkey PRIMARY KEY (id),
  CONSTRAINT departamento _fkey FOREIGNKEY (departamento)
    REFERENCES departamento (id) MATCHSIMPLE
    ON UPDATE NOACTION ON DELETE NO ACTION
)
CREATE TABLE departamento
(
  id bigint NOT NULL,
  nombre character varying(255),
  jefe bigint NOT NULL,
  fechaCreacion timestamp without time zone,
  CONSTRAINT departamento_pkey PRIMARY KEY (id),
  CONSTRAINT jefe_fkey FOREIGN KEY (profesor)
    REFERENCES profesor (id) MATCH SIMPLE
    ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE TABLE asignatura
(
  id bigint NOT NULL,
  nombre character varying(255),
  profesor bigint NOT NULL,
  CONSTRAINT asignatura_pkey PRIMARY KEY (id),
  CONSTRAINT profesor_fkey FOREIGN KEY (profesor)
    REFERENCES profesor (id) MATCH SIMPLE
    ON UPDATE NO ACTION ON DELETE NO ACTION
)

Una asignatura tiene que tener obligatoriamente un profesor como se ve en la restricción NOT NULL. La tabla asignatura no tendrá su propia colección siguiendo con la regla 4.2 y será incorporada como un array de objetos en la colección profesor siguiendo con la regla 5.

Como se puede observar, la tabla profesor tiene la clave foránea con departamento, pero no de forma obligatoria porque no hay restricción NOT NULL ya que un departamento puede tener de 0 a más profesores relacionados y un departamento tiene una clave foránea con profesor ya que debe tener uno que sea jefe del mismo como puede verse en la restricción NOT NULL. Debido a esta referencia circular tanto la tabla profesor como departamento tendrán su propia colección, siguiendo con la regla 3.4.

Además, como ambas tablas tiene su clave foránea a la otra tabla, ambas tendrán un array de índices de la colección referenciada siguiendo con la regla 6.1.

idDepartamento = ObjectId()
idProfesor = ObjectId()
db.createCollection("departamento")
db.departamento.insert(
{
    "_id": idDepartamento,
    "nombre": "departamentoEjemplo",
    "fechaCreacion": {"date": new Date()},
    "jefe": [idProfesor]
}
)
db.createCollection("profesor")
db.profesor.insert(
{
"_id": idProfesor,
"nombre": "profesorNombreEjemplo",
    "apellido": "profesorApellidoEjemplo",
    "departamento": [idDepartamento],
    "fechaAlta": {"date": new Date()},
    "asignatura": {
            "id": 1,
            "nombre": "Álgebra"
    }
}
)

8.4.2 Enumerados

Los enumerados surgen para determinar los valores que puede tener una columna en una tabla. Con este tipo de dato se consigue fijar que valor puede tener de entre unos fijos ya que podría tener varios valores en función de la interpretación de las personas que gestionan la base de datos, como por ejemplo el campo sexo en la tabla persona que podría recogerse como: “hombre”, “mujer” o cómo “masculino”, “femenino” siendo todas las posibilidades válidas sintácticamente hablando.

El problema está en que puede haber registros que tengan un valor y registros que tengan otro. Gracias a los enumerados se evitan este tipo de situaciones, ya que el campo sexo en un usuario será una relación con el tipo enumerado sexo que contendrá los posibles valores admitidos para este campo, por ejemplo “masculino” y “femenino”.

Hay varias formas de representar un enumerado: puede ser un tipo independiente y que la tabla tenga una columna del tipo enumerado, puede ser una restricción de tipo check en la columna al crear la tabla e incluso el tipo enumerado puede ser una tabla que únicamente tiene un campo de tipo string y su clave primaria.

Enumerado con una tabla
Figura.25 - Enumerado con una tabla

Con este ejemplo las tablas son las siguientes:

CREATE TABLE persona
(
  id bigint NOT NULL,
  nombre character varying(255),        
  sexo bigint,
  CONSTRAINT persona_pkey PRIMARY KEY (id),
  CONSTRAINT sexo_fkey FOREIGN KEY (sexo)
    REFERENCES sexo (id) MATCH SIMPLE
    ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE Table sexo    
(
  id bigint NOT NULL,
  nombre character varying(255),
  CONSTRAINT sexo_pkey PRIMARY KEY (id)
)
Enumerado con un tipo independiente
Figura.26 - Enumerado con un tipo independiente

Con este ejemplo las tablas y los tipos son los siguientes:

CREATE TABLE persona
(
  id bigint NOT NULL,
  nombre character varying(255),        
  sexo sexo,
  CONSTRAINT persona_pkey PRIMARY KEY (id)
)
CREATE TYPE sexo AS ENUM    
(
  'Masculino', 'Femenino' 
)

Tmbién podría encontrarse de la siguiente forma:

CREATE TABLE persona
(
  id bigint NOT NULL,
  nombre character varying(255),        
  sexo text (check sexo in ('Masculino', 'Femenino')),
  CONSTRAINT persona_pkey PRIMARY KEY (id)
)

Como puede verse, se crea el tipo a la vez que se cargan valores, de forma que si en el campo sexo de la tabla persona se intenta cargar un dato distinto de los admitidos, se lanzará un error.

En la migración a MongoDB el tipo de dato enumerado no tendrá su propia colección siguiendo con la regla 4.3. La tabla persona tendrá su colección siguiendo con la regla 3.1 ó 3.3 (dependiendo de cómo se encuentre el enumerado).
Al hacer la transformación a MongoDB siempre se migrará de la misma forma: el valor del enumerado será cargado como un array de objetos JSON dentro de la colección relacionada, siguiendo con la regla 5.

De esta forma la estructura en MongoDB será la siguiente:

idPersona = ObjectId()
db.createCollection("persona")
db.persona.insert(
{
    "_id": idPersona,
    "nombre": "john doe",
    "sexo": "masculino"
}
)

8.5 Otras cardinalidades


Dentro de las relaciones N-arias se pueden encontrar más cardinalidades, todas las posibles combinaciones entre 0 y N entre tres tablas.

Siguiendo el criterio del director de este proyecto, el resto de cardinalidades distintas de las N:N:N no van a ser estudiadas ni tenidas en cuenta ya que pueden ser sustituidas por relaciones de tipo binario.

8.6 Conclusión


Se podría decir que este capítulo es el grueso de mi trabajo de fin de grado. Son estas reglas las que definen un método de transformación esquemas de SQL a NoSQL, ya que aunque este trabajo se centra en dos herramientas en concreto, se pueden generalizar a cualquier SGBD SQL y a cualquier SGBD NoSQL orientado a documentos.

Estas reglas no son perfectas.Seguramente el resultado de la migración se podría mejorar y en casi cualquier situación esta transformación dependerá del contexto en el que se manejen los datos. A pesar de esto, apoyarse en unas reglas que transformen esquemas puede ser cómodo, ya que proporciona una técnica para cualquier tipo relación en diseños de base de datos relacionales que sigan la tercera forma normal.

results matching ""

    No results matching ""