1111import org .elasticsearch .common .io .stream .BytesRefStreamOutput ;
1212import org .elasticsearch .compute .data .Block ;
1313import org .elasticsearch .compute .data .BlockFactory ;
14- import org .elasticsearch .compute .data .BlockUtils ;
1514import org .elasticsearch .compute .data .BytesRefBlock ;
1615import org .elasticsearch .compute .data .Page ;
1716import org .elasticsearch .compute .operator .DriverContext ;
1817import org .elasticsearch .compute .operator .EvalOperator ;
18+ import org .elasticsearch .compute .operator .EvalOperator .ExpressionEvaluator ;
1919import org .elasticsearch .core .Releasables ;
20+ import org .elasticsearch .xcontent .ToXContent ;
2021import org .elasticsearch .xcontent .XContentBuilder ;
2122import org .elasticsearch .xcontent .XContentFactory ;
2223import org .elasticsearch .xcontent .XContentType ;
24+ import org .elasticsearch .xpack .esql .action .ColumnInfoImpl ;
25+ import org .elasticsearch .xpack .esql .action .PositionToXContent ;
2326
2427import java .io .IOException ;
2528import java .io .UncheckedIOException ;
29+ import java .util .Arrays ;
2630import java .util .List ;
2731import java .util .Map ;
32+ import java .util .stream .Collectors ;
2833
29- public class XContentRowEncoder implements RowEncoder <BytesRefBlock > {
34+
35+ /**
36+ * Encodes rows into an XContent format (JSON,YAML,...) for further processing.
37+ * Extracted columns can be specified using {@link EvalOperator}ExpressionEvaluator}
38+ */
39+ public class XContentRowEncoder implements ExpressionEvaluator {
3040 private final XContentType xContentType ;
3141 private final BlockFactory blockFactory ;
32- private final String [] fieldNames ;
33- private final EvalOperator .ExpressionEvaluator [] fieldsValueEvaluators ;
34-
35- public static Factory yamlRowEncoderFactory (Map <String , EvalOperator .ExpressionEvaluator .Factory > fieldsEvaluatorFactories ) {
42+ private final ColumnInfoImpl [] columnsInfo ;
43+ private final ExpressionEvaluator [] fieldsValueEvaluators ;
44+
45+ /**
46+ * Creates a factory for YAML XContent row encoding.
47+ *
48+ * @param fieldsEvaluatorFactories A map of column information to expression evaluators.
49+ * @return A Factory instance for creating YAML row encoder for the specified column.
50+ */
51+ public static Factory yamlRowEncoderFactory (Map <ColumnInfoImpl , ExpressionEvaluator .Factory > fieldsEvaluatorFactories ) {
3652 return new Factory (XContentType .YAML , fieldsEvaluatorFactories );
3753 }
3854
3955 private XContentRowEncoder (
4056 XContentType xContentType ,
4157 BlockFactory blockFactory ,
42- String [] fieldNames ,
43- EvalOperator . ExpressionEvaluator [] fieldsValueEvaluators
58+ ColumnInfoImpl [] columnsInfo ,
59+ ExpressionEvaluator [] fieldsValueEvaluators
4460 ) {
45- assert fieldNames .length == fieldsValueEvaluators .length ;
61+ assert columnsInfo .length == fieldsValueEvaluators .length ;
4662 this .xContentType = xContentType ;
4763 this .blockFactory = blockFactory ;
48- this .fieldNames = fieldNames ;
64+ this .columnsInfo = columnsInfo ;
4965 this .fieldsValueEvaluators = fieldsValueEvaluators ;
5066 }
5167
@@ -54,27 +70,36 @@ public void close() {
5470 Releasables .closeExpectNoException (fieldsValueEvaluators );
5571 }
5672
73+ /**
74+ * Process the provided Page and encode its rows into a BytesRefBlock containing XContent-formatted rows.
75+ *
76+ * @param page The input Page containing row data.
77+ * @return A BytesRefBlock containing the encoded rows.
78+ */
5779 @ Override
58- public BytesRefBlock encodeRows (Page page ) {
80+ public BytesRefBlock eval (Page page ) {
5981 Block [] fieldValueBlocks = new Block [fieldsValueEvaluators .length ];
6082 try (
6183 BytesRefStreamOutput outputStream = new BytesRefStreamOutput ();
6284 XContentBuilder xContentBuilder = XContentFactory .contentBuilder (xContentType , outputStream );
6385 BytesRefBlock .Builder outputBlockBuilder = blockFactory .newBytesRefBlockBuilder (page .getPositionCount ());
6486 ) {
87+
88+ PositionToXContent [] toXContents = new PositionToXContent [fieldsValueEvaluators .length ];
6589 for (int b = 0 ; b < fieldValueBlocks .length ; b ++) {
6690 fieldValueBlocks [b ] = fieldsValueEvaluators [b ].eval (page );
91+ toXContents [b ] = PositionToXContent .positionToXContent (columnsInfo [b ], fieldValueBlocks [b ], new BytesRef ());
6792 }
6893
6994 for (int pos = 0 ; pos < page .getPositionCount (); pos ++) {
7095 xContentBuilder .startObject ();
7196 for (int i = 0 ; i < fieldValueBlocks .length ; i ++) {
72- String fieldName = fieldNames [i ];
97+ String fieldName = columnsInfo [i ]. name () ;
7398 Block currentBlock = fieldValueBlocks [i ];
74- if (currentBlock .isNull (pos )) {
99+ if (currentBlock .isNull (pos ) || currentBlock . getValueCount ( pos ) < 1 ) {
75100 continue ;
76101 }
77- xContentBuilder .field (fieldName , toYamlValue ( BlockUtils . toJavaObject ( currentBlock , pos )) );
102+ toXContents [ i ]. positionToXContent ( xContentBuilder .field (fieldName ), ToXContent . EMPTY_PARAMS , pos );
78103 }
79104 xContentBuilder .endObject ().flush ();
80105 outputBlockBuilder .appendBytesRef (outputStream .get ());
@@ -89,46 +114,37 @@ public BytesRefBlock encodeRows(Page page) {
89114 }
90115 }
91116
92- @ Override
93- public String toString () {
94- return "XContentRowEncoder[content_type=[" + xContentType .toString () + "], field_names=" + List .of (fieldNames ) + "]" ;
117+ public List <String > fieldNames () {
118+ return Arrays .stream (columnsInfo ).map (ColumnInfoImpl ::name ).collect (Collectors .toList ());
95119 }
96120
97- private Object toYamlValue (Object value ) {
98- try {
99- return switch (value ) {
100- case BytesRef b -> b .utf8ToString ();
101- case List <?> l -> l .stream ().map (this ::toYamlValue ).toList ();
102- default -> value ;
103- };
104- } catch (Error | Exception e ) {
105- // Swallow errors caused by invalid byteref.
106- return "" ;
107- }
121+ @ Override
122+ public String toString () {
123+ return "XContentRowEncoder[content_type=[" + xContentType .toString () + "], field_names=" + fieldNames () + "]" ;
108124 }
109125
110- public static final class Factory implements RowEncoder .Factory < BytesRefBlock > {
126+ public static class Factory implements ExpressionEvaluator .Factory {
111127 private final XContentType xContentType ;
112- private final Map <String , EvalOperator . ExpressionEvaluator .Factory > fieldsEvaluatorFactories ;
128+ private final Map <ColumnInfoImpl , ExpressionEvaluator .Factory > fieldsEvaluatorFactories ;
113129
114- private Factory (XContentType xContentType , Map <String , EvalOperator . ExpressionEvaluator .Factory > fieldsEvaluatorFactories ) {
130+ private Factory (XContentType xContentType , Map <ColumnInfoImpl , ExpressionEvaluator .Factory > fieldsEvaluatorFactories ) {
115131 this .xContentType = xContentType ;
116132 this .fieldsEvaluatorFactories = fieldsEvaluatorFactories ;
117133 }
118134
119- public RowEncoder < BytesRefBlock > get (DriverContext context ) {
120- return new XContentRowEncoder (xContentType , context .blockFactory (), fieldNames (), fieldsValueEvaluators (context ));
135+ public XContentRowEncoder get (DriverContext context ) {
136+ return new XContentRowEncoder (xContentType , context .blockFactory (), columnsInfo (), fieldsValueEvaluators (context ));
121137 }
122138
123- private String [] fieldNames () {
124- return fieldsEvaluatorFactories .keySet ().toArray (String []::new );
139+ private ColumnInfoImpl [] columnsInfo () {
140+ return fieldsEvaluatorFactories .keySet ().toArray (ColumnInfoImpl []::new );
125141 }
126142
127- private EvalOperator . ExpressionEvaluator [] fieldsValueEvaluators (DriverContext context ) {
143+ private ExpressionEvaluator [] fieldsValueEvaluators (DriverContext context ) {
128144 return fieldsEvaluatorFactories .values ()
129145 .stream ()
130146 .map (factory -> factory .get (context ))
131- .toArray (EvalOperator . ExpressionEvaluator []::new );
147+ .toArray (ExpressionEvaluator []::new );
132148 }
133149 }
134150}
0 commit comments