Los lenguajes de consulta declarativos han supuesto un cambio importante en el mundo de los motores de bases de datos.
En Lenguaje de consulta SQL (antes N1QL) es principalmente un lenguaje de consulta declarativo. Tú le dices a la consulta lo que quieres obtener y N1QL se encarga del resto de los detalles.
Sin embargo, la capacidad de instruir programáticamente su consulta a la base de datos es útil en varias situaciones. Después de todo, usted conoce su propia lógica de negocio - N1QL no.
Entre en funciones definidas por el usuario (UDF). Las UDF le ofrecen un mayor control sobre una consulta concreta y le permiten indicar al lenguaje de consulta cómo se realizan determinadas tareas. La versión 7.0 de Couchbase Server incluye funciones definidas por el usuario para el lenguaje de consulta N1QL.
N1QL es bastante agnóstico en cuanto a lenguajes de programación subyacentes. En lugar de especificar su propio lenguaje procedimental, N1QL emplea un Gestor lingüísticolo que significa que ya está diseñado para admitir multitud de idiomas invitados.
Por ahora, las lenguas admitidas son en líneaun lenguaje interno que permite codificar cualquier expresión N1QL válida (incluidas las subconsultas) y JavaScript. Comencemos comparando estos dos lenguajes soportados para las UDFs de N1QL.
Ejemplo de UDF N1QL: Uso básico
Para añadir su lógica de negocio, tiene que crear funciones, como la función en línea ejemplo siguiente:
|
1 |
CREAR FUNCIÓN añada(arg1, arg2) { arg1 + arg2 } |
A continuación, puede utilizar su lógica de negocio libremente dondequiera que se permita una expresión, o directamente a través de la función EJECUTAR FUNCIÓN declaración:
|
1 2 |
seleccione añada(1, 2) EJECUTAR FUNCIÓN añada(1, 2) |
Y cuando ya no te sirva, lo dejas:
|
1 |
DROP FUNCIÓN añada |
N1QL UDF Ejemplo: JavaScript
Crear y soltar funciones JavaScript definidas por el usuario es un poco más enrevesado, por razones que se harán evidentes en la siguiente sección.
Las UDF de JavaScript son técnicamente externo funciones. Esto se debe a que están escritas en un lenguaje diferente y se ejecutan en un proceso distinto al del Servicio de Consulta N1QL.
El primer paso es crear el código JavaScript:
|
1 |
rizo -v -X POST http://localhost:8093/evaluator/v1/libraries/math -u Administrador:contraseña -d 'function add(a, b) { let data = a + b; return data; }' |
A continuación, cree la función N1QL:
|
1 |
CREAR FUNCIÓN javascriptAñadir(a, b) IDIOMA javascript AS "añadir" EN "matemáticas" |
Una vez creada, puede crear y soltar UDFs JavaScript exactamente igual que las inline. Cuando ya no necesites la biblioteca, puedes eliminarla mediante este comando:
|
1 |
rizo -v -X BORRAR http://localhost:8093/evaluator/v1/libraries/math -u Administrador:contraseña |
Nuevo en Couchbase 7.0: Funciones definidas por el usuario para ámbitos y colecciones
Una de las principales características de la versión 7.0 de Couchbase Server es la introducción de Ámbitos y colecciones para ayudarle a organizar su JSON documentos. Las colecciones son unidades de almacenamiento para un grupo de documentos similares, y los ámbitos son unidades de almacenamiento más grandes que contienen varias colecciones, lo que permite particionar las aplicaciones.
Con ámbitos y colecciones,
Servidor Couchbase - una base de datos documental- ofrece ahora todos los puntos fuertes del modelo relacional -esquema, tablas, columnas- sin ninguno de sus puntos débiles.
Rápido paso lateral: Para permitir la compatibilidad con versiones anteriores, el Servicio de consulta dispone ahora de una función contexto_de_consulta Parámetro de la API REST que indica qué Bucket y Scope deben utilizarse para resolver nombres relativos de espacios de claves, por ejemplo:
|
1 2 |
cbq> \configure -consulta_contexto "default:muestra-viaje.scope1"; cbq> seleccionar * de líneas aéreas; |
En la declaración anterior, el SELECCIONE La declaración resuelve líneas aéreas a la colección denominada default:muestra-viaje.ambito1.aerolineas.
Bien, ahora volvamos a las funciones definidas por el usuario:
La lógica que subyace a los ámbitos y las colecciones con UDF es que se pueden tener entornos de desarrollo, preproducción y producción de la misma aplicación en tres ámbitos diferentes, en los que se despliegan tres copias de la misma aplicación con la misma lógica. A la inversa, podría tener Scopes que contengan diferentes despliegues de la misma aplicación donde se requiera una lógica de negocio diferente (por ejemplo, tiendas online donde se apliquen diferentes estructuras de descuento o entrega, o una aplicación de contabilidad, donde se apliquen diferentes reglas de impuestos).
En el primer caso, basta con tener una definición global para las funciones individuales porque la lógica empresarial es la misma. Pero para el segundo caso, las UDF deben dividirse en ámbitos. Cada ámbito puede tener su propia instancia de la misma UDF, cada una con una lógica diferente.
También vale la pena señalar que Couchbase Server 7.0 no te obliga a cambiar a Ámbitos y Colecciones, por lo que era importante que las UDFs no estuvieran atadas a la característica de Colecciones.
Nota sobre las UDF globales
Las UDF globales no dependen de ámbitos, lo que significa que son compatibles con las UDF introducidas en la versión 6.5.
Si no utiliza Colecciones, no tiene que hacer nada especial para utilizarlas.
Las UDF globales se referencian utilizando un nombre completo de dos partes, como se indica a continuación:
|
1 2 |
CREAR FUNCIÓN por defecto:cubo1.ámbito1.func1() { 0 }; EJECUTAR FUNCIÓN por defecto:cubo1.ámbito1.func1(); |
Importancia del parámetro query_context
"Vale, ahora estoy confuso", te oigo decir. "¿Qué funciones estabas usando entonces en los ejemplos anteriores? Los nombres no estaban completamente cualificados".
La respuesta está en la contexto_de_consulta Parametrización de la API REST: si el contexto_de_consulta no está definido, el analizador N1QL resuelve el nombre como global funciones. Si el contexto_de_consulta el analizador utiliza el parámetro contexto_de_consulta para resolver los nombres en las funciones pertinentes del Ámbito, al igual que se hacía con los nombres no cualificados de los espacios de claves.
Puede desplegar su aplicación sin problemas contra ámbitos o contra cubos simplemente cambiando la configuración de la directiva contexto_de_consulta Parámetro de la API REST.
Resolución de objetos dentro de funciones
Cuando las funciones definidas por el usuario hacen referencia a objetos totalmente cualificados, no hay ambigüedad sobre a qué objeto se refieren:
|
1 |
CREAR FUNCIÓN func { (SELECCIONAR * DESDE por defecto:cubo1.ámbito1.colección1) }; |
La siguiente pregunta es: ¿cómo resuelven las funciones los objetos a los que hacen referencia? Por ejemplo:
|
1 2 |
CREAR FUNCIÓN func1() { func2() }; CREAR FUNCIÓN por defecto:func3 { (SELECCIONAR * DESDE espacio1) }; |
La clave aquí es el principio de menor sorpresa: durante la ejecución, las funciones definidas por el usuario cambian el contexto de consulta a la ruta bajo la cual fueron creadas y siempre hacen referencia a objetos dentro de esa ruta.
No importa si las has llamado con una ruta relativa o completa, una función llamada con los mismos parámetros siempre devuelve los mismos resultados, que se toman de los mismos objetos.
En el primer ejemplo anterior, func2() se resolvería en una función global o Scope dependiendo del contexto_de_consulta en el momento de la creación.
Para func3(), espacio1 sería el Cubo espacio1.
Combinación de funciones globales y de ámbito
No hay nada que le impida utilizar UDF globales junto con la función Colecciones: sólo tiene que utilizar los nombres completos.
Del mismo modo, si desea utilizar una función Scope creada en un Scope diferente, basta con referenciarla directamente.
Las funciones de JavaScript, de nuevo
He aquí por qué las funciones JavaScript deben dividirse en dos partes, es decir, un cuerpo y una definición de función independiente:
-
- Cuando se crea el cuerpo independientemente de la definición de la función, se puede reutilizar el mismo cuerpo en varios lugares. La misma función en varios lugares puede hacer referencia al mismo cuerpo, y cuando el cuerpo se edita o se redefine, el cambio se aplica automáticamente a todas las definiciones de función relacionadas.
- Del mismo modo, este patrón también es importante cuando se trata de soltar o eliminar funciones. Cuando necesitas eliminar una instancia de una función, el cuerpo necesita permanecer en su lugar si es usado por otras instancias.
Trucos y consejos para usar funciones definidas por el usuario N1QL en Couchbase 7.0
Cómo nombrar las UDF
Los nombres UDF son identificadores y no pueden coincidir con nombres de funciones predefinidas. Si los nombres do tiene prioridad la función predefinida, lo que significa que no se utiliza su UDF.
Si desea utilizar un nombre de función predefinido (por algún motivo), debe calificarlo completamente cuando haga referencia a él. Por ejemplo:
|
1 2 |
CREAR FUNCIÓN longitud(arg) { 0 }; SELECCIONE por defecto:longitud(tipo) DESDE `viaje-muestra`; |
Parámetros UDF
Las funciones definidas por el usuario de N1QL admiten tres tipos de listas de parámetros:
- Parámetros vacíos
1CREAR FUNCIÓN func1() { 0 }
La función no admite parámetros. Si se pasa algún parámetro, se produce un error. - Parámetros variádicos
1CREAR FUNCIÓN func1(...) { longitud_array(args) }
Tres puntos indican una función variádica. Puede tomar cualquier número de parámetros de cualquier tipo. Los parámetros están contenidos en una matriz de nombresargs. - Parámetros con nombre
1CREAR FUNCIÓN func1(arg1) { arg1 }
Los parámetros no están tipados, pero se aplica el número de argumentos pasados.
Algunos consejos sobre sobrecarga y manipulación de tipos
Los parámetros no tipados y las funciones variádicas son lo más cercano que Couchbase llegará a la sobrecarga de funciones: donde la misma función se define varias veces con una lista de parámetros diferente - o diferentes tipos de parámetros - con el fin de ser capaz de operar de manera diferente dependiendo de la entrada.
Yo recomendaría esta estrategia N1QL en su lugar: Tener una única función que compruebe los argumentos recibidos y actúe en consecuencia.
A continuación se ofrece un ejemplo de función variádica:
|
1 |
CREAR FUNCIÓN variadic(...) { CASO CUANDO longitud_array(args) != 1 ENTONCES "argumentos erróneos: " || to_string(longitud_array(args)) CUANDO tipo(args[0]) = "cadena" ENTONCES args[0] ELSE "tipo equivocado" || tipo(args[0]) || ": " || to_string(args[0]) FIN } |
Y una función no variable:
|
1 |
CREAR FUNCIÓN dosargs(arg1, arg2) { CASO CUANDO tipo(arg1) != "cadena" ENTONCES "arg1 incorrecto" || tipo(arg1) || ": " || to_string(arg1) CUANDO tipo(arg2) != "cadena" ENTONCES "arg2 incorrecto" || tipo(arg2) || ": " || to_string(arg2) ELSE arg1 || arg2 FIN } |
Nombres de parámetros frente a campos de documento
Considera la siguiente función:
|
1 |
CREAR FUNCIÓN docsOfType(tipo) { (SELECCIONAR * de `viaje-muestra` DONDE tipo=tipo) } |
¿Nota cómo la función declara un parámetro que resulta tener el mismo nombre que un campo de documento? Está claro que esa función no cumple su objetivo, ya que no hay forma de distinguir entre los dos.
A continuación se explica cómo utilizar los nombres de los parámetros para evitar este problema: los nombres de los parámetros sustituyen a los campos de los documentos (de cualquier forma, la consulta anterior devuelve todos los documentos de la carpeta viaje-muestra conjunto de datos).
Para acceder a los campos del documento, tiene dos opciones. O bien hacer referencia al campo del documento con su nombre completo, como puede ver a continuación:
|
1 |
CREAR FUNCIÓN docsOfType(tipo) { (SELECCIONAR * de `viaje-muestra` DONDE viaje-muestra.tipo=tipo) } |
O bien, elimine la ambigüedad cambiando el nombre del parámetro, como se indica a continuación:
|
1 |
CREAR FUNCIÓN docsOfType(vTipo) { (SELECCIONAR * de `viaje-muestra` DONDE tipo=vTipo) } |
Valores de retorno con UDFs
Las funciones definidas por el usuario sólo devuelven un valor de cualquier tipo. Si necesita devolver más de un valor, devuelva una matriz o un objeto.
Pero ¡cuidado con los tipos de devolución! Recuerde que SELECCIONE ejecutadas dentro de UDFs devuelven arrays, por lo que incluso si su SELECCIONE devuelve sólo un documento, es un array de un elemento. En su lugar, devuelve el primer elemento de la matriz.
Por ejemplo, esta función de abajo no hace lo que quieres:
|
1 |
CREAR FUNCIÓN tsample() { (SELECCIONAR * DESDE `viaje-muestra` LÍMITE 1) } |
Pero éste sí:
|
1 |
CREAR FUNCIÓN tsample1st() { (SELECCIONAR * DESDE `viaje-muestra` LÍMITE 1)[0] } |
Utilización de los valores de retorno UDF como datos para la consulta
En el lado opuesto del espectro, dado que las UDFs pueden devolver arrays, pueden utilizarse provechosamente para generar dinámicamente datos para ser consultados, de forma muy similar a como se haría en motores Relacionales con conceptos como Funciones de Tabla o Tablas Derivadas de Colecciones, como en el siguiente ejemplo
|
1 |
SELECCIONAR * DESDE tsample() muestra |
Aunque la función de ejemplo selecciona datos de un cubo, se puede utilizar cualquier forma válida de construir una matriz para generar los datos.
Cuando necesite actualizar una función existente
A veces es necesario redefinir una función. El sitio O SUSTITUIR cláusula del CREAR FUNCIÓN le permite hacerlo en un solo paso:
|
1 |
CREAR O SUSTITUIR FUNCIÓN tsample1st() { (SELECCIONAR * DESDE `viaje-muestra` LÍMITE 1)[0] } |
Privilegios de acceso de los usuarios
Cualquier sentencia N1QL ejecutada dentro de una UDF se ejecuta con los mismos privilegios que el usuario que envió la petición. Por lo tanto, el usuario debe tener los privilegios adecuados para acceder a todos los objetos a los que hace referencia la UDF.
Además de todo esto, el usuario también necesita tener privilegios para ejecutar funciones. Existen distintos niveles de privilegios para las funciones internas, externas, globales y de ámbito. Para crear y soltar funciones, el usuario debe tener permiso para gestionar funciones. Por ejemplo:
|
1 2 |
SUBVENCIÓN query_manage_global_functions A usuario1; SUBVENCIÓN query_execute_external_functions EN por defecto:prueba.ámbito1 A usuario1; |
Conclusión
Espero que hayas encontrado este post útil para entender cuándo y cómo aprovechar las funciones definidas por el usuario N1QL con Couchbase 7.0. Echa un vistazo a la documentación de más información sobre las funciones definidas por el usuarioContexto Objetos N1QL y contexto_de_consulta y funciones de seguridad para las UDF.
Pruebe Couchbase 7
[...] post N1QL Ahora Soporta Funciones Definidas por el Usuario appeared first on The Couchbase [...]