8
8
package org .elasticsearch .compute .operator .fuse ;
9
9
10
10
import org .apache .lucene .util .BytesRef ;
11
+ import org .elasticsearch .TransportVersion ;
12
+ import org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
13
+ import org .elasticsearch .common .io .stream .StreamInput ;
14
+ import org .elasticsearch .common .io .stream .StreamOutput ;
11
15
import org .elasticsearch .compute .data .Block ;
12
16
import org .elasticsearch .compute .data .BytesRefBlock ;
13
17
import org .elasticsearch .compute .data .DoubleVector ;
14
18
import org .elasticsearch .compute .data .DoubleVectorBlock ;
15
19
import org .elasticsearch .compute .data .Page ;
16
20
import org .elasticsearch .compute .operator .DriverContext ;
17
21
import org .elasticsearch .compute .operator .Operator ;
22
+ import org .elasticsearch .core .Releasables ;
23
+ import org .elasticsearch .core .TimeValue ;
24
+ import org .elasticsearch .xcontent .XContentBuilder ;
18
25
26
+ import java .io .IOException ;
19
27
import java .util .ArrayDeque ;
20
28
import java .util .Collection ;
21
29
import java .util .Deque ;
@@ -60,6 +68,12 @@ public String describe() {
60
68
private final Deque <Page > outputPages ;
61
69
private boolean finished ;
62
70
71
+ private long emitNanos ;
72
+ private int pagesReceived = 0 ;
73
+ private int pagesProcessed = 0 ;
74
+ private long rowsReceived = 0 ;
75
+ private long rowsEmitted = 0 ;
76
+
63
77
public LinearScoreEvalOperator (int discriminatorPosition , int scorePosition , LinearConfig config ) {
64
78
this .scorePosition = scorePosition ;
65
79
this .discriminatorPosition = discriminatorPosition ;
@@ -79,6 +93,8 @@ public boolean needsInput() {
79
93
@ Override
80
94
public void addInput (Page page ) {
81
95
inputPages .add (page );
96
+ pagesReceived ++;
97
+ rowsReceived += page .getPositionCount ();
82
98
}
83
99
84
100
@ Override
@@ -90,35 +106,58 @@ public void finish() {
90
106
}
91
107
92
108
private void createOutputPages () {
109
+ final var emitStart = System .nanoTime ();
93
110
normalizer .preprocess (inputPages , scorePosition , discriminatorPosition );
111
+ try {
112
+ while (inputPages .isEmpty () == false ) {
113
+ Page inputPage = inputPages .peek ();
114
+ processInputPage (inputPage );
115
+ inputPages .removeFirst ();
116
+ pagesProcessed += 1 ;
117
+ }
118
+ } finally {
119
+ emitNanos = System .nanoTime () - emitStart ;
120
+ Releasables .close (inputPages );
121
+ }
122
+ }
94
123
95
- while (inputPages .isEmpty () == false ) {
96
- Page inputPage = inputPages .peek ();
124
+ private void processInputPage (Page inputPage ) {
125
+ BytesRefBlock discriminatorBlock = inputPage .getBlock (discriminatorPosition );
126
+ DoubleVectorBlock initialScoreBlock = inputPage .getBlock (scorePosition );
97
127
98
- BytesRefBlock discriminatorBlock = inputPage .getBlock (discriminatorPosition );
99
- DoubleVectorBlock initialScoreBlock = inputPage .getBlock (scorePosition );
128
+ Page newPage = null ;
129
+ Block scoreBlock = null ;
130
+ DoubleVector .Builder scores = null ;
100
131
101
- DoubleVector .Builder scores = discriminatorBlock .blockFactory ().newDoubleVectorBuilder (discriminatorBlock .getPositionCount ());
132
+ try {
133
+ scores = discriminatorBlock .blockFactory ().newDoubleVectorBuilder (discriminatorBlock .getPositionCount ());
102
134
103
135
for (int i = 0 ; i < inputPage .getPositionCount (); i ++) {
104
136
String discriminator = discriminatorBlock .getBytesRef (i , new BytesRef ()).utf8ToString ();
105
137
106
138
var weight = config .weights ().get (discriminator ) == null ? 1.0 : config .weights ().get (discriminator );
107
139
108
- Double score = initialScoreBlock .getDouble (i );
140
+ double score = initialScoreBlock .getDouble (i );
109
141
scores .appendDouble (weight * normalizer .normalize (score , discriminator ));
110
142
}
111
- Block scoreBlock = scores .build ().asBlock ();
112
- inputPage = inputPage .appendBlock (scoreBlock );
113
143
114
- int [] projections = new int [inputPage .getBlockCount () - 1 ];
144
+ scoreBlock = scores .build ().asBlock ();
145
+ newPage = inputPage .appendBlock (scoreBlock );
146
+
147
+ int [] projections = new int [newPage .getBlockCount () - 1 ];
115
148
116
- for (int i = 0 ; i < inputPage .getBlockCount () - 1 ; i ++) {
117
- projections [i ] = i == scorePosition ? inputPage .getBlockCount () - 1 : i ;
149
+ for (int i = 0 ; i < newPage .getBlockCount () - 1 ; i ++) {
150
+ projections [i ] = i == scorePosition ? newPage .getBlockCount () - 1 : i ;
151
+ }
152
+
153
+ outputPages .add (newPage .projectBlocks (projections ));
154
+ } finally {
155
+ if (newPage != null ) {
156
+ newPage .releaseBlocks ();
157
+ }
158
+ if (scoreBlock == null && scores != null ) {
159
+ Releasables .close (scores );
118
160
}
119
- inputPages .removeFirst ();
120
- outputPages .add (inputPage .projectBlocks (projections ));
121
- inputPage .releaseBlocks ();
122
161
}
123
162
}
124
163
@@ -132,7 +171,11 @@ public Page getOutput() {
132
171
if (finished == false || outputPages .isEmpty ()) {
133
172
return null ;
134
173
}
135
- return outputPages .removeFirst ();
174
+
175
+ Page page = outputPages .removeFirst ();
176
+ rowsEmitted += page .getPositionCount ();
177
+
178
+ return page ;
136
179
}
137
180
138
181
@ Override
@@ -156,6 +199,69 @@ public String toString() {
156
199
+ "]" ;
157
200
}
158
201
202
+ @ Override
203
+ public Operator .Status status () {
204
+ return new Status (emitNanos , pagesReceived , pagesProcessed , rowsReceived , rowsEmitted );
205
+ }
206
+
207
+ public record Status (long emitNanos , int pagesReceived , int pagesProcessed , long rowsReceived , long rowsEmitted )
208
+ implements
209
+ Operator .Status {
210
+
211
+ public static final TransportVersion ESQL_FUSE_LINEAR_OPERATOR_STATUS = TransportVersion .fromName (
212
+ "esql_fuse_linear_operator_status"
213
+ );
214
+
215
+ public static final NamedWriteableRegistry .Entry ENTRY = new NamedWriteableRegistry .Entry (
216
+ Operator .Status .class ,
217
+ "linearScoreEval" ,
218
+ Status ::new
219
+ );
220
+
221
+ Status (StreamInput streamInput ) throws IOException {
222
+ this (streamInput .readLong (), streamInput .readInt (), streamInput .readInt (), streamInput .readLong (), streamInput .readLong ());
223
+ }
224
+
225
+ @ Override
226
+ public String getWriteableName () {
227
+ return ENTRY .name ;
228
+ }
229
+
230
+ @ Override
231
+ public boolean supportsVersion (TransportVersion version ) {
232
+ return version .supports (ESQL_FUSE_LINEAR_OPERATOR_STATUS );
233
+ }
234
+
235
+ @ Override
236
+ public TransportVersion getMinimalSupportedVersion () {
237
+ assert false : "must not be called when overriding supportsVersion" ;
238
+ throw new UnsupportedOperationException ("must not be called when overriding supportsVersion" );
239
+ }
240
+
241
+ @ Override
242
+ public void writeTo (StreamOutput out ) throws IOException {
243
+ out .writeLong (emitNanos );
244
+ out .writeInt (pagesReceived );
245
+ out .writeInt (pagesProcessed );
246
+ out .writeLong (rowsReceived );
247
+ out .writeLong (rowsEmitted );
248
+ }
249
+
250
+ @ Override
251
+ public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
252
+ builder .startObject ();
253
+ builder .field ("emit_nanos" , emitNanos );
254
+ if (builder .humanReadable ()) {
255
+ builder .field ("emit_time" , TimeValue .timeValueNanos (emitNanos ));
256
+ }
257
+ builder .field ("pages_received" , pagesReceived );
258
+ builder .field ("pages_processed" , pagesProcessed );
259
+ builder .field ("rows_received" , rowsReceived );
260
+ builder .field ("rows_emitted" , rowsEmitted );
261
+ return builder .endObject ();
262
+ }
263
+ }
264
+
159
265
private Normalizer createNormalizer (LinearConfig .Normalizer normalizer ) {
160
266
return switch (normalizer ) {
161
267
case NONE -> new NoneNormalizer ();
0 commit comments