88package org .elasticsearch .xpack .rank .linear ;
99
1010import org .apache .lucene .search .ScoreDoc ;
11+ import org .elasticsearch .action .ActionRequestValidationException ;
1112import org .elasticsearch .action .ResolvedIndices ;
1213import org .elasticsearch .common .ParsingException ;
1314import org .elasticsearch .common .util .Maps ;
3233import java .util .ArrayList ;
3334import java .util .Arrays ;
3435import java .util .List ;
36+ import java .util .Locale ;
3537import java .util .Map ;
3638
39+ import static org .elasticsearch .action .ValidateActions .addValidationError ;
3740import static org .elasticsearch .xcontent .ConstructingObjectParser .optionalConstructorArg ;
3841import static org .elasticsearch .xpack .rank .RankRRFFeatures .LINEAR_RETRIEVER_SUPPORTED ;
3942import static org .elasticsearch .xpack .rank .linear .LinearRetrieverComponent .DEFAULT_WEIGHT ;
@@ -63,7 +66,7 @@ public final class LinearRetrieverBuilder extends CompoundRetrieverBuilder<Linea
6366 private final ScoreNormalizer [] normalizers ;
6467 private final List <String > fields ;
6568 private final String query ;
66- private final String normalizer ;
69+ private final ScoreNormalizer normalizer ;
6770
6871 @ SuppressWarnings ("unchecked" )
6972 static final ConstructingObjectParser <LinearRetrieverBuilder , RetrieverParserContext > PARSER = new ConstructingObjectParser <>(
@@ -73,7 +76,7 @@ public final class LinearRetrieverBuilder extends CompoundRetrieverBuilder<Linea
7376 List <LinearRetrieverComponent > retrieverComponents = args [0 ] == null ? List .of () : (List <LinearRetrieverComponent >) args [0 ];
7477 List <String > fields = (List <String >) args [1 ];
7578 String query = (String ) args [2 ];
76- String normalizer = ( String ) args [3 ];
79+ ScoreNormalizer normalizer = args [ 3 ] == null ? null : ScoreNormalizer . valueOf (( String ) args [3 ]) ;
7780 int rankWindowSize = args [4 ] == null ? RankBuilder .DEFAULT_RANK_WINDOW_SIZE : (int ) args [4 ];
7881
7982 int index = 0 ;
@@ -140,12 +143,12 @@ public LinearRetrieverBuilder(
140143 List <RetrieverSource > innerRetrievers ,
141144 List <String > fields ,
142145 String query ,
143- String normalizer ,
146+ ScoreNormalizer normalizer ,
144147 int rankWindowSize ,
145148 float [] weights ,
146149 ScoreNormalizer [] normalizers
147150 ) {
148- // Use a mutable list for innerRetrievers so that we can add more child retrievers during rewrite
151+ // Use a mutable list for innerRetrievers so that we can use addChild
149152 super (innerRetrievers == null ? new ArrayList <>() : new ArrayList <>(innerRetrievers ), rankWindowSize );
150153 if (weights .length != this .innerRetrievers .size ()) {
151154 throw new IllegalArgumentException ("The number of weights must match the number of inner retrievers" );
@@ -159,6 +162,55 @@ public LinearRetrieverBuilder(
159162 this .normalizer = normalizer ;
160163 this .weights = weights ;
161164 this .normalizers = normalizers ;
165+
166+ // TODO: Validate simplified query format args here?
167+ // Otherwise some of the validation is skipped when creating the retriever programmatically.
168+ }
169+
170+ @ Override
171+ public ActionRequestValidationException validate (
172+ SearchSourceBuilder source ,
173+ ActionRequestValidationException validationException ,
174+ boolean isScroll ,
175+ boolean allowPartialSearchResults
176+ ) {
177+ validationException = super .validate (source , validationException , isScroll , allowPartialSearchResults );
178+ validationException = SimplifiedInnerRetrieverUtils .validateSimplifiedFormatParams (
179+ innerRetrievers ,
180+ fields ,
181+ query ,
182+ getName (),
183+ RETRIEVERS_FIELD .getPreferredName (),
184+ FIELDS_FIELD .getPreferredName (),
185+ QUERY_FIELD .getPreferredName (),
186+ validationException
187+ );
188+
189+ if (query != null && normalizer == null ) {
190+ validationException = addValidationError (
191+ String .format (
192+ Locale .ROOT ,
193+ "[%s] [%s] must be provided when [%s] is specified" ,
194+ getName (),
195+ NORMALIZER_FIELD .getPreferredName (),
196+ QUERY_FIELD .getPreferredName ()
197+ ),
198+ validationException
199+ );
200+ } else if (innerRetrievers .isEmpty () == false && normalizer != null ) {
201+ validationException = addValidationError (
202+ String .format (
203+ Locale .ROOT ,
204+ "[%s] [%s] cannot be provided when [%s] is specified" ,
205+ getName (),
206+ NORMALIZER_FIELD .getPreferredName (),
207+ RETRIEVERS_FIELD .getPreferredName ()
208+ ),
209+ validationException
210+ );
211+ }
212+
213+ return validationException ;
162214 }
163215
164216 @ Override
@@ -233,27 +285,8 @@ protected LinearRetrieverBuilder doRewrite(QueryRewriteContext ctx) {
233285 LinearRetrieverBuilder rewritten = this ;
234286
235287 ResolvedIndices resolvedIndices = ctx .getResolvedIndices ();
236- if (resolvedIndices != null && ( query != null || fields . isEmpty () == false ) ) {
288+ if (resolvedIndices != null && query != null ) {
237289 // Using the simplified query format
238- if (query == null || query .isEmpty ()) {
239- throw new IllegalArgumentException (
240- "[" + NAME + "] [" + QUERY_FIELD .getPreferredName () + "] must be provided when using the simplified query format"
241- );
242- }
243-
244- if (normalizer == null || normalizer .isEmpty ()) {
245- throw new IllegalArgumentException (
246- "[" + NAME + "] [" + NORMALIZER_FIELD .getPreferredName () + "] must be provided when using the simplified query format"
247- );
248- }
249- ScoreNormalizer fieldsNormalizer = ScoreNormalizer .valueOf (normalizer );
250-
251- if (innerRetrievers .isEmpty () == false ) {
252- throw new IllegalArgumentException (
253- "[" + NAME + "] does not support [" + RETRIEVERS_FIELD .getPreferredName () + "] and the simplified query format combined"
254- );
255- }
256-
257290 var localIndicesMetadata = resolvedIndices .getConcreteLocalIndicesMetadata ();
258291 if (localIndicesMetadata .size () > 1 ) {
259292 throw new IllegalArgumentException (
@@ -274,7 +307,7 @@ protected LinearRetrieverBuilder doRewrite(QueryRewriteContext ctx) {
274307 for (var weightedRetriever : r ) {
275308 retrievers .add (weightedRetriever .retrieverSource ());
276309 weights [index ] = weightedRetriever .weight ();
277- normalizers [index ] = fieldsNormalizer ;
310+ normalizers [index ] = normalizer ;
278311 index ++;
279312 }
280313
@@ -291,7 +324,7 @@ protected LinearRetrieverBuilder doRewrite(QueryRewriteContext ctx) {
291324 Arrays .fill (weights , DEFAULT_WEIGHT );
292325
293326 ScoreNormalizer [] normalizers = new ScoreNormalizer [fieldsInnerRetrievers .size ()];
294- Arrays .fill (normalizers , fieldsNormalizer );
327+ Arrays .fill (normalizers , normalizer );
295328
296329 rewritten = new LinearRetrieverBuilder (fieldsInnerRetrievers , null , null , normalizer , rankWindowSize , weights , normalizers );
297330 }
@@ -330,7 +363,7 @@ public void doToXContent(XContentBuilder builder, Params params) throws IOExcept
330363 builder .field (QUERY_FIELD .getPreferredName (), query );
331364 }
332365 if (normalizer != null ) {
333- builder .field (NORMALIZER_FIELD .getPreferredName (), normalizer );
366+ builder .field (NORMALIZER_FIELD .getPreferredName (), normalizer . getName () );
334367 }
335368
336369 builder .field (RANK_WINDOW_SIZE_FIELD .getPreferredName (), rankWindowSize );
0 commit comments