Topik ini menjelaskan cara menulis dan menggunakan fungsi yang ditentukan pengguna (UDF).
Informasi latar belakang
StarRocks 2.2.0 dan versi lebih baru mendukung UDF yang ditulis dalam Java.
StarRocks 3.0 dan versi lebih baru mendukung UDF global. Tambahkan kata kunci GLOBAL ke pernyataan SQL seperti CREATE, SHOW, dan DROP agar pernyataan tersebut berlaku secara global. Anda tidak perlu mengeksekusi pernyataan SQL untuk setiap database secara individual. UDF dapat dikonfigurasi sesuai dengan skenario bisnis Anda untuk memperluas kemampuan fungsi StarRocks.
StarRocks mendukung jenis UDF berikut:
Fungsi skalar yang ditentukan pengguna (scalar UDF)
Fungsi agregat yang ditentukan pengguna (UDAF)
Fungsi jendela yang ditentukan pengguna (UDWF)
Fungsi bernilai tabel yang ditentukan pengguna (UDTF)
Prasyarat
Pastikan persyaratan berikut terpenuhi sebelum menggunakan fitur Java UDF di StarRocks:
Instal Apache Maven untuk membuat dan menulis proyek Java terkait.
Instal Java Development Kit (JDK) 1.8 di server.
Aktifkan fitur UDF. Di bagian FE pada tab Instance Configuration halaman detail instance EMR Serverless StarRocks Anda, atur parameter
enable_udfmenjadiTRUE, lalu mulai ulang instance agar konfigurasi berlaku.
Pemetaan tipe data
Tipe SQL | Tipe 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 |
Kembangkan dan gunakan UDF
Anda perlu membuat proyek Maven dan menulis fitur yang sesuai menggunakan Java.
Langkah 1: Buat proyek Maven
Buat proyek Maven dengan struktur direktori dasar berikut:
project
|--pom.xml
|--src
| |--main
| | |--java
| | |--resources
| |--test
|--targetLangkah 2: Tambahkan dependensi
Tambahkan dependensi berikut ke file 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>Langkah 3: Kembangkan UDF
Anda harus mengembangkan UDF menggunakan Java.
Kembangkan scalar UDF
Anda dapat menggunakan scalar UDF untuk melakukan operasi pada satu baris data dan menghasilkan satu baris hasil. Saat menggunakan scalar UDF untuk menanyakan data, setiap baris data akhirnya akan muncul di set hasil berdasarkan baris. Scalar UDF tipikal termasuk UPPER, LOWER, ROUND, dan ABS.
Contoh berikut menunjukkan cara mengekstrak data JSON. Misalnya, nilai bidang dalam data JSON dapat berupa string JSON, bukan objek JSON. Dalam situasi ini, untuk mengekstrak string JSON, diperlukan pemanggilan bersarang fungsi GET_JSON_STRING dalam pernyataan SQL, seperti GET_JSON_STRING(GET_JSON_STRING('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key"), "$.k0").
Untuk menyederhanakan pernyataan SQL, Anda dapat mengembangkan UDF untuk mengekstrak string JSON secara langsung, seperti 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 {
// Perpustakaan JSONPath dapat sepenuhnya diperluas, bahkan jika nilai suatu bidang adalah string JSON.
return JSONPath.read(jsonObj, key).toString();
} catch (Exception e) {
return null;
}
}
}Kelas yang ditentukan pengguna harus mengimplementasikan metode berikut.
Tipe data parameter permintaan dan parameter respons dalam metode harus sama dengan yang dideklarasikan dalam pernyataan CREATE FUNCTION di Langkah 6, dan pemetaan tipe data antara parameter dalam metode dan parameter yang dideklarasikan dalam pernyataan CREATE FUNCTION harus sesuai dengan Pemetaan Tipe Data.
Metode | Deskripsi |
TYPE1 evaluate(TYPE2, ...) | Metode |
Kembangkan UDAF
Anda dapat menggunakan UDAF untuk melakukan operasi pada beberapa baris data dan menghasilkan satu baris hasil. UDAF tipikal termasuk SUM, COUNT, MAX, dan MIN. Fungsi-fungsi ini dapat digunakan untuk menggabungkan beberapa baris data dalam setiap grup GROUP BY dan menghasilkan satu baris hasil.
Dalam contoh berikut, fungsi MY_SUM_INT digunakan. Berbeda dengan fungsi bawaan SUM yang mengembalikan nilai bertipe BIGINT, tipe data parameter input dan respons untuk fungsi MY_SUM_INT adalah 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;
}
}Kelas yang ditentukan pengguna harus mengimplementasikan metode-metode berikut.
Tipe data parameter input dan parameter respons dalam metode harus sama dengan yang dideklarasikan dalam pernyataan CREATE FUNCTION di Langkah 6, dan pemetaan tipe data antara parameter dalam metode dan parameter yang dideklarasikan dalam pernyataan CREATE FUNCTION harus sesuai dengan Pemetaan Tipe Data.
Metode | Deskripsi |
State create() | Buat sebuah State. |
void destroy(State) | Hancurkan sebuah State. |
void update(State, ...) | Perbarui sebuah State. Parameter pertama adalah State, dan sisanya adalah parameter input yang dideklarasikan oleh fungsi. Anda dapat menentukan satu parameter input atau beberapa parameter input. |
void serialize(State, ByteBuffer) | Serialisasi sebuah State. |
void merge(State, ByteBuffer) | Gabungkan dan deserialisasi sebuah State. |
TYPE finalize(State) | Gunakan sebuah State untuk mendapatkan hasil akhir fungsi. |
Saat mengembangkan UDAF, gunakan kelas buffer java.nio.ByteBuffer untuk menyimpan dan merepresentasikan hasil sementara, serta variabel lokal serializeLength untuk menentukan panjang serialisasi hasil tersebut.
Kelas dan variabel lokal | Deskripsi |
java.nio.ByteBuffer() | Kelas buffer yang digunakan untuk menyimpan hasil sementara. Mengingat fakta bahwa hasil sementara diserialisasi dan dideserialisasi saat dikirimkan antara node eksekusi yang berbeda, Anda harus menggunakan serializeLength untuk menentukan panjang serialisasi hasil sementara. |
serializeLength() | Panjang hasil sementara setelah serialisasi. Unit: byte. Tipe data serializeLength tetap menjadi INT. Dalam contoh, |
Perhatikan item-item berikut terkait java.nio.ByteBuffer saat serialisasi dilakukan:
Anda tidak dapat menggunakan metode remaining() dari ByteBuffer untuk mendeserialisasi sebuah State.
Anda tidak dapat memanggil metode clear() dalam ByteBuffer.
Nilai
serializeLengthharus konsisten dengan panjang data yang sebenarnya ditulis. Jika tidak, hasil yang salah mungkin diperoleh selama serialisasi dan deserialisasi.
Kembangkan UDWF
UDWF adalah singkatan dari user-defined window function. Berbeda dengan UDAF umum, UDWF dapat digunakan untuk melakukan perhitungan untuk sekumpulan baris (sebuah jendela) dan mengembalikan hasil untuk setiap baris. Dalam banyak kasus, UDWF berisi klausa OVER yang dapat digunakan untuk membagi baris data menjadi beberapa kelompok. UDWF melakukan perhitungan berdasarkan kelompok (sebuah jendela) tempat setiap baris data berada dan mengembalikan hasil untuk setiap baris.
Dalam contoh berikut, fungsi MY_WINDOW_SUM_INT digunakan. Berbeda dengan fungsi bawaan SUM yang mengembalikan nilai bertipe BIGINT, tipe data parameter input dan respons untuk fungsi MY_WINDOW_SUM_INT dapat berupa 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];
}
}
}Kelas yang ditentukan pengguna harus mengimplementasikan metode yang diperlukan oleh UDAF dan metode windowUpdate(). UDWF adalah UDAF khusus.
Tipe data parameter permintaan dan parameter respons dalam metode harus sama dengan yang dideklarasikan dalam pernyataan CREATE FUNCTION di Langkah 6, dan pemetaan tipe data antara parameter dalam metode dan parameter yang dideklarasikan dalam pernyataan CREATE FUNCTION harus sesuai dengan Pemetaan Tipe Data.
Metode lain yang perlu diimplementasikan
Metode | Deskripsi |
| Perbarui data jendela. Untuk informasi lebih lanjut tentang UDWF, lihat Window_function. Saat Anda memasukkan baris data, informasi jendela yang sesuai diperoleh untuk memperbarui hasil sementara.
|
Kembangkan UDTF
UDTF dapat digunakan untuk membaca baris data dan menghasilkan beberapa nilai yang dapat dianggap sebagai tabel. UDTF sering digunakan untuk mengimplementasikan konversi baris-ke-kolom.
UDTF hanya mendukung pengembalian beberapa baris dalam satu kolom.
Dalam contoh berikut, fungsi MY_UDF_SPLIT digunakan. Fungsi MY_UDF_SPLIT mendukung pemisahan string menggunakan spasi sebagai pembatas. Tipe data parameter input dan parameter respons adalah STRING.
package com.starrocks.udf.sample;
public class UDFSplit{
public String[] process(String in) {
if (in == null) return null;
return in.split(" ");
}
}Kelas yang ditentukan pengguna harus mengimplementasikan metode berikut.
Tipe data parameter permintaan dan parameter respons dalam metode harus sama dengan yang dideklarasikan dalam pernyataan CREATE FUNCTION di Langkah 6, dan pemetaan tipe data antara parameter dalam metode dan parameter yang dideklarasikan dalam pernyataan CREATE FUNCTION harus sesuai dengan Pemetaan Tipe Data.
Metode | Deskripsi |
TYPE[] process() | Metode |
Langkah 4: Kemas proyek Java
Jalankan perintah berikut untuk mengemas proyek Java:
mvn packageDua file dihasilkan di direktori target: udf-1.0-SNAPSHOT.jar dan udf-1.0-SNAPSHOT-jar-with-dependencies.jar.
Langkah 5: Unggah proyek
Unggah file udf-1.0-SNAPSHOT-jar-with-dependencies.jar ke Object Storage Service (OSS) dan atur izin baca publik untuk paket JAR tersebut. Untuk detail lebih lanjut, lihat Unggah Sederhana dan ACL Bucket.
Di Langkah 6, node frontend (FE) memverifikasi paket JAR UDF dan menghitung nilai verifikasi. Node backend (BE) mengunduh paket JAR UDF dan mengeksekusi paket tersebut.
Langkah 6: Buat UDF di StarRocks
StarRocks menyediakan UDF untuk namespace tingkat basis data dan global.
Jika Anda tidak memiliki persyaratan khusus terkait visibilitas UDF, Anda dapat membuat UDF global. Saat merujuk UDF global, Anda dapat langsung memanggil nama fungsi tanpa perlu menggunakan katalog dan basis data sebagai awalan, yang lebih nyaman untuk diakses.
Jika Anda memiliki persyaratan khusus terkait visibilitas UDF atau ingin membuat UDF dengan nama yang sama di database yang berbeda, Anda dapat membuat UDF di dalam database. Dalam hal ini, jika sesi Anda berada di database tempat UDF tersebut berada, Anda dapat langsung memanggil nama fungsi. Jika sesi Anda berada di katalog atau database lain, Anda harus menyertakan katalog dan database sebagai awalan, seperti
catalog.database.function.
Untuk membuat UDF global, Anda harus memiliki izin untuk menjalankan pernyataan tingkat sistem CREATE GLOBAL FUNCTION. Untuk membuat UDF tingkat database, Anda harus memiliki izin untuk menjalankan pernyataan tingkat database CREATE FUNCTION. Untuk menggunakan UDF, Anda harus memiliki izin USAGE pada UDF. Untuk informasi tentang cara memberikan izin yang diperlukan, lihat GRANT.
Setelah Anda mengunggah paket JAR, Anda harus membuat UDF yang sesuai di StarRocks berdasarkan kebutuhan bisnis Anda. Untuk membuat UDF global, Anda hanya perlu menyertakan kata kunci GLOBAL dalam pernyataan SQL.
Sintaksis
CREATE [GLOBAL][AGGREGATE | TABLE] FUNCTION function_name(arg_type [, ...])
RETURNS return_type
[PROPERTIES ("key" = "value" [, ...]) ]Parameter
Parameter | Wajib | Deskripsi |
GLOBAL | Tidak | Jika Anda ingin membuat UDF global, Anda harus menentukan kata kunci ini. Parameter ini didukung di StarRocks 3.0 dan versi lebih baru. |
AGGREGATE | Tidak | Jika Anda ingin membuat UDAF atau UDWF, Anda harus menentukan kata kunci ini. |
TABLE | Tidak | Jika Anda ingin membuat UDTF, Anda harus menentukan kata kunci ini. |
function_name | Ya | Nama fungsi, yang dapat berisi nama database, seperti |
arg_type | Ya | Tipe data parameter dalam fungsi. Untuk informasi tentang tipe data yang didukung, lihat Pemetaan tipe data. |
return_type | Ya | Tipe data nilai kembali dalam fungsi. Untuk informasi tentang tipe data yang didukung, lihat Pemetaan tipe data. |
properties | Ya | Properti fungsi. Anda harus mengonfigurasi properti dari berbagai jenis UDF yang Anda buat. Untuk informasi lebih lanjut dan contoh, lihat bagian-bagian berikutnya. |
Buat scalar UDF
Jalankan perintah berikut untuk membuat scalar UDF di 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"
);Parameter | Deskripsi |
symbol | Nama kelas proyek tempat UDF tersebut berada. Kelas tersebut dinamai dalam format |
type | Tipe UDF. Atur nilainya menjadi |
file | Path HTTP dari paket JAR tempat UDF tersebut berada. Atur nilainya menjadi URL HTTP yang sesuai dengan titik akhir internal di OSS. Formatnya adalah |
Buat UDAF
Jalankan perintah berikut untuk membuat UDAF di 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"
);Deskripsi parameter dalam PROPERTIES sama dengan yang dijelaskan dalam bagian properties di Buat scalar UDF.
Buat UDWF
Jalankan perintah berikut untuk membuat UDWF yang digunakan pada Langkah 3 di 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"
);analytic: Menentukan apakah fungsi yang dibuat adalah fungsi jendela. Atur nilainya menjadi true. Deskripsi parameter sisanya sama dengan yang dijelaskan di Buat Scalar UDF.
Buat UDTF
Jalankan perintah berikut untuk membuat UDTF yang digunakan pada Langkah 3 di 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"
);Deskripsi parameter dalam PROPERTIES sama dengan yang dijelaskan pada bagian properties di Buat scalar UDF.
Langkah 7: Gunakan UDF
Setelah membuat UDF, Anda dapat menguji dan menggunakan UDF yang telah dikembangkan.
Gunakan scalar UDF
Jalankan perintah berikut untuk menggunakan scalar UDF yang dibuat pada Langkah 6:
SELECT MY_UDF_JSON_GET('{"key":"{\\"in\\":2}"}', '$.key.in');Gunakan UDAF
Jalankan perintah berikut untuk menggunakan UDAF yang dibuat pada Langkah 6:
SELECT MY_SUM_INT(col1);Gunakan UDWF
Jalankan perintah berikut untuk menggunakan UDWF yang dibuat pada Langkah 6:
SELECT MY_WINDOW_SUM_INT(intcol)
OVER (PARTITION BY intcol2
ORDER BY intcol3
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM test_basic;Gunakan UDTF
Jalankan perintah berikut untuk menggunakan UDTF yang dibuat pada Langkah 6:
-- Anggaplah tabel t1 yang berisi kolom a, b, dan c1 ada.
SELECT t1.a,t1.b,t1.c1 FROM t1;
> output:
1,2.1,"hello world"
2,2.2,"hello UDTF."
-- Gunakan fungsi MY_UDF_SPLIT().
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."MY_UDF_SPLITpertama adalah alias kolom yang dihasilkan setelah fungsiMY_UDF_SPLITdipanggil.Anda tidak dapat menggunakan metode
AS t2(f1)untuk menentukan alias tabel dan alias kolom untuk tabel yang dikembalikan oleh UDTF.
Lihat informasi UDF
Jalankan perintah berikut untuk melihat informasi UDF:
SHOW [GLOBAL] FUNCTIONS;Hapus UDF
Jalankan perintah berikut untuk menghapus UDF yang ditentukan:
DROP [GLOBAL] FUNCTION <function_name>(arg_type [, ...]);Pertanyaan Umum
T: Apakah saya dapat menggunakan variabel statis saat mengembangkan UDF? Apakah variabel statis dari UDF yang berbeda saling memengaruhi?
J: Anda dapat menggunakan variabel statis saat mengembangkan UDF. Variabel statis dari UDF yang berbeda diisolasi satu sama lain dan tidak saling memengaruhi, meskipun UDF tersebut memiliki nama kelas yang sama.