{"id":16846,"date":"2025-02-11T08:52:20","date_gmt":"2025-02-11T16:52:20","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=16846"},"modified":"2025-07-08T09:15:37","modified_gmt":"2025-07-08T16:15:37","slug":"plsql-to-javascript-udf-conversion-tool","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/plsql-to-javascript-udf-conversion-tool\/","title":{"rendered":"Una herramienta para facilitar la transici\u00f3n de Oracle PL\/SQL a Couchbase JavaScript UDF"},"content":{"rendered":"<h2><span style=\"font-weight: 400;\">\u00bfQu\u00e9 es PL\/SQL?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">PL\/SQL es un lenguaje procedimental dise\u00f1ado espec\u00edficamente para incluir sentencias SQL en su sintaxis. Incluye elementos de lenguaje procedimental como condiciones y bucles, y puede manejar excepciones (errores en tiempo de ejecuci\u00f3n).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">PL\/SQL es nativo de las bases de datos Oracle, y bases de datos como IBM DB2, PostgreSQL y MySQL admiten construcciones PL\/SQL a trav\u00e9s de funciones de compatibilidad.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">\u00bfQu\u00e9 es una UDF de JavaScript?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">JavaScript UDF es la alternativa de Couchbase a PL\/SQL.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">JavaScript UDF traslada la flexibilidad de las secuencias de comandos de uso general de JavaScript a las bases de datos, lo que permite realizar operaciones din\u00e1micas y potentes en los sistemas de bases de datos modernos y mejora la flexibilidad en la consulta, el procesamiento y la transformaci\u00f3n de datos.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">La mayor\u00eda de las bases de datos modernas como Couchbase, MongoDB, Snowflake y Google BigQuery soportan JavaScript UDF.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">El problema<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Un problema com\u00fan visto por los usuarios que migran de Oracle a Couchbase es portar sus scripts PL\/SQL. En lugar de soportar PL\/SQL, Couchbase permite a los usuarios construir funciones definidas por el usuario en JavaScript (soportado desde 2021).<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Las UDF de JavaScript permiten una manipulaci\u00f3n f\u00e1cil e intuitiva de los datos de variantes y JSON. Los objetos variantes pasados a una UDF se transforman en tipos y valores nativos de JavaScript.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">La consecuencia involuntaria de esto es que la mayor\u00eda de los RDBMS que han existido en los \u00faltimos diez a\u00f1os han animado encarecidamente a los desarrolladores a acceder a la base de datos utilizando sus extensiones procedimentales de SQL (PL\/pgSQL, PL\/SQL), que soportan construcciones procedimentales, integraci\u00f3n con SQL, gesti\u00f3n de errores, funciones y procedimientos, disparadores y cursores, o como m\u00ednimo, funciones y procedimientos (como Sakila). Para cualquier intento de alejarse de ellos, habr\u00eda que reescribir todos sus scripts.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Reescribir c\u00f3digo es a menudo una tarea tediosa, especialmente cuando se trata de scripts PL\/SQL que han sido escritos en la d\u00e9cada de 2000 y mantenidos desde entonces. Estos scripts pueden ser complejos, a menudo extendi\u00e9ndose a miles de l\u00edneas, lo que puede ser abrumador para el usuario medio de la empresa.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Soluci\u00f3n<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Lo ideal ser\u00eda desarrollar un evaluador PL\/SQL completamente nuevo, pero eso requerir\u00eda una cantidad excesiva de horas de ingenier\u00eda y, para el mismo caso de uso, ya disponemos de un JSEvaluator moderno, estable y r\u00e1pido.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Esto convierte el problema en un caso de uso perfecto para aprovechar los avances actuales en IA y LLM. Y eso es lo que hemos hecho aqu\u00ed. Hemos utilizado modelos de IA generativa para <\/span><b>automatizar la conversi\u00f3n de PL\/SQL a JSUDF<\/b><span style=\"font-weight: 400;\">.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">A partir de junio de 2024, <a href=\"https:\/\/platform.openai.com\/docs\/models\/gpt-4-turbo-and-gpt-4\">los modelos tienen una ventana de contexto limitada<\/a>, w<\/span><span style=\"font-weight: 400;\">hich significa PL\/SQL m\u00e1s largo se golpean con el error:\u00a0\u00a0<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">La longitud m\u00e1xima de contexto de este modelo es de 8192 tokens. Sin embargo, sus mensajes resultaron en  tokens. Por favor, reduce la longitud de los mensajes.<\/pre>\n<p><span style=\"font-weight: 400;\">Tenga en cuenta que esto es para GPT4.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Entonces, \u00bfesperamos a que la IA sea m\u00e1s potente y permita m\u00e1s fichas (como la Ley de Moore, pero para el contexto-longitud-vs-precisi\u00f3n de la IA)?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">No, ah\u00ed es donde <a href=\"https:\/\/www.antlr.org\/\">ANTLR<\/a>, a<\/span><span style=\"font-weight: 400;\"> herramienta generadora de analizadores sint\u00e1cticos. ANTLR es bien conocido por ser utilizado para el desarrollo de compiladores e int\u00e9rpretes. De esta manera podemos dividir el script grande en unidades m\u00e1s peque\u00f1as que se pueden traducir de forma independiente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Entonces, \u00bfestamos construyendo <\/span><b>\u00bftranspilador? <\/b><span style=\"font-weight: 400;\">Bueno, s\u00ed y no.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><strong>Etapas de un transpilador:<\/strong><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">An\u00e1lisis l\u00e9xico (tokenizaci\u00f3n)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">An\u00e1lisis sint\u00e1ctico (Parsing)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">An\u00e1lisis sem\u00e1ntico<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Generaci\u00f3n de representaciones intermedias (RI)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Optimizaci\u00f3n (opcional)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Generaci\u00f3n de c\u00f3digo de destino<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<h4><strong><span style=\"font-size: 19px;\">C\u00f3mo funciona el traductor de IA<\/span><\/strong><\/h4>\n<p><span style=\"font-weight: 400;\">Los pasos 1,2 anteriores se realizan utilizando ANTLR.\u00a0<\/span><span style=\"font-weight: 400;\">Utilizamos la interfaz Listener de ANTLR para <\/span><b>agarrar procedimiento\/funci\u00f3n\/bloque an\u00f3nimo individual<\/b><span style=\"font-weight: 400;\">ya que son bloques de c\u00f3digo independientes. En el caso de que el procedimiento\/funci\u00f3n\/bloque an\u00f3nimo exceda por s\u00ed mismo la ventana de contexto, traducimos a nivel de sentencia (donde el LLM asume la existencia de uso de variables\/llamadas a funciones que no est\u00e1n definidas aqu\u00ed sino en alg\u00fan lugar anterior).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Posteriormente, los pasos 3, 4, 5 y 6 se dejan en manos del LLM (por ejemplo, GPT), es decir, traducir cada bloque PL\/SQL en una funci\u00f3n JavaScript lo mejor posible que tambi\u00e9n preserve la sem\u00e1ntica operativa del bloque y sea sint\u00e1cticamente precisa.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Los resultados son sorprendentemente positivos; la traducci\u00f3n es 80-85% exacta.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Otra ventaja de la soluci\u00f3n es que reducimos las alucinaciones al centrarnos en una tarea cada vez, lo que se traduce en traducciones m\u00e1s precisas.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Para visualizar:<\/span><\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-16847 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1024x904.png\" alt=\"automate the conversion of PL\/SQL to JSUDF\" width=\"900\" height=\"795\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1024x904.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-300x265.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-768x678.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1536x1356.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1-1320x1166.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/image1-1.png 1787w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<h2><span style=\"font-weight: 400;\">C\u00f3mo utilizar la herramienta<\/span><\/h2>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\">Descargue el ejecutable desde <a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/releases\/tag\/v1.0.0\">Couchbase Labs GitHub<\/a> y acceder al <a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/blob\/master\/README.md\">L\u00c9AME<\/a>.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">El ejecutable espera los siguientes argumentos de l\u00ednea de comandos:<\/span><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"font-weight: 400;\">-u: capella signin email<br \/>\n<\/span><span style=\"font-weight: 400;\">-p: contrase\u00f1a de acceso a capella<br \/>\n<\/span><span style=\"font-weight: 400;\">-cpaddr: capella-url para chat-completions api<br \/>\n<\/span><span style=\"font-weight: 400;\">-orgid: id de la organizaci\u00f3n en la ruta de la api de chat-completions<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbhost: nodo-ip: nodo cbcluster<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbuser: cluster-user-name: usuario cbcluster, a\u00f1adido a trav\u00e9s de database-acess<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbpassword: cluster-password: contrase\u00f1a del cbcluster, a\u00f1adida a trav\u00e9s del acceso a la base de datos.<br \/>\n<\/span><span style=\"font-weight: 400;\">-cbport: query-service tls port: normalmente 18093<br \/>\n<\/span><span style=\"font-weight: 400;\">filepath , es decir, la ruta del script PL\/SQL que debe traducirse<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">salida-&gt;\u00a0<\/span><span style=\"font-weight: 400;\">En el directorio de salida, se crear\u00e1 un archivo con el mismo nombre que el archivo <em>plsql<\/em> con el c\u00f3digo traducido de la biblioteca JavaScript.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Un ejemplo:<\/span><\/p>\n<p><code>cat ejemplo1.sql<\/code><\/p>\n<pre class=\"nums:false lang:default decode:true\">DECLARE\r\n   x N\u00daMERO := 0;\r\n   contador N\u00daMERO := 0;\r\nBEGIN\r\n   FOR i IN 1..4 LOOP\r\n      x := x + 1000;\r\n      contador := contador + 1;\r\n      INSERT INTO temp VALUES (x, contador, 'en bucle OUTER');\r\n       --comienza un bloque interno\r\n      DECLARE\r\n         x NUMBER := 0; -- esta es una versi\u00f3n local de x\r\n      BEGIN\r\n         FOR i IN 1..4 LOOP\r\n            x := x + 1; -- esto incrementa la x local\r\n            contador := contador + 1;\r\n            INSERT INTO temp VALUES (x, contador, 'bucle interno');\r\n         END LOOP;\r\n      FIN;\r\n   END LOOP;\r\n   COMMIT;\r\nEND;<\/pre>\n<p><span style=\"font-weight: 400;\">Para explicar brevemente el gui\u00f3n anterior:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\">Un bucle externo se ejecuta durante 4 iteraciones, incrementando x en 1000, contador en 1<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<li><span style=\"font-weight: 400;\">El bucle interno se ejecuta durante 4 iteraciones, incrementando x en 1, contador en 1<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Ejecutar el traductor en el PL\/SQL de prueba:<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">.\/plsql-to-jsudf -u \"capella-signin-mailid\" -p \"capella-signin-password\" -cpaddr https:\/\/api.cloud.couchbase.com -orgid \"capella-organisation-id\" -cbhost \"hostname of data node\" -cbuser \"cbcluster username\" -cbpassword \"cbcluster password\" -cbport 18093 .\/translator\/test\/plsql\/example1.sql\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Salida JSUDF:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><code>cat salida\/ejemplo1.js<\/code><\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:js decode:true\">function nestedloop(){\r\n    var x = 0\r\n    var contador = 0;\r\n    var querybegin = EMPEZAR TRABAJO;\r\n    querybegin.close();\r\n    for (var i = 1; i &lt;= 4; i++){\r\n        x = x + 1000\r\n        contador = contador + 1;\r\n        var params = [x, contador];\r\n        var query = N1QL(&#039;INSERT INTO test.testscope.temp VALUES (uuid(),{&quot;val1&quot;:$1, &quot;val2&quot;:$2, &quot;val3&quot;: &quot;in OUTER loop&quot;})&#039;,params);\r\n        query.close();\r\n        var x_inner = 0;\r\n        for (var j = 1; j &lt;= 4; j++){\r\n            x_inner = x_inner + 1;\r\n            contador = contador + 1;\r\n            var params_inner = [x_inner, contador];\r\n            var query_inner = N1QL(&#039;INSERT INTO test.testscope.temp VALUES (uuid(),{&quot;val1&quot;:$1, &quot;val2&quot;:$2, &quot;val3&quot;: &quot;bucle interno&quot;})&#039;,params_inner);\r\n            query_inner.close();\r\n        }\r\n    }\r\n    var querycommit = COMPROMETER TRABAJO;\r\n    querycommit.close();\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">El script traducido tiene una funci\u00f3n <em>bucle anidado<\/em> (nombre generado por LLM) que hace exactamente lo que especifica el bloque PL\/SQL an\u00f3nimo original.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Nota al margen: para funciones\/procedimientos con nombre, las funciones JS traducidas tendr\u00e1n el mismo nombre.  Para bloques an\u00f3nimos LLM utiliza un nombre que se le ocurre.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Problemas conocidos<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">PL\/SQL y JS son 2 lenguajes diferentes, y la forma en que est\u00e1n soportados en Oracle y Couchbase no permite un mapeo directo entre los 2.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A continuaci\u00f3n se indican algunas de las limitaciones que hemos descubierto y las soluciones que hemos aplicado:<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">1. console.log no es compatible<\/span><\/h4>\n<p><span style=\"font-weight: 400;\"><em>DBMS_OUTPUT.PUT<\/em> y otros 2 similares, <em>DBMS_OUTPUT.PUT_LINE<\/em> y <em>DBMS_OUTPUT.NEW_LINE<\/em> se traducen a <em>console.log()<\/em>pero console.log es una API de navegador y no est\u00e1 soportada por la implementaci\u00f3n de evaluaci\u00f3n JavaScript de Couchbase. Esta ha sido una pregunta frecuente, considerando que la funci\u00f3n de eventos de Couchbase s\u00ed soporta <em>imprimir()<\/em> pero no en las UDF de JavaScript.<\/span><\/p>\n<p><b>Soluci\u00f3n:<\/b><\/p>\n<p><span style=\"font-weight: 400;\">Se espera que los usuarios creen un <em>registro<\/em>\u00a0cubo.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Logs are inserted as part of a document INSERT into `default`.`default` collection. The document would look something like:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">{\r\n   \"udf\": \"func-name\",\r\n   \"log\": \"argumento para console.log\", \/\/ la l\u00ednea de registro real\r\n   \"time\": \"cadena de tiempo ISO actual\"\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">El usuario puede consultar sus registros seleccionando en registro:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">SELECT * FROM logging WHERE udf= \"\"func-name\"\";\r\nSELECT * FROM logging WHERE time BETWEEN \"\"date1\"\" AND \"\"date2\"\";\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Un ejemplo:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">El PL\/SQL original<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">BEGIN\r\n   DBMS.OUTPUT.PUT(\"\u00a1Hola mundo!\");\r\nEND;<\/pre>\n<p><span style=\"font-weight: 400;\">Traducido a JavaScript UDF<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function holaMundo() {\r\n   \/\/ soluci\u00f3n para console.log(\"\u00a1Hola mundo!\");\r\n   var currentDate = new Date();\r\n   var utcISOString = fechaactual.toISOString();\r\n   var params = [utcISOString,'anonymousblock1', \"\u00a1Hola mundo!\"];\r\n   var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n   logquery.close();\r\n}\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Esto ya est\u00e1 implementado en la herramienta.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Para ver el registro:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">EJECUTAR FUNCI\u00d3N helloWorld();\r\n\"resultados\": [\r\n   null\r\n]\r\n\r\nCREAR \u00cdNDICE PRIMARIO SOBRE registro;\r\n\"results\": [\r\n]\r\n\r\nSELECT * FROM registro;\r\n\"resultados\": [\r\n   {\"logging\":{\"log\":\"Hello world!\",\"time\":\"2024-06-26T09:20:56.000Z\",\"udf\":\"anonymousblock1\"}}\r\n]<\/pre>\n<h4><span style=\"font-weight: 400;\">2. Llamadas a funciones entre paquetes<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">Los procedimientos\/funciones enumerados en la especificaci\u00f3n del paquete son globales y pueden utilizarse desde otros paquetes mediante <em>\"nombre_paquete\". \"procedimiento\/funcion_publica\"<\/em>. Pero no ocurre lo mismo con una librer\u00eda JavaScript en Couchbase, ya que las construcciones de importaci\u00f3n-exportaci\u00f3n no est\u00e1n soportadas por la implementaci\u00f3n de evaluaci\u00f3n JavaScript de Couchbase.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><b>Soluci\u00f3n:<\/b><b><\/b><\/p>\n<p><span style=\"font-weight: 400;\">En caso de llamada a una funci\u00f3n interbibliotecaria<em> \"lib_name\". \"function\"()<\/em>se espera que el usuario disponga de la biblioteca de referencia <em>\"lib_name\"<\/em> ya creado; puede comprobarlo mediante <em>GET \/evaluator\/v1\/bibliotecas<\/em><\/span><\/p>\n<p><span style=\"font-size: 19px;\">La funci\u00f3n de referencia <em>\"funci\u00f3n\"<\/em> tambi\u00e9n se espera que sea creada como una UDF global; esto puede ser verificado v\u00eda GET \/admin\/functions_cache o select system:functions keyspace. De esta forma podemos acceder a la funci\u00f3n a trav\u00e9s de SQL++\/N1QL.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Un ejemplo:<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><em>utilidades_matem\u00e1ticas<\/em> Paquete<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREATE OR REPLACE PACKAGE math_utils AS\r\n   -- Funci\u00f3n p\u00fablica para sumar dos n\u00fameros\r\n   FUNCTION sumar_numeros(p_num1 NUMERO, p_num2 NUMERO) RETURN NUMERO;\r\nEND math_utils;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY math_utils AS\r\n   FUNCTION add_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER IS\r\n      BEGIN\r\n         RETURN p_num1 + p_num2;\r\n      END add_numbers;\r\nEND math_utils;\r\n\/\r\n<\/pre>\n<p><span style=\"font-weight: 400;\"><em>mostrar_suma<\/em> Paquete<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREAR O SUSTITUIR PAQUETE show_sum COMO\r\n   -- Procedimiento p\u00fablico para mostrar la suma de dos n\u00fameros\r\n   PROCEDURE mostrar_suma(p_num1 NUMERO, p_num2 NUMERO);\r\nEND show_sum;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY show_sum AS\r\n   PROCEDIMIENTO mostrar_suma(p_num1 N\u00daMERO, p_num2 N\u00daMERO) IS\r\n      v_suma N\u00daMERO;\r\n   BEGIN\r\n      -- Llamada a la funci\u00f3n add_numbers del paquete math_utils\r\n      v_sum := math_utils.add_numbers(p_num1, p_num2);\r\n\r\n      -- Visualizaci\u00f3n de la suma mediante DBMS_OUTPUT.PUT_LINE\r\n      DBMS_OUTPUT.PUT_LINE('La suma de ' || p_num1 || ' y ' || p_num2 || ' es ' || v_sum);\r\n   END mostrar_suma;\r\nEND mostrar_suma;\r\n\/<\/pre>\n<p><span style=\"font-weight: 400;\">C\u00f3digo traducido:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function mostrar_suma(a, b) {\r\n var suma_resultado;\r\n\r\n\/\/ Soluci\u00f3n para la llamada a la funci\u00f3n de biblioteca cruzada math_utils.add_numbers(a, b)\r\nvar crossfunc = N1QL(\"EXECUTE FUNCTION add_numbers($1,$2)\",[a, b])\r\nvar crossfuncres = []\r\nfor(const doc of crossfunc) {\r\n   crossfuncres.push(doc);\r\n}\r\n\r\n\/\/ sustituci\u00f3n real de math_utils.add_numbers(a, b)\r\nsuma_resultado = crossfuncres[0];\r\n\r\n\/\/ soluci\u00f3n para console.log('La suma de ' + a + ' y ' + b + ' es: ' + sum_result);\r\nvar currentDate = new Date();\r\nvar utcISOString = fechaactual.toISOString();\r\nvar params = [utcISOString,'SHOW_SUM','La suma de ' + a + ' y ' + b + ' es: ' + sum_result];\r\nvar logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\nlogquery.close();\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">El programa lo gestiona autom\u00e1ticamente, con la advertencia de que debe ser verificado por una persona.<\/span><\/p>\n<h4><span style=\"font-weight: 400;\">3. Variables globales<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">PL\/SQL soporta variables globales a nivel de paquete y a nivel de sesi\u00f3n, pero las variables globales no est\u00e1n soportadas en JSUDF deliberadamente por dise\u00f1o ya que esto causa preocupaci\u00f3n por fugas de memoria.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">La soluci\u00f3n sugerida requiere un ajuste manual de la traducci\u00f3n generada. Por ejemplo:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">  CREATE OR REPLACE PACKAGE global_vars_pkg AS\r\n     -- Declaraciones de variables globales\r\n     g_counter N\u00daMERO := 0;\r\n     g_message VARCHAR2(100) := 'Mensaje Inicial';\r\n\r\n     -- Declaraciones de procedimientos p\u00fablicos\r\n     PROCEDIMIENTO increment_counter;\r\n     PROCEDURE set_message(p_message VARCHAR2);\r\n     PROCEDIMIENTO show_globals;\r\n   END global_vars_pkg;\r\n   \/\r\n\r\n   CREATE OR REPLACE PACKAGE BODY global_vars_pkg AS\r\n\r\n     -- Procedimiento para incrementar el contador\r\n     PROCEDIMIENTO increment_contador IS\r\n     BEGIN\r\n       g_contador := g_contador + 1;\r\n     END increment_counter;\r\n\r\n     -- Procedimiento para establecer el mensaje global\r\n     PROCEDURE set_message(p_message VARCHAR2) IS\r\n     BEGIN\r\n       g_message := p_message;\r\n     END set_message;\r\n\r\n     -- Procedimiento para mostrar los valores actuales de las variables globales\r\n     PROCEDIMIENTO show_globals IS\r\n     BEGIN\r\n       DBMS_OUTPUT.PUT_LINE('g_contador = ' | g_contador);\r\n       DBMS_OUTPUT.PUT_LINE('g_message = ' | g_message);\r\n     END mostrar_globales;\r\n\r\n   END global_vars_pkg;\r\n   \/<\/pre>\n<p><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Cualquier funci\u00f3n que modifique una variable global debe aceptarla como argumento y devolverla a quien la llama.<\/span><\/p>\n<p><span style=\"font-size: 19px;\"><em>contador_de_incrementos<\/em>:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">function increment_contador(contador){\r\n   contador = contador + 1;\r\n   return contador\r\n}\r\n<\/pre>\n<p><span style=\"font-weight: 400;\">Cualquier funci\u00f3n que s\u00f3lo lea un global puede aceptarlo como argumento.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><em>mostrar_globales<\/em>:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">function mostrar_globales(contador, mensaje){\r\n   \/\/ soluci\u00f3n para console.log(counter);\r\n   var currentDate = new Date();\r\n   var utcISOString = fechaactual.toISOString();\r\n   var params = [utcISOString,'SHOW_GLOBALS',couter];\r\n   var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n   logquery.close();\r\n\r\n   \/\/ soluci\u00f3n para console.log(message);\r\n   var currentDate = new Date();\r\n   var utcISOString = currentDate.toISOString();\r\n   var params = [utcISOString,'SHOW_GLOBALS',mensaje];\r\n   var logquery = N1QL('INSERT INTO logging VALUES(UUID(),{\"udf\":$2, \"log\":$3, \"time\":$1}, {\"expiration\": 5*24*60*60 })', params);\r\n   logquery.close();\r\n}\r\n<\/pre>\n<h3><span style=\"font-weight: 400;\">Paquete a la biblioteca<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Esta secci\u00f3n muestra una conversi\u00f3n de paquete a biblioteca de extremo a extremo utilizando la herramienta.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Paquete PL\/SQL de ejemplo:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">CREATE OR REPLACE PAQUETE emp_pkg IS\r\n  PROCEDURE insertar_empleado(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name IN empleados.first_name%TYPE,\r\n    p_last_name IN empleados.apellido%TYPE,\r\n    p_salario IN empleados.salario%TYPE\r\n  );\r\n\r\n  PROCEDURE actualizar_empleado(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name IN empleados.nombre%TYPE,\r\n    p_last_name IN empleados.apellido%TYPE,\r\n    p_salario IN empleados.salario%TYPE\r\n  );\r\n\r\n  PROCEDURE eliminar_empleado(\r\n    p_emp_id IN empleados.emp_id%TYPE\r\n  );\r\n\r\n  PROCEDURE get_employee(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name OUT empleados.nombre%TYPE,\r\n    p_last_name OUT empleados.apellido%TYPE,\r\n    p_salario OUT empleados.salario%TYPE\r\n  );\r\n\r\nEND emp_pkg;\r\n\/\r\n\r\nCREATE OR REPLACE PACKAGE BODY emp_pkg IS\r\n  PROCEDURE insertar_empleado(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name IN empleados.first_name%TYPE,\r\n    p_last_name IN empleados.apellido%TYPE,\r\n    p_salario IN empleados.salario%TYPE\r\n  ) IS\r\n\r\n  BEGIN\r\n    INSERT INTO empleados (emp_id, nombre_apellido, salario)\r\n    VALUES (p_emp_id, p_first_name, p_last_name, p_salary);\r\n  END insertar_empleado;\r\n\r\n  PROCEDIMIENTO update_employee(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name IN empleados.first_name%TYPE,\r\n    p_last_name IN empleados.apellido%TYPE,\r\n    p_salario IN empleados.salario%TYPE\r\n  ) IS\r\n\r\n  BEGIN\r\n    UPDATE empleados\r\n    SET nombre = p_nombre,\r\n        apellido = p_apellido_nombre,\r\n        salario = p_salario\r\n    WHERE emp_id = p_emp_id;\r\n  END actualizar_empleado;\r\n\r\n  PROCEDURE delete_employee(\r\n    p_emp_id IN empleados.emp_id%TYPE\r\n  ) IS\r\n\r\n  BEGIN\r\n    DELETE FROM empleados\r\n    WHERE emp_id = p_emp_id;\r\n  END borrar_empleado;\r\n\r\n  PROCEDURE get_employee(\r\n    p_emp_id IN empleados.emp_id%TYPE,\r\n    p_first_name OUT empleados.first_name%TYPE,\r\n    p_last_name OUT empleados.apellido%TYPE,\r\n    p_salario OUT empleados.salario%TYPE\r\n  ) IS\r\n\r\n  BEGIN\r\n    SELECT nombre, apellido, salario\r\n    INTO p_nombre, p_apellido, p_salario\r\n    FROM empleados\r\n    WHERE emp_id = p_emp_id;\r\n  END get_employee;\r\nEND emp_pkg;\r\n\/<\/pre>\n<p><span style=\"font-weight: 400;\">Traducci\u00f3n:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">.\/plsql-to-jsudf -u \"capella-signin-mailid\" -p \"capella-signin-password\" -cpaddr https:\/\/api.cloud.couchbase.com -orgid \"capella-organisation-id\" -cbhost \"hostname of data node\" -cbuser \"cbcluster username\" -cbpassword \"cbcluster password\" -cbport 18093 translator\/test\/plsql\/blog_test.sql<\/pre>\n<p><span style=\"font-weight: 400;\">C\u00f3digo:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function insert_employee(p_emp_id, p_first_name, p_last_name, p_salary){\r\n    var params = [p_emp_id, p_first_name, p_last_name, p_salary];\r\n    var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\"emp_id\":$1, \"first_name\":$2, \"last_name\":$3, \"salary\":$4})', params);\r\n    query.close();\r\n}\r\n\r\nfunction update_employee(p_emp_id, p_first_name, p_last_name, p_salary){\r\n    var params = [p_nombre, p_apellido, p_salario, p_emp_id];\r\n    var query = N1QL('UPDATE test.testscope.employees SET first_name = $1, last_name = $2, salary = $3 WHERE emp_id = $4', params);\r\n    query.close();\r\n}\r\n\r\nfunction eliminar_empleado(p_emp_id){\r\n    var querybegin=INICIAR TRABAJO;\r\n    var params = [p_emp_id];\r\n    var query= N1QL('DELETE FROM test.testscope.employees WHERE emp_id = $1',params);\r\n    query.close();\r\n    var querycommit=COMITAR TRABAJO;\r\n    querycommit.close();\r\n}\r\n\r\nfunction get_employee(p_emp_id){\r\n    var query = N1QL('SELECT first_name, last_name, salary FROM test.testscope.employees WHERE emp_id = $1', [p_emp_id]);\r\n    var rs = [];\r\n    for (const row of query) {\r\n        rs.push(fila);\r\n    }\r\n    query.close();\r\n    var p_first_name = rs[0]['first_name'];\r\n    var p_last_name = rs[0]['last_name'];\r\n    var p_salario = rs[0]['salario'];\r\n    return {nombre: p_nombre, apellido: p_apellido, salario: p_salario};\r\n}\r\n\r\n<\/pre>\n<h4>Insertemos un nuevo documento de empleado<\/h4>\n<p><span style=\"font-weight: 400;\">Crear colecci\u00f3n de empleados:<\/span><\/p>\n<pre class=\"nums:false wrap:true lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:8091\/pools\/default\/buckets\/test\/scopes\/testscope\/collections -d name=empleados<\/pre>\n<p><span style=\"font-weight: 400;\">Inserta un empleado:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION insert_employee(1, \"joe\", \"briggs\", 10000)' -k\r\n{\r\n\"requestID\": \"2c0854c1-d221-42e9-af47-b6aa0801a46c\",\r\n\"firma\": null,\r\n\"results\": [\r\n],\r\n\"errores\": [{\"code\":10109, \"msg\": \"Error al ejecutar la funci\u00f3n 'insert_employee' (blog_test:insert_employee)\", \"reason\":{\"details\":{\"Code\":\" var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\\\"emp_id\\\":$1, \\\"first_name\\\":$2, \\\"last_name\\\":$3, \\\"salary\\\":$4})', params);\",\"Exception\":{\"_level\":\"exception\",\"caller\":\"insert_send:207\",\"code\":5070,\"key\":\"execution.insert_key_type_error\", \"message\": \"Cannot INSERT non-string key 1 of type value.intValue.\"}, \"Location\": \"functions\/blog_test.js:5\", \"Stack\":\" at insert_employee (functions\/blog_test.js:5:17)\"}, \"type\": \"Exceptions from JS code\"}}],\r\n\"status\": \"fatal\",\r\n\"metrics\": {\"elapsedTime\": \"104.172666ms\",\"executionTime\": \"104.040291ms\",\"resultCount\": 0, \"resultSize\": 0, \"serviceLoad\": 2, \"errorCount\": 1}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Esto da error, y <\/span><b>Est\u00e1 bien.<\/b><span style=\"font-weight: 400;\"> podemos arreglarlo manualmente.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Lectura del motivo y <em>excepci\u00f3n: <\/em><\/span><i><span style=\"font-weight: 400;\"><em>No se puede INSERTAR la clave 1 del tipo valor.intValor<\/em>, <\/span><\/i><span style=\"font-weight: 400;\">\u00a1Ah! siempre se espera que la clave sea una cadena, pasando <\/span><b>insert_employee(\"1\", \"joe\", \"briggs\", 10000) <\/b><span style=\"font-weight: 400;\">ser\u00eda suficiente, pero no es intuitivo esperar que <em>empleado_id<\/em> sea una cadena.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Modifiquemos el c\u00f3digo generado:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function insert_employee(p_emp_id, p_nombre, p_apellido, p_salario){\r\n    var params = [p_emp_id.toString(), p_emp_id, p_first_name, p_last_name, p_salary];\r\n    var query = N1QL('INSERT INTO test.testscope.employees VALUES ($1, {\"emp_id\":$2, \"first_name\":$3, \"last_name\":$4, \"salary\":$5})', params);\r\n    query.close();\r\n}\r\n<\/pre>\n<p><b><\/b><span style=\"font-weight: 400;\">Y vuelva a crear la UDF:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=CREATE OR REPLACE FUNCTION insert_employee(p_emp_id, p_first_name, p_last_name, p_salary) LANGUAGE JAVASCRIPT AS \"insert_employee\" AT \"blog_test\"' -k\r\n{\r\n\"requestID\": \"89df65ac-2026-4f42-8839-b1ce7f0ea2be\",\r\n\"signature\": null,\r\n\"results\": [\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"27.730875ms\",\"executionTime\": \"27.620083ms\",\"resultCount\": 0, \"resultSize\": 0, \"serviceLoad\": 2}\r\n}<\/pre>\n<p><b><\/b><b>Intentando insertar de nuevo:<\/b><\/p>\n<pre class=\"nums:false lang:js decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION insert_employee(1, \"joe\", \"briggs\", 10000)' -k\r\n{\r\n\"requestID\": \"41fb76bf-a87f-4472-b8ba-1949789ae74b\",\r\n\"firma\": null,\r\n\"results\": [\r\nnull\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"62.431667ms\",\"executionTime\": \"62.311583ms\",\"resultCount\": 1, \"resultSize\": 4, \"serviceLoad\": 2}\r\n}\r\n<\/pre>\n<h4><span style=\"font-weight: 400;\">Actualizar a un empleado:<\/span><\/h4>\n<p><span style=\"font-weight: 400;\">\u00a1Dispara! Hay un error, el empleado 1 no es Joe, es Emily.\u00a0<\/span><\/p>\n<p><b>Actualicemos al empleado 1<\/b><b><\/b><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION update_employee(1, \"Emily\", \"Alvarez\", 10000)' -k\r\n{\r\n\"requestID\": \"92a0ca70-6d0d-4eb1-bf8d-0b4294ae987d\",\r\n\"firma\": null,\r\n\"results\": [\r\nnull\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"100.967708ms\",\"executionTime\": \"100.225333ms\",\"resultCount\": 1, \"resultSize\": 4, \"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Ver al empleado:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION get_employee(1)' -k\r\n\r\n{\r\n\"requestID\": \"8f180e27-0028-4653-92e0-606c80d5dabb\",\r\n\"signature\": null,\r\n\"resultados\": [\r\n{\"first_name\":\"Emily\",\"last_name\":\"Alvarez\",\"salary\":10000}\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"101.995584ms\",\"executionTime\": \"101.879ms\",\"resultCount\": 1, \"resultSize\": 59, \"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Elimina al empleado:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Emily se fue.<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION delete_employee(1)' -k\r\n\r\n{\r\n\"requestID\": \"18539991-3d97-40e2-bde3-6959200791b1\",\r\n\"signature\": null,\r\n\"resultados\": [\r\n],\r\n\"errores\": [{\"code\":10109, \"msg\": \"Error al ejecutar la funci\u00f3n 'delete_employee' (blog_test:delete_employee)\", \"reason\":{\"details\":{\"Code\":\" var querycommit=N1QL('COMMIT WORK;', {}, false); \", \"Exception\":{\"_level\": \"exception\", \"caller\": \"txcouchbase:240\", \"cause\":{\"causa\":{\"bucket\":\"test\", \"collection\":\"_default\", \"document_key\":\"_txn:atr-988-#1b0\", \"error_description\": \"Es imposible cumplir los requisitos de durabilidad\", \"error_name\": \"DurabilityImpossible\", \"last_connection_id\": \"eda95f8c35df6746\/d275e8398a49e515\", \"last_dispatched_from\": \"127.0.0.1:50069\",\"last_dispatched_to\":\"127.0.0.1:11210\",\"msg\":\"durability impossible\",\"opaque\":7,\"scope\":\"_default\",\"status_code\":161},\"raise\":\"failed\",\"retry\":false,\"rollback\":false},\"code\":17007,\"key\":\"transaction.statement.commit\", \"message\": \"Commit Transaction statement error\"}, \"Location\": \"functions\/blog_test.js:29\", \"Stack\":\" at delete_employee (functions\/blog_test.js:29:21)\"}, \"type\": \"Exceptions from JS code\"}}],\r\n\"status\": \"fatal\",\r\n\"metrics\": {\"elapsedTime\": \"129.02975ms\",\"executionTime\": \"128.724ms\",\"resultCount\": 0, \"resultSize\": 0, \"serviceLoad\": 2, \"errorCount\": 1}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">De nuevo un error con el c\u00f3digo generado, mirando la raz\u00f3n y la excepci\u00f3n podemos confirmar que el c\u00f3digo traducido encierra delete en una transacci\u00f3n, lo que no era el caso en el original. <\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Para las transacciones, los buckets deben tener <a href=\"https:\/\/docs.couchbase.com\/server\/current\/learn\/data\/durability.html\">durabilidad<\/a><\/span><span style=\"font-weight: 400;\">\u00a0pero esto requiere m\u00e1s de un servidor de datos, de ah\u00ed el error.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">La soluci\u00f3n consiste en modificar el c\u00f3digo para eliminar la traducci\u00f3n adjunta:<\/span><\/p>\n<pre class=\"nums:false lang:js decode:true\">function eliminar_empleado(p_emp_id){\r\n    var params = [p_emp_id];\r\n    var query= N1QL('DELETE FROM test.testscope.employees WHERE emp_id = $1',params);\r\n    query.close();\r\n}\r\n<\/pre>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=CREATE OR REPLACE FUNCTION delete_employee(p_emp_id) LANGUAGE JAVASCRIPT AS \"delete_employee\" AT \"blog_test\"' -k\r\n\r\n{\r\n\"requestID\": \"e7432b82-1af8-4dc4-ad94-c34acea59334\",\r\n\"signature\": null,\r\n\"resultados\": [\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"31.129459ms\",\"executionTime\": \"31.022ms\",\"resultCount\": 0, \"resultSize\": 0, \"serviceLoad\": 2}\r\n}\r\n<\/pre>\n<pre class=\"nums:false lang:default decode:true\">curl -u Administrador:contrase\u00f1a https:\/\/127.0.0.1:18093\/query\/service -d 'statement=EXECUTE FUNCTION delete_employee(1)' -k\r\n\r\n{\r\n\"requestID\": \"d440913f-58ff-4815-b671-1a72b75bb7eb\",\r\n\"firma\": null,\r\n\"resultados\": [\r\nnull\r\n],\r\n\"estado\": \"success\",\r\n\"m\u00e9tricas\": {\"elapsedTime\": \"33.8885ms\",\"executionTime\": \"33.819042ms\",\"resultCount\": 1, \"resultSize\": 4, \"serviceLoad\": 2}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Ahora, todas las funciones en el PL\/SQL original funcionan en Couchbase a trav\u00e9s de JavaScript UDFs. S\u00ed, el ejemplo es bastante trivial, pero puedes entender c\u00f3mo usar la herramienta para migrar tus scripts PL\/SQL con poca supervisi\u00f3n manual. <\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">Recuerda que se supone que la herramienta te llevar\u00e1 80%, los otros 20% a\u00fan los tienes que hacer t\u00fa, \u00a1pero mucho mejor que escribir todo ese c\u00f3digo t\u00fa mismo!<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">El futuro<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Este proyecto es de c\u00f3digo abierto, as\u00ed que si\u00e9ntete libre de contribuir. <\/span><span style=\"font-weight: 400;\">Algunas ideas que se han sugerido:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">IA cr\u00edtica que puede criticar el c\u00f3digo generado para garantizar que la intervenci\u00f3n manual no sea necesaria en absoluto.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Actualmente, el c\u00f3digo fuente es un c\u00f3digo que simplemente funciona; no se ha pensado en el paralelismo ni en la reutilizaci\u00f3n del c\u00f3digo.<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Y tambi\u00e9n incluye las limitaciones comentadas anteriormente.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Recursos<\/span><\/h2>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><span style=\"font-weight: 400;\"><a href=\"https:\/\/github.com\/couchbaselabs\/plsql-to-jsudf2\/releases\/tag\/v1.0.0\">Couchbase Labs GitHub<\/a> - PL\/SQL a JSUDF<\/span><\/li>\n<li><span style=\"font-weight: 400;\"><a href=\"https:\/\/www.antlr.org\/\">ANTLR<\/a> generador de analizadores sint\u00e1cticos<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Por \u00faltimo, me gustar\u00eda dar las gracias a Kamini Jagtiani por guiarme y <\/span><span style=\"font-weight: 400;\">Pierre Regazzoni<\/span><span style=\"font-weight: 400;\"> por ayudarme a probar la herramienta de conversi\u00f3n.<\/span><\/p>","protected":false},"excerpt":{"rendered":"<p>What is PL\/SQL? PL\/SQL is a procedural language designed specifically to embrace SQL statements within its syntax. It includes procedural language elements such as conditions and loops, and can handle exceptions (run-time errors). PL\/SQL is native to Oracle databases, and [&hellip;]<\/p>","protected":false},"author":84423,"featured_media":16851,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1815,2225,10133,9973,9327,1812],"tags":[10043,10090,9870,1336,1592,10089,8911],"ppma_author":[9835],"class_list":["post-16846","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-cloud","category-engineering","category-generative-ai-genai","category-javascript","category-n1ql-query","tag-developer-tools","tag-javascript-udf","tag-llms","tag-mysql","tag-oracle","tag-pl-sql","tag-udf"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/es\/plsql-to-javascript-udf-conversion-tool\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\" \/>\n<meta property=\"og:description\" content=\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/plsql-to-javascript-udf-conversion-tool\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-11T16:52:20+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-08T16:15:37+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf-1024x536.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"536\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"},\"author\":{\"name\":\"Gaurav Jayaraj - Software Engineer\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42\"},\"headline\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\",\"datePublished\":\"2025-02-11T16:52:20+00:00\",\"dateModified\":\"2025-07-08T16:15:37+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"},\"wordCount\":1605,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"keywords\":[\"developer tools\",\"javascript UDF\",\"LLMs\",\"mysql\",\"oracle\",\"pl\/sql\",\"User Defined Function (UDF)\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Couchbase Capella\",\"Engineering\",\"Generative AI (GenAI)\",\"JavaScript\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\",\"name\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"datePublished\":\"2025-02-11T16:52:20+00:00\",\"dateModified\":\"2025-07-08T16:15:37+00:00\",\"description\":\"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png\",\"width\":2400,\"height\":1256,\"caption\":\"A tool for converting PL\/SQL to JavaScript UDF (JSUDF) using LLMs\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42\",\"name\":\"Gaurav Jayaraj - Software Engineer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"caption\":\"Gaurav Jayaraj - Software Engineer\"},\"description\":\"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/es\/author\/gauravjayaraj\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog","description":"Convierte PL\/SQL a JavaScript UDFs sin problemas con una herramienta impulsada por IA. Automatice la migraci\u00f3n de Oracle PL\/SQL a Couchbase con gran precisi\u00f3n mediante ANTLR y LLM.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/es\/plsql-to-javascript-udf-conversion-tool\/","og_locale":"es_MX","og_type":"article","og_title":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF","og_description":"Convert PL\/SQL to JavaScript UDFs seamlessly with an AI-powered tool. Automate Oracle PL\/SQL migration to Couchbase with high accuracy using ANTLR and LLMs.","og_url":"https:\/\/www.couchbase.com\/blog\/es\/plsql-to-javascript-udf-conversion-tool\/","og_site_name":"The Couchbase Blog","article_published_time":"2025-02-11T16:52:20+00:00","article_modified_time":"2025-07-08T16:15:37+00:00","og_image":[{"width":1024,"height":536,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf-1024x536.png","type":"image\/png"}],"author":"Gaurav Jayaraj - Software Engineer","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Gaurav Jayaraj - Software Engineer","Est. reading time":"8 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"},"author":{"name":"Gaurav Jayaraj - Software Engineer","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42"},"headline":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF","datePublished":"2025-02-11T16:52:20+00:00","dateModified":"2025-07-08T16:15:37+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"},"wordCount":1605,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","keywords":["developer tools","javascript UDF","LLMs","mysql","oracle","pl\/sql","User Defined Function (UDF)"],"articleSection":["Best Practices and Tutorials","Couchbase Capella","Engineering","Generative AI (GenAI)","JavaScript","SQL++ \/ N1QL Query"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","url":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/","name":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","datePublished":"2025-02-11T16:52:20+00:00","dateModified":"2025-07-08T16:15:37+00:00","description":"Convierte PL\/SQL a JavaScript UDFs sin problemas con una herramienta impulsada por IA. Automatice la migraci\u00f3n de Oracle PL\/SQL a Couchbase con gran precisi\u00f3n mediante ANTLR y LLM.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/blog-plsql-convert-to-javascript-udf.png","width":2400,"height":1256,"caption":"A tool for converting PL\/SQL to JavaScript UDF (JSUDF) using LLMs"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/plsql-to-javascript-udf-conversion-tool\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"A Tool to Ease Your Transition From Oracle PL\/SQL to Couchbase JavaScript UDF"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42","name":"Gaurav Jayaraj - Ingeniero de software","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","caption":"Gaurav Jayaraj - Software Engineer"},"description":"Gaurav Jayaraj es becario en el equipo de consultas de Couchbase R&amp;D. Gaurav es licenciado en Inform\u00e1tica por la Universidad PES de Bangalore.","url":"https:\/\/www.couchbase.com\/blog\/es\/author\/gauravjayaraj\/"}]}},"authors":[{"term_id":9835,"user_id":84423,"is_guest":0,"slug":"gauravjayaraj","display_name":"Gaurav Jayaraj - Software Engineer","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg"},"author_category":"","last_name":"Jayaraj - Software Engineer","first_name":"Gaurav","job_title":"","user_url":"","description":"Gaurav Jayaraj es becario en el equipo de consultas de Couchbase R&amp;D. Gaurav es licenciado en Inform\u00e1tica por la Universidad PES de Bangalore."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/16846","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/84423"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=16846"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/16846\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/16851"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=16846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=16846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=16846"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=16846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}