4.14 User-Defined Functions (UDFs) in PGX

User-defined functions (UDFs) allow users of PGX to add custom logic to their PGQL queries or custom graph algorithms, to complement built-in functions with custom requirements.

Caution:

UDFs enable running arbitrary code in the PGX server, possibly accessing sensitive data. Additionally, any PGX session can invoke any of the UDFs that are enabled on the PGX server. The application administrator who enables UDFs is responsible for checking the following:

  • All the UDF code can be trusted.
  • The UDFs are stored in a secure location that cannot be tampered with.

Furthermore, PGX assumes UDFs to be state-less and side-effect free.

PGX supports two types of UDFs:

  • Java UDFs
  • JavaScript UDFs

How to Use Java UDFs

The following simple example shows how to register a Java UDF at the PGX server and invoke it.

  1. Create a class with a public static method. For example:
    package my.udfs;
     
    public class MyUdfs {
      public static String concat(String a, String b) {
        return a + b;
      }
    }
    
  2. Compile the class and compress into a JAR file. For example:
    mkdir ./target
    javac -d ./target *.java
    cd target
    jar cvf MyUdfs.jar *
    
  3. Copy the JAR file into /opt/oracle/graph/pgx/server/lib.
  4. Create a UDF JSON configuration file. For example, assume that /path/to/my/udfs/dir/my_udfs.json contains the following:
    {
      "user_defined_functions": [
        {
          "namespace": "my",
          "language": "java",
          "implementation_reference": "my.udfs.MyUdfs",
          "function_name": "concat",
          "return_type": "string",
          "arguments": [
             {
               "name": "a",
               "type": "string"
             },
             {
               "name": "b",
               "type": "string"
             }
           ]
        }
      ]
    }
  5. Point to the directory containing the UDF configuration file in /etc/oracle/graph/pgx.conf. For example:
    "udf_config_directory": "/path/to/my/udfs/dir/"
  6. Restart the PGX server. For example:
    sudo systemctl restart pgx
  7. Try to invoke the UDF from within a PGQL query. For example:
    graph.queryPgql("SELECT my.concat(my.concat(n.firstName, ' '), n.lastName) FROM MATCH (n:Person)")
  8. Try to invoke the UDF from within a PGX algorithm. For example:

    Note:

    For each UDF you want to use, you need to create an abstract method with the same schema that gets annotated with the @Udf annotation.
    import oracle.pgx.algorithm.annotations.Udf;
    ....
     
    @GraphAlgorithm
    public class MyAlogrithm {
      public void bomAlgorithm(PgxGraph g, VertexProperty<String> firstName, VertexProperty<String> lastName, @Out VertexProperty<String> fullName) {
     
     
      ... fullName.set(v, concat(firstName.get(v), lastName.get(v))); ...
     
      }
     
      @Udf(namespace = "my")
      abstract String concat(String a, String b);
    }

JavaScript UDFs

The requirements for a JavaScript UDF is as follows:

  • The JavaScript source must contain all dependencies.
  • The source must contain at least one valid export.
  • The language parameter must be set to javascript in the UDF configuration file.

For example, consider a JavaScript source file format.js as shown:

//format.js
const fun = function(name, country) {
  if (country == null) return name;
  else return name + " (" + country + ")";
}

module.exports = {stringFormat: fun};

In order to load the UDF from format.js, the UDF configuration file will appear as follows:

{
  "namespace": "my",
  "function_name": "format",
  "language": "javascript",
  "source_location": "format.js",
  "source_function_name": "stringFormat",
  "return_type": "string",
  "arguments": [
    {
      "name": "name",
      "type": "string"
    },
    {
      "name": "country",
      "type": "string"
    }
  ]
}

Note:

In this case, since the name of the UDF and the implementing method differ, you need to set the name of the UDF in the source_function_name field. Also, you can provide the path of the source code file in the source_location field.

UDF Configuration File Information

A UDF configuration file is a JSON file containing an array of user_defined_functions. (An example of such a file is in the step to "Create a UDF JSON configuration file" in the preceding How to Use Java UDFs subsection.)

Each user-defined function supports the fields shown in the following table.

Table 4-6 Fields for Each UDF

Field Data Type Description Required?
function_name string Name of the function used as identifier in PGX Required
language enum[java, javascript] Source language for he function (java or javascript) Required
return_type enum[boolean, integer, long, float, double, string] Return type of the function Required
arguments array of object Array of arguments. For each argument: type, argument name, required? []
implementation_reference string Reference to the function name on the classpath null
namespace string Namespace of the function in PGX null
source_code string Source code of the function provided inline null
source_function_name string Name of the function in the source language null
source_location string Local file path to the function's source code null

All configured UDFs must be unique with regard to the combination of the following fields:

  • namespace
  • function_name
  • arguments