Todos los productos
Search
Centro de documentos

E-MapReduce:Java UDF

Última actualización: Sep 04, 2026

Este tema describe cómo desarrollar y utilizar funciones definidas por el usuario (UDF) en Java.

Contexto

A partir de la versión 2.2.0, StarRocks admite funciones definidas por el usuario (UDF) escritas en Java.

Desde la versión 3.0, StarRocks es compatible con UDF globales. Solo necesita agregar la palabra clave GLOBAL a las instrucciones SQL correspondientes (CREATE, SHOW y DROP) para aplicarlas de forma global. Esto evita tener que ejecutar las instrucciones individualmente para cada base de datos. Puede desarrollar funciones personalizadas según sus escenarios de negocio para ampliar las capacidades de StarRocks.

Actualmente, StarRocks admite los siguientes tipos de UDF:

  • función definida por el usuario escalar (scalar UDF)

  • función de agregación definida por el usuario (UDAF)

  • función de ventana definida por el usuario (UDWF)

  • función con valor de tabla definida por el usuario (UDTF)

Requisitos previos ​

Antes de utilizar UDF de Java en StarRocks, asegúrese de cumplir con los siguientes requisitos:

  • Debe tener Apache Maven instalado para crear y desarrollar proyectos de Java.

  • Debe tener JDK 1.8 instalado en su servidor.

  • Habilite la función UDF estableciendo el elemento de configuración del FE enable_udf en TRUE en la página Instance Configuration. A continuación, reinicie la instancia para aplicar el cambio.

Mapeo de tipos de datos

Tipo SQL

Tipo Java

BOOLEAN

java.lang.Boolean

TINYINT

java.lang.Byte

SMALLINT

java.lang.Short

INT

java.lang.Integer

BIGINT

java.lang.Long

FLOAT

java.lang.Float

DOUBLE

java.lang.Double

STRING/VARCHAR

java.lang.String

Desarrollar y utilizar UDF

Deberá crear un proyecto Maven y escribir la función en Java.

Paso 1: Crear un proyecto Maven

Cree un proyecto Maven con la siguiente estructura básica de directorio.

project
|--pom.xml
|--src
|  |--main
|  |  |--java
|  |  |--resources
|  |--test
|--target

Paso 2: Agregar dependencias ​

Agregue las siguientes dependencias al archivo pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>udf</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Paso 3: Desarrollar la UDF ​

Desarrolle las UDF necesarias en Java.

Desarrollar una scalar UDF

Una scalar UDF procesa una fila a la vez y devuelve un único valor por cada fila. Cuando se utiliza una scalar UDF en una consulta, cada fila aparece en el conjunto de resultados. Las funciones escalares típicas incluyen UPPER, LOWER, ROUND y ABS.

