Add support for parameters for runtime fields.

Original Pull Request #2677
Closes #2303
This commit is contained in:
Peter-Josef Meisch 2023-08-26 22:13:26 +02:00 committed by GitHub
parent ed898431ab
commit 922c7dd4a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 28 deletions

View File

@ -19,7 +19,7 @@ public record FailureDetails(Integer status, String errorMessage) {
The classes `org.springframework.data.elasticsearch.core.RuntimeField` and `org.springframework.data.elasticsearch.core.query.ScriptType` have been moved to the subpackage `org.springframework.data.elasticsearch.core.query`.
The `type` parameter of the `ScriptData` constructir is not nullable any longer.
The `type` parameter of the `ScriptData` constructor is not nullable any longer.
[[elasticsearch-migration-guide-5.1-5.2.deprecations]]
== Deprecations

View File

@ -67,7 +67,6 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.query.ScriptType;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*;
@ -1237,14 +1236,23 @@ class RequestConverter {
Map<String, RuntimeField> runtimeMappings = new HashMap<>();
query.getRuntimeFields().forEach(runtimeField -> {
RuntimeField esRuntimeField = RuntimeField.of(rt -> {
RuntimeField.Builder builder = rt
RuntimeField.Builder rfb = rt
.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType()));
String script = runtimeField.getScript();
if (script != null) {
builder = builder.script(s -> s.inline(is -> is.source(script)));
rfb
.script(s -> s
.inline(is -> {
is.source(script);
if (runtimeField.getParams() != null) {
is.params(TypeUtils.paramsMap(runtimeField.getParams()));
}
return is;
}));
}
return builder;
return rfb;
});
runtimeMappings.put(runtimeField.getName(), esRuntimeField);
});
@ -1393,14 +1401,23 @@ class RequestConverter {
Map<String, RuntimeField> runtimeMappings = new HashMap<>();
query.getRuntimeFields()
.forEach(runtimeField -> runtimeMappings.put(runtimeField.getName(), RuntimeField.of(runtimeFieldBuilder -> {
runtimeFieldBuilder.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType()));
.forEach(runtimeField -> runtimeMappings.put(runtimeField.getName(), RuntimeField.of(rfb -> {
rfb.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType()));
String script = runtimeField.getScript();
if (script != null) {
runtimeFieldBuilder.script(s -> s.inline(is -> is.source(script)));
rfb
.script(s -> s
.inline(is -> {
is.source(script);
if (runtimeField.getParams() != null) {
is.params(TypeUtils.paramsMap(runtimeField.getParams()));
}
return is;
}));
}
return runtimeFieldBuilder;
return rfb;
})));
builder.runtimeMappings(runtimeMappings);
}

View File

@ -18,12 +18,20 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
import co.elastic.clients.elasticsearch.core.search.HighlighterOrder;
import co.elastic.clients.elasticsearch.core.search.HighlighterTagsSchema;
import co.elastic.clients.elasticsearch.core.search.HighlighterType;
import co.elastic.clients.elasticsearch.core.search.ScoreMode;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.json.JsonData;
import java.io.StringReader;
import java.time.Duration;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -31,10 +39,16 @@ import java.util.stream.Collectors;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
import org.springframework.data.elasticsearch.core.query.Order;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Utility to handle new Elasticsearch client type values.
@ -438,4 +452,18 @@ final class TypeUtils {
return settings != null ? IndexSettings.of(b -> b.withJson(new StringReader(Document.from(settings).toJson())))
: null;
}
/**
* @since 5.2
*/
static Map<String, JsonData> paramsMap(Map<String, Object> params) {
Assert.notNull(params, "params must not be null");
Map<String, JsonData> mappedParams = new LinkedHashMap<>();
params.forEach((key, value) -> {
mappedParams.put(key, JsonData.of(value));
});
return mappedParams;
}
}

View File

@ -31,14 +31,26 @@ import org.springframework.util.Assert;
public class RuntimeField {
private final String name;
/**
* the type of the runtime field (long, keyword, etc.)
*/
private final String type;
@Nullable private final String script;
/**
* @since 5.2
*/
@Nullable Map<String, Object> params;
public RuntimeField(String name, String type) {
this(name, type, null);
this(name, type, null, null);
}
public RuntimeField(String name, String type, @Nullable String script) {
public RuntimeField(String name, String type, String script) {
this(name, type, script, null);
}
public RuntimeField(String name, String type, @Nullable String script, @Nullable Map<String, Object> params) {
Assert.notNull(name, "name must not be null");
Assert.notNull(type, "type must not be null");
@ -46,6 +58,7 @@ public class RuntimeField {
this.name = name;
this.type = type;
this.script = script;
this.params = params;
}
public String getName() {
@ -78,4 +91,12 @@ public class RuntimeField {
public @Nullable String getScript() {
return script;
}
/**
* @since 5.2
*/
@Nullable
public Map<String, Object> getParams() {
return params;
}
}

View File

@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core.query.scriptedandruntimefiel
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
@ -251,6 +252,29 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
operations.save(entity);
}
@Test // #2303
@DisplayName("should use parameters for runtime fields in search queries")
void shouldUseParametersForRuntimeFieldsInSearchQueries() {
insert("1", "item 1", 80.0);
insert("2", "item 2", 90.0);
RuntimeField runtimeField = new RuntimeField(
"priceWithTax",
"double",
"emit(doc['price'].value * params.tax)",
Map.of("tax", 1.19)
);
var query = CriteriaQuery.builder(
Criteria.where("priceWithTax").greaterThan(100.0))
.withRuntimeFields(List.of(runtimeField))
.build();
var searchHits = operations.search(query, SomethingToBuy.class);
assertThat(searchHits).hasSize(1);
}
@SuppressWarnings("unused")
@Document(indexName = "#{@indexNameProvider.indexName()}-something-to-by")
private static class SomethingToBuy {
@ -386,13 +410,13 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
@org.springframework.data.elasticsearch.annotations.Query("""
{
"term": {
"value": {
"value": "?0"
}
}
}
""")
"term": {
"value": {
"value": "?0"
}
}
}
""")
SearchHits<SAREntity> findWithScriptedFields(Integer value,
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField1,
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField2);
@ -401,13 +425,13 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
@org.springframework.data.elasticsearch.annotations.Query("""
{
"term": {
"value": {
"value": "?0"
}
}
}
""")
"term": {
"value": {
"value": "?0"
}
}
}
""")
SearchHits<SAREntity> findWithRuntimeFields(Integer value, RuntimeField runtimeField1, RuntimeField runtimeField2);
}
}