DocumentDB Query’s

Escrito por  Robert Bermejo

DocumentDB es el servicio NoSQL que nos proporciona Azure como servicio PaaS. Sabemos que las bases de datos NoSQL no tienen esquema y en el caso de DocumentDB guardan su información en formato JSON.

Entonces, si no tenemos esquemas y los datos se guardan en formato JSON ¿Cómo puedo consultar los datos que tengo? La respuesta es mediante lenguaje SQL (Structured Query Language).

En este artículo veremos que sentencias SQL pueden utilizarse y cómo hacerlo.

Esquema de una sentencia SQL en DocumentDB

SELECT  [TOP <top_expression>] <select_list>

[FROM <from_sepecification]

[WHERE <filter_condition>}

[ORDER BY <sort_specification>]

Como se puede observar el esquema para realizar consultas es muy familiar si se conoce SQL Cada sentencia consiste en un SELECT acompañado opcionalmente por un FROM, WHERE o ORDER BY. Veamos un ejemplo.

Tenemos la siguiente información en nuestra collection Familias de DocumentDB:

1{
2  "id": "FamiliaCompartiMOSS",
3  "padres": [
4    {
5      "apellido": "CompartiMOSS",
6      "nombre": "Revista"
7    },
8    {
9      "apellido": "Apellido1",
10      "nombre": "Nombre1"
11    }
12  ],
13  "hijos": [
14    {
15      "apellido": "Apellido2",
16      "nombre": "Nombre2",
17      "sexo": "mujer",
18      "grado": 1,
19      "mascotas": [
20        {"nombre": "Goofy" },
21        {"nombre": "Mickey" }
22      ]
23    },
24    {
25      "apellido": "Apellido3",
26      "nombre": "Nombre3",
27      "sexo": "mujer",
28      "grado": 8
29    }
30  ],
31  "direccion": {
32    "pais": "España",
33    "ciudad": "BCN"
34  },
35  "contacto": {
36    "email": "email@email.com",
37    "telefono": "000000000"
38  },
39  "registrado": false
40}
41

 Y realizamos la siguiente consulta:

1 SELECT {
2  "Nombre": "f.id",
3  "Ciudad": "f.direccion.ciudad"
4} AS Familia FROM Familias f WHERE f.direccion.ciudad = “BCN”
5

El resultado será:

1[
2  {
3    "Familia": {
4      "Name": "FamiliaCompartiMOSS",
5      "Ciudad": "BCN"
6    }
7  }
8]
9

Aunque no tengamos un esquema fijo podemos ver cómo podemos realizar consultas utilizando la potencia de SQL. Ahora vamos a ver más en detalle todas las opciones.

FROM

Clausula opcional, aunque si se aplica un filtro en la query o se proyecta algún campo como resultado, entonces será obligatorio definir esta cláusula. Como en una consulta SQL convencional, la cláusula FROM indica la collection sobre la cual se ejecutará la consulta, también puede identificar un subconjunto de la collection como fuente de la consulta.

Un ejemplo de utilización de un subconjunto de la collection sería:

1SELECT * 
2FROM Familias.hijos 
3

El resultado será:

1 [
2  [
3    {
4      "apellido": "Apellido2",
5      "nombre": "Nombre2",
6      "sexo": "mujer",
7      "grado": 1
8    },
9    {
10      "apellido": "Apellido3",
11      "nombre": "Nombre3",
12      "sexo": "mujer",
13      "grado": 8
14    }
15  ]
16]
17

También se puede utilizar alias en las consultas de la forma convencional: SELECT f.id FROM Familias AS f. Una vez se utiliza el alias la fuente ya no se reconocerá, es decir, si realizáramos la consulta SELECT Familias id FROM Familias AS f la query no funcionaría, como tampoco funcionaria SELECT id FROM Familias AS f.

Por último, también se puede utilizar la cláusula IN, que nos permite realizar iteraciones sobre JSON arrays. La cláusula FROM nos da soporte para iteraciones. Vamos a ver un ejemplo, cogiendo como base el ejemplo anterior:

