Integrando CosmosDB en una aplicación Java con SpringBoot (Parte 2)

Esta es la continuación de la parte 1 que publicamos la semana pasada en el blog. En esta parte veremos como implementar operaciones en Bulk, limitaciones existentes de la API SQL, y como abordar estas limitaciones respecto a la sentencias UPDATE y DELETE en SQL.

Operaciones Bulk

CosmosDB también provee de una clase que se genera a partir del cliente, que permite operaciones en masa (múltiples documentos): insert, update y delete. Esta clase es DocumentBulkExecutor, y Azure recomienda que también sea un singleton en nuestra aplicación. No obstante, este executor se crea asociado a una colección, por lo que si vamos a trabajar con varias necesitaremos un Singleton por colección.

Para esto lo mejor es crearse un componente de Spring que gestione un mapa de Singletons de este tipo.

DocumentBulkExecutor Manager

Y desde nuestro repositorio invocaremos a este manager para tener acceso al bulkExecutor oportuno:

El método importAll tiene un parámetro isUpsert que nos permite usar dicha operación tanto como para inserción como para actualización. Además, la respuesta BulkImportResponse, nos da mucha información sobre los tiempos y recursos utilizados en la operación.

INFO  c.m.o.p.p.cosmosdb.CosmosDBBasicOpsDBRepository - Inserted 150 documents for ontology RestaurantesN on CosmosDb with a total RU consumed of 2947.71. Total time taken 45 milliseconds with avg 3 imports/millisecond

Funciones de agregado

Para las funciones de agregado (COUNT, SUM,MIN,MAX,MEDIA), al igual de que las queries convencionales, podremos realizarlas sobre una partición concreta o ‘cross partition’. Si decidimos hacerlas en el último modo, debemos ser conscientes de la limitación que present

“Cross partition query only supports ‘VALUE ‘ for aggregates.”

Por lo que las queries deberán tener la palabra clave VALUE para que funcionen:

Query de agregación en la API
Cross partition query agregada sin VALUE
Cross partition query agregada con VALUE

Limitaciones de sintaxis SQL: UPDATE y DELETE

Una de las grandes limitaciones a día de hoy en CosmosDB es la carencia de soporte de las operaciones de UPDATE y DELETE en SQL.

Si necesitamos implementar el soporte para estas operaciones, deberemos hacer un workaround siguiendo los siguientes pasos:

UPDATE

  • Hacer un fetch de todos los documentos que cumplan la condición de la query.
  • Actualizar los campos oportunos (sentencia SET)
  • Guardar los documentos con sus valores actualizados.

DELETE

  • Hacer un fetch de todos los documentos que cumplan la condición de la query.
  • Borrar uno a uno los documentos: a partir de su ID y su valor de PK.

Esta funcionalidad se puede conseguir a través de dos alternativas:

  • Creación de un Stored proccedure: No es más que un Script en JavaScript, al que le pasamos la query, y en él definimos la lógica. Aquí tenemos un ejemplo de implementación.
  • Implementar estos pasos en Java: este es el método de implementación que hemos escogido

Lo que vamos a hacer para los DELETE y UPDATE SQL en nuestro caso, es extraer el segmento WHERE y SET de la query, con la ayuda de la librería JSQLParser , para primero convertirla en un SELECT, traer todos los documentos que cumplan dicha condición, y proceder a actualizar los campos de los documentos JSON. Por último, con los documentos actualizados, hacer uso de la clase DocumentBulkExecutor para la importación en masa.

Como podemos ver, este es un proceso costoso, tanto a nivel de procesamiento como a nivel económico (RUs), por lo que si tu aplicación va a tener mucha carga de actualización,borrado de datos en grandes volúmenes, quizás debas replantearte si CosmosDB es la mejor opción.

Para el caso del UPDATE:

Implementación UPDATE SQL

En la línea 123 convertimos el UPDATE en un SELECT con el WHERE del primero. Acto seguido nos traemos los documentos que cumplan la condición, línea 126.

Procedemos a actualizar todos los campos de cada documento, a través del método updateInstancesFromStatement, línea 128.

Procesamiento de las operaciones de UPDATE sobre documentos

En este método, primero creamos un Mapa de JSON paths (String) y operaciones de Update (UpdateOperation), para posteriormente aplicárselo a cada JSON. Hemos creado la clase UpdateOperation para simplicidad y conveniencia:

UpdateOperation class

Para obtener el mapa de paths y operaciones utilizamos la librería JSQLParser, que nos facilita mucho la vida.

Extracción de operaciones de UPDATE para cada JSON path

El CosmosDBExpressionVisitorAdapter se encarga de generar una UpdateOperation para cada Expression de la sentencia SET. Aquí podemos implementar funciones especiales para cubrir nuestras necesidades, como podría ser una función APPEND() para añadir registros a un array del documento, que como ya sabemos, en SQL no hay nada soportado.

Transformación de Expression del SET a una UpdateOperation
Transformación de Expression del SET a una UpdateOperation: Funciones custom

Como vemos, esta clase nos mapeará cada Expression con una operación UpdateOperation con un valor y un tipo determinados. Por ejemplo, si se trata de un APPEND(), la operación tendrá como valor un JsonNode o un valor primitivo, y el tipo de la operación será APPEND o APPEND_MULTIPLE si son varios registros los que hay que añadir al array.

Para aplicar el mapa de operaciones, nos ayudaremos de la librería JsonPath:

Aplicación de las operaciones al documento JSON

Por último, una vez tenemos todos los documentos, utilizamos el bulkExecutor asociado, con el flag isUpsert a true, línea 129. El DocumentBulkExecutor también provee del método updateAll para esta funcionalidad.

El procedimiento para el DELETE SQL es muy similar.

Otras limitaciones: Redefinición de los JOIN SQL

CosmosDB utiliza su propia interpretación de la sentencia JOIN SQL. Ésta se utiliza para hacer cruces intra-documentales, por lo que no es posible utilizarla para hacer cruces entre colecciones, debido al modelo de datos que utiliza el propio CosmosDB.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s