mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-01 09:42:11 +00:00
parent
910ca7b665
commit
fe8c4f12ed
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for a @Query annotation with the count parameter set to true.
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||||
|
@Documented
|
||||||
|
@Query(count = true)
|
||||||
|
public @interface CountQuery {
|
||||||
|
|
||||||
|
@AliasFor(annotation = Query.class)
|
||||||
|
String value() default "";
|
||||||
|
}
|
@ -22,24 +22,32 @@ import java.lang.annotation.*;
|
|||||||
*
|
*
|
||||||
* @author Rizwan Idrees
|
* @author Rizwan Idrees
|
||||||
* @author Mohsin Husen
|
* @author Mohsin Husen
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Query {
|
public @interface Query {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
|
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Named Query Named looked up by repository.
|
* Named Query Named looked up by repository.
|
||||||
*
|
*
|
||||||
* @return
|
* @deprecated since 4.2, not implemented and used anywhere
|
||||||
*/
|
*/
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the query defined should be executed as count projection.
|
||||||
|
*
|
||||||
|
* @return {@literal false} by default.
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
boolean count() default false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
|||||||
*
|
*
|
||||||
* @author Rizwan Idrees
|
* @author Rizwan Idrees
|
||||||
* @author Mohsin Husen
|
* @author Mohsin Husen
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class AbstractElasticsearchRepositoryQuery implements RepositoryQuery {
|
public abstract class AbstractElasticsearchRepositoryQuery implements RepositoryQuery {
|
||||||
@ -42,4 +43,10 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
|||||||
public QueryMethod getQueryMethod() {
|
public QueryMethod getQueryMethod() {
|
||||||
return queryMethod;
|
return queryMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@literal true} if this is a count query
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public abstract boolean isCountQuery();
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import org.springframework.data.repository.query.ResultProcessor;
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractReactiveElasticsearchRepositoryQuery implements RepositoryQuery {
|
abstract class AbstractReactiveElasticsearchRepositoryQuery implements RepositoryQuery {
|
||||||
|
|
||||||
private final ReactiveElasticsearchQueryMethod queryMethod;
|
protected final ReactiveElasticsearchQueryMethod queryMethod;
|
||||||
private final ReactiveElasticsearchOperations elasticsearchOperations;
|
private final ReactiveElasticsearchOperations elasticsearchOperations;
|
||||||
|
|
||||||
AbstractReactiveElasticsearchRepositoryQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
AbstractReactiveElasticsearchRepositoryQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||||
|
@ -59,6 +59,11 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
|||||||
this.mappingContext = elasticsearchConverter.getMappingContext();
|
this.mappingContext = elasticsearchConverter.getMappingContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCountQuery() {
|
||||||
|
return tree.isCountProjection();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object execute(Object[] parameters) {
|
public Object execute(Object[] parameters) {
|
||||||
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
|
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
|
||||||
|
@ -20,7 +20,8 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||||
import org.springframework.data.elasticsearch.annotations.Query;
|
import org.springframework.data.elasticsearch.annotations.Query;
|
||||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||||
@ -34,7 +35,9 @@ import org.springframework.data.mapping.context.MappingContext;
|
|||||||
import org.springframework.data.projection.ProjectionFactory;
|
import org.springframework.data.projection.ProjectionFactory;
|
||||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||||
import org.springframework.data.repository.query.QueryMethod;
|
import org.springframework.data.repository.query.QueryMethod;
|
||||||
|
import org.springframework.data.util.ClassTypeInformation;
|
||||||
import org.springframework.data.util.Lazy;
|
import org.springframework.data.util.Lazy;
|
||||||
|
import org.springframework.data.util.TypeInformation;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
@ -53,9 +56,9 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
|
|
||||||
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
|
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
|
||||||
private @Nullable ElasticsearchEntityMetadata<?> metadata;
|
private @Nullable ElasticsearchEntityMetadata<?> metadata;
|
||||||
private final Method method; // private in base class, but needed here as well
|
protected final Method method; // private in base class, but needed here and in derived classes as well
|
||||||
private final Query queryAnnotation;
|
@Nullable private final Query queryAnnotation;
|
||||||
private final Highlight highlightAnnotation;
|
@Nullable private final Highlight highlightAnnotation;
|
||||||
private final Lazy<HighlightQuery> highlightQueryLazy = Lazy.of(this::createAnnotatedHighlightQuery);
|
private final Lazy<HighlightQuery> highlightQueryLazy = Lazy.of(this::createAnnotatedHighlightQuery);
|
||||||
|
|
||||||
public ElasticsearchQueryMethod(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory factory,
|
public ElasticsearchQueryMethod(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory factory,
|
||||||
@ -67,16 +70,32 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
|
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.mappingContext = mappingContext;
|
this.mappingContext = mappingContext;
|
||||||
this.queryAnnotation = method.getAnnotation(Query.class);
|
this.queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||||
this.highlightAnnotation = method.getAnnotation(Highlight.class);
|
this.highlightAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Highlight.class);
|
||||||
|
|
||||||
|
verifyCountQueryTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void verifyCountQueryTypes() {
|
||||||
|
|
||||||
|
if (hasCountQueryAnnotation()) {
|
||||||
|
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||||
|
|
||||||
|
if (returnType.getType() != long.class && !Long.class.isAssignableFrom(returnType.getType())) {
|
||||||
|
throw new InvalidDataAccessApiUsageException("count query methods must return a Long");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasAnnotatedQuery() {
|
public boolean hasAnnotatedQuery() {
|
||||||
return this.queryAnnotation != null;
|
return this.queryAnnotation != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the query String. Must not be {@literal null} when {@link #hasAnnotatedQuery()} returns true
|
||||||
|
*/
|
||||||
public String getAnnotatedQuery() {
|
public String getAnnotatedQuery() {
|
||||||
return (String) AnnotationUtils.getValue(queryAnnotation, "value");
|
return queryAnnotation.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,4 +236,14 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
|||||||
public boolean isNotSearchPageMethod() {
|
public boolean isNotSearchPageMethod() {
|
||||||
return !isSearchPageMethod();
|
return !isSearchPageMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@literal true} if the method is annotated with
|
||||||
|
* {@link org.springframework.data.elasticsearch.annotations.CountQuery} or with {@link Query}(count =true)
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public boolean hasCountQueryAnnotation() {
|
||||||
|
return queryAnnotation != null && queryAnnotation.count();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,14 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue
|
|||||||
this.query = query;
|
this.query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCountQuery() {
|
||||||
|
return queryMethod.hasCountQueryAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object execute(Object[] parameters) {
|
public Object execute(Object[] parameters) {
|
||||||
|
|
||||||
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
|
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
|
||||||
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
|
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
|
||||||
|
|
||||||
@ -86,7 +92,9 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue
|
|||||||
|
|
||||||
Object result = null;
|
Object result = null;
|
||||||
|
|
||||||
if (queryMethod.isPageQuery()) {
|
if (isCountQuery()) {
|
||||||
|
result = elasticsearchOperations.count(stringQuery, clazz, index);
|
||||||
|
} else if (queryMethod.isPageQuery()) {
|
||||||
stringQuery.setPageable(accessor.getPageable());
|
stringQuery.setPageable(accessor.getPageable());
|
||||||
SearchHits<?> searchHits = elasticsearchOperations.search(stringQuery, clazz, index);
|
SearchHits<?> searchHits = elasticsearchOperations.search(stringQuery, clazz, index);
|
||||||
result = SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable());
|
result = SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable());
|
||||||
|
@ -18,9 +18,11 @@ package org.springframework.data.elasticsearch.repository.query;
|
|||||||
import static org.springframework.data.repository.util.ClassUtils.*;
|
import static org.springframework.data.repository.util.ClassUtils.*;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@ -59,7 +61,6 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
|
|||||||
if (hasParameterOfType(method, Pageable.class)) {
|
if (hasParameterOfType(method, Pageable.class)) {
|
||||||
|
|
||||||
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||||
|
|
||||||
boolean multiWrapper = ReactiveWrappers.isMultiValueType(returnType.getType());
|
boolean multiWrapper = ReactiveWrappers.isMultiValueType(returnType.getType());
|
||||||
boolean singleWrapperWithWrappedPageableResult = ReactiveWrappers.isSingleValueType(returnType.getType())
|
boolean singleWrapperWithWrappedPageableResult = ReactiveWrappers.isSingleValueType(returnType.getType())
|
||||||
&& (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())
|
&& (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())
|
||||||
@ -87,6 +88,20 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
|
|||||||
&& ReactiveWrappers.isMultiValueType(metadata.getReturnType(method).getType()) || super.isCollectionQuery()));
|
&& ReactiveWrappers.isMultiValueType(metadata.getReturnType(method).getType()) || super.isCollectionQuery()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void verifyCountQueryTypes() {
|
||||||
|
if (hasCountQueryAnnotation()) {
|
||||||
|
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||||
|
List<TypeInformation<?>> typeArguments = returnType.getTypeArguments();
|
||||||
|
|
||||||
|
if (!Mono.class.isAssignableFrom(returnType.getType()) || typeArguments.size() != 1
|
||||||
|
|| (typeArguments.get(0).getType() != long.class
|
||||||
|
&& !Long.class.isAssignableFrom(typeArguments.get(0).getType()))) {
|
||||||
|
throw new InvalidDataAccessApiUsageException("count query methods must return a Mono<Long>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ElasticsearchParameters createParameters(Method method) {
|
protected ElasticsearchParameters createParameters(Method method) {
|
||||||
return new ElasticsearchParameters(method);
|
return new ElasticsearchParameters(method);
|
||||||
|
@ -75,7 +75,7 @@ public class ReactiveElasticsearchStringQuery extends AbstractReactiveElasticsea
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isCountQuery() {
|
boolean isCountQuery() {
|
||||||
return false;
|
return queryMethod.hasCountQueryAnnotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* (c) Copyright 2021 sothawo
|
* Copyright 2021 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.mapping;
|
package org.springframework.data.elasticsearch.core.mapping;
|
||||||
|
|
||||||
@ -10,7 +22,7 @@ import org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy;
|
|||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author P.J. Meisch (pj.meisch@sothawo.com)
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
@ContextConfiguration(classes = { FieldNamingStrategyIntegrationTemplateTest.Config.class })
|
@ContextConfiguration(classes = { FieldNamingStrategyIntegrationTemplateTest.Config.class })
|
||||||
public class FieldNamingStrategyIntegrationTemplateTest extends FieldNamingStrategyIntegrationTest {
|
public class FieldNamingStrategyIntegrationTemplateTest extends FieldNamingStrategyIntegrationTest {
|
||||||
|
@ -35,6 +35,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
@ -44,6 +45,7 @@ import org.springframework.data.domain.PageRequest;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Order;
|
import org.springframework.data.domain.Sort.Order;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.CountQuery;
|
||||||
import org.springframework.data.elasticsearch.annotations.Document;
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||||
@ -911,6 +913,31 @@ public abstract class CustomMethodRepositoryBaseTests {
|
|||||||
assertThat(count).isEqualTo(1L);
|
assertThat(count).isEqualTo(1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should count with query by type")
|
||||||
|
void shouldCountWithQueryByType() {
|
||||||
|
|
||||||
|
String documentId = nextIdAsString();
|
||||||
|
SampleEntity sampleEntity = new SampleEntity();
|
||||||
|
sampleEntity.setId(documentId);
|
||||||
|
sampleEntity.setType("test");
|
||||||
|
sampleEntity.setMessage("some message");
|
||||||
|
|
||||||
|
repository.save(sampleEntity);
|
||||||
|
|
||||||
|
documentId = nextIdAsString();
|
||||||
|
SampleEntity sampleEntity2 = new SampleEntity();
|
||||||
|
sampleEntity2.setId(documentId);
|
||||||
|
sampleEntity2.setType("test2");
|
||||||
|
sampleEntity2.setMessage("some message");
|
||||||
|
|
||||||
|
repository.save(sampleEntity2);
|
||||||
|
|
||||||
|
long count = repository.countWithQueryByType("test");
|
||||||
|
|
||||||
|
assertThat(count).isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
@Test // DATAES-106
|
@Test // DATAES-106
|
||||||
public void shouldCountCustomMethodForNot() {
|
public void shouldCountCustomMethodForNot() {
|
||||||
|
|
||||||
@ -1746,6 +1773,9 @@ public abstract class CustomMethodRepositoryBaseTests {
|
|||||||
SearchHits<SampleEntity> searchBy(Sort sort);
|
SearchHits<SampleEntity> searchBy(Sort sort);
|
||||||
|
|
||||||
SearchPage<SampleEntity> searchByMessage(String message, Pageable pageable);
|
SearchPage<SampleEntity> searchByMessage(String message, Pageable pageable);
|
||||||
|
|
||||||
|
@CountQuery("{\"bool\" : {\"must\" : {\"term\" : {\"type\" : \"?0\"}}}}")
|
||||||
|
long countWithQueryByType(String type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.repository.query;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.CountQuery;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
|
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||||
|
import org.springframework.data.projection.ProjectionFactory;
|
||||||
|
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||||
|
import org.springframework.data.repository.Repository;
|
||||||
|
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
*/
|
||||||
|
public class ElasticsearchQueryMethodUnitTests {
|
||||||
|
|
||||||
|
private SimpleElasticsearchMappingContext mappingContext;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
mappingContext = new SimpleElasticsearchMappingContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should reject count query method not returning a Long")
|
||||||
|
void shouldRejectCountQueryMethodNotReturningLong() {
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> queryMethod(PersonRepository.class, "invalidCountQueryResult", String.class))
|
||||||
|
.isInstanceOf(InvalidDataAccessApiUsageException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should accept count query method returning a Long")
|
||||||
|
void shouldAcceptCountQueryMethodReturningALong() throws Exception {
|
||||||
|
queryMethod(PersonRepository.class, "validCountQueryResult", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ElasticsearchQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
Method method = repository.getMethod(name, parameters);
|
||||||
|
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||||
|
return new ElasticsearchQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, mappingContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PersonRepository extends Repository<ReactiveElasticsearchQueryMethodUnitTests.Person, String> {
|
||||||
|
@CountQuery("{}")
|
||||||
|
List<Person> invalidCountQueryResult(String name); // invalid return type here
|
||||||
|
|
||||||
|
@CountQuery("{}")
|
||||||
|
Long validCountQueryResult(String name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Document(indexName = "query-method-unit-tests")
|
||||||
|
private static class Person {
|
||||||
|
@Id private String id;
|
||||||
|
@Nullable private String name;
|
||||||
|
@Nullable private String firstName;
|
||||||
|
}
|
||||||
|
}
|
@ -32,12 +32,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.CountQuery;
|
||||||
import org.springframework.data.elasticsearch.annotations.Document;
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
@ -52,7 +54,6 @@ import org.springframework.lang.Nullable;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @currentRead Fool's Fate - Robin Hobb
|
|
||||||
*/
|
*/
|
||||||
public class ReactiveElasticsearchQueryMethodUnitTests {
|
public class ReactiveElasticsearchQueryMethodUnitTests {
|
||||||
|
|
||||||
@ -115,6 +116,20 @@ public class ReactiveElasticsearchQueryMethodUnitTests {
|
|||||||
assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(Person.class);
|
assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(Person.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should reject count query method not returning a Mono of Long")
|
||||||
|
void shouldRejectCountQueryMethodNotReturningAMonoOfLong() {
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> queryMethod(PersonRepository.class, "invalidCountQueryResult", String.class))
|
||||||
|
.isInstanceOf(InvalidDataAccessApiUsageException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should accept count query method returning a Mono of Long")
|
||||||
|
void shouldAcceptCountQueryMethodReturningAMonoOfLong() throws Exception {
|
||||||
|
queryMethod(PersonRepository.class, "validCountQueryResult", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
private ReactiveElasticsearchQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters)
|
private ReactiveElasticsearchQueryMethod queryMethod(Class<?> repository, String name, Class<?>... parameters)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
@ -137,6 +152,12 @@ public class ReactiveElasticsearchQueryMethodUnitTests {
|
|||||||
Flux<Person> findByName(String name, Pageable pageRequest);
|
Flux<Person> findByName(String name, Pageable pageRequest);
|
||||||
|
|
||||||
void deleteByName(String name);
|
void deleteByName(String name);
|
||||||
|
|
||||||
|
@CountQuery("{}")
|
||||||
|
Flux<Person> invalidCountQueryResult(String name); // invalid return type here
|
||||||
|
|
||||||
|
@CountQuery("{}")
|
||||||
|
Mono<Long> validCountQueryResult(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NonReactiveRepository extends Repository<Person, Long> {
|
interface NonReactiveRepository extends Repository<Person, Long> {
|
||||||
@ -156,6 +177,7 @@ public class ReactiveElasticsearchQueryMethodUnitTests {
|
|||||||
@Nullable @Id private String id;
|
@Nullable @Id private String id;
|
||||||
|
|
||||||
@Nullable private String name;
|
@Nullable private String name;
|
||||||
|
@Nullable private String firstName;
|
||||||
|
|
||||||
@Nullable @Field(type = FieldType.Nested) private List<Car> car;
|
@Nullable @Field(type = FieldType.Nested) private List<Car> car;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.repository.support.simple;
|
package org.springframework.data.elasticsearch.repository.support;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||||
@ -77,7 +77,7 @@ class SimpleElasticsearchRepositoryIntegrationTests {
|
|||||||
@Configuration
|
@Configuration
|
||||||
@Import({ ElasticsearchRestTemplateConfiguration.class })
|
@Import({ ElasticsearchRestTemplateConfiguration.class })
|
||||||
@EnableElasticsearchRepositories(
|
@EnableElasticsearchRepositories(
|
||||||
basePackages = { "org.springframework.data.elasticsearch.repository.support.simple" },
|
basePackages = { "org.springframework.data.elasticsearch.repository.support" },
|
||||||
considerNestedRepositories = true)
|
considerNestedRepositories = true)
|
||||||
static class Config {}
|
static class Config {}
|
||||||
|
|
@ -37,6 +37,7 @@ import java.util.stream.IntStream;
|
|||||||
import org.elasticsearch.ElasticsearchStatusException;
|
import org.elasticsearch.ElasticsearchStatusException;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -48,6 +49,7 @@ import org.springframework.data.domain.PageRequest;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Order;
|
import org.springframework.data.domain.Sort.Order;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.CountQuery;
|
||||||
import org.springframework.data.elasticsearch.annotations.Document;
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||||
@ -316,8 +318,7 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
|||||||
|
|
||||||
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
|
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
|
||||||
SampleEntity.builder().id("id-two").message("test message").build(), //
|
SampleEntity.builder().id("id-two").message("test message").build(), //
|
||||||
SampleEntity.builder().id("id-three").message("test test").build()) //
|
SampleEntity.builder().id("id-three").message("test test").build()).block();
|
||||||
.block();
|
|
||||||
|
|
||||||
repository.countAllByMessage("test") //
|
repository.countAllByMessage("test") //
|
||||||
.as(StepVerifier::create) //
|
.as(StepVerifier::create) //
|
||||||
@ -325,6 +326,20 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
|||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1156
|
||||||
|
@DisplayName("should count with string query")
|
||||||
|
void shouldCountWithStringQuery() {
|
||||||
|
|
||||||
|
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
|
||||||
|
SampleEntity.builder().id("id-two").message("test message").build(), //
|
||||||
|
SampleEntity.builder().id("id-three").message("test test").build()).block();
|
||||||
|
|
||||||
|
repository.retrieveCountByText("test") //
|
||||||
|
.as(StepVerifier::create) //
|
||||||
|
.expectNext(2L) //
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@Test // DATAES-519
|
@Test // DATAES-519
|
||||||
void existsShouldReturnTrueIfExists() {
|
void existsShouldReturnTrueIfExists() {
|
||||||
|
|
||||||
@ -593,6 +608,9 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
|||||||
Mono<Boolean> existsAllByMessage(String message);
|
Mono<Boolean> existsAllByMessage(String message);
|
||||||
|
|
||||||
Mono<Long> deleteAllByMessage(String message);
|
Mono<Long> deleteAllByMessage(String message);
|
||||||
|
|
||||||
|
@CountQuery(value = "{\"bool\": {\"must\": [{\"term\": {\"message\": \"?0\"}}]}}")
|
||||||
|
Mono<Long> retrieveCountByText(String message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user