Skip to content

Commit 3d605ee

Browse files
committed
Resolved errors
1 parent ff65d27 commit 3d605ee

File tree

4 files changed

+259
-62
lines changed

4 files changed

+259
-62
lines changed

docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ Combining `query` and `retrievers` is not supported.
3131
`normalizer` {applies_to}`stack: ga 9.1`
3232
: (Optional, String)
3333

34-
The normalizer to use when using the [multi-field query format](../retrievers.md#multi-field-query-format).
34+
The normalizer to use for score normalization. This serves as the default normalizer for all sub-retrievers.
3535
See [normalizers](#linear-retriever-normalizers) for supported values.
36-
Required when `query` is specified.
36+
37+
When using the [multi-field query format](../retrievers.md#multi-field-query-format), this field is required when `query` is specified.
38+
39+
When using the `retrievers` array format, this field serves as the default normalizer for all sub-retrievers. Individual sub-retrievers can override this default by specifying their own `normalizer` field.
3740

3841
::::{warning}
3942
Avoid using `none` as that will disable normalization and may bias the result set towards lexical matches.
@@ -91,3 +94,47 @@ The `linear` retriever supports the following normalizers:
9194
score = (score - min) / (max - min)
9295
```
9396
* `l2_norm`: Normalizes scores using the L2 norm of the score values {applies_to}`stack: ga 9.1`
97+
98+
## Examples [linear-retriever-examples]
99+
100+
### Top-level normalizer example
101+
102+
This example shows how to use a top-level normalizer that applies to all sub-retrievers:
103+
104+
```console
105+
GET my_index/_search
106+
{
107+
"retriever": {
108+
"linear": {
109+
"retrievers": [
110+
{
111+
"retriever": {
112+
"standard": {
113+
"query": {
114+
"match": {
115+
"title": "elasticsearch"
116+
}
117+
}
118+
}
119+
},
120+
"weight": 1.0
121+
},
122+
{
123+
"retriever": {
124+
"knn": {
125+
"field": "title_vector",
126+
"query_vector": [0.1, 0.2, 0.3],
127+
"k": 10,
128+
"num_candidates": 100
129+
}
130+
},
131+
"weight": 2.0
132+
}
133+
],
134+
"normalizer": "minmax"
135+
}
136+
}
137+
}
138+
```
139+
140+
In this example, the `minmax` normalizer is applied to both the standard retriever and the kNN retriever. The top-level normalizer serves as a default that can be overridden by individual sub-retrievers. When using the multi-field query format, the top-level normalizer is applied to all generated inner retrievers.

x-pack/plugin/rank-rrf/src/main/java/org/elasticsearch/xpack/rank/linear/LinearRetrieverBuilder.java

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,17 @@ public LinearRetrieverBuilder(
212212
}
213213
this.weights = weights;
214214
this.normalizers = normalizers;
215-
this.fields = fields;
216-
this.query = query;
217-
this.normalizer = normalizer;
218-
219-
normalizeNormalizerArray(normalizer, normalizers);
220-
215+
// Apply top-level normalizer priority system:
216+
// 1. Retriever-specific override (if specified)
217+
// 2. Top-level normalizer (if specified)
218+
// 3. Default (IdentityScoreNormalizer.INSTANCE)
219+
ScoreNormalizer effectiveNormalizer = normalizer != null ? normalizer : DEFAULT_NORMALIZER;
220+
for (int i = 0; i < normalizers.length; i++) {
221+
if (normalizers[i] == null || normalizers[i].equals(DEFAULT_NORMALIZER)) {
222+
normalizers[i] = effectiveNormalizer;
223+
}
224+
// If per-retriever normalizer is explicitly specified, keep it (allow override)
225+
}
221226
}
222227

223228
public LinearRetrieverBuilder(
@@ -271,19 +276,7 @@ public ActionRequestValidationException validate(
271276
),
272277
validationException
273278
);
274-
} else if (innerRetrievers.isEmpty() == false && normalizer != null) {
275-
validationException = addValidationError(
276-
String.format(
277-
Locale.ROOT,
278-
"[%s] [%s] cannot be provided when [%s] is specified",
279-
getName(),
280-
NORMALIZER_FIELD.getPreferredName(),
281-
RETRIEVERS_FIELD.getPreferredName()
282-
),
283-
validationException
284-
);
285279
}
286-
287280
return validationException;
288281
}
289282

x-pack/plugin/rank-rrf/src/test/java/org/elasticsearch/xpack/rank/linear/LinearRetrieverBuilderTests.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.index.query.QueryRewriteContext;
2323
import org.elasticsearch.search.builder.PointInTimeBuilder;
2424
import org.elasticsearch.search.retriever.CompoundRetrieverBuilder;
25+
import org.elasticsearch.search.retriever.KnnRetrieverBuilder;
2526
import org.elasticsearch.search.retriever.RetrieverBuilder;
2627
import org.elasticsearch.search.retriever.StandardRetrieverBuilder;
2728
import org.elasticsearch.test.ESTestCase;
@@ -326,4 +327,63 @@ public int hashCode() {
326327
return Objects.hash(retriever, weight, normalizer);
327328
}
328329
}
330+
331+
public void testTopLevelNormalizerWithRetrieversArray() {
332+
StandardRetrieverBuilder standardRetriever = new StandardRetrieverBuilder(new MatchQueryBuilder("title", "elasticsearch"));
333+
KnnRetrieverBuilder knnRetriever = new KnnRetrieverBuilder(
334+
"title_vector",
335+
new float[] { 0.1f, 0.2f, 0.3f },
336+
null,
337+
10,
338+
100,
339+
null,
340+
null
341+
);
342+
343+
LinearRetrieverBuilder retriever = new LinearRetrieverBuilder(
344+
List.of(
345+
CompoundRetrieverBuilder.RetrieverSource.from(standardRetriever),
346+
CompoundRetrieverBuilder.RetrieverSource.from(knnRetriever)
347+
),
348+
null, // fields
349+
null, // query
350+
MinMaxScoreNormalizer.INSTANCE, // top-level normalizer
351+
DEFAULT_RANK_WINDOW_SIZE,
352+
new float[] { 1.0f, 2.0f },
353+
new ScoreNormalizer[] { null, null }
354+
);
355+
356+
assertEquals(MinMaxScoreNormalizer.INSTANCE, retriever.getNormalizers()[0]);
357+
assertEquals(MinMaxScoreNormalizer.INSTANCE, retriever.getNormalizers()[1]);
358+
}
359+
360+
public void testTopLevelNormalizerWithPerRetrieverOverrides() {
361+
StandardRetrieverBuilder standardRetriever = new StandardRetrieverBuilder(new MatchQueryBuilder("title", "elasticsearch"));
362+
KnnRetrieverBuilder knnRetriever = new KnnRetrieverBuilder(
363+
"title_vector",
364+
new float[] { 0.1f, 0.2f, 0.3f },
365+
null,
366+
10,
367+
100,
368+
null,
369+
null
370+
);
371+
372+
LinearRetrieverBuilder retriever = new LinearRetrieverBuilder(
373+
List.of(
374+
CompoundRetrieverBuilder.RetrieverSource.from(standardRetriever),
375+
CompoundRetrieverBuilder.RetrieverSource.from(knnRetriever)
376+
),
377+
null, // fields
378+
null, // query
379+
MinMaxScoreNormalizer.INSTANCE, // top-level normalizer
380+
DEFAULT_RANK_WINDOW_SIZE,
381+
new float[] { 1.0f, 2.0f },
382+
new ScoreNormalizer[] { L2ScoreNormalizer.INSTANCE, null }
383+
);
384+
385+
assertEquals(L2ScoreNormalizer.INSTANCE, retriever.getNormalizers()[0]);
386+
assertEquals(MinMaxScoreNormalizer.INSTANCE, retriever.getNormalizers()[1]);
387+
}
388+
329389
}

0 commit comments

Comments
 (0)