El siguiente ejemplo muestra cómo extraer datos de un objeto JSON. En algunos escenarios de negocio, el valor de un campo en datos JSON puede ser una cadena JSON en lugar de un objeto JSON. Para extraer la cadena JSON anidada, debe usar una llamada anidada a la función GET_JSON_STRING, como GET_JSON_STRING(GET_JSON_STRING('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key"), "$.k0").

Para simplificar la instrucción SQL, puede desarrollar una UDF que extraiga la cadena JSON directamente; por ejemplo, MY_UDF_JSON_GET('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key.k0").

package com.starrocks.udf.sample;
import com.alibaba.fastjson.JSONPath;

public class UDFJsonGet {
    public final String evaluate(String jsonObj, String key) {
        if (obj == null || key == null) return null;
        try {
            // The JSONPath library can fully expand the path, even if a field value is a JSON-formatted string.
            return JSONPath.read(jsonObj, key).toString();
        } catch (Exception e) {
            return null;
        }
    }
}

Su clase personalizada debe implementar el siguiente método.

Nota

Los tipos de datos de los parámetros de solicitud y del valor devuelto en el método deben coincidir con los declarados en la instrucción CREATE FUNCTION del paso 6. El mapeo de tipos de datos debe cumplir con las reglas descritas en Data type mapping.

Método

Descripción

TYPE1 evaluate(TYPE2, ...)

El método evaluate es el punto de entrada de la UDF y debe ser un método miembro público.

Desarrollar una UDAF

Una UDAF opera sobre un grupo de filas y devuelve un único valor. Las funciones de agregación típicas incluyen SUM, COUNT, MAX y MIN. Estas funciones agregan datos de varias filas dentro de cada grupo GROUP BY y generan un único resultado por grupo.

El siguiente ejemplo utiliza la función MY_SUM_INT. A diferencia de la función integrada SUM, que devuelve un valor BIGINT, MY_SUM_INT toma un argumento INT y devuelve un INT.

package com.starrocks.udf.sample;

public class SumInt {
    public static class State {
        int counter = 0;
        public int serializeLength() { return 4; }
    }

    public State create() {
        return new State();
    }

    public void destroy(State state) {
    }

    public final void update(State state, Integer val) {
        if (val != null) {
            state.counter+= val;
        }
    }

    public void serialize(State state, java.nio.ByteBuffer buff) {
        buff.putInt(state.counter);
    }

    public void merge(State state, java.nio.ByteBuffer buffer) {
        int val = buffer.getInt();
        state.counter += val;
    }

    public Integer finalize(State state) {
        return state.counter;
    }
}

Su clase personalizada debe implementar los siguientes métodos.

Nota

Los tipos de datos de los parámetros de solicitud y del valor devuelto en los métodos deben coincidir con los declarados en la instrucción CREATE FUNCTION del paso 6. El mapeo de tipos de datos debe cumplir con las reglas descritas en Data type mapping.

Método

Descripción

State create()

Crea un objeto de estado.

void destroy(State)

Destruye el objeto de estado.

void update(State, ...)

Actualiza el estado. El primer argumento es el objeto de estado y los argumentos subsiguientes son los parámetros de solicitud especificados en la declaración de la función. Se admiten uno o más parámetros de solicitud.

void serialize(State, ByteBuffer)

Serializa el estado.

void merge(State, ByteBuffer)

Fusiona un estado serializado en el estado actual.

TYPE finalize(State)

Calcula y devuelve el resultado final a partir del estado.

Al desarrollar una UDAF, debe utilizar la clase de búfer java.nio.ByteBuffer para almacenar el resultado intermedio y el método serializeLength para especificar la longitud del resultado intermedio serializado.

Clase y método

Descripción

java.nio.ByteBuffer()

Esta clase de búfer almacena el resultado intermedio. Dado que el resultado se serializa para transferirse entre nodos, debe usar serializeLength() para especificar su tamaño serializado.

serializeLength()

La longitud en bytes del resultado intermedio serializado. El método serializeLength() debe devolver un INT. Por ejemplo, State { int counter = 0; public int serializeLength() { return 4; }} indica que el resultado intermedio es un INT y su longitud serializada es de 4 bytes. Puede personalizar esto según sus necesidades. Por ejemplo, si el resultado intermedio es un LONG con una longitud de 8 bytes, usaría State { long counter = 0; public int serializeLength() { return 8; }}.

Nota

Tenga en cuenta los siguientes requisitos para la serialización de java.nio.ByteBuffer:

  • No dependa del método remaining() de ByteBuffer para deserializar el estado.

  • No llame al método clear() en el ByteBuffer.

  • El valor devuelto por serializeLength() debe coincidir con la longitud real de los datos escritos en el búfer. Una discrepancia provoca errores de serialización y deserialización.

Desarrollar una UDWF

Una UDWF es un tipo especial de función de agregación. A diferencia de una función de agregación normal, una función de ventana calcula un valor sobre un grupo de filas (una ventana) y devuelve un resultado separado para cada fila. Por lo general, una función de ventana incluye una cláusula OVER que divide las filas en particiones. Luego, la función calcula un resultado para cada fila basándose en la ventana de filas a la que pertenece.

El siguiente ejemplo utiliza la función MY_WINDOW_SUM_INT. A diferencia de la función integrada SUM, que devuelve un valor BIGINT, MY_WINDOW_SUM_INT toma un argumento INT y devuelve un INT.

package com.starrocks.udf.sample;

public class WindowSumInt {    
    public static class State {
        int counter = 0;
        public int serializeLength() { return 4; }
        @Override
        public String toString() {
            return "State{" +
                    "counter=" + counter +
                    '}';
        }
    }

    public State create() {
        return new State();
    }

    public void destroy(State state) {

    }

    public void update(State state, Integer val) {
        if (val != null) {
            state.counter+=val;
        }
    }

    public void serialize(State state, java.nio.ByteBuffer buff) {
        buff.putInt(state.counter);
    }

    public void merge(State state, java.nio.ByteBuffer buffer) {
        int val = buffer.getInt();
        state.counter += val;
    }

    public Integer finalize(State state) {
        return state.counter;
    }

    public void reset(State state) {
        state.counter = 0;
    }

    public void windowUpdate(State state,
                            int peer_group_start, int peer_group_end,
                            int frame_start, int frame_end,
                            Integer[] inputs) {
        for (int i = (int)frame_start; i < (int)frame_end; ++i) {
            state.counter += inputs[i];
        }
    }
}

Dado que una función de ventana es un tipo especial de función de agregación, su clase personalizada debe implementar todos los métodos requeridos para UDAF, además del método windowUpdate().

Nota

Los tipos de datos de los parámetros de solicitud y del valor devuelto en los métodos deben coincidir con los declarados en la instrucción CREATE FUNCTION del paso 6. El mapeo de tipos de datos debe cumplir con las reglas descritas en Data type mapping.

Método adicional obligatorio

Método

Descripción

void windowUpdate(State state, int, int, int , int, ...)

Actualiza los datos de la ventana. Para obtener información detallada sobre las funciones de ventana, consulte Window functions. Para cada fila de entrada, se recupera la información de ventana correspondiente para actualizar el resultado intermedio.

  • peer_group_start: La posición inicial de la partición actual.

    Una partición es un grupo de filas que comparten el mismo valor para la columna especificada en la cláusula PARTITION BY.

  • peer_group_end: La posición final de la partición actual.

  • frame_start: La posición inicial del marco de ventana actual.

    Un marco de ventana es un subconjunto de filas dentro de una partición que define el alcance de cálculo para la fila actual. Por ejemplo, ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING define un marco que incluye la fila actual, la fila anterior y la fila siguiente.

  • frame_end: La posición final del marco de ventana actual.

  • inputs: Los datos de entrada para una ventana, proporcionados como una matriz de clases wrapper. La clase wrapper debe corresponder al tipo de datos de entrada. En este ejemplo, el tipo de datos de entrada es INT, por lo que la matriz de clases wrapper es Integer[].

Desarrollar una UDTF

Una UDTF toma una sola fila como entrada y produce una tabla de filas de salida. Las UDTF se utilizan frecuentemente para operaciones como dividir una columna en varias filas.

Nota

Actualmente, las UDTF solo admiten la devolución de varias filas con una sola columna.

El siguiente ejemplo utiliza la función MY_UDF_SPLIT. Esta divide una cadena utilizando un espacio como separador. Tanto el argumento de entrada como el valor devuelto son del tipo STRING.

package com.starrocks.udf.sample;

public class UDFSplit{
    public String[] process(String in) {
        if (in == null) return null;
        return in.split(" ");
    }
}

Su clase personalizada debe implementar el siguiente método.

Nota

Los tipos de datos de los parámetros de solicitud y del valor devuelto en el método deben coincidir con los declarados en la instrucción CREATE FUNCTION del paso 6. El mapeo de tipos de datos debe cumplir con las reglas descritas en Data type mapping.

Método

Descripción

TYPE[] process()

El método process() es el punto de entrada de la UDTF y debe devolver una matriz.

Paso 4: Empaquetar el proyecto Java

Ejecute el siguiente comando para empaquetar el proyecto Java.

mvn package

Este comando genera dos archivos JAR en el directorio target: udf-1.0-SNAPSHOT.jar y udf-1.0-SNAPSHOT-jar-with-dependencies.jar.

Paso 5: Cargar el proyecto

Cargue el archivo udf-1.0-SNAPSHOT-jar-with-dependencies.jar en un bucket de OSS y otorgue permiso de lectura pública al archivo JAR. Para obtener más información, consulte Simple upload y Bucket ACL.

Nota

En el paso 6, el FE valida el archivo JAR de la UDF y calcula una suma de comprobación. Posteriormente, el BE descarga y ejecuta el archivo JAR.

Paso 6: Crear la UDF en StarRocks

StarRocks proporciona dos namespaces para las UDF: nivel de base de datos y nivel global.

  • Si no requiere aislamiento de visibilidad para sus UDF, puede crear una UDF global. Al hacer referencia a una UDF global, puede llamarla directamente por su nombre de función sin ningún prefijo de catálogo o base de datos, lo que simplifica el acceso.

  • Si requiere aislamiento de visibilidad o necesita crear UDF con el mismo nombre en diferentes bases de datos, puede crear una UDF a nivel de base de datos. Si su sesión actual se encuentra dentro de esa base de datos, puede llamar a la función directamente por su nombre. Si su sesión está en un catálogo o base de datos diferente, debe usar un nombre completo, como catalog.database.function.

Nota

La creación de una UDF global requiere el permiso CREATE GLOBAL FUNCTION a nivel de sistema. La creación de una UDF a nivel de base de datos requiere el permiso CREATE FUNCTION en la base de datos. El uso de cualquier UDF requiere el permiso USAGE sobre ella. Para obtener información sobre cómo otorgar permisos, consulte GRANT.

Después de cargar el archivo JAR, cree la UDF correspondiente en StarRocks. Para crear una UDF global, simplemente agregue la palabra clave GLOBAL a la instrucción SQL.

Sintaxis

CREATE [GLOBAL][AGGREGATE | TABLE] FUNCTION function_name(arg_type [, ...])
RETURNS return_type
[PROPERTIES ("key" = "value" [, ...]) ]

Parámetros

Parámetro

Obligatorio

Descripción

GLOBAL

No

Especifica que la UDF es una UDF global. Compatible desde StarRocks v3.0.

AGGREGATE

No

Especifica que la función que se va a crear es una UDAF o UDWF.

TABLE

No

Especifica que la función que se va a crear es una UDTF.

function_name

Sí

El nombre de la función. Puede incluir un nombre de base de datos, como db1.my_func. Si un function_name incluye un nombre de base de datos, la UDF se crea en esa base de datos. De lo contrario, se crea en la base de datos actual. La combinación del nombre de la función y los argumentos debe ser única dentro de la base de datos. Sin embargo, puede sobrecargar una función creando otra con el mismo nombre pero con diferentes tipos de argumentos.

arg_type

Sí

El tipo de datos de un argumento de función. Para ver los tipos de datos compatibles, consulte Data type mapping.

return_type

Sí

El tipo de datos del valor devuelto por la función. Para ver los tipos de datos compatibles, consulte Data type mapping.

properties

Sí

Propiedades relacionadas con la función. Se requieren diferentes propiedades para distintos tipos de UDF. Consulte los siguientes ejemplos para obtener más detalles.

Crear una scalar UDF

Ejecute el siguiente comando para crear la scalar UDF del ejemplo anterior en StarRocks.

CREATE [GLOBAL] FUNCTION MY_UDF_JSON_GET(string, string) 
RETURNS string
PROPERTIES (
    "symbol" = "com.starrocks.udf.sample.UDFJsonGet", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Parámetro

Descripción

symbol

El nombre de clase completo de la UDF, en el formato <package_name>.<class_name>.

type

El tipo de UDF. Para UDF de Java, establézcalo en StarrocksJar.

file

La ruta HTTP al archivo JAR de la UDF. Debe ser la URL HTTP del archivo en OSS, preferiblemente utilizando un endpoint interno. El formato es http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/<jar_package_name>.

Crear una UDAF

Ejecute el siguiente comando para crear la UDAF del ejemplo anterior en StarRocks.

CREATE [GLOBAL] AGGREGATE FUNCTION MY_SUM_INT(INT) 
RETURNS INT
PROPERTIES 
( 
    "symbol" = "com.starrocks.udf.sample.SumInt", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Los parámetros en la cláusula PROPERTIES son los mismos que se describen en Create a scalar UDF.

Crear una UDWF

Ejecute el siguiente comando para crear la UDWF del ejemplo anterior en StarRocks.

CREATE [GLOBAL] AGGREGATE FUNCTION MY_WINDOW_SUM_INT(Int)
RETURNS Int
PROPERTIES 
(
    "analytic" = "true",
    "symbol" = "com.starrocks.udf.sample.WindowSumInt", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

La propiedad analytic identifica la función como una función de ventana. Para una UDWF, establézcala en true. Los demás parámetros son los mismos que se describen en Create a scalar UDF.

Crear una UDTF

Ejecute el siguiente comando para crear la UDTF del ejemplo anterior en StarRocks.

CREATE [GLOBAL] TABLE FUNCTION MY_UDF_SPLIT(string)
RETURNS string
PROPERTIES 
(
    "symbol" = "com.starrocks.udf.sample.UDFSplit", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Los parámetros en la cláusula PROPERTIES son los mismos que se describen en Create a scalar UDF.

Paso 7: Utilizar la UDF

Después de crear la UDF, puede probarla y utilizarla.

Utilizar la scalar UDF

Ejecute el siguiente comando para utilizar la scalar UDF creada en el paso 6.

SELECT MY_UDF_JSON_GET('{"key":"{\\"in\\":2}"}', '$.key.in');

Utilizar la UDAF

Ejecute el siguiente comando para utilizar la UDAF creada en el paso 6.

SELECT MY_SUM_INT(col1);

Utilizar la UDWF

Ejecute el siguiente comando para utilizar la UDWF creada en el paso 6.

SELECT MY_WINDOW_SUM_INT(intcol) 
            OVER (PARTITION BY intcol2
                  ORDER BY intcol3
                  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM test_basic;

Utilizar la UDTF

Ejecute el siguiente comando para utilizar la UDTF del ejemplo anterior.

-- Assume that table t1 exists with columns a, b, and c1.
SELECT t1.a,t1.b,t1.c1 FROM t1;
> output:
1,2.1,"hello world"
2,2.2,"hello UDTF."

-- Use the MY_UDF_SPLIT() function.
SELECT t1.a,t1.b, MY_UDF_SPLIT FROM t1, MY_UDF_SPLIT(t1.c1); 
> output:
1,2.1,"hello"
1,2.1,"world"
2,2.2,"hello"
2,2.2,"UDTF."
Nota
  • En la lista SELECT, el primer MY_UDF_SPLIT es el alias de columna predeterminado para la salida de la función MY_UDF_SPLIT.

  • Actualmente, no puede especificar un alias de tabla ni alias de columna para la salida de la UDTF, como con AS t2(f1).

Consultar UDF ​

Ejecute el siguiente comando para consultar información sobre las UDF.

SHOW [GLOBAL] FUNCTIONS;

Eliminar UDF

Ejecute el siguiente comando para eliminar una UDF específica.

DROP [GLOBAL] FUNCTION <function_name>(arg_type [, ...]);

Preguntas frecuentes

P: ¿Puedo usar variables estáticas al desarrollar una UDF? ¿Interferirán entre sí las variables estáticas de diferentes UDF?

R: Sí, puede usar variables estáticas. Las variables estáticas están aisladas entre diferentes UDF y no interfieren entre sí, incluso si las UDF comparten el mismo nombre de clase.