Skip to content

Commit c6a8057

Browse files
committed
[Feature] Core Implementation of rex Command In PPL (opensearch-project#4109)
* rex - initial implementation Signed-off-by: Jialiang Liang <jiallian@amazon.com> * stop using utils Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix spotless check Signed-off-by: Jialiang Liang <jiallian@amazon.com> * offset_field - initial implementation Signed-off-by: Jialiang Liang <jiallian@amazon.com> * max_match - initial implementation Signed-off-by: Jialiang Liang <jiallian@amazon.com> * sed - initial implementation Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix name capture group for extraction Signed-off-by: Jialiang Liang <jiallian@amazon.com> * add rex rst doc Signed-off-by: Jialiang Liang <jiallian@amazon.com> * IT - initial setup Signed-off-by: Jialiang Liang <jiallian@amazon.com> * add a analyzer test for legacy engine Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Add UT for rex Signed-off-by: Jialiang Liang <jiallian@amazon.com> * sed - add pushdown for sed and explain IT and IT with fix Signed-off-by: Jialiang Liang <jiallian@amazon.com> * anonymizer - add rex for anonymizer and test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Add cross cluster IT for rex Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - resolve comments for rst doc 0 Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - address some comments 1 Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - resolve comment in rst doc to add a java doc link Signed-off-by: Jialiang Liang <jiallian@amazon.com> * kai - modify the bin ast builder test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - fix the extraction behavior without filter even when there is zero match Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix rex explain no pushdown Signed-off-by: Jialiang Liang <jiallian@amazon.com> * change the offset val output format Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix rst file Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - SWITCH TO USE CALCITE NATIVE OPERATORS Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Peng - fix tests after operator change Signed-off-by: Jialiang Liang <jiallian@amazon.com> * support mode=extract and update doc Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix the issue after rebase Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - enforce specifying field in antlr for now Signed-off-by: Jialiang Liang <jiallian@amazon.com> * relocate rex cmd IT Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - simplify vistFunciton Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - add UT for RexExtractMultiFunction Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - add UT RexOffsetFunction Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix some tests Signed-off-by: Jialiang Liang <jiallian@amazon.com> * DECOUPLE SED + OFFSET FIELD Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Improve error handling for extract Signed-off-by: Jialiang Liang <jiallian@amazon.com> * add this rex rst into index Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix return type in extract multi Signed-off-by: Jialiang Liang <jiallian@amazon.com> * add rex doc into doc test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix doc test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Fix linting Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix rebase issue Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix regex anonymizer tests Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix analyzer test and setup to use util function Signed-off-by: Jialiang Liang <jiallian@amazon.com> * lint fix Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix doc test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * Add max match limit implementation Signed-off-by: Jialiang Liang <jiallian@amazon.com> * fix anonymizer test Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - simplify if Signed-off-by: Jialiang Liang <jiallian@amazon.com> * peng - make extract multi to only handle the case of max_match > 1 Signed-off-by: Jialiang Liang <jiallian@amazon.com> --------- Signed-off-by: Jialiang Liang <jiallian@amazon.com>
1 parent f662af8 commit c6a8057

31 files changed

Lines changed: 1624 additions & 4 deletions

File tree

common/src/main/java/org/opensearch/sql/common/setting/Settings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public enum Key {
3131
PATTERN_MODE("plugins.ppl.pattern.mode"),
3232
PATTERN_MAX_SAMPLE_COUNT("plugins.ppl.pattern.max.sample.count"),
3333
PATTERN_BUFFER_LIMIT("plugins.ppl.pattern.buffer.limit"),
34+
PPL_REX_MAX_MATCH_LIMIT("plugins.ppl.rex.max_match.limit"),
3435

3536
/** Enable Calcite as execution engine */
3637
CALCITE_ENGINE_ENABLED("plugins.calcite.enabled"),

core/src/main/java/org/opensearch/sql/analysis/Analyzer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import org.opensearch.sql.ast.tree.RelationSubquery;
8585
import org.opensearch.sql.ast.tree.Rename;
8686
import org.opensearch.sql.ast.tree.Reverse;
87+
import org.opensearch.sql.ast.tree.Rex;
8788
import org.opensearch.sql.ast.tree.Sort;
8889
import org.opensearch.sql.ast.tree.Sort.SortOption;
8990
import org.opensearch.sql.ast.tree.SubqueryAlias;
@@ -749,6 +750,11 @@ public LogicalPlan visitRegex(Regex node, AnalysisContext context) {
749750
throw getOnlyForCalciteException("Regex");
750751
}
751752

753+
@Override
754+
public LogicalPlan visitRex(Rex node, AnalysisContext context) {
755+
throw getOnlyForCalciteException("Rex");
756+
}
757+
752758
@Override
753759
public LogicalPlan visitPaginate(Paginate paginate, AnalysisContext context) {
754760
LogicalPlan child = paginate.getChild().get(0).accept(this, context);

core/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import org.opensearch.sql.ast.tree.RelationSubquery;
7373
import org.opensearch.sql.ast.tree.Rename;
7474
import org.opensearch.sql.ast.tree.Reverse;
75+
import org.opensearch.sql.ast.tree.Rex;
7576
import org.opensearch.sql.ast.tree.Sort;
7677
import org.opensearch.sql.ast.tree.SubqueryAlias;
7778
import org.opensearch.sql.ast.tree.TableFunction;
@@ -264,6 +265,10 @@ public T visitRegex(Regex node, C context) {
264265
return visitChildren(node, context);
265266
}
266267

268+
public T visitRex(Rex node, C context) {
269+
return visitChildren(node, context);
270+
}
271+
267272
public T visitLambdaFunction(LambdaFunction node, C context) {
268273
return visitChildren(node, context);
269274
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.tree;
7+
8+
import com.google.common.collect.ImmutableList;
9+
import java.util.List;
10+
import java.util.Optional;
11+
import lombok.EqualsAndHashCode;
12+
import lombok.Getter;
13+
import lombok.Setter;
14+
import lombok.ToString;
15+
import org.opensearch.sql.ast.AbstractNodeVisitor;
16+
import org.opensearch.sql.ast.expression.Literal;
17+
import org.opensearch.sql.ast.expression.UnresolvedExpression;
18+
19+
/** AST node represent Rex field extraction operation. */
20+
@Getter
21+
@ToString
22+
@EqualsAndHashCode(callSuper = false)
23+
public class Rex extends UnresolvedPlan {
24+
25+
public enum RexMode {
26+
EXTRACT
27+
}
28+
29+
/** Field to extract from. */
30+
private final UnresolvedExpression field;
31+
32+
/** Pattern with named capture groups. */
33+
private final Literal pattern;
34+
35+
/** Rex mode (only EXTRACT supported). */
36+
private final RexMode mode;
37+
38+
/** Maximum number of matches (optional). */
39+
private final Optional<Integer> maxMatch;
40+
41+
/** Child Plan. */
42+
@Setter private UnresolvedPlan child;
43+
44+
public Rex(UnresolvedExpression field, Literal pattern) {
45+
this(field, pattern, RexMode.EXTRACT, Optional.empty());
46+
}
47+
48+
public Rex(UnresolvedExpression field, Literal pattern, Optional<Integer> maxMatch) {
49+
this(field, pattern, RexMode.EXTRACT, maxMatch);
50+
}
51+
52+
public Rex(
53+
UnresolvedExpression field, Literal pattern, RexMode mode, Optional<Integer> maxMatch) {
54+
this.field = field;
55+
this.pattern = pattern;
56+
this.mode = mode;
57+
this.maxMatch = maxMatch;
58+
}
59+
60+
@Override
61+
public Rex attach(UnresolvedPlan child) {
62+
this.child = child;
63+
return this;
64+
}
65+
66+
@Override
67+
public List<UnresolvedPlan> getChild() {
68+
return ImmutableList.of(child);
69+
}
70+
71+
@Override
72+
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
73+
return nodeVisitor.visitRex(this, context);
74+
}
75+
}

core/src/main/java/org/opensearch/sql/calcite/CalciteRelNodeVisitor.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
import org.opensearch.sql.ast.tree.Regex;
106106
import org.opensearch.sql.ast.tree.Relation;
107107
import org.opensearch.sql.ast.tree.Rename;
108+
import org.opensearch.sql.ast.tree.Rex;
108109
import org.opensearch.sql.ast.tree.Sort;
109110
import org.opensearch.sql.ast.tree.Sort.SortOption;
110111
import org.opensearch.sql.ast.tree.SubqueryAlias;
@@ -125,6 +126,7 @@
125126
import org.opensearch.sql.exception.SemanticCheckException;
126127
import org.opensearch.sql.expression.function.BuiltinFunctionName;
127128
import org.opensearch.sql.expression.function.PPLFuncImpTable;
129+
import org.opensearch.sql.expression.parse.RegexCommonUtils;
128130
import org.opensearch.sql.utils.ParseUtils;
129131

130132
public class CalciteRelNodeVisitor extends AbstractNodeVisitor<RelNode, CalcitePlanContext> {
@@ -203,6 +205,50 @@ public RelNode visitRegex(Regex node, CalcitePlanContext context) {
203205
return context.relBuilder.peek();
204206
}
205207

208+
public RelNode visitRex(Rex node, CalcitePlanContext context) {
209+
visitChildren(node, context);
210+
211+
RexNode fieldRex = rexVisitor.analyze(node.getField(), context);
212+
String patternStr = (String) node.getPattern().getValue();
213+
214+
List<String> namedGroups = RegexCommonUtils.getNamedGroupCandidates(patternStr);
215+
216+
if (namedGroups.isEmpty()) {
217+
throw new IllegalArgumentException(
218+
"Rex pattern must contain at least one named capture group");
219+
}
220+
221+
List<RexNode> newFields = new ArrayList<>();
222+
List<String> newFieldNames = new ArrayList<>();
223+
224+
for (int i = 0; i < namedGroups.size(); i++) {
225+
RexNode extractCall;
226+
if (node.getMaxMatch().isPresent() && node.getMaxMatch().get() > 1) {
227+
extractCall =
228+
PPLFuncImpTable.INSTANCE.resolve(
229+
context.rexBuilder,
230+
BuiltinFunctionName.REX_EXTRACT_MULTI,
231+
fieldRex,
232+
context.rexBuilder.makeLiteral(patternStr),
233+
context.relBuilder.literal(i + 1),
234+
context.relBuilder.literal(node.getMaxMatch().get()));
235+
} else {
236+
extractCall =
237+
PPLFuncImpTable.INSTANCE.resolve(
238+
context.rexBuilder,
239+
BuiltinFunctionName.REX_EXTRACT,
240+
fieldRex,
241+
context.rexBuilder.makeLiteral(patternStr),
242+
context.relBuilder.literal(i + 1));
243+
}
244+
newFields.add(extractCall);
245+
newFieldNames.add(namedGroups.get(i));
246+
}
247+
248+
projectPlusOverriding(newFields, newFieldNames, context);
249+
return context.relBuilder.peek();
250+
}
251+
206252
private boolean containsSubqueryExpression(Node expr) {
207253
if (expr == null) {
208254
return false;

core/src/main/java/org/opensearch/sql/calcite/utils/PPLOperandTypes.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ private PPLOperandTypes() {}
5151
UDFOperandMetadata.wrap((FamilyOperandTypeChecker) OperandTypes.NUMERIC_NUMERIC);
5252
public static final UDFOperandMetadata STRING_INTEGER =
5353
UDFOperandMetadata.wrap(OperandTypes.family(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER));
54+
public static final UDFOperandMetadata STRING_STRING_INTEGER =
55+
UDFOperandMetadata.wrap(
56+
OperandTypes.family(
57+
SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER));
58+
public static final UDFOperandMetadata STRING_STRING_INTEGER_INTEGER =
59+
UDFOperandMetadata.wrap(
60+
OperandTypes.family(
61+
SqlTypeFamily.CHARACTER,
62+
SqlTypeFamily.CHARACTER,
63+
SqlTypeFamily.INTEGER,
64+
SqlTypeFamily.INTEGER));
5465

5566
public static final UDFOperandMetadata NUMERIC_NUMERIC_OPTIONAL_NUMERIC =
5667
UDFOperandMetadata.wrap(

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ public enum BuiltinFunctionName {
219219
POSITION(FunctionName.of("position")),
220220
REGEXP(FunctionName.of("regexp")),
221221
REGEX_MATCH(FunctionName.of("regex_match")),
222+
REX_EXTRACT(FunctionName.of("REX_EXTRACT")),
223+
REX_EXTRACT_MULTI(FunctionName.of("REX_EXTRACT_MULTI")),
222224
REPLACE(FunctionName.of("replace")),
223225
REVERSE(FunctionName.of("reverse")),
224226
RIGHT(FunctionName.of("right")),
@@ -315,7 +317,10 @@ public enum BuiltinFunctionName {
315317
INTERNAL_UNCOLLECT_PATTERNS(FunctionName.of("uncollect_patterns")),
316318
INTERNAL_REGEXP_EXTRACT(FunctionName.of("regexp_extract"), true),
317319
INTERNAL_GROK(FunctionName.of("grok"), true),
318-
INTERNAL_REGEXP_REPLACE_3(FunctionName.of("regexp_replace_3"), true);
320+
INTERNAL_REGEXP_REPLACE_3(FunctionName.of("regexp_replace_3"), true),
321+
INTERNAL_REGEXP_REPLACE_PG_4(FunctionName.of("regexp_replace_pg_4"), true),
322+
INTERNAL_REGEXP_REPLACE_5(FunctionName.of("regexp_replace_5"), true),
323+
INTERNAL_TRANSLATE3(FunctionName.of("translate3"), true);
319324

320325
private final FunctionName name;
321326
private boolean isInternal;

core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.opensearch.sql.expression.function.udf.CryptographicFunction;
5757
import org.opensearch.sql.expression.function.udf.GrokFunction;
5858
import org.opensearch.sql.expression.function.udf.RelevanceQueryFunction;
59+
import org.opensearch.sql.expression.function.udf.RexExtractFunction;
60+
import org.opensearch.sql.expression.function.udf.RexExtractMultiFunction;
5961
import org.opensearch.sql.expression.function.udf.SpanFunction;
6062
import org.opensearch.sql.expression.function.udf.condition.EarliestFunction;
6163
import org.opensearch.sql.expression.function.udf.condition.EnhancedCoalesceFunction;
@@ -401,6 +403,9 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable {
401403
public static final SqlOperator RANGE_BUCKET =
402404
new org.opensearch.sql.expression.function.udf.binning.RangeBucketFunction()
403405
.toUDF("RANGE_BUCKET");
406+
public static final SqlOperator REX_EXTRACT = new RexExtractFunction().toUDF("REX_EXTRACT");
407+
public static final SqlOperator REX_EXTRACT_MULTI =
408+
new RexExtractMultiFunction().toUDF("REX_EXTRACT_MULTI");
404409

405410
public static final SqlOperator ENHANCED_COALESCE =
406411
new EnhancedCoalesceFunction().toUDF("COALESCE");

core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_PATTERN_PARSER;
8484
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_REGEXP_EXTRACT;
8585
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_REGEXP_REPLACE_3;
86+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_REGEXP_REPLACE_5;
87+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_REGEXP_REPLACE_PG_4;
88+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.INTERNAL_TRANSLATE3;
8689
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_BLANK;
8790
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_EMPTY;
8891
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL;
@@ -159,6 +162,8 @@
159162
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REGEX_MATCH;
160163
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REPLACE;
161164
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REVERSE;
165+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REX_EXTRACT;
166+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REX_EXTRACT_MULTI;
162167
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RIGHT;
163168
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RINT;
164169
import static org.opensearch.sql.expression.function.BuiltinFunctionName.ROUND;
@@ -685,6 +690,9 @@ void populate() {
685690
registerOperator(SHA1, SqlLibraryOperators.SHA1);
686691
registerOperator(INTERNAL_REGEXP_EXTRACT, SqlLibraryOperators.REGEXP_EXTRACT);
687692
registerOperator(INTERNAL_REGEXP_REPLACE_3, SqlLibraryOperators.REGEXP_REPLACE_3);
693+
registerOperator(INTERNAL_REGEXP_REPLACE_PG_4, SqlLibraryOperators.REGEXP_REPLACE_PG_4);
694+
registerOperator(INTERNAL_REGEXP_REPLACE_5, SqlLibraryOperators.REGEXP_REPLACE_5);
695+
registerOperator(INTERNAL_TRANSLATE3, SqlLibraryOperators.TRANSLATE3);
688696

689697
// Register PPL UDF operator
690698
registerOperator(COSH, PPLBuiltinOperators.COSH);
@@ -710,6 +718,8 @@ void populate() {
710718
registerOperator(SIMPLE_QUERY_STRING, PPLBuiltinOperators.SIMPLE_QUERY_STRING);
711719
registerOperator(QUERY_STRING, PPLBuiltinOperators.QUERY_STRING);
712720
registerOperator(MULTI_MATCH, PPLBuiltinOperators.MULTI_MATCH);
721+
registerOperator(REX_EXTRACT, PPLBuiltinOperators.REX_EXTRACT);
722+
registerOperator(REX_EXTRACT_MULTI, PPLBuiltinOperators.REX_EXTRACT_MULTI);
713723

714724
// Register PPL Datetime UDF operator
715725
registerOperator(TIMESTAMP, PPLBuiltinOperators.TIMESTAMP);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.RexToLixTranslator;
15+
import org.apache.calcite.linq4j.tree.Expression;
16+
import org.apache.calcite.linq4j.tree.Expressions;
17+
import org.apache.calcite.rex.RexCall;
18+
import org.apache.calcite.sql.type.ReturnTypes;
19+
import org.apache.calcite.sql.type.SqlReturnTypeInference;
20+
import org.opensearch.sql.calcite.utils.PPLOperandTypes;
21+
import org.opensearch.sql.expression.function.ImplementorUDF;
22+
import org.opensearch.sql.expression.function.UDFOperandMetadata;
23+
24+
/** Custom REX_EXTRACT function for extracting regex named capture groups. */
25+
public final class RexExtractFunction extends ImplementorUDF {
26+
27+
public RexExtractFunction() {
28+
super(new RexExtractImplementor(), NullPolicy.ARG0);
29+
}
30+
31+
@Override
32+
public SqlReturnTypeInference getReturnTypeInference() {
33+
return ReturnTypes.VARCHAR_2000_NULLABLE;
34+
}
35+
36+
@Override
37+
public UDFOperandMetadata getOperandMetadata() {
38+
return PPLOperandTypes.STRING_STRING_INTEGER;
39+
}
40+
41+
private static class RexExtractImplementor implements NotNullImplementor {
42+
43+
@Override
44+
public Expression implement(
45+
RexToLixTranslator translator, RexCall call, List<Expression> translatedOperands) {
46+
Expression field = translatedOperands.get(0);
47+
Expression pattern = translatedOperands.get(1);
48+
Expression groupIndex = translatedOperands.get(2);
49+
50+
return Expressions.call(RexExtractFunction.class, "extractGroup", field, pattern, groupIndex);
51+
}
52+
}
53+
54+
public static String extractGroup(String text, String pattern, int groupIndex) {
55+
try {
56+
Pattern compiledPattern = Pattern.compile(pattern);
57+
Matcher matcher = compiledPattern.matcher(text);
58+
59+
if (matcher.find() && groupIndex > 0 && groupIndex <= matcher.groupCount()) {
60+
return matcher.group(groupIndex);
61+
}
62+
return null;
63+
} catch (PatternSyntaxException e) {
64+
throw new IllegalArgumentException(
65+
"Error in 'rex' command: Encountered the following error while compiling the regex '"
66+
+ pattern
67+
+ "': "
68+
+ e.getMessage());
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)