1SELECT * 
2FROM c IN Familias.hijos 
3El resultado será:
4
5

[   {     "apellido": "Apellido2",     "nombre": "Nombre2",     "sexo": "mujer",     "grado": 1   },   {     "apellido": "Apellido3",     "nombre": "Nombre3",     "sexo": "mujer",     "grado": 8   } ]

1
2Podemos ver que al aplicar la cláusula IN, todos los resultados se devuelven en una única lista, mientras que en el primer ejemplo devolvía una lista por resultado.
3
4**WHERE**
5
6El comando WHERE es opcional, y si lo añadimos dentro de una query DocumentDB lo que estamos indicando que condiciones deben satisfacer los resultados que serán incluidos en el resultado de la ejecución.
7
8En el siguiente ejemplo podemos ver como se aplica el operador WHERE:
9
10

SELECT f.direccion FROM Familias f  WHERE f.id = "FamiliaCompartiMOSS "

1
2El resultado será:
3
4

[   {     "direccion": {       "pais": "España",       "ciudad": "BCN"     }   } ]

1
2En el ejemplo anterior hemos aplicado el operador =, pero no es el único que podemos utilizar con esta cláusula, también se pueden utilizar los siguientes:
3
4·       Aritméticos: +,-,*,/,%
5·       Lógicos: AND, OR
6·       Strings: || (concatenación)
7·       Comparación: =, !=, >, >=, <, <=, <>
8·       Bits: |, &, ^
9·       Binarios y unarios: true, false, null, undefined… Un ejemplo de uso de un operador binario sería:
10
11

SELECT * FROM Familias f WHERE f.registrado

1
2Esta query nos devolvería todos aquellos documentos que contengan true como valor.
3
4·       BETWEEN: Palabra clave que especifica un rango de valores. Puede ser usada con strings o números. Por ejemplo:
5
6

SELECT *  FROM Familias.hijos [ 0 ] c  WHERE c.grado BETWEEN 1 AND 5

1
2·       IN: Palabra clave que se utiliza para verificar que un valor esté en un conjunto predefinido de valores. Por ejemplo:
3
4

SELECT * FROM Familias  WHERE Familias.id IN ('Familia2', 'FamiliaCompartiMOSS')

1
2**SELECT**
3
4Cualquier consulta sobre DocumentDB debe contener la cláusula SELECT. Esta cláusula nos indica los campos que serán devueltos al ejecutarse la query. Por ejemplo, si queremos devolver solo la dirección ejecutaríamos la siguiente query:
5
6

SELECT f.contacto FROM Familias f  WHERE f.id = " FamiliaCompartiMOSS "

1
2El resultado será:
3
4

[   {     "contacto": {       "email": "email@email.com",       "telefono": "000000000"     }   } ]

1
2Dentro del operador SELECT podemos realizar las siguientes operaciones:
3
4·       Propiedades Anidadas: Devolver una propiedad que está dentro de otra. Por ejemplo: 
5
6

SELECT f.direccion.pais, f.direccion.ciudad FROM Familias f  WHERE f.id = " FamiliaCompartiMOSS "

El resultado será:

1[
2  {
3    "pais": "España",
4    "ciudad": "BCN"
5  }
6]
7
8·       Proyecciones: Devolvemos el resultado en una estructura propia.
9
10

SELECT {   "pais": "f.direccion.pais",   "ciudad": "f.direccion.ciudad",   "nombre": "f.id" } FROM Familias f WHERE f.id = " FamiliaCompartiMOSS "

1
2El resultado será:
3
4

[{   "$1": {     "pais": "España",     "ciudad": "BCN",     "name": " FamiliaCompartiMOSS "   } }]

1
2·       Utilización de Alias: Cuando queremos devolver la estructura propia con un nombre propio.
3
4

SELECT {   "pais": "f.direccion.pais",   "ciudad": "f.direccion.ciudad" } AS DireccionInfo,         { "nombre ": f.id } NombreInfo FROM Familias f  WHERE f.id = " FamiliaCompartiMOSS "

