diff --git a/modules/guides/examples/javascript-udfs/execute-javascript-function.n1ql b/modules/guides/examples/javascript-udfs/execute-javascript-function.n1ql index aea6b4586..d8a23f1c9 100644 --- a/modules/guides/examples/javascript-udfs/execute-javascript-function.n1ql +++ b/modules/guides/examples/javascript-udfs/execute-javascript-function.n1ql @@ -1 +1 @@ -EXECUTE FUNCTION GetBusinessDays("02/14/2022", "04/16/2022"); \ No newline at end of file +EXECUTE FUNCTION GetBusinessDays("02/14/2025", "04/16/2025"); \ No newline at end of file diff --git a/modules/guides/examples/javascript-udfs/execute-scoped-function.n1ql b/modules/guides/examples/javascript-udfs/execute-scoped-function.n1ql index e7f1a0297..58d8dd163 100644 --- a/modules/guides/examples/javascript-udfs/execute-scoped-function.n1ql +++ b/modules/guides/examples/javascript-udfs/execute-scoped-function.n1ql @@ -1 +1 @@ -EXECUTE FUNCTION default:`travel-sample`.`inventory`.GetBusinessDays("03/10/2022", "05/10.2022"); \ No newline at end of file +EXECUTE FUNCTION default:`travel-sample`.`inventory`.GetBusinessDays("03/10/2025", "05/10/2025"); \ No newline at end of file diff --git a/modules/guides/examples/javascript-udfs/select-get-business-days.n1ql b/modules/guides/examples/javascript-udfs/select-get-business-days.n1ql index 01e7f2f28..7dcef974c 100644 --- a/modules/guides/examples/javascript-udfs/select-get-business-days.n1ql +++ b/modules/guides/examples/javascript-udfs/select-get-business-days.n1ql @@ -1 +1 @@ -SELECT GetBusinessDays("02/14/2022", "04/16/2022"); \ No newline at end of file +SELECT GetBusinessDays("02/14/2025", "04/16/2025"); \ No newline at end of file diff --git a/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days-cbq.sh b/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days-cbq.sh new file mode 100644 index 000000000..aca726f46 --- /dev/null +++ b/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days-cbq.sh @@ -0,0 +1,5 @@ +cbq> SELECT CASE + WHEN GetBusinessDays('02/14/2025', '4/16/2025') > 44 THEN "true" + ELSE "false" + END + AS response; \ No newline at end of file diff --git a/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days.n1ql b/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days.n1ql index 875ecbc7e..ece198c90 100644 --- a/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days.n1ql +++ b/modules/guides/examples/javascript-udfs/select-true-alias-get-business-days.n1ql @@ -1,5 +1,5 @@ SELECT CASE - WHEN GetBusinessDays('02/14/2022', '4/16/2022') > 44 THEN "true" + WHEN GetBusinessDays('02/14/2025', '4/16/2025') > 44 THEN "true" ELSE "false" END AS response; \ No newline at end of file diff --git a/modules/guides/pages/.call-user-defined-function.adoc b/modules/guides/pages/.call-user-defined-function.adoc deleted file mode 100644 index a92ef220a..000000000 --- a/modules/guides/pages/.call-user-defined-function.adoc +++ /dev/null @@ -1,84 +0,0 @@ -= Calling a User-Defined Function -:description: How to call a user-defined function from {sqlpp} statements. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-pagination: prev -:page-topic-type: guide -:page-toclevels: 2 -:imagesdir: ../assets/images -:no-escape-hatch: - -[abstract] -{description} - -include::ROOT:partial$component-signpost.adoc[] - -== Introduction - -A user-defined function can be called like any other {sqlpp} function. - -== Calling the {sqlpp} User-Defined Function - -An {sqlpp} user-defined function can be called from anywhere that a standard {sqlpp} function can be called. - -[tabs] -==== -Query Workbench:: -+ --- -. Access the Query Workbench UI from the Administration Console. -+ -image::javascript-udfs/select-query-tool-ui.png[,500, alt="accessing the query tool"] - -. Set the context to match the namespace of the function you are calling. -+ -image::javascript-udfs/switch-context-to-travel-sample.png[] - -. Enter the {sqlpp} statement in the query editor to run your function: -+ -[source, sqlpp] ----- -include::example$javascript-udfs/execute-javascript-function.n1ql[] ----- --- - -REST API:: -+ --- -. Open up a shell session. -. Execute a `curl` command to run the function: -+ -[source, console] ----- -include::example$javascript-udfs/execute-scoped-function.sh[] ----- --- - -{sqlpp}:: -+ --- - -Run the `EXECUTE FUNCTION` function in the CBQ Shell. - -[source, sqlpp] ----- -include::example$javascript-udfs/execute-scoped-function.n1ql[] ----- - -[NOTE] -===== -The {sqlpp} user-defined function can be used in any {sqlpp} statement in exactly the same way as a standard built-in function. - -[source, sqlpp] ----- -include::example$javascript-udfs/select-true-alias-get-business-days.n1ql[] ----- -===== --- -==== - - -== Related Links - -include::partial$javascript-udfs/further-reading.adoc[tags="create-function;user-defined-functions"] \ No newline at end of file diff --git a/modules/guides/pages/.create-javascript-library.adoc b/modules/guides/pages/.create-javascript-library.adoc deleted file mode 100644 index d10a710f9..000000000 --- a/modules/guides/pages/.create-javascript-library.adoc +++ /dev/null @@ -1,100 +0,0 @@ -= Creating a JavaScript Library -:description: How to create a JavaScript library. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-pagination: -:page-topic-type: guide -:page-toclevels: 2 -:imagesdir: ../assets/images -:no-escape-hatch: - -[abstract] -{description} - -include::ROOT:partial$component-signpost.adoc[] - -== Introduction - -You can create an external library for storing JavaScript functions. -When you create a new library you can add a new JavaScript function to the library at the same time. - -include::partial$before-you-begin.adoc[tag=body] -include::partial$query-tools.adoc[tags=body;!thumbs] - -[#creating-the-library-and-adding-your-first-function] -== Creating the Library and Adding JavaScript Code - -You can use the Query Workbench UI or the REST API to create a library. -The process for creating the library is as follows: - -include::partial$javascript-udfs/diagrams.adoc[tags=javascript-udf-library-creation-sequence] - -*(1) Create library*:: Create the library by creating the logical storage for the library. - -*(2) Add the JavaScript function to the library*:: Edit the library to add your JavaScript function. - -*(3) Create {sqlpp} User-Defined Function*:: The {sqlpp} User Defined Function is needed so that it can be called as part of {sqlpp} statements (such as `SELECT` and `EXECUTE FUNCTION`). -Creating the {sqlpp} User-Defined Function is covered in xref:create-user-defined-function.adoc[]. - -As shown in xref:create-library-udf-sequence[xrefstyle=short], the library is created and the first function is added in the same step. - -[tabs] -==== -Query Workbench:: -+ --- - -. Select btn:[Query] to access the Query Workbench, then select btn:[UDF] Query Workbench menu. -+ -image::javascript-udfs/navigate-to-udf-query.png[alt="route to the user-defined functions screen"] - -. Click on the *{plus} add function library* link in the `JavaScript Function Libraries` table to show the `Add Library` screen. - -. Select your `Namespace` from the drop-down lists. -In this example, the namespace has been set to the `inventory` scope inside the `travel-sample` bucket. -You also have the option of leaving the Namespace unset, which will the library accessible at the cluster level. -+ -image::javascript-udfs/add-scoped-library.png[,500] -+ -.A Note on Namespaces -[sidebar] -**** -The `Namespace` defines the `scope` of the library within the containing bucket. -(You can read about scopes xref:clusters:data-service/scopes-collections.adoc[here].) -Setting the namespace means that functions in the library can only be called users who have their context set to the same scope. -**** - -. Enter a name for the library in the `Library Name` field. - -. Add your own function to the library, for example: -+ -[source, javascript] ----- -include::example$javascript-udfs/get-business-days.js[] ----- -. Save the library by pressing the btn:[Save] button. -+ -TIP: You can, of course, create an empty library and add functions to it later. --- - - -REST API:: -+ --- -. Start a shell session. -. Run a `curl` command to create a JavaScript library within a desired scope. -+ -[source, console] ----- -include::example$javascript-udfs/create-scoped-javascript-function-with-rest.sh[] ----- -The parameters in the URL denote that the function should reside in the `travel-sample` bucket, under the `inventory` scope within that bucket. --- -==== - -When you have created an external library and added JavaScript code, you must create an {sqlpp} user-defined function to reference the JavaScript code in the library, so it can be called as part of any {sqlpp} statement. - -== Related Links - -include::partial$javascript-udfs/further-reading.adoc[tags="user-defined-functions-ui;rest-create-library-call"] diff --git a/modules/guides/pages/.create-user-defined-function.adoc b/modules/guides/pages/.create-user-defined-function.adoc deleted file mode 100644 index 4aae487e3..000000000 --- a/modules/guides/pages/.create-user-defined-function.adoc +++ /dev/null @@ -1,87 +0,0 @@ -= Creating a User-Defined Function -:description: How to create a user-defined function to call external JavaScript code. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-pagination: -:page-topic-type: guide -:page-toclevels: 2 -:keywords: N1QL, UDF, user-defined function, SQL++ -:imagesdir: ../assets/images -:no-escape-hatch: - -[abstract] -{description} - -include::ROOT:partial$component-signpost.adoc[] - -== Introduction - -You cannot call external JavaScript code directly from a {sqlpp} query. -You must create a {sqlpp} user-defined function to reference the external JavaScript code. - -If you have created a JavaScript function in an external library (see xref:create-javascript-library.adoc[]), you must create a {sqlpp} user-defined function to reference it. - -You can also create a {sqlpp} user-defined function and the external JavaScript code in a single operation. -In this case, the JavaScript code is not stored in an external library. - -[#creating-the-n1ql-udf-function] -== Creating a {sqlpp} User-Defined Function to Reference an External Library - -To create a {sqlpp} user-defined function to reference an external library, do one of the following: - -* Use the xref:tools:udfs-ui.adoc[UDF UI] in the Query Workbench. -* Use the {sqlpp} xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION] statement, and reference the external library and JavaScript function. - -[tabs] -==== -Query Workbench:: -+ --- -. Access the *UDF* screen from the administration console. -+ -image::javascript-udfs/navigate-to-udf-query.png[] - -. Click on the *{plus}{nbsp}add function* link. -+ -image::javascript-udfs/my-library-list-add-function-button.png[alt="library list"] -+ -The *Add Function* dialog is displayed. -+ -image::javascript-udfs/add-function-dialog.png[ ,400] - -. Use the *Namespace* drop-down lists to select the bucket and scope where your JavaScript function resides. - -. Fill in the *Function Name* of your {sqlpp} user-defined function. - -. Specify *Parameters* for the function. -+ -NOTE: The `...` in the parameters box denotes a variable length list of parameters. - -. Select *JavaScript* for the function type. -A field appears in the dialog with a list of available libraries in the namespace you selected. -+ -image::javascript-udfs/add-function-dialog-switch-to-javascript.png[] -+ -From this list select the library containing your function. - -. Enter the name of the JavaScript function in the `Library Function Name` field. --- - -{sqlpp}:: -+ --- -Execute the `CREATE FUNCTION` in the CBQ Shell to create the {sqlpp} user-defined function: - -[source, sqlpp] ----- -include::example$javascript-udfs/create-scoped-n1ql-udf.n1ql[] ----- --- -==== - -NOTE: The {sqlpp} user-defined function will take the same scope as the JavaScript UDF it is referencing. - -== Related Links - -* To create a {sqlpp} user-defined function and the external JavaScript code in a single operation, see xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION]. diff --git a/modules/guides/pages/.javascript-udfs.adoc b/modules/guides/pages/.javascript-udfs.adoc deleted file mode 100644 index 012a2a00f..000000000 --- a/modules/guides/pages/.javascript-udfs.adoc +++ /dev/null @@ -1,45 +0,0 @@ -= User-Defined Functions with JavaScript -:description: How to extend the {sqlpp} query language by adding your own functions written in JavaScript. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-pagination: next -:page-topic-type: guide -:page-toclevels: 2 -:xrefstyle: short -:no-escape-hatch: - -[abstract] -{description} - -include::ROOT:partial$component-signpost.adoc[] - -== Introduction - -{sqlpp} includes a large number of built-in operations and functions that cover every aspect of data manipulation. -User-defined functions enable you to create your own extensions to the language. - -Using user-defined functions, you can: - -* Create reuseable, domain-specific functions for use in your applications. -* Execute complex logic that may be difficult to do in {sqlpp}. -* Migrate from RDBMS stored procedures. - -== User-Defined Functions with JavaScript - -JavaScript supported in Couchbase shares the same constructs of the https://en.wikipedia.org/wiki/ECMAScript[ECMAScript]. However, you should be aware of the restrictions and extensions that come with the Couchbase implementation. -These are covered in xref:javascript-udfs:javascript-functions-with-couchbase.adoc[] - -== Next Steps - -If you're looking to create your own JavaScript libraries, then there are a number of guides to get you started. - -* xref:create-javascript-library.adoc[] -* xref:call-user-defined-function.adoc[] - -If you wish to look into the constructs and available in the language itself, then you can have a look through the following pages: - -* xref:javascript-udfs:javascript-functions-with-couchbase.adoc[] -* xref:javascript-udfs:calling-javascript-from-n1ql.adoc[] -* xref:javascript-udfs:calling-n1ql-from-javascript.adoc[] -* xref:javascript-udfs:handling-errors-javascript-udf.adoc[] diff --git a/modules/guides/pages/call-user-defined-function.adoc b/modules/guides/pages/call-user-defined-function.adoc new file mode 100644 index 000000000..3f0751a5a --- /dev/null +++ b/modules/guides/pages/call-user-defined-function.adoc @@ -0,0 +1,88 @@ += Call a User-Defined Function +:description: Call a user-defined JavaScript function from the Query Tab or cbq and use it with your Capella cluster. +:page-pagination: prev +:page-topic-type: guide +:page-toclevels: 2 +:imagesdir: ../assets/images +:tabs: + +[abstract] +{description} + +include::ROOT:partial$component-signpost.adoc[] + +A user-defined function can be called like any other {sqlpp} function. + +== Prerequisites + +* If you want to use cbq to run your user-defined function, you must complete the prerequisites for using cbq. +For more information, see xref:n1ql:n1ql-intro/cbq.adoc#prerequisites[Prerequisites]. +* You have created a user-defined function. +For more information, see xref:create-user-defined-function.adoc[]. + +== Procedure + +You can run user-defined functions from the Query Tab or cbq. + +[tabs] +==== +Query Tab:: ++ +-- +To run a user-defined function from the Query Tab: + +. On the *Operational Clusters* page, select the operational cluster where you want to work with your user-defined function. +. Go to menu:Data Tools[Query]. +. Enter a {sqlpp} statement in the query editor to run your function. ++ +If you created a scoped user-defined function, make sure your xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] is set to the same bucket and scope as the namespace for your function. ++ +For example, the following statement executes a function called `GetBusinessDays`, which takes 2 dates: ++ +[source, sqlpp] +---- +include::example$javascript-udfs/execute-javascript-function.n1ql[] +---- ++ +You can also use a user-defined function in any {sqlpp} statement, just like a standard built-in function. +For example: ++ +[source, sqlpp] +---- +include::example$javascript-udfs/select-true-alias-get-business-days.n1ql[] +---- +-- + +cbq:: ++ +-- +To run a user-defined function using the command line tool, cbq: + +. Open a terminal window. +. Navigate to the directory where you installed cbq. +. Connect to your Capella cluster. +For more information, see xref:n1ql:n1ql-intro/cbq.adoc#cbq-connect-to-cluster[Connecting to the Cluster]. +. Run the `EXECUTE FUNCTION` command with your user-defined function. ++ +For example, the following command executes a function called `GetBusinessDays`, which takes 2 dates, on the `travel-sample`/`inventory` keyspace: ++ +[source, sqlpp] +---- +include::example$javascript-udfs/execute-scoped-function.n1ql[] +---- ++ +You can also use a user-defined function in any {sqlpp} statement, just like a standard built-in function. +For example: ++ +[source, console] +---- +include::example$javascript-udfs/select-true-alias-get-business-days-cbq.sh[] +---- +-- +==== + +== See Also + +include::partial$javascript-udfs/further-reading.adoc[tags="create-function;user-defined-functions"] +* xref:n1ql:n1ql-language-reference/execfunction.adoc[] +* xref:javascript-udfs:javascript-functions-with-couchbase.adoc[] \ No newline at end of file diff --git a/modules/guides/pages/create-javascript-library.adoc b/modules/guides/pages/create-javascript-library.adoc new file mode 100644 index 000000000..a18f87f98 --- /dev/null +++ b/modules/guides/pages/create-javascript-library.adoc @@ -0,0 +1,97 @@ += Create a User-Defined Function Library +:description: Create an user-defined function (UDF) library to store and organize your JavaScript functions. +:page-pagination: +:page-topic-type: guide +:page-toclevels: 2 +:imagesdir: ../assets/images +:no-escape-hatch: + +[abstract] +{description} + +To create a UDF library that you can use to xref:create-user-defined-function.adoc[]: + +. <> +. <> + +Or, you can <>. + +[#creating-the-library-and-adding-your-first-function] +== Create a User-Defined Function (UDF) Library + +To create a new UDF library from the Query Tab: + +. On the *Operational Clusters* page, select the operational cluster where you want to work with user-defined functions. +. Go to menu:Data Tools[Query]. +. In the Data Insights area, to the left of the query editor, find the *Functions* section. +. Next to the *Functions* section header, go to menu:Create (+)[Library]. +. In the *Library Name* field, enter a name for your new UDF library. +. Choose the access level for your UDF library: +.. Choose *Global* to allow all buckets and scopes on this cluster to use functions in this library. +.. Choose *Specific* to choose a specific bucket and scope on this cluster that can use this library. ++ +Use this bucket and scope as the namespace for xref:create-user-defined-function.adoc[your user-defined function] to use this library and its functions later. +. (Optional) Add functions to your UDF library. +See <>. +. Click btn:[Create]. + +[#add-functions-now] +== Add Functions to a New User-Defined Function (UDF) Library + +To add functions to your new UDF library while you create your library: + +. Do one of the following: +.. To manually define functions for your library, on the *Define Functions* tab, enter the code for each function. +.. To import a `.js` file that contains function definitions, on the *Import Library* tab, drag and drop or choose your `.js` file. ++ +TIP: Each function should have an assigned name and follow the https://en.wikipedia.org/wiki/ECMAScript[ECMAScript^] standard. + +[#add-functions-later] +== Add Functions to an Existing User-Defined Function (UDF) Library + +To add or edit functions in an existing UDF library: + +. On the *Operational Clusters* page, select the operational cluster where you want to work with user-defined functions. +. Go to menu:Data Tools[Query]. +. In the Data Insights area, to the left of the query editor, find the *Functions* section. +. Next to the user-defined library where you want to add a function, go to menu:More Options (⋮)[Edit]. +. Do one of the following: +.. To manually define functions for your library, on the *Create Functions* tab, enter or edit the code for each function. +.. To import a `.js` file that contains function definitions, on the *Import Library* tab, drag and drop or choose your `.js` file. ++ +TIP: Each function should have an assigned name and follow the https://en.wikipedia.org/wiki/ECMAScript[ECMAScript] standard. +. Click btn:[Update]. + +== Creating Functions with Variable Length Parameter Lists + +If you want to create a JavaScript function that can take a variable length list of parameters, rather than a fixed number of parameters: + +. Add a variable that starts with `...` to your function - such as `... args`. +. Define your user-defined function with a `...` parameter. + +You can then pass a variable length list of parameters from your user-defined function to your JavaScript function. + +If you define your JavaScript function with all named variables but still want to use a variable length of parameters in your user-defined function: + +. Define your user-defined function with a `...` parameter. + +Your user-defined function will accept an array of values as a parameter. +The user-defined function assigns each value it receives to the named variables in your JavaScript function. + +For more information and examples, see xref:javascript-udfs:calling-javascript-from-n1ql.adoc#variadic-parameters[Variadic Parameters]. + +[#delete-udf] +== Delete a User-Defined Function (UDF) Library + +To delete an existing UDF library: + +. On the *Operational Clusters* page, select the operational cluster where you want to delete a UDF library. +. Go to menu:Data Tools[Query]. +. In the Data Insights area, to the left of the query editor, find the *Functions* section. +. Next to the user-defined library where you want to add a function, go to menu:More Options (⋮)[Delete]. +. Confirm that you want to delete your UDF library. +. Click btn:[Delete Library]. + +== Next Steps + +To use your JavaScript functions from {sqlpp} after you have added them to a library, see xref:create-user-defined-function.adoc[]. \ No newline at end of file diff --git a/modules/guides/pages/create-user-defined-function.adoc b/modules/guides/pages/create-user-defined-function.adoc new file mode 100644 index 000000000..03a530185 --- /dev/null +++ b/modules/guides/pages/create-user-defined-function.adoc @@ -0,0 +1,138 @@ += Create a User-Defined Function +:description: Create a user-defined function (UDF) to call an inline function or a specific JavaScript function stored in a library. +:page-pagination: +:page-topic-type: guide +:page-toclevels: 2 +:keywords: N1QL, UDF, user-defined function, SQL++ +:imagesdir: ../assets/images +:no-escape-hatch: + +[abstract] +{description} + +For more information about how to create a UDF library, see xref:create-javascript-library.adoc[]. + +== Prerequisites + +* If you want to use cbq to create your user-defined function, you must complete the prerequisites for using cbq. +For more information, see xref:n1ql:n1ql-intro/cbq.adoc#prerequisites[Prerequisites]. +* If you want to use libraries to organize your user-defined functions, you need to create a UDF library and add a JavaScript function. +For more information, see xref:create-javascript-library.adoc[]. + +[#creating-the-n1ql-udf-function] +== Create a User-Defined Function From a UDF Library + +To create a user-defined function that references a JavaScript function from a UDF library: + +* Use the Query Tab. +* Use the {sqlpp} xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION] statement, and reference the UDF library and JavaScript function. + +[tabs] +==== +Query Tab:: ++ +-- +. On the *Operational Clusters* page, select the operational cluster where you want to create a user-defined function from a UDF library. +. Go to menu:Data Tools[Query]. + +. In the Data Insights area, to the left of the query editor, find the *Functions* section. +. Next to the *Functions* section header, go to menu:Create (+)[Function]. +. In the *Function Name* field, enter a name for your function. +. Choose the access level for your user-defined function: +.. Choose *Global* to allow all buckets and scopes on this cluster to use this function. +.. Choose *Specific* to choose a specific bucket and scope on this cluster that can use this function. ++ +TIP: Choose the same access level and namespace as your UDF library for your user-defined function. +Users must set this bucket and scope as their xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] to use this function later. +. In the *Parameters* field, enter a list of parameters, separated by commas (`,`) for any values you need to process or use in your function. ++ +For example, if you created a JavaScript function that has a variable named `a`, you should add a parameter `a`. ++ +TIP: If you want to create a user-defined function that can take a variable length list of parameters, rather than a comma separated list of parameters, add `...` as a parameter. +Your function will accept an array of values as a parameter, and assign each value it receives to the named variables in your function. +. Click the *UDF Library* tab. +. Choose the UDF library and specific JavaScript function you want to assign to this user-defined function. +. Click btn:[Create Function]. +-- + +{sqlpp}:: ++ +-- +Execute a `CREATE FUNCTION` statement in cbq to create a user-defined function: + +[source, sqlpp] +---- +include::example$javascript-udfs/create-scoped-n1ql-udf.n1ql[] +---- + +TIP: Set the same namespace as your UDF library for your user-defined function. +Users must set this bucket and scope as their xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] to use this function later. + +For more information on the `CREATE FUNCTION` statement, see xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION]. +-- +==== + +[#create-inline] +== Create an Inline User-Defined Function + +To create a user-defined function that uses inline JavaScript or {sqlpp}: + +* Use the Query Tab. +* Use the {sqlpp} xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION] statement. + +[tabs] +==== +Query Tab:: ++ +-- +. On the *Operational Clusters* page, select the operational cluster where you want to create a user-defined function. +. Go to menu:Data Tools[Query]. + +. In the Data Insights area, to the left of the query editor, find the *Functions* section. +. Next to the *Functions* section header, go to menu:Create (+)[Function]. +. In the *Function Name* field, enter a name for your function. +. Choose the access level for your user-defined function: +.. Choose *Global* to allow all buckets and scopes on this cluster to use this function. +.. Choose *Specific* to choose a specific bucket and scope on this cluster that can use this function. ++ +TIP: Users must set this bucket and scope as their xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] to use this function later. +. In the *Parameters* field, enter a list of parameters, separated by commas (`,`) for any values you need to process or use in your function. ++ +For example, if you created a JavaScript function that has a variable named `a`, you should add a parameter `a`. ++ +TIP: If you want to create a user-defined function that can take a variable length list of parameters, rather than a comma separated list of parameters, add `...` as a parameter. +Your function will accept an array of values as a parameter, and assign each value it receives to the named variables in your function. +You can also define your function with a variable length parameter list, by adding a variable to your function definition that starts with `...` - such as `... args`. + +. Click the *Inline {sqlpp}* or *Inline JavaScript* tab. +. Enter a {sqlpp} expression or JavaScript function. +. Click btn:[Create Function]. +-- + +{sqlpp}:: ++ +-- +Execute a `CREATE FUNCTION` statement in cbq to create an inline user-defined function: + +.Inline JavaScript +[source,sqlpp] +---- +CREATE FUNCTION celsius(...) LANGUAGE INLINE AS (args[0] - 32) * 5/9; +---- + +.Inline {sqlpp} +[source,sqlpp] +---- +CREATE FUNCTION locations(vActivity) { ( + SELECT id, name, address, city + FROM landmark + WHERE activity = vActivity) }; +---- + +For more information about the `CREATE FUNCTION` statement, see xref:n1ql:n1ql-language-reference/createfunction.adoc[CREATE FUNCTION]. +-- +==== + +== Next Steps + +* To use your new user-defined function from {sqlpp}, see xref:call-user-defined-function.adoc[]. diff --git a/modules/guides/pages/javascript-udfs.adoc b/modules/guides/pages/javascript-udfs.adoc new file mode 100644 index 000000000..ff493683f --- /dev/null +++ b/modules/guides/pages/javascript-udfs.adoc @@ -0,0 +1,72 @@ += User-Defined Functions with JavaScript +:description: Couchbase Capella lets you extend the {sqlpp} query language by adding your own functions written in JavaScript. +:page-pagination: next +:page-topic-type: guide +:page-toclevels: 2 +:xrefstyle: short +:no-escape-hatch: + +[abstract] +{description} + +include::ROOT:partial$component-signpost.adoc[] + +On its own, {sqlpp} includes built-in operations and functions for data manipulation. +You can use user-defined functions to create your own extensions to the language. + +With user-defined functions, you can: + +* Create reusable, domain-specific functions for use in your applications. +* Execute complex logic that may be difficult to do in {sqlpp}. +* Migrate from Relational Database Management System (RDBMS) stored procedures. + +Couchbase Capella's user-defined functions are defined with JavaScript, specifically the https://en.wikipedia.org/wiki/ECMAScript[ECMAScript^] standard, with some restrictions and extensions. +For more information, see xref:javascript-udfs:javascript-functions-with-couchbase.adoc[]. + +== Using User-Defined Functions in Capella + +You cannot call JavaScript code directly from a {sqlpp} query. +You must create a user-defined function to call JavaScript code in your queries. + +You can create 2 types of user-defined functions: + +* <>. +* <>. + +Creating a UDF library for your JavaScript functions is optional, but simplifies organization and access control for user-defined functions. + +After you have created your user-defined functions, you can xref:call-user-defined-function.adoc[] like any other {sqlpp} function. + +[#inline-functions] +=== Inline Functions + +You can create a user-defined function that executes inline {sqlpp} commands or inline JavaScript. +You do not need to create a UDF library before you can create and use inline functions. + +If you create a user-defined function this way, you cannot group related functions or change cluster access restrictions for multiple related functions at once. + +For more information about how to create inline functions, see xref:create-user-defined-function.adoc#create-inline[Create an Inline User-Defined Function]. + +[#library-functions] +=== Functions From User-Defined Function (UDF) Libraries + +A UDF library is a collection of JavaScript functions. +UDF libraries keep your JavaScript functions organized and allow you to set access controls across multiple functions at once. + +You can define functions xref:create-javascript-library.adoc#add-functions-now[while creating a library] or xref:create-javascript-library.adoc#add-functions-later[add them to an existing library]. + +After you have xref:create-javascript-library.adoc[created a UDF library], you must xref:create-user-defined-function.adoc[create user-defined functions] to use the JavaScript functions in that library. +The user-defined function creates a link between the JavaScript function in your library and {sqlpp}, letting you call your JavaScript code. + +== Next Steps + +* To get started with a UDF library, see xref:create-javascript-library.adoc[]. +* To create inline functions, see xref:create-user-defined-function.adoc[]. +* To start using your functions in the Query Tab, see xref:call-user-defined-function.adoc[]. + +For more information about the specifics of JavaScript for user-defined functions in Capella, see: + +* xref:javascript-udfs:javascript-functions-with-couchbase.adoc[] +* xref:javascript-udfs:calling-javascript-from-n1ql.adoc[] +* xref:javascript-udfs:calling-n1ql-from-javascript.adoc[] +* xref:javascript-udfs:handling-errors-javascript-udf.adoc[] diff --git a/modules/javascript-udfs/examples/add-airline-check-parse.js b/modules/javascript-udfs/examples/add-airline-check-parse.js index 1b6645e6d..84c2ab2d1 100644 --- a/modules/javascript-udfs/examples/add-airline-check-parse.js +++ b/modules/javascript-udfs/examples/add-airline-check-parse.js @@ -19,7 +19,7 @@ function addAirlineWithCheck(id, name, callsign, country) { return "success" } catch (error) { - const message = JSON.parse(error.message) // <.> + const message = JSON.parse(error.message) return message } diff --git a/modules/javascript-udfs/examples/add-airline-parse-check-2.js b/modules/javascript-udfs/examples/add-airline-parse-check-2.js index fd77d7e49..21c2a352a 100644 --- a/modules/javascript-udfs/examples/add-airline-parse-check-2.js +++ b/modules/javascript-udfs/examples/add-airline-parse-check-2.js @@ -25,11 +25,11 @@ function addAirlineWithCheck(id, name, callsign, country) { const message = JSON.parse(error.message) - if (message.cause.key == 'dml.statement.duplicatekey') { // <.> + if (message.cause.key == 'dml.statement.duplicatekey') { var myErrorObject = {} myErrorObject.error_type = 'duplicate key' - myErrorObject.key_used = message.cause.message.replace('Duplicate Key: ', '') // <.> + myErrorObject.key_used = message.cause.message.replace('Duplicate Key: ', '') return myErrorObject } else { diff --git a/modules/javascript-udfs/examples/create-globally-scoped-n1ql-udf.n1ql b/modules/javascript-udfs/examples/create-globally-scoped-n1ql-udf.n1ql index c64379d58..87a72fdda 100644 --- a/modules/javascript-udfs/examples/create-globally-scoped-n1ql-udf.n1ql +++ b/modules/javascript-udfs/examples/create-globally-scoped-n1ql-udf.n1ql @@ -1,3 +1,3 @@ CREATE FUNCTION GetBusinessDays(startDate, endDate) LANGUAGE JAVASCRIPT as "getBusinessDays" -AT "my-library"; -- <.> \ No newline at end of file +AT "my-library"; \ No newline at end of file diff --git a/modules/javascript-udfs/examples/create-relatively-scoped-n1ql-udf.n1ql b/modules/javascript-udfs/examples/create-relatively-scoped-n1ql-udf.n1ql index eee12a215..a6476af8d 100644 --- a/modules/javascript-udfs/examples/create-relatively-scoped-n1ql-udf.n1ql +++ b/modules/javascript-udfs/examples/create-relatively-scoped-n1ql-udf.n1ql @@ -1,3 +1,3 @@ CREATE FUNCTION GetBusinessDays(startDate, endDate) LANGUAGE JAVASCRIPT as "getBusinessDays" -AT "./my-library"; -- <.> \ No newline at end of file +AT "./my-library"; \ No newline at end of file diff --git a/modules/javascript-udfs/examples/create-scoped-n1ql-udf.n1ql b/modules/javascript-udfs/examples/create-scoped-n1ql-udf.n1ql index 63d1e001f..d66e8fe0f 100644 --- a/modules/javascript-udfs/examples/create-scoped-n1ql-udf.n1ql +++ b/modules/javascript-udfs/examples/create-scoped-n1ql-udf.n1ql @@ -1,3 +1,3 @@ -CREATE FUNCTION default:`travel-sample`.`inventory`.GetBusinessDays(startDate, endDate) -- <.> -LANGUAGE JAVASCRIPT as "getBusinessDays" -- <.> -AT "travel-sample/inventory/my-library"; -- <.> \ No newline at end of file +CREATE FUNCTION default:`travel-sample`.`inventory`.GetBusinessDays(startDate, endDate) +LANGUAGE JAVASCRIPT as "getBusinessDays" +AT "travel-sample/inventory/my-library"; \ No newline at end of file diff --git a/modules/javascript-udfs/examples/do-recursion-response.jsonc b/modules/javascript-udfs/examples/do-recursion-response.jsonc index 54fc55718..26fc96f7f 100644 --- a/modules/javascript-udfs/examples/do-recursion-response.jsonc +++ b/modules/javascript-udfs/examples/do-recursion-response.jsonc @@ -9,7 +9,8 @@ "caller": "javascript:133", "code": 10112, "key": "function.nested.error", - "message": "Error executing function 'doRecursion': {number-of-calls} nested javascript calls" // <.> + // The function returns an error because of {number-of-calls} nested calls + "message": "Error executing function 'doRecursion': {number-of-calls} nested javascript calls" }, "Location": "functions/my-library.js:30", "Stack": " at doRecursion (functions/my-library.js:30:13)" diff --git a/modules/javascript-udfs/examples/select-airline-inline.js b/modules/javascript-udfs/examples/select-airline-inline.js index 499e1bc4a..162fcb537 100644 --- a/modules/javascript-udfs/examples/select-airline-inline.js +++ b/modules/javascript-udfs/examples/select-airline-inline.js @@ -1,16 +1,20 @@ function selectAirline(country) { + // SQL++ is written directly into the JavaScript code without the need + // for a function call. var q = SELECT name as airline_name, callsign as airline_callsign FROM `travel-sample`.`inventory`.`airline` - WHERE country = $country; //<1> + WHERE country = $country; var res = []; for (const doc of q) { var airline = {} - airline.name = doc.airline_name // <2> - airline.callsign = doc.airline_callsign // <2> + // Use a standard JavaScript iterator to access the values + // from the SQL++ statement + airline.name = doc.airline_name + airline.callsign = doc.airline_callsign res.push(airline); } diff --git a/modules/javascript-udfs/examples/select-hotels-inline.js b/modules/javascript-udfs/examples/select-hotels-inline.js index 0f41eb020..9d8b5fcf5 100644 --- a/modules/javascript-udfs/examples/select-hotels-inline.js +++ b/modules/javascript-udfs/examples/select-hotels-inline.js @@ -1,14 +1,14 @@ function selectHotels() { - var q = SELECT * FROM `travel-sample`.`inventory`.`hotel`; // <.> + var q = SELECT * FROM `travel-sample`.`inventory`.`hotel`; var res = []; - for (const doc of q) { // <.> + for (const doc of q) { - res.push(doc); // <.> + res.push(doc); } - return res; // <.> + return res; } \ No newline at end of file diff --git a/modules/javascript-udfs/examples/variadic-sum.js b/modules/javascript-udfs/examples/variadic-sum.js index fb83d1b9e..bfe1b8e5a 100644 --- a/modules/javascript-udfs/examples/variadic-sum.js +++ b/modules/javascript-udfs/examples/variadic-sum.js @@ -1,8 +1,8 @@ -function sumListOfNumbers(... args) { // <.> +function sumListOfNumbers(... args) { var sum = 0; - args.forEach(value => sum = sum + value); // <.> + args.forEach(value => sum = sum + value); return sum; diff --git a/modules/javascript-udfs/pages/.calling-javascript-from-n1ql.adoc b/modules/javascript-udfs/pages/.calling-javascript-from-n1ql.adoc deleted file mode 100644 index 6ae86761b..000000000 --- a/modules/javascript-udfs/pages/.calling-javascript-from-n1ql.adoc +++ /dev/null @@ -1,141 +0,0 @@ -= Calling JavaScript from {sqlpp} -:description: Using a {sqlpp} User-Defined Function to call JavaScript functions. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-toclevels: 2 -:keywords: scopes, variadic - -[abstract] -{description} - -== Introduction - -Before you can call a JavaScript function, you must first create a {sqlpp} User-Defined Function to call it. -The process to do this is explained in the xref:guides:create-user-defined-function.adoc#creating-the-n1ql-udf-function[Creating the {sqlpp} User-Defined Function] section of the xref:guides:javascript-udfs.adoc[] guide. - -If you are unfamiliar with creating User-Defined Functions to call JavaScript, then the xref:guides:javascript-udfs.adoc[guide] is the best place to start. - -In this section, you're going to take a closer look at concepts around {sqlpp} User-Defined Functions, such as variadic parameter lists. - -== Scopes and {sqlpp} User-Defined Functions - -A JavaScript function can be xref:guides:create-javascript-library.adoc#creating-the-library-and-adding-your-first-function[created through the WorkBench or through the REST API]. - -[source, javascript, role="no-callouts"] ----- -include::example$get-business-days.js[] ----- - -And the corresponding {sqlpp} User-Defined Function can be created through the xref:guides:create-user-defined-function.adoc#creating-the-n1ql-udf-function[Query Workbench] or by executing a {sqlpp} statement: - -[source, sqlpp] ----- -include::example$create-scoped-n1ql-udf.n1ql[] ----- -<.> The new {sqlpp} User-Defined Function is called `GetBusinessDays` and takes the `inventory` scope inside the `travel-sample` bucket. -As well as providing a logical separation between JavaScript libraries, using scopes provides a means of securing access to the library: a user must have a context that matches the scope of the library in order to access it. -<.> This function will reference the `getBusinessDays` JavaScript function … -<.> … in a library called `my-library` which is set to the `inventory` scope within the `travel-sample` bucket. - -=== Global Library Path - -Of course, you can define the library at the cluster level, where it will be accessible to anyone who has access rights to the cluster. -Functions in the global library are accessible across the cluster. - -.Creating a {sqlpp} User-Defined Function to access the JavaScript function in the global library -[source, sqlpp] ----- -include::example$create-globally-scoped-n1ql-udf.n1ql[] ----- -<.> There is no prefix path before `my-library` which means the library is a globally accessible library defined at the cluster level. - -=== Relative Library Path - -You can also use relative paths for the library location: - -[source, sqlpp] ----- -include::example$create-relatively-scoped-n1ql-udf.n1ql[] ----- -<.> In this case, the User-Defined Function will be created for the JavaScript function under the current xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context]. - -== Calling the Function - -Once the {sqlpp} User-Defined Function is defined, it can be called as if it were a built-in {sqlpp} function: - -.Query -[source, sqlpp] ----- -include::example$select-get-business-days.n1ql[] ----- - -which will return the following result: - -.Result -[source, json] ----- -include::example$select-get-business-days-response.jsonc[] ----- - -You can also use the `EXECUTE FUNCTION` statement to execute the function: - -[source, sqlpp] ----- -include::example$execute-javascript-function.n1ql[] ----- - -or as part of a complex statement: - -[source, n1ql, role=no-callouts] ----- -include::example$select-true-alias-get-business-days.n1ql[] ----- - -[#variadic-parameters] -== Variadic Parameters - -You can define a {sqlpp} User-Defined Function with a variadic parameter, which means that the parameter will accept a list of values which it will pass to the JavaScript function it references. -You can create the `GetBusinessDays` function using a variadic parameter rather than the `startDate` and `endDate` parameters: - -[source, sqlpp] ----- -include::example$create-variadic-n1ql-udf.n1ql[] ----- -Note that the statement used three dots (...) rather than a list of parameter list. This indicates a variable length parameter list. -The underlying JavaScript function will reference the parameter list as named variables: - -[source, javascript, role=no-callouts] ----- -include::example$get-business-days-no-ops.js[] ----- - -You can also use a variable length parameter list in the JavaScript function itself: - -[source, javascript] ----- -include::example$variadic-sum.js[] ----- -<.> JavaScript uses three dots (...) followed by a parameter name to denote a parameter that is an array of values. -<.> Scans through the variadic parameter list, summing all the numbers it contains. - -A {sqlpp} User-Defined Function can now be created that takes a variable length list of numbers as an argument: - -[source, sqlpp] ----- -include::example$create-sum-function.n1ql[] ----- - -which can then be called with a variable length list of numbers as a parameter - -.Query -[source, sqlpp] ----- -include::example$execute-sum-function.n1ql[] ----- - -.Result -[source, json] ----- -include::example$execute-sum-function-result.jsonc[] ----- diff --git a/modules/javascript-udfs/pages/.calling-n1ql-from-javascript.adoc b/modules/javascript-udfs/pages/.calling-n1ql-from-javascript.adoc deleted file mode 100644 index 63712b2ec..000000000 --- a/modules/javascript-udfs/pages/.calling-n1ql-from-javascript.adoc +++ /dev/null @@ -1,186 +0,0 @@ -= Calling {sqlpp} from JavaScript -:description: Executing {sqlpp} statements from JavaScript functions. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-toclevels: 2 -:stem: - -[abstract] -{description} - -== Introduction - -As well as being able to call JavaScript functions from {sqlpp}, you can also call {sqlpp} statements from inside your JavaScript functions. - -[#calling-statements-inline] -== Calling {sqlpp} Statements Inline - -You can embed a {sqlpp} statement directly in your JavaScript code: - -[source,javascript] ----- -include::example$add-airline-inline-call.js[] ----- - -== Executing {sqlpp} Statements Using the N1QL() Call - -In addition, you can also execute a {sqlpp} statement by calling it from the `N1QL(…)` function. -[source, javascript] ----- -include::example$add-airline-n1ql-call.js[] ----- - -NOTE: Behind the scenes, the inline call method will generate the equivalent `{sqlpp}` call, so whichever you choose to use will come down to personal preference. - - -== Side Effects - -Functions executed as part of expressions cannot have side effects that will change data stored by the Couchbase engine. -For example, this statement: - -[source, sqlpp] ----- -include::example$add-airline-with-side-effect.n1ql[] ----- - -will generate an error because the `AddAirline()` function will attempt to alter data, which the caller may be unaware of. - -IMPORTANT: Functions that change data must be called using the `EXECUTE FUNCTION` statement. - - -== Returning Values from {sqlpp} Statements - -As shown in the <>, embedded {sqlpp} statements return values which can be used later on in your code. - -The values returned from the statement calls are JavaScript https://www.w3schools.com/js/js_object_iterables.asp[iterators^]: lists of values or documents returned from the database. -In the next example, you're going to retrieve a list of the hotels stored in the `travel-sample` database: - -[source, javascript] ----- -include::example$select-hotels-inline.js[] ----- - -<.> The {sqlpp} statement returns an iterator containing the items retrieved by the query. -<.> Using the standard JavaScript iterator pattern to loop through the items returned in `q`. -<.> Add the current document from the iterator to the result array `res`. -<.> Once all the items have been retrieved, return the result array. - -[IMPORTANT] -==== -If an inline statement/{sqlpp} call does not return a value, then the associated {sqlpp} statement is executed as part of a synchronous operation. i.e. the runtime will wait until the statement completes before moving on to the next line of JavaScript. - -If the inline statement/{sqlpp} call returns a value then it is executed _asynchronously_: execution continues before the iterator is returned. -Each document is fetched from the bucket as it requested by the iterator. - -[plantuml,inline-call-sequence,svg,subs=attributes] -.... -include::partial$diagrams/inline-call-sequence.puml[] -.... -==== - -== Passing Parameters to {sqlpp} Statements - -You can pass parameters from your JavaScript to your {sqlpp} statements. -Parameters can either be _positional_ or _named_. - -Positional:: The parameters are applied to the statement in the order they appear in the list. -+ -[source, javascript] ----- -include::example$add-airline-positional-pararamers.js[] ----- - -Named:: The parameters are given a mnemonic name attached to the value, so they can be included directly in the {sqlpp} statement. -+ -[source, javascript] ----- -include::example$add-airline-named-parameters.js[] ----- -+ -NOTE: The names of the parameters passed into the JavaScript function are used in the {sqlpp} statement without any need to assign the parameters in a separate step. - -[sidebar] -**** -{sqlpp} calls support both _named_ and _positional_ parameters. -Inline calls only support named parameters. - -[cols="^,^,^"] - -|=== -|Call |Named Parameters |Positional Parameters - -|{sqlpp} calls -| ✔️ -| ✔ - -|Inline Calls -| ✔️ -| ❌ -|=== - -**** - -== Transactions - -Transactions are supported from {sqlpp} statements called from JavaScript functions. - -* The function can run statements in a transaction that was started before the function was executed. -* The function can run a statement that starts the transaction. -* The function can run a statement that rolls back a transaction. - -[NOTE] -==== -A {sqlpp} statement and its corresponding iterator must live entirely within the scope of a transaction. -If a transaction is started during the iteration process, then the transaction cannot be rolled back entirely. - -[plantuml,transactions-and-iterators,svg,subs=attributes] -.... -include::partial$diagrams/transactions-and-iterators.puml[] -.... -==== - -== Role-Based Access Control - -In order to execute {sqlpp} statements as part of a JavaScript function, the user executing the function must have the appropriate privileges to perform the action on any objects referenced in the {sqlpp} statement. - -== Executing {sqlpp} Statements that Call Functions - -It is often the case that JavaScript function will call a {sqlpp} statement that may itself call another JavaScript function. -However, it is important to be aware that each JavaScript function call executed from a parent call will use a new JavaScript worker process to run. -The deeper the calls are nested, the fewer JavaScript workers are available to run, so the calling chain will eventually fail and throw an error. -This can be demonstrated using a recursive call sequence as shown below: - -[source, javascript] ----- -include::example$do-recursion.js[] ----- - -Then executing the function: - -[source, sqlpp] ----- -include::example$execute-do-recursion.n1ql[] ----- - -returns the following result: - -:number-of-calls: 10 -[source, json, subs="+attributes"] ----- -include::example$do-recursion-response.jsonc[] ----- -<.> The call failed after {number-of-calls} nested call, which exhausted the number of JavaScript workers available during the call sequence. - -[NOTE] -==== -The JavaScript workers are created when the Couchbase server is started up. - -asciimath:["Number of JavaScript Workers" = 4 xx "Number of CPUs"] - -The service will automatically prevent recursive calls if there are fewer than 50% JavaScript workers available. -==== - -== Further Reading - -include::guides:partial$javascript-udfs/further-reading.adoc[tags="select;execute-function;transactions;rbac"] diff --git a/modules/javascript-udfs/pages/.handling-errors-javascript-udf.adoc b/modules/javascript-udfs/pages/.handling-errors-javascript-udf.adoc deleted file mode 100644 index 8e5ce12d7..000000000 --- a/modules/javascript-udfs/pages/.handling-errors-javascript-udf.adoc +++ /dev/null @@ -1,113 +0,0 @@ -= Handling Errors in JavaScript Functions -:description: Error handling in JavaScript user-defined functions use the same standard exception mechanism as part of the language standard. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] -:page-topic-type: reference -:page-toclevels: 2 - -[abstract] -{description} - -== Handle Errors with the Runtime - -Errors that occur during the execution of a {sqlpp} statement are usually handled by the runtime, which will return a JSON object giving details of the error. -For example, if you execute a record insertion function with a key that already exists: - -[source, sqlpp] ----- -include::example$execute-airline-function-with-date.n1ql[] ----- - -then an error object is returned: - -[source, json] ----- -include::example$duplicate-key-error.json5[] ----- - -== Handle Errors with the Function - -In most cases, it's a lot better if the JavaScript function itself can handle errors that are likely to occur. -This gives the developer the option of responding with a more user-friendly message, or taking an alternative course of action. - -The following function will add an airline record, but will return an `failure` message if the attempt isn't successful. - -[source, javascript] ----- -include::example$add-airline-with-check-return.js[] ----- - -If the record key already exists, then calling this method with `EXECUTE FUNCTION` will produce the following result: - -[source, json] ----- -include::example$add-airline-with-check-failure-return-response.jsonc[] ----- - -Alternatively, you can simply throw the error, rather than returning it as a string: - -[source, javascript] ----- -include::example$add-airline-with-check-throw.js[] ----- - -which will produce the following result: - -[source,javascript] ----- -include::example$add-airline-with-check-failure-throw-response.jsonc[] ----- - -As well as wrapping the expection in a detailed JSON object, there is another fundamental difference between throwing an error or returning it. - -=== Throw vs Return - -Aside from the data returned, throwing an error or returning a value/error will affect how subsequent {sqlpp} operations are processed. - -*Returning an error*:: If the JavaScript function _returns_ any value, then the {sqlpp} runtime will assume that the function completed successfully, and the caller will continue to run subsequent statements. - -*Throwing an error*:: If an error is _thrown_ then this is treated as an error condition, so further statements in the request will not be run. - -You can, of course, throw the error object itself, rather than just a string. - -[source, javascript] ----- -include::example$add-airline-with-check-throw-object.js[] ----- - -which deliver a lot more useful information than just posting back a string: - -[source, json] ----- -include::example$add-airline-with-check-failure-throw-object-response.jsonc[] ----- - -=== Parse the Error - -Another approach is to parse the error using the `JSON.parse()` function and return the resulting object: - -[source, javascript] ----- -include::example$add-airline-check-parse.js[] ----- -<.> The `error` object contains a JSON string (`message`) detailing the nature of the error. -It is much easier to interrogate the message if it's converted back into a JSON object on its own. -This code will send back the entire message structure. - -[source, json] ----- -include::example$parsed-error-in-full.json5[] ----- - -=== Carry Out Alternative Actions - -Once you know the structure of the error message, there's no reason why you can't carry out alternative actions depending on the type of error encountered: - -[source, javascript] ----- -include::example$add-airline-parse-check-2.js[] ----- -<.> Check to see if this is a message that can be handled by the function itself. -<.> Strips out the `"Duplicate Key: "` part of the message, leaving just the duplicate key. - diff --git a/modules/javascript-udfs/pages/calling-javascript-from-n1ql.adoc b/modules/javascript-udfs/pages/calling-javascript-from-n1ql.adoc new file mode 100644 index 000000000..7134e01c7 --- /dev/null +++ b/modules/javascript-udfs/pages/calling-javascript-from-n1ql.adoc @@ -0,0 +1,143 @@ += Call JavaScript from {sqlpp} +:description: You can use user-defined functions (UDFs) to call JavaScript code from the Query Tab. +:page-topic-type: guide +:page-toclevels: 2 +:keywords: scopes, variadic + +[abstract] +{description} + +For more information about user-defined functions in Capella, see xref:guides:javascript-udfs.adoc[]. + +== Create a Scoped User-Defined Function with {sqlpp} + +For example, the following JavaScript function calculates the number of business days between a given start date and end date: + +[source, javascript, role="no-callouts"] +---- +include::example$get-business-days.js[] +---- + +You could define this JavaScript function as part of a xref:guides:create-javascript-library.adoc[UDF library] or as an xref:guides:create-user-defined-function.adoc#create-inline[inline user-defined function]. + +If the JavaScript is already defined in a UDF library, you can xref:guides:create-user-defined-function.adoc[create the user-defined function] from the Query tab or by running a {sqlpp} statement. + +The following {sqlpp} statement creates the `GetBusinessDays` function inside the `travel-sample`.`inventory` namespace. + +It references the `GetBusinessDays` function stored inside the `my-library` UDF library, also stored in `travel-sample`.`inventory`. +`GetBusinessDays` becomes a *Scoped* function: + +[source, sqlpp] +---- +include::example$create-scoped-n1ql-udf.n1ql[] +---- + +=== Create a Global User-Defined Function with {sqlpp} + +You can also define *Global* user-defined functions from the Query Tab using {sqlpp}. +Any function added globally will be accessible across your cluster: + +[source, sqlpp] +---- +include::example$create-globally-scoped-n1ql-udf.n1ql[] +---- +If you do not add a prefix before the library name in your {sqlpp} statement, the user-defined function will be created as a *Global* function. + +=== Create a User-Defined Function with {sqlpp} and A Relative Library Path + +You can also use {sqlpp} and define a relative path for your UDF library location. +If you use a relative path, the library location will resolve based on the current xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context]. + +For example, the following {sqlpp} statement creates the user-defined function in `my-library`, relative to the current query context: + +[source, sqlpp] +---- +include::example$create-relatively-scoped-n1ql-udf.n1ql[] +---- + +== Call a User-Defined Function with {sqlpp} + +After you create your user-defined function, you can call it like any built-in {sqlpp} function. + +For example, you could use the following to call the `GetBusinessDays` function: + +.Query +[source, sqlpp] +---- +include::example$select-get-business-days.n1ql[] +---- + +The function would return the following result, in JSON format: + +.Result +[source, json] +---- +include::example$select-get-business-days-response.jsonc[] +---- + +You can also use the `EXECUTE FUNCTION` statement to execute the function: + +[source, sqlpp] +---- +include::example$execute-javascript-function.n1ql[] +---- + +Or, you can call a function as part of a complex statement: + +[source, n1ql, role=no-callouts] +---- +include::example$select-true-alias-get-business-days.n1ql[] +---- + +[#variadic-parameters] +== Create and Call Functions with a Variadic Parameter + +You can define user-defined functions with a *variadic parameter*. +Variadic parameters accept a list of values, which they pass to your defined JavaScript function. + +For example, you could define the `GetBusinessDays` function to use a variadic parameter, instead of setting both the `startDate` and `endDate` parameters: + +[source, sqlpp] +---- +include::example$create-variadic-n1ql-udf.n1ql[] +---- + +You can add a variadic parameter to any user-defined function by using 3 periods (...) instead of a list of parameters. +The 3 periods indicate a variable length parameter list. + +If you use a user-defined function that has a variadic parameter list, but the original JavaScript function has named variables, the JavaScript function still references the variadic parameter list as named variables: + +[source, javascript, role=no-callouts] +---- +include::example$get-business-days-no-ops.js[] +---- + +When you define your JavaScript function, you can also use a variable length parameter list. + +For example, the following function iterates through a variadic parameter list, and returns the sum of all the numbers it contains: + +[source, javascript] +---- +include::example$variadic-sum.js[] +---- + +Then, you can create a user-defined function that takes a variable length list of numbers as an argument: + +[source, sqlpp] +---- +include::example$create-sum-function.n1ql[] +---- + +You can then call the user-defined function in a query, with a variable length list of numbers as a parameter: + +.Query +[source, sqlpp] +---- +include::example$execute-sum-function.n1ql[] +---- + +.Result +[source, json] +---- +include::example$execute-sum-function-result.jsonc[] +---- diff --git a/modules/javascript-udfs/pages/calling-n1ql-from-javascript.adoc b/modules/javascript-udfs/pages/calling-n1ql-from-javascript.adoc new file mode 100644 index 000000000..842c0dd3f --- /dev/null +++ b/modules/javascript-udfs/pages/calling-n1ql-from-javascript.adoc @@ -0,0 +1,200 @@ += Calling {sqlpp} from JavaScript +:description: You can run {sqlpp} statements from inside the JavaScript code you use for a user-defined function. +:page-topic-type: guide +:page-toclevels: 2 +:stem: + +[abstract] +{description} + +User-defined functions support calling JavaScript and executing {sqlpp} statements together. + +For more information about user-defined functions in Capella, see xref:guides:javascript-udfs.adoc[]. + +[#calling-statements-inline] +== Calling {sqlpp} Statements Inline + +You can embed a {sqlpp} statement directly in your JavaScript code: + +[source,javascript] +---- +include::example$add-airline-inline-call.js[] +---- + +== Executing {sqlpp} Statements Using the N1QL() Call + +You can also execute a {sqlpp} statement by calling it from the `N1QL(…)` function. + +[source, javascript] +---- +include::example$add-airline-n1ql-call.js[] +---- + +NOTE: The `N1QL()` function generates the equivalent {sqlpp} call. +You can choose to use either method and get the same results. +The `N1QL()` function changes your available parameter support. +See <>. + + +== Side Effects + +If you choose to use a {sqlpp} statement inside a JavaScript function, that function cannot have side effects that change data stored by Capella. + +For example, in this {sqlpp} statement, the `AddAirline()` function attempts to alter data, which may be an unintended side effect. +The statement returns an error: + +[source, sqlpp] +---- +include::example$add-airline-with-side-effect.n1ql[] +---- + +IMPORTANT: Functions that change data must be called using the `EXECUTE FUNCTION` statement. + + +== Returning Values from {sqlpp} Statements + +An embedded {sqlpp} statement can return values that you can use later in your JavaScript code for a user-defined function. + +The values returned from statement calls are JavaScript https://www.w3schools.com/js/js_object_iterables.asp[iterators^]. +Iterators are lists of values or documents returned from your cluster. + +In the following example, the function `selectHotels()` retrieves a list of hotels stored in the `travel-sample`.`inventory`.`hotel` collection. +It returns the list of hotels as an iterator. +The iterator is stored in the variable, `q`. +The function then iterates through each item in `q` to create and return a new result array, `res`: + +[source, javascript] +---- +include::example$select-hotels-inline.js[] +---- + +[IMPORTANT] +==== +If an inline statement or {sqlpp} call does not return a value, then the associated {sqlpp} statement is executed as part of a synchronous operation. +This means the runtime will wait until the statement completes before moving on to the next line of JavaScript. + +If the inline statement or {sqlpp} call returns a value, then it's executed asynchronously. +Execution of the JavaScript continues before the iterator is returned. +Each document is fetched from the bucket as it's requested by the iterator. + +[plantuml,inline-call-sequence,svg,subs=attributes] +.... +include::partial$diagrams/inline-call-sequence.puml[] +.... +==== + +[#pass-parameters] +== Passing Parameters to {sqlpp} Statements + +You can pass parameters from your JavaScript code to your {sqlpp} statements. +Parameters can either be *positional* or *named*. + +Positional:: The parameters are applied to the statement in the order they appear in the list. +For example, in the following `addAirlineWithPositionalParameters` function, the `id` would be set as `1600`, type as `airline`, `name` to the supplied value of the `name` variable, and so on: ++ +[source, javascript] +---- +include::example$add-airline-positional-pararamers.js[] +---- + +Named:: The parameters are given a mnemonic name attached to the value, so they can be included directly in the {sqlpp} statement. +For example, in the following `addAirlineWithNamedParameters` function, the names of the parameters passed into the JavaScript function are used in the {sqlpp} statement, without needing to assign the parameters in a separate step. +The parameters are assigned by prefixing the parameter names with a `$`: ++ +[source, javascript] +---- +include::example$add-airline-named-parameters.js[] +---- + +Calling {sqlpp} from the `N1QL()` function supports both *named* and *positional* parameters. +Calling a {sqlpp} statement or expression inline supports only *named* parameters. + +[cols="^,^,^"] + +|=== +|Call |Named Parameters |Positional Parameters + +|Calling {sqlpp} using `N1QL()` +| ✔️ +| ✔️ + +|Inline Calls +| ✔️ +| ❌ +|=== + + +== Transactions + +Transactions are supported from {sqlpp} statements called from JavaScript functions. + +Functions can: + +* Run statements in a transaction that was started before the function was executed. +* Run a statement that starts the transaction. +* Run a statement that rolls back a transaction. + +[NOTE] +==== +A {sqlpp} statement and its corresponding iterator must live entirely within the scope of a transaction. +If a transaction is started during the iteration process, then the transaction cannot be rolled back entirely. + +[plantuml,transactions-and-iterators,svg,subs=attributes] +.... +include::partial$diagrams/transactions-and-iterators.puml[] +.... +==== + +== Role-Based Access Control and Functions + +To execute a {sqlpp} statement from a JavaScript function, you must have the correct permissions on your account to perform actions on any objects referenced in the {sqlpp} statement. + +For more information about roles that control data access in Capella, see xref:projects:project-roles.adoc[]. + +== Executing {sqlpp} Statements That Call Functions + +You can also create a JavaScript function that calls a {sqlpp} statement, which then calls another JavaScript function. + +Be careful when nesting calls to multiple JavaScript functions. +Each function call executed from a parent call uses a new JavaScript worker process to run. +The deeper you nest your calls, the fewer JavaScript workers you will have available to execute those calls. +Your calling chain will eventually fail and throw an error. + +For example, if you used the following user-defined function, `doRecursion()`: + +[source, javascript] +---- +include::example$do-recursion.js[] +---- + +Executing the function with the `EXECUTE FUNCTION` call would start an infinite recursion: + +[source, sqlpp] +---- +include::example$execute-do-recursion.n1ql[] +---- + +The function call would eventually return the following result, stopping the call after too many nested function calls exhausted the JavaScript workers: + +:number-of-calls: 10 +[source, json, subs="+attributes"] +---- +include::example$do-recursion-response.jsonc[] +---- + +[NOTE] +==== +JavaScript workers are created on your Capella cluster based on the following formula: + +asciimath:["Number of JavaScript Workers" = 4 xx "Number of CPUs"] + +Capella will automatically prevent recursive calls if there are fewer than 50% of the total JavaScript workers available. +==== + +== See Also + +* xref:calling-javascript-from-n1ql.adoc[] +* xref:handling-errors-javascript-udf.adoc[] +* xref:n1ql:n1ql-language-reference/execfunction.adoc[] +* xref:n1ql:n1ql-language-reference/index.adoc[] +* xref:guides:transactions.adoc[] diff --git a/modules/javascript-udfs/pages/handling-errors-javascript-udf.adoc b/modules/javascript-udfs/pages/handling-errors-javascript-udf.adoc new file mode 100644 index 000000000..fbbf3899c --- /dev/null +++ b/modules/javascript-udfs/pages/handling-errors-javascript-udf.adoc @@ -0,0 +1,127 @@ += Handling Errors in JavaScript Functions +:description: You can handle errors in JavaScript user-defined functions with the same standard exception mechanism you would use in any JavaScript code. +:page-topic-type: reference +:page-toclevels: 2 + +[abstract] +{description} + +For more information about user-defined functions in Capella, see xref:guides:javascript-udfs.adoc[]. + +You can also choose to handle errors <> or <>. + +{sqlpp} also supports using the xref:n1ql:n1ql-language-reference/metafun.adoc#abort[ABORT()] function inside a query or a user-defined function to return an error. + +[#runtime] +== Handle Errors with the Runtime + +If you run into an error during the execution of a {sqlpp} statement, you can handle those errors with the runtime. + +The runtime returns a JSON object with the details of the error that occurred. +For example, if you execute a record insertion function with a key that already exists: + +[source, sqlpp] +---- +include::example$execute-airline-function-with-date.n1ql[] +---- + +then an error object is returned: + +[source, json] +---- +include::example$duplicate-key-error.json5[] +---- + +[#function] +== Handle Errors with the Function + +In most cases, it's better to use your JavaScript function to handle likely errors. +You can provide a more user-friendly message or take an alternative course of action. + +For example, the following function will add an airline record, but will return a `failure` message if the `try` {sqlpp} statement fails: + +[source, javascript] +---- +include::example$add-airline-with-check-return.js[] +---- + +If the document with the given `id` value already exists in the `travel-sample`.`inventory`.`airline` collection, then the function would return the following: + +[source, json] +---- +include::example$add-airline-with-check-failure-return-response.jsonc[] +---- + +You can also choose to throw the error, rather than returning it as a string: + +[source, javascript] +---- +include::example$add-airline-with-check-throw.js[] +---- + +Running the preceding function with an `id` value that already exists would return the following: + +[source,javascript] +---- +include::example$add-airline-with-check-failure-throw-response.jsonc[] +---- + +When you throw the error, the exception is returned as a detailed JSON object. + +=== Throwing or Returning Errors + +The data the JavaScript engine returns is different on a throw vs. a return. +Throw and return also affect how subsequent {sqlpp} operations are processed in a function. + +*Returning an error*:: If the JavaScript function returns any value, including an error, then the {sqlpp} runtime will assume that the function completed successfully, and the caller will continue to run subsequent statements. + +*Throwing an error*:: If an error is thrown, then this is treated as an error condition. +Further statements in the request will not run. + +You can also choose to throw the error object, rather than just the string. +For example, the following function throws the entire error object, rather than just a string: + +[source, javascript] +---- +include::example$add-airline-with-check-throw-object.js[] +---- + +Throwing the error object returns more useful information than just returning a string. +For example, the following response makes it clear that the function failed to run because of a duplicate key value: + +[source, json] +---- +include::example$add-airline-with-check-failure-throw-object-response.jsonc[] +---- + +=== Parsing Errors + +You can also choose to parse the error with the `JSON.parse()` function and return the resulting object. + +For example, the following function takes the object from `error.message` and returns that if an error occurs: + +[source, javascript] +---- +include::example$add-airline-check-parse.js[] +---- + +If you ran the function on an `id` value that already exists in your cluster, the function would return a JSON object like the following: + +[source, json] +---- +include::example$parsed-error-in-full.json5[] +---- + +=== Complex Error Handling + +Using the structure of the error message returned by `catch (error)`, you can also use `JSON.parse()` to add more complex error handling to your JavaScript function. + +You could parse out the specific cause of an error, and display a more user-friendly error message that tells the user of your function how to resolve their error, or other, more complex logic. + +For example, in this function, if the error returned has a `message.cause.key` value of `dml.statement.duplicatekey`, the function returns only the duplicate key, without the `Duplicate Key:` part of the message: + +[source, javascript] +---- +include::example$add-airline-parse-check-2.js[] +---- + diff --git a/modules/javascript-udfs/pages/.javascript-functions-with-couchbase.adoc b/modules/javascript-udfs/pages/javascript-functions-with-couchbase.adoc similarity index 67% rename from modules/javascript-udfs/pages/.javascript-functions-with-couchbase.adoc rename to modules/javascript-udfs/pages/javascript-functions-with-couchbase.adoc index 82b44b81b..b891f2744 100644 --- a/modules/javascript-udfs/pages/.javascript-functions-with-couchbase.adoc +++ b/modules/javascript-udfs/pages/javascript-functions-with-couchbase.adoc @@ -1,8 +1,5 @@ = JavaScript Functions for Query Reference -:description: This reference guide describes how to write extension functions for {sqlpp} for Query using the JavaScript language. -ifndef::flag-devex-javascript-udfs[] -:page-embargo: EMBARGOED -endif::flag-devex-javascript-udfs[] +:description: You can write extension functions for {sqlpp} for Query in Couchbase Capella, using the JavaScript programming language. :page-topic-type: reference :page-toclevels: 2 :keywords: library namespacing @@ -10,26 +7,24 @@ endif::flag-devex-javascript-udfs[] [abstract] {description} -== Introduction +== About User-Defined Functions in {sqlpp} for Query include::partial$javascript-udf-introduction.adoc[] -This reference includes details of external functions using JavaScript. - include::partial$libraries-and-scopes.adoc[] include::partial$sqlpp-managed-udfs.adoc[] -== Added Constructs +== Added Language Constructs -JavaScript functions in {sqlpp} for Query support most of the language constructs available in https://en.wikipedia.org/wiki/ECMAScript[ECMAScript], though there are a number of restrictions related to the Couchbase environment. -There are also additions that have been made to the language for working specifically with Couchbase. +User-defined functions in {sqlpp} for Query support most of the language constructs available in https://en.wikipedia.org/wiki/ECMAScript[ECMAScript^]. +Couchbase's implementation makes specific changes to support working with JavaScript through {sqlpp}. === {sqlpp} Embedded Statements -Top level {sqlpp} keywords, such as SELECT, UPDATE, INSERT and DELETE, are available as inline keywords in functions. -Operations that return values such as SELECT are accessible through a returned iterable handle. -{sqlpp} Query results, via a SELECT, are streamed in batches to the iterable handle as the iteration progresses through the result set. +Top level {sqlpp} keywords, such as `SELECT`, `UPDATE`, `INSERT` and `DELETE`, are available as inline keywords in functions. +Operations that return values such as `SELECT` are accessible through a returned iterable handle. +{sqlpp} Query results, through `SELECT`, are streamed in batches to the iterable handle as the iteration progresses through the result set. .JavaScript code with embedded {sqlpp} statements ==== @@ -37,25 +32,28 @@ Operations that return values such as SELECT are accessible through a returned i ---- include::example$select-airline-inline.js[] ---- - -<1> The {sqlpp} is written directly into the JavaScript code without having to be used as part of a function call. -You can even provide parameters that can be used in the {sqlpp} statement. -<2> A standard JavaScript iterator is used to access the values returned from the {sqlpp} statement. +You can even provide parameters in your JavaScript code that can be used in the {sqlpp} statement. ==== -For more details, see xref:calling-n1ql-from-javascript.adoc[]. +For more information, see xref:calling-n1ql-from-javascript.adoc[]. -== Unsupported Features +== Unsupported JavaScript Features -The following features are not supported in JavaScript functions for Query. +The following features are not supported in JavaScript functions for Query: + +* <> +* <> +* <> === Browser Extensions -Because JavaScript functions in {sqlpp} for Query do not execute in the context of a browser, the extensions that browsers add to the core language, such as window methods, DOM events, and so on, are not available. +JavaScript functions in {sqlpp} for Query do not execute in the context of a browser. +The extensions that browsers add to the core language, such as window methods, DOM events, and so on, are not available. === Global State -All variables must be local to the function; global state is not permitted. +All variables must be local to the function. +Global state is not permitted. .JavaScript code with global variable ==== @@ -65,18 +63,21 @@ include::example$illegal-global-variable.js[] ---- ==== -Along with global state, global https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions[arrow functions] are not supported. +Along with global state, global https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions[arrow functions^] are not supported. Arrow functions local to individual JavaScript functions are supported. === Logging Logging using the `console.log(..)` function is not supported. -== Restricted Features +== Restricted JavaScript Features [.status]#Couchbase Server 7.6.2# -The following features are restricted in JavaScript functions for Query in Couchbase Server 7.6.2 and later. +The following features are restricted in JavaScript functions for Query in Couchbase Server 7.6.2 and later: + +* <> +* <> === Code Injection @@ -169,8 +170,7 @@ function sleep(milliseconds) { * xref:n1ql:n1ql-language-reference/userfun.adoc[] .External Libraries -* xref:tools:udfs-ui.adoc[] -* xref:n1ql-rest-functions:index.adoc[] +* xref:guides:create-javascript-library.adoc[] .JavaScript Functions * xref:calling-javascript-from-n1ql.adoc[] diff --git a/modules/javascript-udfs/partials/javascript-udf-introduction.adoc b/modules/javascript-udfs/partials/javascript-udf-introduction.adoc index d7f473006..d3b504cfd 100644 --- a/modules/javascript-udfs/partials/javascript-udf-introduction.adoc +++ b/modules/javascript-udfs/partials/javascript-udf-introduction.adoc @@ -1,8 +1,10 @@ -There are two types of user-defined function in {sqlpp} for Query: +Capella supports 2 types of user-defined function in {sqlpp} for Query: -* [.term]#Inline functions# are defined using {sqlpp} expressions. -They enable you to name and reuse complex or repetitive expressions, including subqueries, in order to simplify your queries. +* [.term]#*Inline functions*# are defined using {sqlpp} or JavaScript expressions. +Use an inline function to reuse complex or repetitive expressions, including subqueries, and simplify your {sqlpp} queries. -* [.term]#External functions# are defined using an external language. -They enable you to create functions that may be difficult or impossible to define using built-in {sqlpp} expressions. -The only supported language is JavaScript. \ No newline at end of file +* [.term]#*External functions*# are defined using an external language and stored in a user-defined function (UDF) library. +External functions can be collectively managed through their UDF library. +Capella supports defining external functions using JavaScript. + +You can use inline or external JavaScript functions to run expressions or queries that may be difficult or impossible to define using built-in {sqlpp} expressions. \ No newline at end of file diff --git a/modules/javascript-udfs/partials/libraries-and-scopes.adoc b/modules/javascript-udfs/partials/libraries-and-scopes.adoc index b6bba3717..7545fff68 100644 --- a/modules/javascript-udfs/partials/libraries-and-scopes.adoc +++ b/modules/javascript-udfs/partials/libraries-and-scopes.adoc @@ -7,21 +7,24 @@ The libraries and scope section can be reused in the SQL++ reference === External Libraries (((library namespacing))) -You can store JavaScript functions in [.term]#external libraries#. +You can store JavaScript functions in a [.term]#user-defined function (UDF) library#. This enables you to share external function code for use in more than one {sqlpp} user-defined function. -A library can contain one or more JavaScript functions. +A library can contain 1 or more JavaScript functions. -You must create the external library and the external function code using the xref:tools:udfs-ui.adoc[Query Workbench] or the {sqlpp} xref:n1ql-rest-functions:index.adoc[Functions REST API]. +For more information about how to create a UDF library, see xref:guides:create-javascript-library.adoc[]. // This line left blank intentionally // end::extract[] -You do not call the external function code directly from {sqlpp}. -Instead, when you have created an external library and added a JavaScript function to it, you must define a {sqlpp} user-defined function to call the JavaScript function. +You cannot call external function code directly from {sqlpp}. +If you want to manage your external function code with a library, you must: + +* xref:guides:create-javascript-library.adoc[Create a UDF library] and add a JavaScript function or functions to that library. +* xref:guides:create-user-defined-function.adoc[Create a user-defined function] to call a specific JavaScript function from that library. // tag::extract[] -External libraries, like {sqlpp} user-defined functions, may be scoped or global. -This enables you to keep the code for external functions separate where required. +UDF libraries, like {sqlpp} user-defined functions, may be scoped or global. +Set a UDF library or user-defined function as *Scoped* to keep the code for external functions separate. // This line left blank intentionally // end::extract[] @@ -36,14 +39,14 @@ include::partial$diagrams/javascript-scopes.puml[] * A [.term]#scoped library# is created within a xref:n1ql:n1ql-intro/queriesandresults.adoc#logical-hierarchy[scope], at the same level as the collections within a scope. -You can apply access restrictions to scopes, so that only certain groups of users will be able to access collections and libraries within that scope. +//You can apply access restrictions to scopes, so that only certain groups of users will be able to access collections and libraries within that scope. // tag::extract[] -Code which is stored in a scoped library is private to users of that scope, and is not visible or available to users of another scope. -Code which is stored in a global library is available to users of all scopes. +//Code which is stored in a scoped library is private to users of that scope, and is not visible or available to users of another scope. +Any code that you store in a global library is available to all users with read and write permissions on your cluster. A global library may have the same name as a scoped library, and scoped libraries may have the same name as each other. -For example, you may have a global `math` library, and a `math` library in each scope. +For example, you can have a global `math` library, and a `math` library in each scope. // end::extract[] .Calling a function in a scoped external library @@ -52,4 +55,4 @@ For example, you may have a global `math` library, and a `math` library in each include::partial$diagrams/udf-scopes-diagram.puml[] .... -In order to use a {sqlpp} user-defined function which calls external JavaScript code in a scoped library, you must set the xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] to the same bucket and scope as the scoped library. +When you want to use a *Scoped* user-defined function from the Query Tab, you must set your xref:n1ql:n1ql-intro/queriesandresults.adoc#query-context[query context] to the same bucket and scope as the library. diff --git a/modules/javascript-udfs/partials/sqlpp-managed-udfs.adoc b/modules/javascript-udfs/partials/sqlpp-managed-udfs.adoc index a42f091b6..77955b480 100644 --- a/modules/javascript-udfs/partials/sqlpp-managed-udfs.adoc +++ b/modules/javascript-udfs/partials/sqlpp-managed-udfs.adoc @@ -2,8 +2,8 @@ [.status]#Couchbase Server 7.6# -In clusters using Couchbase Server 7.6 and later, you can create the code for an external function and the corresponding {sqlpp} user-defined function in a single operation. -This means that you don't have to specify an external library and create the code for the external function, before creating the {sqlpp} user-defined function. +For clusters using Couchbase Server 7.6 and later, you can create the code for a JavaScript function and the corresponding {sqlpp} user-defined function in a single operation in the Query Tab or cbq. +You do not have to create a UDF library before creating a {sqlpp} user-defined function. -With a {sqlpp} managed user-defined function, the external function code is stored inline, along with the {sqlpp} user-defined function. -You cannot share this external function code with other user-defined functions, or access it from any external libraries. +With a {sqlpp} managed user-defined function, the JavaScript function code is stored inline, along with the {sqlpp} user-defined function. +You cannot share this JavaScript function code with other user-defined functions, or access it from a UDF library. diff --git a/modules/n1ql/pages/n1ql-language-reference/createfunction.adoc b/modules/n1ql/pages/n1ql-language-reference/createfunction.adoc index 0c0fab15f..b7610e0c8 100644 --- a/modules/n1ql/pages/n1ql-language-reference/createfunction.adoc +++ b/modules/n1ql/pages/n1ql-language-reference/createfunction.adoc @@ -16,7 +16,7 @@ include::partial$n1ql-language-reference/horizontal-style.adoc[] include::javascript-udfs:partial$javascript-udf-introduction.adoc[] -External functions in {sqlpp} support most of the language constructs available in ECMAScript. +JavaScript functions in {sqlpp} support most of the language constructs available in ECMAScript. For more information about the restrictions and extensions that come with the Couchbase implementation, see xref:javascript-udfs:javascript-functions-with-couchbase.adoc[]. [[context]] @@ -42,52 +42,13 @@ If you want to call a user-defined function outside of the current query context Finally, it is important to note that a global function is _not_ the same as a scoped function stored in the default scope in a bucket. -ifdef::flag-devex-javascript-udfs[] include::javascript-udfs:partial$libraries-and-scopes.adoc[tags=extract] -endif::flag-devex-javascript-udfs[] include::javascript-udfs:partial$sqlpp-managed-udfs.adoc[] == Prerequisites -[cols="2,3"] -|=== -| To manage ... | You must have ... - -| Global inline functions -| *Manage Global Functions* role. - -| Scoped inline functions -| *Manage Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Manage Global External Functions* role. - -| Scoped external functions -| *Manage Scope External Functions* role, with permissions on the specified bucket and scope. -|=== - -Users with the *Manage Scope External Functions* role also have read-only access to any global external library. - -[cols="2,3"] -|=== -| To execute ... | You must have ... - -| Global inline functions -| *Execute Global Functions* role. - -| Scoped inline functions -| *Execute Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Execute Global External Functions* role. - -| Scoped external functions -| *Execute Scope External Functions* role, with permissions on the specified bucket and scope. -|=== - -For more details about user roles, see -xref:server:learn:security/authorization-overview.adoc[Authorization]. +* To manage user-defined functions on your cluster, you must have the xref:projects:project-roles.adoc#project-owner-role[`Project Owner`] or the xref:projects:project-roles.adoc#project-cluster-data-reader-writer[`Cluster Data Reader/Writer`] role. == Syntax @@ -200,11 +161,9 @@ If you need to return multiple values, construct an array. [[create-function-external]] :section: external -=== External Functions +=== JavaScript Functions -ifdef::flag-devex-javascript-udfs[] -There are two alternative syntaxes for defining an external function: one where the function code is stored in an external library, and one for creating a {sqlpp} managed user-defined function. -endif::flag-devex-javascript-udfs[] +There are two alternative syntaxes for defining a JavaScript function: one where the function code is stored in an external library, and one for creating a {sqlpp} managed user-defined function. [source,ebnf] ---- @@ -231,31 +190,19 @@ include::createfunction.adoc[tag=arguments] [Optional] Use this parameter where the function code is stored in an external library. -ifndef::flag-devex-javascript-udfs[] -Note that Capella does not currently support a way to create or manage an external library. -endif::flag-devex-javascript-udfs[] - -ifdef::flag-devex-javascript-udfs[] The name of the JavaScript function that you want to use for the user-defined function. This parameter is a string and must be wrapped in quotes. -endif::flag-devex-javascript-udfs[] [[external-library]] ==== External Library [Optional] Use this parameter where the function code is stored in an external library. -ifndef::flag-devex-javascript-udfs[] -Note that Capella does not currently support a way to create or manage an external library. -endif::flag-devex-javascript-udfs[] - -ifdef::flag-devex-javascript-udfs[] The name of the JavaScript library that contains the JavaScript function you want to use. This parameter is a string and must be wrapped in quotes. The name of a scoped external library must include the bucket name, the scope name, and the library name, separated by slashes. For example, to refer to a scoped library called `my-library` located in the `inventory` scope within the `travel-sample` bucket, you would specify the library name as `travel-sample/inventory/my-library`. -endif::flag-devex-javascript-udfs[] [[javascript]] ==== Function Body @@ -492,7 +439,7 @@ EXECUTE FUNCTION add100(100); ifdef::flag-devex-javascript-udfs[] [[ex-external]] -.External functions +.JavaScript functions ==== The following command registers two JavaScript functions called `encodeGeoHash` and `calculateAdjacent` in a library called `geohash-js`. footnote:[Credit: https://github.com/davetroy/geohash-js] @@ -639,9 +586,7 @@ endif::flag-devex-javascript-udfs[] == Related Links -ifdef::flag-devex-javascript-udfs[] -* To manage external libraries and external functions, see xref:n1ql-rest-functions:index.adoc[]. -endif::flag-devex-javascript-udfs[] +* To manage UDF libraries and JavaScript functions, see xref:guides:create-javascript-library.adoc[]. * To execute a user-defined function, see xref:n1ql-language-reference/execfunction.adoc[]. * To see the execution plan for a user-defined function, see xref:n1ql-language-reference/explainfunction.adoc[]. * To include a user-defined function in an expression, see xref:n1ql-language-reference/userfun.adoc[]. diff --git a/modules/n1ql/pages/n1ql-language-reference/dropfunction.adoc b/modules/n1ql/pages/n1ql-language-reference/dropfunction.adoc index 685b28972..149b4d280 100644 --- a/modules/n1ql/pages/n1ql-language-reference/dropfunction.adoc +++ b/modules/n1ql/pages/n1ql-language-reference/dropfunction.adoc @@ -12,25 +12,7 @@ include::partial$n1ql-language-reference/horizontal-style.adoc[] == Prerequisites -[cols="2,3"] -|=== -| To manage ... | You must have ... - -| Global inline functions -| *Manage Global Functions* role. - -| Scoped inline functions -| *Manage Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Manage Global External Functions* role. - -| Scoped external functions -| *Manage Scope External Functions* role, with permissions on the specified bucket and scope. -|=== - -For more details about user roles, see -xref:server:learn:security/authorization-overview.adoc[Authorization]. +* To manage user-defined functions on your cluster, you must have the xref:projects:project-roles.adoc#project-owner-role[`Project Owner`] or the xref:projects:project-roles.adoc#project-cluster-data-reader-writer[`Cluster Data Reader/Writer`] role. == Syntax @@ -80,14 +62,12 @@ footnote:context[That is, you are dropping a global function, and the function d == Usage -ifdef::flag-devex-javascript-udfs[] -When you drop a user-defined function whose definition is stored in an external library, the external library and function on which the user-defined function depended are not deleted. -This enables you to create a new user-defined function with a different name, or a different number of parameters, using the same JavaScript library and function. +When you drop a user-defined function whose definition is stored in a UDF library, the library and function on which the user-defined function depended are not deleted. +This enables you to create a new user-defined function with a different name, or a different number of parameters, using the same UDF library and JavaScript function. -To change or delete an external library or the external function code, you must use the {sqlpp} xref:n1ql-rest-functions:index.adoc[Functions REST API]. -endif::flag-devex-javascript-udfs[] +To change or delete a UDF library or the JavaScript function code, see xref:guides:create-javascript-library.adoc#delete-udf[Delete a User-Defined Function Library]. -When you drop a {sqlpp} managed user-defined function, the associated external function code is deleted also. +When you drop an inline {sqlpp} user-defined function, the associated JavaScript function code is deleted also. == Examples @@ -110,7 +90,7 @@ SELECT * FROM system:functions; .Drop a {sqlpp} managed user-defined function ==== -This statement deletes a {sqlpp} managed user-defined function called `add100`. +This statement deletes an inline {sqlpp} user-defined function called `add100`. [source,sqlpp] ---- @@ -125,10 +105,9 @@ SELECT * FROM system:functions; ---- ==== -ifdef::flag-devex-javascript-udfs[] -.Drop an external function +.Drop a UDF library function ==== -These statements delete two external functions: +These statements delete two UDF library functions: . A function called `geohash`, which depends on the JavaScript `encodeGeoHash` function in the `geohash-js` library; . A function called `adjacent`, which depends on the JavaScript `calculateAdjacent` function in the `geohash-js` library. @@ -140,7 +119,8 @@ DROP FUNCTION geohash; DROP FUNCTION adjacent; ---- -You can run the following command to check that the JavaScript `geohash-js` library and the `encodeGeoHash` and `calculateAdjacent` functions are still available. +//// +You can run the following command to check that the `geohash-js` library and the `encodeGeoHash` and `calculateAdjacent` functions are still available. [source,sh] ---- @@ -148,15 +128,13 @@ curl -v -X GET \ http://localhost:8093/evaluator/v1/libraries/geohash-js \ -u Administrator:password ---- -==== -endif::flag-devex-javascript-udfs[] +==== +//// == Related Links -* To create user-defined functions, refer to xref:n1ql-language-reference/createfunction.adoc[]. -ifdef::flag-devex-javascript-udfs[] -* To manage external libraries and external functions, see xref:n1ql-rest-functions:index.adoc[]. -endif::flag-devex-javascript-udfs[] +* To create user-defined functions, see xref:n1ql-language-reference/createfunction.adoc[]. +* To manage external libraries and external functions, see xref:guides:create-javascript-library.adoc[]. * To execute a user-defined function, see xref:n1ql-language-reference/execfunction.adoc[]. * To see the execution plan for a user-defined function, see xref:n1ql-language-reference/explainfunction.adoc[]. * To include a user-defined function in an expression, see xref:n1ql-language-reference/userfun.adoc[]. diff --git a/modules/n1ql/pages/n1ql-language-reference/execfunction.adoc b/modules/n1ql/pages/n1ql-language-reference/execfunction.adoc index eef2abdd2..d9d7f0c87 100644 --- a/modules/n1ql/pages/n1ql-language-reference/execfunction.adoc +++ b/modules/n1ql/pages/n1ql-language-reference/execfunction.adoc @@ -21,25 +21,7 @@ If you do this, error `10101: Function not found` is generated. == Prerequisites -[cols="2,3"] -|=== -| To execute ... | You must have ... - -| Global inline functions -| *Execute Global Functions* role. - -| Scoped inline functions -| *Execute Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Execute Global External Functions* role. - -| Scoped external functions -| *Execute Scope External Functions* role, with permissions on the specified bucket and scope. -|=== - -For more details about user roles, see -xref:server:learn:security/authorization-overview.adoc[Authorization]. +* To manage user-defined functions on your cluster, you must have the xref:projects:project-roles.adoc#project-owner-role[`Project Owner`] or the xref:projects:project-roles.adoc#project-cluster-data-reader-writer[`Cluster Data Reader/Writer`] role. == Syntax @@ -87,14 +69,12 @@ include::partial$n1ql-language-reference/udf-output.adoc[] == Examples -For examples, refer to xref:n1ql-language-reference/createfunction.adoc#examples[CREATE FUNCTION]. +For examples, see xref:n1ql-language-reference/createfunction.adoc#examples[CREATE FUNCTION]. == Related Links -* To create user-defined functions, refer to xref:n1ql-language-reference/createfunction.adoc[]. -ifdef::flag-devex-rest-api[] -* To manage external libraries and external functions, see xref:n1ql-rest-functions:index.adoc[]. -endif::flag-devex-rest-api[] +* To create user-defined functions, see xref:n1ql-language-reference/createfunction.adoc[]. +* To manage UDF libraries and JavaScript functions, see xref:guides:create-javascript-library.adoc[]. * To see the execution plan for a user-defined function, see xref:n1ql-language-reference/explainfunction.adoc[]. * To include a user-defined function in an expression, see xref:n1ql-language-reference/userfun.adoc[]. * To monitor user-defined functions, see xref:n1ql:n1ql-intro/sysinfo.adoc#sys-functions[Monitor Functions]. diff --git a/modules/n1ql/pages/n1ql-language-reference/explainfunction.adoc b/modules/n1ql/pages/n1ql-language-reference/explainfunction.adoc index 8ce6cdbe8..3e667e990 100644 --- a/modules/n1ql/pages/n1ql-language-reference/explainfunction.adoc +++ b/modules/n1ql/pages/n1ql-language-reference/explainfunction.adoc @@ -30,28 +30,10 @@ The following constraints apply: == Prerequisites -[cols="2,3"] -|=== -| To execute EXPLAIN FUNCTION on ... | You must have ... - -| Global inline functions -| *Execute Global Functions* role. - -| Scoped inline functions -| *Execute Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Execute Global External Functions* role. - -| Scoped external functions -| *Execute Scope External Functions* role, with permissions on the specified bucket and scope. -|=== +* To manage user-defined functions on your cluster, you must have the xref:projects:project-roles.adoc#project-owner-role[`Project Owner`] or the xref:projects:project-roles.adoc#project-cluster-data-reader-writer[`Cluster Data Reader/Writer`] role. You must also have the necessary privileges required for the {sqlpp} statements inside the function. -For more information about user roles, see -xref:server:learn:security/authorization-overview.adoc[Authorization]. - == Syntax [source,ebnf] @@ -136,10 +118,8 @@ include::n1ql:example$utility/explainfunctionjs.jsonc[] == Related Links -* To create user-defined functions, refer to xref:n1ql-language-reference/createfunction.adoc[]. -ifdef::flag-devex-rest-api[] -* To manage external libraries and external functions, see xref:n1ql-rest-functions:index.adoc[]. -endif::flag-devex-rest-api[] +* To create user-defined functions, see xref:n1ql-language-reference/createfunction.adoc[]. +* To manage UDF libraries and JavaScript functions, see xref:guides:create-javascript-library.adoc[]. * To execute a user-defined function, see xref:n1ql-language-reference/execfunction.adoc[]. * To include a user-defined function in an expression, see xref:n1ql-language-reference/userfun.adoc[]. * To monitor user-defined functions, see xref:n1ql:n1ql-intro/sysinfo.adoc#sys-functions[Monitor Functions]. diff --git a/modules/n1ql/pages/n1ql-language-reference/userfun.adoc b/modules/n1ql/pages/n1ql-language-reference/userfun.adoc index c8f4e4341..85fd01bf4 100644 --- a/modules/n1ql/pages/n1ql-language-reference/userfun.adoc +++ b/modules/n1ql/pages/n1ql-language-reference/userfun.adoc @@ -27,25 +27,7 @@ When you do this, an error is generated. == Prerequisites -[cols="2,3"] -|=== -| To execute ... | You must have ... - -| Global inline functions -| *Execute Global Functions* role. - -| Scoped inline functions -| *Execute Scope Functions* role, with permissions on the specified bucket and scope. - -| Global external functions -| *Execute Global External Functions* role. - -| Scoped external functions -| *Execute Scope External Functions* role, with permissions on the specified bucket and scope. -|=== - -For more details about user roles, see -xref:server:learn:security/authorization-overview.adoc[Authorization]. +* To manage user-defined functions on your cluster, you must have the xref:projects:project-roles.adoc#project-owner-role[`Project Owner`] or the xref:projects:project-roles.adoc#project-cluster-data-reader-writer[`Cluster Data Reader/Writer`] role. == Arguments @@ -142,7 +124,7 @@ include::example$n1ql-language-reference/udf-locations-query.jsonc[] .External functions ==== -For this example, it is assumed that you have created two external functions: +For this example, it is assumed that you have created two JavaScript functions in a UDF library: . A function called `geohash`, which depends on the JavaScript `encodeGeoHash` function in the `geohash-js` library; . A function called `adjacent`, which depends on the JavaScript `calculateAdjacent` function in the `geohash-js` library. @@ -185,9 +167,9 @@ At the latitude of the selected hotel, each geohash represents an area of approx == Related Links -* To create user-defined functions, refer to xref:n1ql-language-reference/createfunction.adoc[]. +* To create user-defined functions, see xref:n1ql-language-reference/createfunction.adoc[]. ifdef::flag-devex-rest-api[] -* To manage external libraries and external functions, see xref:n1ql-rest-functions:index.adoc[]. +* To manage UDF libraries and JavaScript functions, see xref:n1ql-rest-functions:index.adoc[]. endif::flag-devex-rest-api[] * To execute a user-defined function, see xref:n1ql-language-reference/execfunction.adoc[]. * To see the execution plan for a user-defined function, see xref:n1ql-language-reference/explainfunction.adoc[]. diff --git a/modules/n1ql/partials/nav.adoc b/modules/n1ql/partials/nav.adoc index 2c0ac4549..b61a43aaf 100644 --- a/modules/n1ql/partials/nav.adoc +++ b/modules/n1ql/partials/nav.adoc @@ -21,12 +21,10 @@ *** xref:guides:insert.adoc[] *** xref:guides:update.adoc[] *** xref:guides:delete.adoc[] -ifdef::flag-devex-javascript-udfs[] ** xref:guides:javascript-udfs.adoc[] *** xref:guides:create-javascript-library.adoc[] *** xref:guides:create-user-defined-function.adoc[] *** xref:guides:call-user-defined-function.adoc[] -endif::flag-devex-javascript-udfs[] ** xref:n1ql:advanced.adoc[] *** xref:n1ql:n1ql-language-reference/cost-based-optimizer.adoc[] *** xref:guides:cbo.adoc[] @@ -159,9 +157,7 @@ endif::flag-devex-javascript-udfs[] *** xref:learn:services-and-indexes/indexes/early-filters-and-pagination.adoc[] *** xref:learn:services-and-indexes/indexes/index-replication.adoc[] *** xref:learn:services-and-indexes/indexes/storage-modes.adoc[] -ifdef::flag-devex-javascript-udfs[] ** xref:javascript-udfs:javascript-functions-with-couchbase.adoc[] *** xref:javascript-udfs:calling-javascript-from-n1ql.adoc[] *** xref:javascript-udfs:calling-n1ql-from-javascript.adoc[] *** xref:javascript-udfs:handling-errors-javascript-udf.adoc[] -endif::flag-devex-javascript-udfs[]