1+ /*
2+ * Copyright OpenSearch Contributors
3+ * SPDX-License-Identifier: Apache-2.0
4+ */
5+
6+ package org .opensearch .sql .expression .function .udf ;
7+
8+ import java .util .List ;
9+ import java .util .regex .Matcher ;
10+ import java .util .regex .Pattern ;
11+ import java .util .regex .PatternSyntaxException ;
12+ import org .apache .calcite .adapter .enumerable .NotNullImplementor ;
13+ import org .apache .calcite .adapter .enumerable .NullPolicy ;
14+ import org .apache .calcite .adapter .enumerable .RexImpTable ;
15+ import org .apache .calcite .adapter .enumerable .RexToLixTranslator ;
16+ import org .apache .calcite .linq4j .tree .Expression ;
17+ import org .apache .calcite .linq4j .tree .Types ;
18+ import org .apache .calcite .rex .RexCall ;
19+ import org .apache .calcite .schema .impl .ScalarFunctionImpl ;
20+ import org .apache .calcite .sql .type .ReturnTypes ;
21+ import org .apache .calcite .sql .type .SqlReturnTypeInference ;
22+ import org .opensearch .sql .expression .function .ImplementorUDF ;
23+ import org .opensearch .sql .expression .function .UDFOperandMetadata ;
24+
25+ /**
26+ * REX_EXTRACT UDF implementation for Calcite engine. This function extracts specific named
27+ * capture groups from text using Java regex patterns.
28+ */
29+ public class RexExtractFunctionImpl extends ImplementorUDF {
30+
31+ public RexExtractFunctionImpl () {
32+ super (new RexExtractImplementor (), NullPolicy .ANY );
33+ }
34+
35+ @ Override
36+ public SqlReturnTypeInference getReturnTypeInference () {
37+ return ReturnTypes .VARCHAR_2000_NULLABLE ;
38+ }
39+
40+ @ Override
41+ public UDFOperandMetadata getOperandMetadata () {
42+ return null ;
43+ }
44+
45+ public static class RexExtractImplementor implements NotNullImplementor {
46+ @ Override
47+ public Expression implement (
48+ RexToLixTranslator translator , RexCall call , List <Expression > translatedOperands ) {
49+ ScalarFunctionImpl function =
50+ (ScalarFunctionImpl )
51+ ScalarFunctionImpl .create (
52+ Types .lookupMethod (
53+ RexExtractFunctionImpl .class , "eval" , String .class , String .class , String .class ));
54+ return function .getImplementor ().implement (translator , call , RexImpTable .NullAs .NULL );
55+ }
56+ }
57+
58+ /**
59+ * Evaluation method for REX_EXTRACT function. This method is called by Calcite's generated code
60+ * during execution.
61+ *
62+ * @param field The field value to extract from
63+ * @param pattern The Java regex pattern with named capture groups
64+ * @param groupName The name of the capture group to extract
65+ * @return String value of the extracted group, or null if no match or group not found
66+ */
67+ public static String eval (String field , String pattern , String groupName ) {
68+ if (field == null || pattern == null || groupName == null ) {
69+ return null ;
70+ }
71+
72+ try {
73+ Pattern compiledPattern = Pattern .compile (pattern );
74+ Matcher matcher = compiledPattern .matcher (field );
75+
76+ if (matcher .find ()) {
77+ try {
78+ return matcher .group (groupName );
79+ } catch (IllegalArgumentException e ) {
80+ // Named group doesn't exist
81+ return null ;
82+ }
83+ }
84+ return null ; // No match found
85+ } catch (PatternSyntaxException e ) {
86+ throw new IllegalArgumentException ("Invalid regex pattern: " + e .getMessage ());
87+ }
88+ }
89+
90+ /**
91+ * Overloaded eval method with max_match parameter.
92+ *
93+ * @param field The field value to extract from
94+ * @param pattern The Java regex pattern with named capture groups
95+ * @param maxMatch Maximum number of matches to process (currently ignored, defaults to first match)
96+ * @param groupName The name of the capture group to extract
97+ * @return String value of the extracted group, or null if no match or group not found
98+ */
99+ public static String eval (String field , String pattern , Integer maxMatch , String groupName ) {
100+ // For now, we only support the first match, but maxMatch parameter is ready for future extension
101+ return eval (field , pattern , groupName );
102+ }
103+ }
0 commit comments