1
2El resultado será:
3
4

[{   "DireccionInfo": {     "pais": "España",      "ciudad": "BCN"   },   "NombreInfo": {     "nombre": " FamiliaCompartiMOSS "   } }]

1
2·       Expresiones escalares: SELECT soporta expresiones del tipo constantes, expresiones aritméticas, lógicas…. Por ejemplo:
3
4

SELECT ((2 + 11 % 7)-2)/3   

1
2El resultado será:         
3
4

[   {     "$1": 1.33333   } ] 

1
2·       VALUE: Es una palabra clave que nos permite devolver un valor. Por ejemplo, si lo quisiéramos devolver una constante:
3
4

SELECT VALUE "CompartiMOSS" 

1
2El resultado será: 
3
4

[   "CompartiMOSS" ]

1
2También nos permitiría eliminar etiquetas de los resultados generados:
3
4

SELECT VALUE f.direccion FROM Familias f 

1
2El resultado será:         
3
4

[   {     "pais": "España",     "ciudad": "BCN"   } ]

1
2·       * :  Operador especial que nos devolverá todos los campos de la consulta que realicemos:
3
4

SELECT *  FROM Familias f  WHERE f.id = " FamiliaCompartiMOSS 

1
2El Resultado será:
3
4

[   {     "id": "FamiliaCompartiMOSS",     "padres": [       {         "apellido": "CompartiMOSS",         "nombre": "Revista"       },       {         "apellido": "Apellido1",         "nombre": "Nombre1"       }     ],     "hijos": [       {         "apellido": "Apellido2",         "nombre": "Nombre2",         "sexo": "mujer",         "grado": 1,         "mascotas": [           { "nombre": "Goofy" },           { "nombre": "Mickey" }         ]       },       {         "apellido": "Apellido3",         "nombre": "Nombre3",         "sexo": "mujer",         "grado": 8       }     ],     "direccion": {       "pais": "España",       "ciudad": "BCN"     },     "contacto": {       "email": "email@email.com",       "telefono": "000000000"     },     ]

1
2·       BETWEEN: Tiene el mismo significado y uso que en el Where.
3
4

SELECT (c.grado BETWEEN 0 AND 10)  FROM Familias.hijos [ 0 ] c

1
2·       Operdores ? (Ternary) y ?? (Coalesce): Estos operadores nos permiten aplicar condiciones. Por ejemplo:
3
4

SELECT (c.grado < 5)? "directo": "indirecto" AS nivelgrado  FROM Familias.hijos [ 0 ] c 

1

SELECT f.nombre ?? f.apellido AS apellido  FROM Familias f

1
2·       TOP: Operador que nos permitirá devolver un número concreto de resultados deseados.
3
4

SELECT TOP 1 *  FROM Familias f

1
2El resultado será:
3
4

[   {     "id": "FamiliaCompartiMOSS",     "padres": [       {         "apellido": "CompartiMOSS",         "nombre": "Revista"       },       {         "apellido": "Apellido1",         "nombre": "Nombre1"       }     ],     "hijos": [       {         "apellido": "Apellido2",         "nombre": "Nombre2",         "sexo": "mujer",         "grado": 1,         "mascotas": [           { "nombre": "Goofy" },           { "nombre": "Mickey" }         ]       },       {         "apellido": "Apellido3",         "nombre": "Nombre3",         "sexo": "mujer",         "grado": 8       }     ],     "direccion": {       "pais": "España",       "ciudad": "BCN"     },     "contacto": {       "email": "email@email.com",       "telefono": "000000000"     },​     "registrado": false   } ]

1
2**Arrays y Objetos**
3
4DocumentDB permite la creación de arrays y/o objetos. Podemos construir arrays de la siguiente forma:
5
6

SELECT [ "f.direccion.ciudad", "f.direccion.pais" ] AS PaisCiudad  FROM Familias f

1
2El Resultado será:
3
4

[   {     " PaisCiudad ": [       "BCN",       "España"     ]   },   {     "CiudadState": [       "BCN",       "BCN"     ]   } ]

1
2**JOINS**
3
4DocumentDB nos permite realizar JOINS, pero a diferencia del concepto JOIN de una Base de Datos relacional, no nos sirve para relacionar tablas de forma que podamos obtener los datos deseados, sino que nos permite relacionar el schema consigo mismo, lo que en una base de datos relacional equivaldría a realizar una “self-join”.
5
6El resultado de realizar la operación JOIN es un conjunto de N-tuplas con N-Values por tupla, con valores obtenidos de iterar los todos los alias de las collections sobre sus conjuntos.
7
8Por ejemplo, podemos realizar una JOIN entre la raíz y un hijo, el resultado es el producto cruzado entre los dos objetos JSON.
9
10Supongamos que, para este ejemplo, además de la entrada anterior, disponemos de la siguiente entrada
11
12

{   "id": "FamiliaCompartiMOSS2",   "padres": [     {       "apellido": "CompartiMOSS22",       "nombre": "Revista"2     },     {       "apellido": "Apellido12",       "nombre": "Nombre12"     }   ],   "hijos": [     {       "apellido": "Apellido22",       "nombre": "Nombre22",       "sexo": "mujer",       "grado": 1,       "mascotas": [         {"nombre": "Goofy2" },         {"nombre": "Mickey2" }       ]       ],   "direccion": {     "pais": "España",     "ciudad": "MAD"   },   "contacto": {     "email": "email2@email.com",     "telefono": "000000001"   },   "registrado": true }​

1
2Y realizamos la siguiente *query*:
3
4

SELECT f.id FROM Familias f JOIN c IN f.hijos 

1
2El Resultado será:
3
4

[   {     "id": " FamiliaCompartiMOSS2"   },   {     "id": "FamiliaCompartiMOSS"   },   {     "id": "FamiliaCompartiMOSS"   } ]

1
2ORDER BY
3
4En caso de añadir este operador, nos permitirá ordenar los resultados de la query de la forma deseada, seguimos teniendo las dos entradas en nuestra collection.
5
6

SELECT f.id, f.direccion.ciudad FROM Familias f  ORDER BY f.direccion.ciudad

1
2El Resultado será:
3
4

[   {     "id": "FamiliaCompartiMOSS",     "ciudad": "BCN"   },   {     "id": " FamiliaCompartiMOSS2",     "ciudad": "MAD"   } ]

1
2CONCLUSIONES
3
4Como podéis observar, realizar queries sobre DobumentDB es muy similar a realizar queries sobre SQL, todos los operadores que tiene son familiares y funcionan de la forma que esperamos. Query playground es un sitio web proporcionado por Microsoft para poder practicar y ver más en profundidad como realizar queries en DobumentDB.
5
6Destacar que DocumentDB también permite trabajar con funciones del tipo:
7
8·       Matemático: ABS, CEILING, EXP…..
9
10·       Comprobación de tipos: IS\_ARRAY, IS\_BOOL…
11
12·       Funciones de Strings: CONCAT, CONTAINS, ENDSWITH…
13
14·       Funciones de Arrays: ARRAY\_CONCAT, ARRAY\_CONTAINS…
15
16·       Funciones de espacio: ST\_DISTANCE, ST\_WITHIN…
17
18Para saber más sobre funciones en DocubmentDB, visitar los links del apartado referencias.
19
20**REFERENCIAS:**
21
22·      
23[https://docs.microsoft.com/en-us/azure/documentdb/documentdb-sql-query#built-in-functions](https&#58;//docs.microsoft.com/en-us/azure/documentdb/documentdb-sql-query#built-in-functions)
24
25·      
26[https://www.documentdb.com/sql/tutorial](https&#58;//www.documentdb.com/sql/tutorial)
27
28·       [https://www.documentdb.com/sql/demo](https&#58;//www.documentdb.com/sql/demo)
29
30
31
32**Robert Bermejo**
33Arquitecto .Net
34
35roberto.bermejo@sogeti.com
36 @robertbemejo
37 www.robertbermejo.com​
38
39
40
41export const _frontmatter = {}
42