77
88package org .elasticsearch .xpack .esql .expression .function .scalar .string .regex ;
99
10+ import org .apache .lucene .search .MultiTermQuery ;
11+ import org .apache .lucene .util .automaton .Automaton ;
12+ import org .apache .lucene .util .automaton .CharacterRunAutomaton ;
13+ import org .elasticsearch .TransportVersion ;
14+ import org .elasticsearch .TransportVersions ;
1015import org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
1116import org .elasticsearch .common .io .stream .StreamInput ;
1217import org .elasticsearch .common .io .stream .StreamOutput ;
18+ import org .elasticsearch .index .mapper .MappedFieldType ;
19+ import org .elasticsearch .index .query .SearchExecutionContext ;
1320import org .elasticsearch .xpack .esql .core .expression .Expression ;
1421import org .elasticsearch .xpack .esql .core .expression .FieldAttribute ;
1522import org .elasticsearch .xpack .esql .core .expression .predicate .regex .RLikePattern ;
1623import org .elasticsearch .xpack .esql .core .expression .predicate .regex .RLikePatternList ;
17- import org .elasticsearch .xpack .esql .core .querydsl .query .EsqlAutomatonQuery ;
1824import org .elasticsearch .xpack .esql .core .querydsl .query .Query ;
1925import org .elasticsearch .xpack .esql .core .tree .NodeInfo ;
2026import org .elasticsearch .xpack .esql .core .tree .Source ;
2127import org .elasticsearch .xpack .esql .expression .function .Param ;
28+ import org .elasticsearch .xpack .esql .io .stream .ExpressionQuery ;
2229import org .elasticsearch .xpack .esql .io .stream .PlanStreamInput ;
2330import org .elasticsearch .xpack .esql .optimizer .rules .physical .local .LucenePushdownPredicates ;
2431import org .elasticsearch .xpack .esql .planner .TranslatorHandler ;
2532
2633import java .io .IOException ;
34+ import java .util .function .Supplier ;
2735import java .util .stream .Collectors ;
2836
2937public class RLikeList extends RegexMatch <RLikePatternList > {
@@ -33,6 +41,30 @@ public class RLikeList extends RegexMatch<RLikePatternList> {
3341 RLikeList ::new
3442 );
3543
44+ Supplier <Automaton > automatonSupplier = new Supplier <>() {
45+ Automaton cached ;
46+
47+ @ Override
48+ public Automaton get () {
49+ if (cached == null ) {
50+ cached = pattern ().createAutomaton (caseInsensitive ());
51+ }
52+ return cached ;
53+ }
54+ };
55+
56+ Supplier <CharacterRunAutomaton > characterRunAutomatonSupplier = new Supplier <>() {
57+ CharacterRunAutomaton cached ;
58+
59+ @ Override
60+ public CharacterRunAutomaton get () {
61+ if (cached == null ) {
62+ cached = new CharacterRunAutomaton (automatonSupplier .get ());
63+ }
64+ return cached ;
65+ }
66+ };
67+
3668 /**
3769 * The documentation for this function is in RLike, and shown to the users as `RLIKE` in the docs.
3870 */
@@ -75,39 +107,62 @@ public String getWriteableName() {
75107 return ENTRY .name ;
76108 }
77109
110+ @ Override
111+ protected NodeInfo <RLikeList > info () {
112+ return NodeInfo .create (this , RLikeList ::new , field (), pattern (), caseInsensitive ());
113+ }
114+
78115 @ Override
79116 protected RLikeList replaceChild (Expression newChild ) {
80117 return new RLikeList (source (), newChild , pattern (), caseInsensitive ());
81118 }
82119
120+ /**
121+ * Returns {@link Translatable#YES} if the field is pushable, otherwise {@link Translatable#NO}.
122+ */
83123 @ Override
84124 public Translatable translatable (LucenePushdownPredicates pushdownPredicates ) {
85- return pushdownPredicates .isPushableAttribute (field ()) ? Translatable .YES : Translatable .NO ;
125+ if (supportsPushdown (pushdownPredicates .minTransportVersion ())) {
126+ return pushdownPredicates .isPushableAttribute (field ()) ? Translatable .YES : Translatable .NO ;
127+ } else {
128+ // The ExpressionQuery we use isn't serializable to all nodes in the cluster.
129+ return Translatable .NO ;
130+ }
86131 }
87132
88- /**
89- * Returns a {@link Query} that matches the field against the provided patterns.
90- * For now, we only support a single pattern in the list for pushdown.
91- */
92133 @ Override
93134 public Query asQuery (LucenePushdownPredicates pushdownPredicates , TranslatorHandler handler ) {
94135 var field = field ();
95136 LucenePushdownPredicates .checkIsPushableAttribute (field );
96137 return translateField (handler .nameOf (field instanceof FieldAttribute fa ? fa .exactAttribute () : field ));
97138 }
98139
99- private Query translateField ( String targetFieldName ) {
100- return new EsqlAutomatonQuery ( source (), targetFieldName , pattern (). createAutomaton ( caseInsensitive ()), getAutomatonDescription () );
140+ private boolean supportsPushdown ( TransportVersion version ) {
141+ return version == null || version . onOrAfter ( TransportVersions . ESQL_RLIKE_LIST );
101142 }
102143
103144 @ Override
104- protected NodeInfo <? extends Expression > info () {
105- return NodeInfo .create (this , RLikeList ::new , field (), pattern (), caseInsensitive ());
145+ public org .apache .lucene .search .Query asLuceneQuery (
146+ MappedFieldType fieldType ,
147+ MultiTermQuery .RewriteMethod constantScoreRewrite ,
148+ SearchExecutionContext context
149+ ) {
150+ return fieldType .automatonQuery (
151+ automatonSupplier ,
152+ characterRunAutomatonSupplier ,
153+ constantScoreRewrite ,
154+ context ,
155+ getLuceneQueryDescription ()
156+ );
106157 }
107158
108- private String getAutomatonDescription () {
159+ private String getLuceneQueryDescription () {
109160 // we use the information used to create the automaton to describe the query here
110161 String patternDesc = pattern ().patternList ().stream ().map (RLikePattern ::pattern ).collect (Collectors .joining ("\" , \" " ));
111162 return "RLIKE(\" " + patternDesc + "\" ), caseInsensitive=" + caseInsensitive ();
112163 }
164+
165+ private Query translateField (String targetFieldName ) {
166+ return new ExpressionQuery (source (), targetFieldName , this );
167+ }
113168}
0 commit comments