사용자 정의 함수
사용자 정의 함수 (User-Defined Functions, UDFs)
Pinot은 현재 두 가지 방식으로 사용자 정의 함수를 구현할 수 있게 지원해요:
- Groovy 스크립트
- 스칼라 함수(Scalar Functions)
출처: 문서
본문
Groovy 스크립트
Pinot은 Apache Groovy 스크립트를 사용해 어떤 함수든 실행할 수 있게 해줘요. 쿼리 안에서 Groovy 스크립트를 실행하는 문법은 다음과 같습니다:
GROOVY('result value metadata json', 'groovy script', arg0, arg1, arg2...)
이 함수는 제공된 인자를 사용해 groovy 스크립트를 실행하고 제공된 결과 값 메타데이터와 일치하는 결과를 반환해요. 이 함수는 다음 인자를 요구합니다:
Result value metadata json- 결과 값 메타데이터를 나타내는 json 문자열. null이 아닌 키resultType과isSingleValue를 포함해야 해요.Groovy script to execute- 스크립트 안에서 제공된 인자를 참조하는 데arg0,arg1,arg2등을 사용하는 groovy 스크립트 문자열arguments- groovy 스크립트의 인자인 pinot 컬럼/다른 변환 함수
예시
-
colA와 colB를 더해 단일 값 INT를 반환
groovy( '{"returnType":"INT","isSingleValue":true}', 'arg0 + arg1', colA, colB) -
mvColumn 배열에서 max 요소를 찾아 단일 값 INT를 반환
groovy('{"returnType":"INT","isSingleValue":true}', 'arg0.toList().max()', mvColumn) -
배열 mvColumn의 모든 요소를 찾아 다중 값 LONG 컬럼으로 반환
groovy('{"returnType":"LONG","isSingleValue":false}', 'arg0.findIndexValues{ it > 5 }', mvColumn) -
배열 mvColumn의 길이를 colB와 곱해 단일 값 DOUBLE을 반환
groovy('{"returnType":"DOUBLE","isSingleValue":true}', 'arg0 * arg1', arraylength(mvColumn), colB) -
mvColumnA에서
foo값을 가진 모든 인덱스를 찾아 mvColumnB에서 해당 인덱스의 값을 더함groovy( '{"returnType":"DOUBLE","isSingleValue":true}', 'def x = 0; arg0.eachWithIndex{item, idx-> if (item == "foo") {x = x + arg1[idx] }}; return x' , mvColumnA, mvColumnB) -
mvCol 배열 길이에 따라 FLOAT 값을 반환하는 switch case
groovy('{"returnType":"FLOAT", "isSingleValue":true}', 'def result; switch(arg0.length()) { case 10: result = 1.1; break; case 20: result = 1.2; break; default: result = 1.3;}; return result.floatValue()', mvCol) -
인자를 받지 않는 모든 Groovy 스크립트
groovy('new Date().format( "yyyyMMdd" )', '{"returnType":"STRING","isSingleValue":true}')
:warning: Groovy 스크립트는 Pinot 쿼리에 특화된 내장 스칼라 함수를 받아들이지 않습니다. 자세한 내용은 아래 섹션을 참고하세요.
:warning: Groovy 활성화
쿼리에서 실행 가능한 Groovy를 허용하면 보안 취약점이 될 수 있어요. Groovy를 허용하기로 결정하면 보안 위험을 인지하고 주의하세요. Pinot 쿼리에서 Groovy를 활성화하려면 다음 브로커 설정을 지정할 수 있습니다.
pinot.broker.disable.query.groovy=false
설정하지 않으면 쿼리의 Groovy는 기본적으로 비활성화됩니다.
위 설정은 전체 Pinot 클러스터에 적용됩니다. 테이블 수준에서 Groovy 쿼리를 활성화/비활성화하는 재정의가 필요하다면 쿼리 테이블 설정에서 다음 속성을 지정할 수 있어요.
{
"tableName": "myTable",
"tableType": "OFFLINE",
"query" : {
"disableGroovy": false
}
}
스칼라 함수 (Scalar Functions)
0.5.0 릴리스부터 Pinot은 여러 입력에 대해 단일 출력을 반환하는 사용자 정의 함수를 지원해요. 스칼라 함수의 예시는 StringFunctions와 DateTimeFunctions에서 찾을 수 있어요.
Pinot은 @ScalarFunction 어노테이션이 있는 모든 함수를 자동으로 식별하고 등록해요.
Java 메서드만 지원됩니다.
사용자 정의 스칼라 함수 추가
다음과 같이 새 스칼라 함수를 추가할 수 있어요:
- 새 java 프로젝트를 만듭니다. 패키지 이름이
org.apache.pinot로 시작하고 그 안에.function.이 포함되어 있어야 해요. - java 프로젝트에 의존성을 포함합니다.
Maven
<dependency>
<groupId>org.apache.pinot</groupId>
<artifactId>pinot-common</artifactId>
<version>1.2.0</version>
</dependency>
Gradle
include 'org.apache.pinot:pinot-common:1.2.0'
- 메서드에
@ScalarFunction어노테이션을 붙입니다. 메서드가static이고 단일 값 출력만 반환하는지 확인하세요. 입력과 출력은 다음 타입 중 하나일 수 있습니다.- Integer
- Long
- Double
- String
//Example Scalar function
@ScalarFunction
static String mySubStr(String input, Integer beginIndex) {
return input.substring(beginIndex);
}
수집을 위한 함수 휘발성(volatility) 선언
스칼라 UDF를 영속 수집 변환(persisted ingestion transform)에 사용할 수 있다면 그 휘발성을 정확히 선언하세요. Pinot은 새롭거나 변경된 수집 변환에서 IMMUTABLE 스칼라 함수만 허용해요. IMMUTABLE 함수는 명시적 인자에만 의존하고, STABLE 함수는 쿼리 간에 바뀔 수 있으며, VOLATILE 함수는 호출마다 바뀌거나 부작용이 있을 수 있습니다. STABLE과 VOLATILE 함수는 쿼리에서 계속 사용할 수 있지만, 새롭거나 변경된 영속 수집 변환에서는 유효하지 않습니다.
@ScalarFunction은 호환성을 위해 기본적으로 IMMUTABLE입니다. 함수에 맞을 때만 기본값을 그대로 두세요. 시간, 난수, 변경 가능한 상태 또는 외부 시스템을 읽는 UDF라면 적절한 카테고리를 명시적으로 설정하세요:
import org.apache.pinot.spi.annotations.FunctionVolatility;
import org.apache.pinot.spi.annotations.ScalarFunction;
@ScalarFunction(volatility = FunctionVolatility.VOLATILE)
static long currentTime() {
return System.currentTimeMillis();
}
함수 휘발성은 Pinot의 컴파일 타임 쿼리 평가 동작을 제어하는 isDeterministic과 별개입니다. isDeterministic = false인 UDF는 영속 수집 변환 검증에서도 VOLATILE로 처리됩니다.
- 컴파일된 JAR을 pinot의
/plugins디렉토리에 배치합니다. Pinot 인스턴스가 이미 실행 중이라면 모두 재시작해야 합니다. - 이제 쿼리에서 함수를 다음과 같이 사용할 수 있어요.
SELECT mysubstr(playerName, 4)
FROM baseballStats
:warning: SQL의 함수 이름은 Java의 함수 이름과 동일합니다. SQL 함수 이름은 대소문자를 구분하지 않습니다.