Enable IndexCoordinates a parameter in repository search queries.
Closes #2506 Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com> Co-authored-by: Urs Keller <urs.keller@lightspeedhq.com>
This commit is contained in:
committed by
GitHub
parent
2278d075a7
commit
548841cc22
+2
-1
@@ -90,7 +90,8 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
||||
|
||||
Query query = createQuery(parameters);
|
||||
|
||||
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
|
||||
IndexCoordinates index = parameterAccessor
|
||||
.getIndexCoordinates(elasticsearchOperations.getIndexCoordinatesFor(clazz));
|
||||
|
||||
Object result = null;
|
||||
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor
|
||||
evaluationContextProvider);
|
||||
|
||||
String indexName = queryMethod.getEntityInformation().getIndexName();
|
||||
IndexCoordinates index = IndexCoordinates.of(indexName);
|
||||
IndexCoordinates index = parameterAccessor.getIndexCoordinates(IndexCoordinates.of(indexName));
|
||||
|
||||
ReactiveElasticsearchQueryExecution execution = getExecution(parameterAccessor,
|
||||
new ResultProcessingConverter(processor));
|
||||
|
||||
+7
-1
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.core.TypeInformation;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
@@ -42,7 +43,8 @@ public class ElasticsearchParameter extends Parameter {
|
||||
|
||||
@Override
|
||||
public boolean isSpecialParameter() {
|
||||
return super.isSpecialParameter() || isScriptedFieldParameter() || isRuntimeFieldParameter();
|
||||
return super.isSpecialParameter() || isScriptedFieldParameter() || isRuntimeFieldParameter()
|
||||
|| isIndexCoordinatesParameter();
|
||||
}
|
||||
|
||||
public Boolean isScriptedFieldParameter() {
|
||||
@@ -52,4 +54,8 @@ public class ElasticsearchParameter extends Parameter {
|
||||
public Boolean isRuntimeFieldParameter() {
|
||||
return RuntimeField.class.isAssignableFrom(getType());
|
||||
}
|
||||
|
||||
public Boolean isIndexCoordinatesParameter() {
|
||||
return IndexCoordinates.class.isAssignableFrom(getType());
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
/**
|
||||
@@ -29,4 +30,11 @@ public interface ElasticsearchParameterAccessor extends ParameterAccessor {
|
||||
* @return
|
||||
*/
|
||||
Object[] getValues();
|
||||
|
||||
/**
|
||||
* If there is a parameter of type IndexCoordinates, this parameter value is returned, otherwise the defaults value
|
||||
*
|
||||
* @param defaults default value
|
||||
*/
|
||||
IndexCoordinates getIndexCoordinates(IndexCoordinates defaults);
|
||||
}
|
||||
|
||||
+28
-1
@@ -29,10 +29,11 @@ import org.springframework.data.repository.query.ParametersSource;
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ElasticsearchParameters extends Parameters<ElasticsearchParameters, ElasticsearchParameter> {
|
||||
|
||||
private final List<ElasticsearchParameter> scriptedFields = new ArrayList<>();
|
||||
private final List<ElasticsearchParameter> runtimeFields = new ArrayList<>();
|
||||
|
||||
private final int indexCoordinatesIndex;
|
||||
|
||||
public ElasticsearchParameters(ParametersSource parametersSource) {
|
||||
|
||||
super(parametersSource,
|
||||
@@ -53,6 +54,23 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
|
||||
runtimeFields.add(parameter);
|
||||
}
|
||||
}
|
||||
this.indexCoordinatesIndex = initIndexCoordinatesIndex();
|
||||
}
|
||||
|
||||
private int initIndexCoordinatesIndex() {
|
||||
int indexCoordinatesIndex = -1;
|
||||
int index = 0;
|
||||
for (ElasticsearchParameter parameter : this) {
|
||||
if (parameter.isIndexCoordinatesParameter()) {
|
||||
if (indexCoordinatesIndex != -1) {
|
||||
throw new IllegalArgumentException(this + " can only contain at most one IndexCoordinates parameter.");
|
||||
} else {
|
||||
indexCoordinatesIndex = index;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return indexCoordinatesIndex;
|
||||
}
|
||||
|
||||
private ElasticsearchParameter parameterFactory(MethodParameter methodParameter, TypeInformation<?> domainType) {
|
||||
@@ -61,6 +79,7 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
|
||||
|
||||
private ElasticsearchParameters(List<ElasticsearchParameter> parameters) {
|
||||
super(parameters);
|
||||
this.indexCoordinatesIndex = initIndexCoordinatesIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,4 +94,12 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
|
||||
List<ElasticsearchParameter> getRuntimeFields() {
|
||||
return runtimeFields;
|
||||
}
|
||||
|
||||
public boolean hasIndexCoordinatesParameter() {
|
||||
return this.indexCoordinatesIndex != -1;
|
||||
}
|
||||
|
||||
public int getIndexCoordinatesIndex() {
|
||||
return indexCoordinatesIndex;
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
/**
|
||||
@@ -25,6 +26,7 @@ public class ElasticsearchParametersParameterAccessor extends ParametersParamete
|
||||
implements ElasticsearchParameterAccessor {
|
||||
|
||||
private final Object[] values;
|
||||
private final ElasticsearchParameters eleasticSearchParameters;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ElasticsearchParametersParameterAccessor}.
|
||||
@@ -36,10 +38,19 @@ public class ElasticsearchParametersParameterAccessor extends ParametersParamete
|
||||
|
||||
super(method.getParameters(), values);
|
||||
this.values = values;
|
||||
this.eleasticSearchParameters = method.getParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexCoordinates getIndexCoordinates(IndexCoordinates defaults) {
|
||||
if (!eleasticSearchParameters.hasIndexCoordinatesParameter()) {
|
||||
return defaults;
|
||||
}
|
||||
return (IndexCoordinates) getValues()[eleasticSearchParameters.getIndexCoordinatesIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -373,6 +373,11 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
return fieldNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchParameters getParameters() {
|
||||
return (ElasticsearchParameters) super.getParameters();
|
||||
}
|
||||
|
||||
// region Copied from QueryMethod base class
|
||||
/*
|
||||
* Copied from the QueryMethod class adding support for collections of SearchHit instances. No static method here.
|
||||
|
||||
-5
@@ -136,11 +136,6 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchParameters getParameters() {
|
||||
return (ElasticsearchParameters) super.getParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAllowedGenericType(ParameterizedType methodGenericReturnType) {
|
||||
return super.isAllowedGenericType(methodGenericReturnType)
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@ContextConfiguration(classes = { IndexCoordinatesParameterELCIntegrationTests.Config.class })
|
||||
public class IndexCoordinatesParameterELCIntegrationTests extends IndexCoordinatesParameterIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ElasticsearchTemplateConfiguration.class })
|
||||
@EnableElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("query-index-coordinates");
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
|
||||
@SpringIntegrationTest
|
||||
abstract class IndexCoordinatesParameterIntegrationTests {
|
||||
@Autowired ElasticsearchOperations operations;
|
||||
@Autowired IndexNameProvider indexNameProvider;
|
||||
@Autowired RecordRepository recordRepository;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
indexNameProvider.increment();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(Integer.MAX_VALUE)
|
||||
void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
|
||||
}
|
||||
|
||||
@Test // #2506
|
||||
@DisplayName("should use indexcoordinates passes as repository query argument")
|
||||
void shouldUseIndexCoordinatesPassesAsRepositoryQueryArgument() {
|
||||
|
||||
var record1 = new Record("1", "one");
|
||||
var indexName1 = indexNameProvider.indexName();
|
||||
var indexCoordinates1 = IndexCoordinates.of(indexName1);
|
||||
operations.save(record1, indexCoordinates1);
|
||||
|
||||
var record2 = new Record("2", "two");
|
||||
var indexName2 = indexName1 + "second";
|
||||
var indexCoordinates2 = IndexCoordinates.of(indexName2);
|
||||
operations.save(record2, indexCoordinates2);
|
||||
|
||||
// search for record1
|
||||
var searchHits = recordRepository.findByText("one");
|
||||
assert searchHits.getTotalHits() == 1;
|
||||
searchHits = recordRepository.findByText("one", indexCoordinates2);
|
||||
assert searchHits.getTotalHits() == 0;
|
||||
|
||||
// search for record2
|
||||
searchHits = recordRepository.findByText("two");
|
||||
assert searchHits.getTotalHits() == 0;
|
||||
searchHits = recordRepository.findByText("two", indexCoordinates2);
|
||||
assert searchHits.getTotalHits() == 1;
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class Record {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) private String text;
|
||||
|
||||
public Record(@Nullable String id, @Nullable String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public @Nullable String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public @Nullable String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(@Nullable String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
interface RecordRepository extends ElasticsearchRepository<Record, String> {
|
||||
SearchHits<Record> findByText(String text);
|
||||
|
||||
SearchHits<Record> findByText(String text, IndexCoordinates index);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@ContextConfiguration(classes = { ReactiveIndexCoordinatesParameterELCIntegrationTests.Config.class })
|
||||
public class ReactiveIndexCoordinatesParameterELCIntegrationTests
|
||||
extends ReactiveIndexCoordinatesParameterIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
|
||||
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("reactive-query-index-coordinates");
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
|
||||
@SpringIntegrationTest
|
||||
abstract class ReactiveIndexCoordinatesParameterIntegrationTests {
|
||||
@Autowired ReactiveElasticsearchOperations operations;
|
||||
@Autowired IndexNameProvider indexNameProvider;
|
||||
@Autowired RecordRepository recordRepository;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
indexNameProvider.increment();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(Integer.MAX_VALUE)
|
||||
void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
|
||||
}
|
||||
|
||||
@Test // #2506
|
||||
@DisplayName("should use indexcoordinates passes as repository query argument")
|
||||
void shouldUseIndexCoordinatesPassesAsRepositoryQueryArgument() {
|
||||
|
||||
var record1 = new Record("1", "one");
|
||||
var indexName1 = indexNameProvider.indexName();
|
||||
var indexCoordinates1 = IndexCoordinates.of(indexName1);
|
||||
operations.save(record1, indexCoordinates1).block();
|
||||
|
||||
var record2 = new Record("2", "two");
|
||||
var indexName2 = indexName1 + "second";
|
||||
var indexCoordinates2 = IndexCoordinates.of(indexName2);
|
||||
operations.save(record2, indexCoordinates2).block();
|
||||
|
||||
// search for record1
|
||||
recordRepository.findByText("one")
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
recordRepository.findByText("one", indexCoordinates2)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
|
||||
// search for record2
|
||||
recordRepository.findByText("two")
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
recordRepository.findByText("two", indexCoordinates2)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class Record {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) private String text;
|
||||
|
||||
public Record(@Nullable String id, @Nullable String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public @Nullable String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public @Nullable String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(@Nullable String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
interface RecordRepository extends ReactiveElasticsearchRepository<Record, String> {
|
||||
Flux<SearchHit<Record>> findByText(String text);
|
||||
|
||||
Flux<SearchHit<Record>> findByText(String text, IndexCoordinates index);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user