Fungsi yang ditentukan pengguna (UDF) di legacy SQL
Dokumen ini menjelaskan cara menggunakan fungsi JavaScript yang ditentukan pengguna dalam sintaksis kueri legacy SQL. Sintaksis kueri pilihan untuk BigQuery adalah GoogleSQL. Untuk mengetahui informasi tentang fungsi yang ditentukan pengguna (UDF) di GoogleSQL, lihat Fungsi GoogleSQL yang ditentukan pengguna.
Legacy SQL BigQuery mendukung fungsi yang ditentukan pengguna (UDF) (UDF) yang ditulis dalam JavaScript. UDF mirip dengan fungsi "Map" di MapReduce: UDF mengambil satu baris sebagai input dan menghasilkan nol baris atau beberapa baris sebagai output. Output tersebut dapat berpotensi memiliki skema yang berbeda dengan inputnya.
Untuk mengetahui informasi tentang fungsi yang ditentukan pengguna (UDF) di GoogleSQL, lihat fungsi yang ditentukan pengguna (UDF) di GoogleSQL.
Contoh UDF
// UDF definition function urlDecode(row, emit) { emit({title: decodeHelper(row.title), requests: row.num_requests}); } // Helper function with error handling function decodeHelper(s) { try { return decodeURI(s); } catch (ex) { return s; } } // UDF registration bigquery.defineFunction( 'urlDecode', // Name used to call the function from SQL ['title', 'num_requests'], // Input column names // JSON representation of the output schema [{name: 'title', type: 'string'}, {name: 'requests', type: 'integer'}], urlDecode // The function reference );
Struktur UDF
function name(row, emit) { emit(<output data>); }
UDF BigQuery beroperasi pada tiap-tiap baris pada tabel atau hasil kueri subpilihan. UDF memiliki dua parameter formal:
row
: baris input.emit
: hook yang digunakan oleh BigQuery untuk mengumpulkan data output. Fungsiemit
mengambil satu parameter: objek JavaScript yang merepresentasikan satu baris data output. Fungsiemit
dapat dipanggil lebih dari sekali, misalnya dalam satu loop, untuk menghasilkan beberapa baris data.
Contoh kode berikut menunjukkan UDF dasar.
function urlDecode(row, emit) { emit({title: decodeURI(row.title), requests: row.num_requests}); }
Mendaftarkan UDF
Anda harus mendaftarkan nama untuk fungsi Anda agar fungsi tersebut dapat dipanggil dari BigQuery SQL. Nama yang terdaftar tidak harus sama dengan nama yang Anda gunakan untuk fungsi di JavaScript.
bigquery.defineFunction( '<UDF name>', // Name used to call the function from SQL ['<col1>', '<col2>'], // Input column names // JSON representation of the output schema [<output schema>], // UDF definition or reference <UDF definition or reference> );
Kolom input
Nama kolom input harus cocok dengan nama (atau alias, jika ada) kolom dalam tabel atau subkueri input.
Untuk kolom input yang berupa kumpulan data, Anda harus menentukan—dalam daftar kolom input—kolom leaf yang ingin diakses dari kumpulan data.
Misalnya, jika Anda memiliki data yang menyimpan nama dan usia seseorang:
person RECORD REPEATED name STRING OPTIONAL age INTEGER OPTIONAL
Penentu input untuk nama dan usia adalah:
['person.name', 'person.age']
Penggunaan ['person']
tanpa nama atau usia akan mengakibatkan error.
Output yang dihasilkan akan cocok dengan skema. Anda akan memiliki array objek JavaScript, dengan setiap objek memiliki properti "nama" dan "usia". Contoh:
[ {name: 'alice', age: 23}, {name: 'bob', age: 64}, ... ]
Skema output
Anda harus memberi BigQuery skema atau struktur kumpulan yang dihasilkan UDF, yang direpresentasikan sebagai JSON. Skema dapat berisi jenis data BigQuery yang didukung termasuk kumpulan data bertingkat. Penentu jenis yang didukung adalah:
- boolean
- float
- bilangan bulat
- kumpulan data
- string
- stempel waktu
Contoh kode berikut menunjukkan sintaksis untuk kumpulan data dalam skema output. Setiap kolom output memerlukan atribut name
dan type
. Kolom bertingkat juga harus berisi atribut fields
.
[{name: 'foo_bar', type: 'record', fields: [{name: 'a', type: 'string'}, {name: 'b', type: 'integer'}, {name: 'c', type: 'boolean'}] }]
Setiap kolom dapat berisi atribut mode
opsional, yang mendukung nilai berikut:
- nullable : nilai ini tersedia secara default dan dapat dihilangkan.
- wajib : jika ditentukan, kolom yang dimaksud harus ditetapkan ke sebuah nilai dan tidak boleh tidak ditetapkan.
- berulang : jika ditetapkan, kolom yang diberikan harus berupa array.
Baris yang diteruskan ke fungsi emit()
harus cocok dengan jenis data skema output.
Kolom yang direpresentasikan dalam skema output yang dihilangkan dalam fungsi emit akan menghasilkan output sebagai null.
Definisi atau referensi UDF
Kalau mau, Anda dapat menentukan UDF secara inline di bigquery.defineFunction
. Contoh:
bigquery.defineFunction( 'urlDecode', // Name used to call the function from SQL ['title', 'num_requests'], // Input column names // JSON representation of the output schema [{name: 'title', type: 'string'}, {name: 'requests', type: 'integer'}], // The UDF function(row, emit) { emit({title: decodeURI(row.title), requests: row.num_requests}); } );
Jika tidak, Anda dapat menentukan UDF secara terpisah, dan meneruskan referensi ke fungsi di bigquery.defineFunction
. Contoh:
// The UDF function urlDecode(row, emit) { emit({title: decodeURI(row.title), requests: row.num_requests}); } // UDF registration bigquery.defineFunction( 'urlDecode', // Name used to call the function from SQL ['title', 'num_requests'], // Input column names // JSON representation of the output schema [{name: 'title', type: 'string'}, {name: 'requests', type: 'integer'}], urlDecode // The function reference );
Penanganan error
Jika pengecualian atau error ditampilkan selama pemrosesan UDF, seluruh kueri akan gagal. Anda dapat menggunakan blok try-catch untuk menangani error. Contoh:
// The UDF function urlDecode(row, emit) { emit({title: decodeHelper(row.title), requests: row.num_requests}); } // Helper function with error handling function decodeHelper(s) { try { return decodeURI(s); } catch (ex) { return s; } } // UDF registration bigquery.defineFunction( 'urlDecode', // Name used to call the function from SQL ['title', 'num_requests'], // Input column names // JSON representation of the output schema [{name: 'title', type: 'string'}, {name: 'requests', type: 'integer'}], urlDecode // The function reference );
Menjalankan kueri dengan UDF
Anda dapat menggunakan UDF pada legacy SQL dengan alat command line bq atau BigQuery API. Konsol Google Cloud tidak mendukung UDF dalam legacy SQL.
Menggunakan alat command line bq
Untuk menjalankan kueri yang berisi satu atau beberapa UDF, tentukan flag --udf_resource
pada alat command line bq dari Google Cloud CLI. Nilai flag ini dapat berupa URI (gs://...
) Cloud Storage atau jalur ke file lokal. Untuk menentukan beberapa file resource UDF, ulangi flag ini.
Gunakan sintaksis berikut untuk menjalankan kueri dengan UDF:
bq query --udf_resource=<file_path_or_URI> <sql_query>
Contoh berikut menjalankan kueri yang menggunakan UDF yang disimpan dalam file lokal dan kueri SQL yang juga disimpan dalam file lokal.
Membuat UDF
Anda dapat menyimpan UDF di Cloud Storage atau sebagai file teks lokal. Misalnya, untuk menyimpan UDF urlDecode
berikut, buat file bernama urldecode.js
dan tempelkan kode JavaScript berikut ke dalam file tersebut sebelum menyimpan file.
// UDF definition function urlDecode(row, emit) { emit({title: decodeHelper(row.title), requests: row.num_requests}); } // Helper function with error handling function decodeHelper(s) { try { return decodeURI(s); } catch (ex) { return s; } } // UDF registration bigquery.defineFunction( 'urlDecode', // Name used to call the function from SQL ['title', 'num_requests'], // Input column names // JSON representation of the output schema [{name: 'title', type: 'string'}, {name: 'requests', type: 'integer'}], urlDecode // The function reference );
Membuat kueri
Anda juga dapat menyimpan kueri dalam file agar command line tidak terlalu panjang. Misalnya, Anda dapat membuat file lokal bernama query.sql
dan menempelkan pernyataan BigQuery berikut ke dalam file tersebut.
#legacySQL SELECT requests, title FROM urlDecode( SELECT title, sum(requests) AS num_requests FROM [fh-bigquery:wikipedia.pagecounts_201504] WHERE language = 'fr' GROUP EACH BY title ) WHERE title LIKE '%ç%' ORDER BY requests DESC LIMIT 100
Setelah menyimpan file, Anda dapat mereferensikan file tersebut pada command line.
Menjalankan kueri
Setelah menentukan UDF dan kueri dalam file terpisah, Anda dapat mereferensikan uDF dan kueri pada command line.
Misalnya, perintah berikut menjalankan kueri yang Anda simpan sebagai file bernama query.sql
dan mereferensikan UDF yang Anda buat.
$ bq query --udf_resource=urldecode.js "$(cat query.sql)"
Menggunakan BigQuery API
configuration.query
Kueri yang menggunakan UDF harus berisi elemen userDefinedFunctionResources
yang menyediakan kode, atau lokasi untuk membuat kode resource, yang akan digunakan dalam kueri. Kode yang disediakan harus menyertakan pemanggilan fungsi pendaftaran untuk setiap UDF yang direferensikan oleh kueri.
Resource kode
Konfigurasi kueri Anda dapat mencakup blob kode JavaScript, serta referensi ke file sumber JavaScript yang tersimpan di Cloud Storage.
Blob kode JavaScript inline diisi di bagian inlineCode
pada elemen userDefinedFunctionResource
. Namun, kode yang akan digunakan kembali atau direferensikan di beberapa kueri harus dipertahankan di Cloud Storage dan direferensikan sebagai resource eksternal.
Untuk mereferensikan file sumber JavaScript dari Cloud Storage, tetapkan bagian resourceURI
dari elemen userDefinedFunctionResource
ke URI gs://
file.
Konfigurasi kueri dapat berisi beberapa elemen userDefinedFunctionResource
.
Setiap elemen dapat berisi bagian inlineCode
atau resourceUri
.
Contoh
Contoh JSON berikut menunjukkan permintaan kueri yang mereferensikan dua resource UDF: satu blob kode inline, dan satu file lib.js
untuk dibaca dari Cloud Storage. Dalam contoh ini, myFunc
dan pemanggilan pendaftaran untuk myFunc
disediakan oleh lib.js
.
{ "configuration": { "query": { "userDefinedFunctionResources": [ { "inlineCode": "var someCode = 'here';" }, { "resourceUri": "gs://some-bucket/js/lib.js" } ], "query": "select a from myFunc(T);" } } }
Praktik terbaik
Mengembangkan UDF Anda
Anda bisa menggunakan alat pengujian UDF untuk menguji dan men-debug UDF tanpa menghabiskan tagihan BigQuery.
Memfilter input terlebih dahulu
Jika input Anda dapat difilter dengan mudah sebelum diteruskan ke UDF, kueri Anda kemungkinan akan lebih cepat dan lebih terjangkau.
Dalam contoh menjalankan kueri, subkueri diteruskan sebagai input ke urlDecode
, bukan tabel penuh. Tabel [fh-bigquery:wikipedia.pagecounts_201504]
memiliki sekitar 5,6 miliar baris, dan jika kita menjalankan UDF di seluruh tabel, framework JavaScript perlu memproses lebih dari 21 baris lebih banyak daripada dengan subkueri yang difilter.
Menghindari status persisten yang dapat berubah
Jangan menyimpan atau mengakses status yang dapat berubah pada panggilan UDF. Contoh kode berikut menjelaskan skenario ini:
// myCode.js var numRows = 0; function dontDoThis(r, emit) { emit({rowCount: ++numRows}); } // The query. SELECT max(rowCount) FROM dontDoThis(t);
Contoh di atas tidak akan berperilaku sebagaimana mestinya, karena BigQuery melakukan sharding kueri di banyak node. Setiap node memiliki lingkungan pemrosesan JavaScript mandiri yang mengakumulasi nilai terpisah untuk numRows
.
Menggunakan memori secara efisien
Lingkungan pemrosesan JavaScript memiliki memori terbatas per kueri. Kueri UDF yang mengakumulasi terlalu banyak status lokal dapat gagal karena kehabisan memori.
Meluaskan kueri tertentu
Anda harus mencantumkan kolom yang dipilih dari UDF secara eksplisit.
SELECT * FROM <UDF name>(...)
tidak didukung.
Untuk memeriksa struktur data baris input, Anda dapat menggunakan JSON.stringify()
untuk menghasilkan kolom output string:
bigquery.defineFunction( 'examineInputFormat', ['some', 'input', 'columns'], [{name: 'input', type: 'string'}], function(r, emit) { emit({input: JSON.stringify(r)}); } );
Batas
- Jumlah data yang dihasilkan UDF saat memproses satu baris harus sekitar 5 MB atau lebih sedikit.
- Setiap pengguna dibatasi untuk menjalankan sekitar 6 kueri UDF dalam project tertentu secara bersamaan. Jika Anda menerima pesan error bahwa Anda melebihi batas kueri serentak, tunggu beberapa menit, lalu coba lagi.
- UDF dapat berada pada waktu tunggu habis dan mencegah kueri Anda selesai. Waktu tunggu bisa paling singkat 5 menit, tetapi dapat bervariasi tergantung beberapa faktor, termasuk jumlah waktu yang digunakan CPU pengguna yang dipakai oleh fungsi Anda serta besarnya input dan output Anda ke fungsi JS.
- Tugas kueri dapat memiliki maksimal 50 resource UDF (blob kode inline atau file eksternal).
- Setiap blob kode inline dibatasi ukuran maksimumnya hingga 32 KB. Untuk menggunakan resource kode yang lebih besar, simpan kode Anda di Cloud Storage dan referensikan sebagai resource eksternal.
- Setiap resource kode eksternal dibatasi ukuran maksimumnya hingga 1 MB.
- Ukuran kumulatif semua resource kode eksternal dibatasi maksimum 5 MB.
Batasan
- Objek DOM
Window
,Document
, danNode
, serta fungsi yang memerlukan objek tersebut, tidak didukung. - Fungsi JavaScript yang mengandalkan kode native tidak didukung.
- Operasi bitwise dalam JavaScript hanya menangani 32 bit yang paling signifikan.
- Karena sifatnya yang non-deterministik, kueri yang memanggil fungsi yang ditentukan pengguna (UDF) tidak dapat menggunakan hasil yang di-cache.