En esta entrada del blog presentaremos una nueva y emocionante función del próximo Versión de Couchbase Server 4.5
(nombre en clave Watson), ahora en Beta.
Estamos hablando de la API de subdocumentos (abreviado subdoc).
Editar: Esta entrada de blog ha sido editada con las actualizaciones de la 4.5 Beta y Java SDK 2.2.6.
Para conocer otras características de Couchbase 4.5, consulte las publicaciones del blog de Don Pinto sobre la versión Vista previa para desarrolladores y el Beta.
¿De qué se trata?
El subdocumento es una función del lado del servidor que se añade a la función Memcached
con el que funciona Couchbase Operaciones clave-valor. Añade algunas operaciones que actúan sobre un único JSON pero, en lugar de obligarle a recuperar el contenido completo del documento, le permite especificar la parte del documento que desea recuperar. ruta
dentro del JSON que te interesa recuperar o mutar.
Permítame mostrarle por qué es interesante con el siguiente escenario: supongamos que tiene un documento JSON grande (y me refiero a laaaargo) almacenado bajo el ID K
en Couchbase. Dentro del documento, en el diccionario sub
hay un algunos
que desea actualizar. ¿Cómo lo harías con Couchbase 4.1?
Así es, tienes que realizar un obtener(k)
de todo el documento, modificar su contenido localmente y, a continuación, realizar una upsert
(o un sustituir
). No sólo transmite todo el documento grande a través de la red cuando sólo le interesa una pequeña parte, sino que además transmitirlo dos veces!
En subdoc API permite operaciones más sencillas en estos casos. Elija la operación que desea realizar en su documento y proporcione la clave, la ruta dentro del JSON y, opcionalmente, el valor que desea utilizar para esa operación, y voilà!
En nuestro ejemplo anterior, el ruta
sería "sub.valor
". Esto es bastante natural y coherente con la sintaxis de rutas utilizada en nuestro lenguaje de consulta, N1QL
. Esto es lo que parece, no dibujado a escala :)
En realidad, el mensaje no es más que unos pocos bytes, por lo que cuanto mayor sea el documento JSON original que lo contiene, mayores serán las ventajas.
La API cliente
Seguro que ahora he captado tu atención. Pero, ¿cuáles son las operaciones que se ofrecen y qué aspecto tiene la API?
Tomaremos el ejemplo de la API de Java, ofrecida por el programa 2.2.6
SDK de Java.
Para cada ejemplo, consideraremos que un "subdoc
" El documento JSON existe en la base de datos y tiene el siguiente contenido:
1 2 3 4 5 6 7 8 9 |
{ "frutas": [ "manzana", "plátano", "ananas" ], "sub": { "valor": "algunaCadena", "bool": falso }, "contador": 1, "basura": [ ... ] /un conjunto muy muy largo } |
También consideramos que un Cubo
instancia, cubo
está disponible.
Operaciones de búsqueda
Sin hablar de mutaciones dentro de un JSON, a veces sólo quieres leer un único valor escondido en lo más profundo de tu documento. O simplemente quieres comprobar si una entrada está ahí. Subdoc ofrece dos operaciones (consiga
y existe
) para hacer precisamente eso, que se ponen a disposición a través del bucket.lookupIn(String key)
método.
En buscarEn
le proporciona un constructor dirigido a un único documento JSON ("clave
"), que a su vez puede utilizar para describir con fluidez las operaciones que desea realizar. Una vez que tenga listo todo el conjunto de especificaciones de búsqueda, puede ejecutar la operación llamando al constructor ejecutar()
método. Por supuesto, también puede utilizarse para una única búsqueda.
Este método devuelve un DocumentoFragmento
en Java, que representa el resultado. Tenga en cuenta que las búsquedas múltiples siempre devuelven un resultado de este tipo siempre que no se produzca ningún error a nivel de documento (es decir, el documento no existe o no es JSON).
Puede obtener el resultado individual de cada operación llamando a result.content(String path)
o result.content(int operationIndex)
(si tiene varias operaciones dirigidas a la misma ruta, en cuyo caso la primera siempre devolverá el resultado de la primera). Si se ha producido un error, se obtiene la clase hija apropiada de SubDocumentException
. Si no, se obtiene el valor (o "true" en el caso de existe
).
También hay un existe
similar al contenido pero devolviendo verdadero sólo si el objeto resultado contiene un resultado para esa ruta/índice y la operación correspondiente fue un éxito.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
DocumentoFragmento resultado = cubo .buscarEn("subdoc") .consiga("sub.valor") .existe("frutas") .existe("sub.foo") .ejecutar(); Cadena subValor = resultado.contenido("sub.valor", Cadena.clase); booleano frutasExisten = resultado.contenido("frutas", Booleano.clase); booleano fooExistir = cubo.existe("sub.foo"); Sistema.fuera.println(subValor + ", " + frutasExisten + ", " + foExist); |
El código anterior imprime:
1 |
algunaCadena, verdadero, falso |
Operaciones de mutación
Hay más variedades de operaciones de mutación. Algunas están hechas a medida para tratar con matrices, mientras que otras están más adaptadas para tratar con diccionarios.
Una vez más proporcionamos un constructor para orientar las mutaciones en un documento JSON específico a través de bucket.mutateIn(String key)
. Se encadenan las operaciones que se desean realizar y finalmente se llama a ejecutar()
para realizar el conjunto de mutaciones.
Las mutaciones pueden tener en cuenta el CAS del documento adjunto, comparando el CAS proporcionado con el actual antes de aplicar todo el conjunto de mutaciones. Para ello, proporcione el CAS que desea comprobar, llamando a .withCas(long cas)
una vez en su cadena de construcción.
Otros con
(que sólo deben invocarse una vez) son:
withDurability(PersistTo p, ReplicateTo r)
: permite observar una restricción de durabilidad en todo el conjunto de mutaciones.withExpiry(int ttl)
: permite dar una caducidad al documento cuando se muta con éxito.
Además, algunas mutaciones permiten crear objetos más profundos (por ejemplo, crear la entrada en la ruta nuevoDict.sub.entrada
a pesar de nuevoDict
y sub
no existente). Esto se representa con el crearPadres
en los métodos del constructor.
Tomando el ejemplo anterior y utilizando algunas de las operaciones de mutación, podemos hacer eficientemente lo siguiente:
- Sustituir la fruta
ananas
(que incorrectamente es una palabra francesa) conpiña
. - Añade una nueva fruta,
pera
en el frente del surtido de frutas. - Añadir una nueva entrada a
sub
llamadonuevoValor
. - Incrementar a
contador
por un delta de100
. - Deshazte del
chatarra
gran conjunto. - Espere a que cbserver confirme que los datos se han escrito en el disco del maestro.
- Abortar todo si CAS en el servidor no es
1234
.
A continuación se explica cómo hacerlo utilizando el SDK de Java:
1 2 3 4 5 6 7 8 9 10 11 12 |
booleano crearPadres = falso; DocumentoFragmento resultado = cubo .mutateIn("subdoc") .sustituir("frutas[2]", "piña") .arrayPrepend("frutas", "pera", crearPadres) .insertar("sub.newValue", "theNewValue", crearPadres) .contador("contador", 100L, crearPadres) .eliminar("basura") .conDurabilidad(PersistTo.MAESTRO, ReplicateTo.NONE) .conCas(1234L) .ejecutar(); |
Otras operaciones de mutación disponibles son:
upsert
arrayInsert
(especializado en insertar un valor en una matriz, en un índice específico)arrayAppend
(especializado en insertar un valor al final de una matriz)- arrayInsertAll (igual que arrayInsert pero insertando cada elemento de una colección en el array)
- arrayPrependAll (igual que arrayPrepend pero añadiendo cada elemento de una colección al principio de una matriz)
- arrayAppendAll (igual que arrayAppend pero añadiendo cada elemento de una colección a la parte posterior de un array)
arrayAddUnique
(especializado en insertar un valor en una matriz si el valor no está ya presente en la matriz)
Tras aplicar estas 5 mutaciones, éste es el aspecto del documento en la base de datos:
1 2 3 4 5 6 7 8 9 |
{ "frutas": [ "pera", "manzana", "plátano", "piña" ], "sub": { "valor": "algunaCadena", "bool": falso, "nuevoValor": "theNewValue" }, "contador": 101 } |
Al contrario de lo que ocurre cuando se realizan búsquedas múltiples, si falla alguna de las mutaciones se omite todo el conjunto de operaciones y no se realiza ninguna mutación. En este caso, recibirá un mensaje MultiMutationException
que puede comprobar el índice de la primera operación que falla (así como el estado de error correspondiente).
Tratamiento de errores
Cada operación de subdocumento puede dar lugar a errores relacionados con el subdocumento: ¿qué pasa si el documento que corresponde a la clave dada no es JSON? ¿Y si se proporciona una ruta que no existe? O si la ruta contiene un índice de matriz en un elemento que no es una matriz (por ejemplo. sub[1]
)?
Todos los errores específicos de subdoc tienen su correspondiente ResponseStatus
en el SDK de Java y una subclase dedicada de SubDocumentException
. Estas excepciones son coherentes en los SDK que ofrecen una API para subdocumento:
La lista completa en el SDK de Java es:
- PathNotFoundException
- PathExistsException
- NumberTooBigException
- PathTooDeepException
- PathMismatchException
- ValueTooDeepException
- DeltaTooBigException
- CannotInsertValueException
- PathInvalidException
- Excepción DocumentNotJsonException*
- DocumentTooDeepException* (Excepción de documento demasiado profundo)
- MultiMutationException*
Los 3 últimos son a nivel de documento (lo que significa que se aplican a todo el documento, sin tener en cuenta las rutas individuales), por lo que siempre serán lanzados directamente por los constructores'. ejecutar()
métodos.
Los otros también pueden lanzarse al llamar a DocumentoFragmento
's contenido
en los casos en que haya especificado varias operaciones de búsqueda.
Como se ha dicho en la sección anterior, las operaciones de mutación múltiples en las que al menos una falla desencadenan una MultiMutationException
que se lanza. Esta excepción tiene un firstFailureIndex()
y firstFailureStatus()
para obtener información sobre qué especificación provocó el fallo de toda la operación.
Conclusión
Esperamos que esta nueva función te resulte muy útil y que te encante. Así que coge el Couchbase 4.5 BetaJuega con la API y no dudes en enviarnos tus comentarios. comentarios!
Mientras tanto, Feliz codificación! – el equipo del SDK de Java
Gracias por el post. Sin embargo, tengo una pregunta sobre el manejo de matrices: ¿Existe la posibilidad de abordar los elementos de la matriz, además de su posición en la matriz, por lo que las identificaciones en los subdocumentos no se puede utilizar para acceder a los elementos?
Estoy utilizando este código
DocumentFragment result = couchbaseBucket.async().lookupIn(docId).get(subDocId).execute().
toBlocking().singleOrDefault(null);
No sé por qué, pero result.rawContent(subDocId) devuelve null, mientras que result.content(subDocId) devuelve el valor correcto.
¿No podría indicar qué es lo que está causando este problema?