Skip to content

Commit 3086a4b

Browse files
authored
Merge branch 'main' into markjhoy/default_token_pruning_sparse_vector
2 parents e593f17 + 23b7a31 commit 3086a4b

File tree

22 files changed

+501
-102
lines changed

22 files changed

+501
-102
lines changed

BUILDING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ To wire this registered cluster into a `TestClusterAware` task (e.g. `RestIntegT
144144
Additional integration tests for a certain Elasticsearch modules that are specific to certain cluster configuration can be declared in a separate so called `qa` subproject of your module.
145145

146146
The benefit of a dedicated project for these tests are:
147-
- `qa` projects are dedicated two specific use-cases and easier to maintain
147+
- `qa` projects are dedicated to specific use-cases and easier to maintain
148148
- It keeps the specific test logic separated from the common test logic.
149149
- You can run those tests in parallel to other projects of the build.
150150

docs/changelog/127229.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 127229
2+
summary: Return BAD_REQUEST when a field scorer references a missing field
3+
area: Ranking
4+
type: bug
5+
issues:
6+
- 127162

docs/changelog/127414.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 127414
2+
summary: Fix npe when using source confirmed text query against missing field
3+
area: Search
4+
type: bug
5+
issues: []

modules/data-streams/src/test/java/org/elasticsearch/datastreams/action/TransportGetDataStreamsActionTests.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.action.datastreams.GetDataStreamAction;
1212
import org.elasticsearch.cluster.ClusterName;
1313
import org.elasticsearch.cluster.ClusterState;
14+
import org.elasticsearch.cluster.metadata.ComponentTemplate;
1415
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
1516
import org.elasticsearch.cluster.metadata.DataStream;
1617
import org.elasticsearch.cluster.metadata.DataStreamFailureStoreSettings;
@@ -40,6 +41,7 @@
4041
import java.time.temporal.ChronoUnit;
4142
import java.util.ArrayList;
4243
import java.util.List;
44+
import java.util.Map;
4345
import java.util.Set;
4446
import java.util.stream.Collectors;
4547

@@ -543,11 +545,44 @@ public void testGetEffectiveSettingsTemplateOnlySettings() {
543545
GetDataStreamAction.Request req = new GetDataStreamAction.Request(TEST_REQUEST_TIMEOUT, new String[] {});
544546
final String templatePolicy = "templatePolicy";
545547
final String templateIndexMode = IndexMode.LOOKUP.getName();
546-
final String dataStreamPolicy = "dataStreamPolicy";
547-
final String dataStreamIndexMode = IndexMode.LOGSDB.getName();
548548

549549
ClusterState state = getClusterStateWithDataStreamWithSettings(
550550
projectId,
551+
Settings.builder()
552+
.put(IndexMetadata.LIFECYCLE_NAME, templatePolicy)
553+
.put(IndexSettings.MODE.getKey(), templateIndexMode)
554+
.build(),
555+
Settings.EMPTY,
556+
Settings.EMPTY
557+
);
558+
559+
GetDataStreamAction.Response response = TransportGetDataStreamsAction.innerOperation(
560+
state.projectState(projectId),
561+
req,
562+
resolver,
563+
systemIndices,
564+
ClusterSettings.createBuiltInClusterSettings(),
565+
dataStreamGlobalRetentionSettings,
566+
emptyDataStreamFailureStoreSettings,
567+
new IndexSettingProviders(Set.of()),
568+
null
569+
);
570+
assertNotNull(response.getDataStreams());
571+
assertThat(response.getDataStreams().size(), equalTo(1));
572+
assertThat(response.getDataStreams().get(0).getIlmPolicy(), equalTo(templatePolicy));
573+
assertThat(response.getDataStreams().get(0).getIndexModeName(), equalTo(templateIndexMode));
574+
}
575+
576+
public void testGetEffectiveSettingsComponentTemplateOnlySettings() {
577+
// Set a lifecycle only in the template, and make sure that is in the response:
578+
ProjectId projectId = randomProjectIdOrDefault();
579+
GetDataStreamAction.Request req = new GetDataStreamAction.Request(TEST_REQUEST_TIMEOUT, new String[] {});
580+
final String templatePolicy = "templatePolicy";
581+
final String templateIndexMode = IndexMode.LOOKUP.getName();
582+
583+
ClusterState state = getClusterStateWithDataStreamWithSettings(
584+
projectId,
585+
Settings.EMPTY,
551586
Settings.builder()
552587
.put(IndexMetadata.LIFECYCLE_NAME, templatePolicy)
553588
.put(IndexSettings.MODE.getKey(), templateIndexMode)
@@ -582,6 +617,10 @@ public void testGetEffectiveSettings() {
582617
// Now set a lifecycle in both the template and the data stream, and make sure the response has the data stream one:
583618
ClusterState state = getClusterStateWithDataStreamWithSettings(
584619
projectId,
620+
Settings.builder()
621+
.put(IndexMetadata.LIFECYCLE_NAME, templatePolicy)
622+
.put(IndexSettings.MODE.getKey(), templateIndexMode)
623+
.build(),
585624
Settings.builder()
586625
.put(IndexMetadata.LIFECYCLE_NAME, templatePolicy)
587626
.put(IndexSettings.MODE.getKey(), templateIndexMode)
@@ -611,6 +650,7 @@ public void testGetEffectiveSettings() {
611650
private static ClusterState getClusterStateWithDataStreamWithSettings(
612651
ProjectId projectId,
613652
Settings templateSettings,
653+
Settings componentTemplateSettings,
614654
Settings dataStreamSettings
615655
) {
616656
String dataStreamName = "data-stream-1";
@@ -625,8 +665,16 @@ private static ClusterState getClusterStateWithDataStreamWithSettings(
625665
.indexPatterns(List.of("*"))
626666
.template(Template.builder().settings(templateSettings))
627667
.dataStreamTemplate(new ComposableIndexTemplate.DataStreamTemplate())
668+
.componentTemplates(List.of("component_template_1"))
628669
.build()
629670
);
671+
ComponentTemplate componentTemplate = new ComponentTemplate(
672+
Template.builder().settings(componentTemplateSettings).build(),
673+
null,
674+
null,
675+
null
676+
);
677+
builder.componentTemplates(Map.of("component_template_1", componentTemplate));
630678

631679
List<IndexMetadata> backingIndices = new ArrayList<>();
632680
for (int backingIndexNumber = 1; backingIndexNumber <= numberOfBackingIndices; backingIndexNumber++) {

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/SourceConfirmedTextQuery.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,11 @@ public boolean isCacheable(LeafReaderContext ctx) {
267267
@Override
268268
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
269269
NumericDocValues norms = context.reader().getNormValues(field);
270-
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier(context).get(0);
270+
ScorerSupplier scorerSupplier = scorerSupplier(context);
271+
if (scorerSupplier == null) {
272+
return Explanation.noMatch("No matching phrase");
273+
}
274+
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier.get(0);
271275
if (scorer == null) {
272276
return Explanation.noMatch("No matching phrase");
273277
}
@@ -277,6 +281,7 @@ public Explanation explain(LeafReaderContext context, int doc) throws IOExceptio
277281
}
278282
float phraseFreq = scorer.freq();
279283
Explanation freqExplanation = Explanation.match(phraseFreq, "phraseFreq=" + phraseFreq);
284+
assert simScorer != null;
280285
Explanation scoreExplanation = simScorer.explain(freqExplanation, getNormValue(norms, doc));
281286
return Explanation.match(
282287
scoreExplanation.getValue(),
@@ -321,7 +326,11 @@ public Matches matches(LeafReaderContext context, int doc) throws IOException {
321326
Weight innerWeight = in.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, 1);
322327
return innerWeight.matches(context, doc);
323328
}
324-
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier(context).get(0L);
329+
ScorerSupplier scorerSupplier = scorerSupplier(context);
330+
if (scorerSupplier == null) {
331+
return null;
332+
}
333+
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier.get(0L);
325334
if (scorer == null) {
326335
return null;
327336
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/SourceConfirmedTextQueryTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.lucene.queries.spans.SpanTermQuery;
2525
import org.apache.lucene.search.BooleanClause.Occur;
2626
import org.apache.lucene.search.BooleanQuery;
27+
import org.apache.lucene.search.Explanation;
2728
import org.apache.lucene.search.IndexSearcher;
2829
import org.apache.lucene.search.MatchNoDocsQuery;
2930
import org.apache.lucene.search.Matches;
@@ -101,6 +102,26 @@ public void testTerm() throws Exception {
101102
}
102103
}
103104

105+
public void testMissingPhrase() throws Exception {
106+
try (Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(Lucene.STANDARD_ANALYZER))) {
107+
108+
Document doc = new Document();
109+
doc.add(new TextField("body", "a b c b a b c", Store.YES));
110+
w.addDocument(doc);
111+
112+
try (IndexReader reader = DirectoryReader.open(w)) {
113+
IndexSearcher searcher = newSearcher(reader);
114+
PhraseQuery query = new PhraseQuery("missing_field", "b", "c");
115+
Query sourceConfirmedPhraseQuery = new SourceConfirmedTextQuery(query, SOURCE_FETCHER_PROVIDER, Lucene.STANDARD_ANALYZER);
116+
Explanation explanation = searcher.explain(sourceConfirmedPhraseQuery, 0);
117+
assertFalse(explanation.isMatch());
118+
119+
Weight weight = searcher.createWeight(query, ScoreMode.COMPLETE, 1);
120+
assertNull(weight.matches(getOnlyLeafReader(reader).getContext(), 0));
121+
}
122+
}
123+
}
124+
104125
public void testPhrase() throws Exception {
105126
try (Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(Lucene.STANDARD_ANALYZER))) {
106127

plugins/examples/rescore/src/yamlRestTest/resources/rest-api-spec/test/example-rescore/30_factor_field.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,24 @@ setup:
4848
- match: { hits.hits.1._score: 20 }
4949
- match: { hits.hits.2._score: 10 }
5050

51+
---
52+
"referencing a missing field returns bad request":
53+
- requires:
54+
cluster_features: [ "search.rescorer.missing.field.bad.request" ]
55+
reason: "Testing the behaviour change with this feature"
56+
- do:
57+
catch: bad_request
58+
search:
59+
index: test
60+
body:
61+
rescore:
62+
example:
63+
factor: 1
64+
factor_field: missing
65+
- match: { status: 400 }
66+
- match: { error.root_cause.0.type: "illegal_argument_exception" }
67+
- match: { error.root_cause.0.reason: "Missing value for field [missing]" }
68+
5169
---
5270
"sorted based on a numeric field and rescored based on a factor field using a window size":
5371
- do:

server/src/main/java/org/elasticsearch/cluster/metadata/DataStream.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,12 +381,7 @@ public ComposableIndexTemplate getEffectiveIndexTemplate(ProjectMetadata project
381381

382382
public Settings getEffectiveSettings(ProjectMetadata projectMetadata) {
383383
ComposableIndexTemplate template = getMatchingIndexTemplate(projectMetadata);
384-
final Settings templateSettings;
385-
if (template.template() == null || template.template().settings() == null) {
386-
templateSettings = Settings.EMPTY;
387-
} else {
388-
templateSettings = template.template().settings();
389-
}
384+
Settings templateSettings = MetadataIndexTemplateService.resolveSettings(template, projectMetadata.componentTemplates());
390385
return templateSettings.merge(settings);
391386
}
392387

server/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import org.apache.lucene.index.LeafReaderContext;
1313
import org.apache.lucene.search.Explanation;
14-
import org.elasticsearch.ElasticsearchException;
1514
import org.elasticsearch.common.io.stream.StreamInput;
1615
import org.elasticsearch.common.io.stream.StreamOutput;
1716
import org.elasticsearch.common.io.stream.Writeable;
@@ -73,7 +72,7 @@ public double score(int docId, float subQueryScore) throws IOException {
7372
if (missing != null) {
7473
value = missing;
7574
} else {
76-
throw new ElasticsearchException("Missing value for field [" + field + "]");
75+
throw new IllegalArgumentException("Missing value for field [" + field + "]");
7776
}
7877
}
7978
double val = value * boostFactor;

server/src/main/java/org/elasticsearch/search/SearchFeatures.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ public Set<NodeFeature> getFeatures() {
2828
public static final NodeFeature COMPLETION_FIELD_SUPPORTS_DUPLICATE_SUGGESTIONS = new NodeFeature(
2929
"search.completion_field.duplicate.support"
3030
);
31+
public static final NodeFeature RESCORER_MISSING_FIELD_BAD_REQUEST = new NodeFeature("search.rescorer.missing.field.bad.request");
3132

3233
@Override
3334
public Set<NodeFeature> getTestFeatures() {
34-
return Set.of(RETRIEVER_RESCORER_ENABLED, COMPLETION_FIELD_SUPPORTS_DUPLICATE_SUGGESTIONS);
35+
return Set.of(RETRIEVER_RESCORER_ENABLED, COMPLETION_FIELD_SUPPORTS_DUPLICATE_SUGGESTIONS, RESCORER_MISSING_FIELD_BAD_REQUEST);
3536
}
3637
}

0 commit comments

Comments
 (0)