Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e110fe12a2 | |||
| a707b0bc47 | |||
| ec563e02f4 | |||
| f32cbb81f8 | |||
| 7b782c7c62 | |||
| 6d0560751d |
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.3.10</version>
|
||||
<version>5.3.11</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.3.10</version>
|
||||
<version>3.3.11</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,7 +18,7 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<springdata.commons>3.3.10</springdata.commons>
|
||||
<springdata.commons>3.3.11</springdata.commons>
|
||||
|
||||
<!-- version of the ElasticsearchClient -->
|
||||
<elasticsearch-java>8.13.4</elasticsearch-java>
|
||||
|
||||
+74
-72
@@ -1358,15 +1358,85 @@ public class MappingElasticsearchConverter
|
||||
return;
|
||||
}
|
||||
|
||||
String[] fieldNames = field.getName().split("\\.");
|
||||
var propertyNamesUpdate = updatePropertyNames(persistentEntity, field.getName());
|
||||
|
||||
var fieldNames = propertyNamesUpdate.names();
|
||||
field.setName(String.join(".", fieldNames));
|
||||
|
||||
if (propertyNamesUpdate.propertyCount() > 1 && propertyNamesUpdate.nestedProperty()) {
|
||||
List<String> propertyNames = Arrays.asList(fieldNames);
|
||||
field.setPath(String.join(".", propertyNames.subList(0, propertyNamesUpdate.propertyCount - 1)));
|
||||
}
|
||||
|
||||
if (propertyNamesUpdate.persistentProperty != null) {
|
||||
|
||||
if (propertyNamesUpdate.persistentProperty.hasPropertyValueConverter()) {
|
||||
PropertyValueConverter propertyValueConverter = Objects
|
||||
.requireNonNull(propertyNamesUpdate.persistentProperty.getPropertyValueConverter());
|
||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||
|
||||
if (criteriaEntry.getKey().hasValue()) {
|
||||
Object value = criteriaEntry.getValue();
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = propertyNamesUpdate.persistentProperty
|
||||
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
|
||||
|
||||
if (fieldAnnotation != null) {
|
||||
field.setFieldType(fieldAnnotation.type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static record PropertyNamesUpdate(
|
||||
String[] names,
|
||||
Boolean nestedProperty,
|
||||
Integer propertyCount,
|
||||
ElasticsearchPersistentProperty persistentProperty) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
Assert.notNull(persistentEntity, "persistentEntity must not be null");
|
||||
|
||||
var propertyNamesUpdate = updatePropertyNames(persistentEntity, propertyPath);
|
||||
return String.join(".", propertyNamesUpdate.names());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a propertyPath and replace the path values with the field names from a persistentEntity. path entries not
|
||||
* found in the entity are kept as they are.
|
||||
*
|
||||
* @return the eventually modified names, a flag if a nested entity was encountered the number of processed
|
||||
* propertiesand the last processed PersistentProperty.
|
||||
*/
|
||||
PropertyNamesUpdate updatePropertyNames(ElasticsearchPersistentEntity<?> persistentEntity, String propertyPath) {
|
||||
|
||||
String[] propertyNames = propertyPath.split("\\.");
|
||||
String[] fieldNames = Arrays.copyOf(propertyNames, propertyNames.length);
|
||||
|
||||
ElasticsearchPersistentEntity<?> currentEntity = persistentEntity;
|
||||
ElasticsearchPersistentProperty persistentProperty = null;
|
||||
|
||||
int propertyCount = 0;
|
||||
boolean isNested = false;
|
||||
|
||||
for (int i = 0; i < fieldNames.length; i++) {
|
||||
persistentProperty = currentEntity.getPersistentProperty(fieldNames[i]);
|
||||
for (int i = 0; i < propertyNames.length; i++) {
|
||||
persistentProperty = currentEntity.getPersistentProperty(propertyNames[i]);
|
||||
|
||||
if (persistentProperty != null) {
|
||||
propertyCount++;
|
||||
@@ -1393,75 +1463,7 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
field.setName(String.join(".", fieldNames));
|
||||
|
||||
if (propertyCount > 1 && isNested) {
|
||||
List<String> propertyNames = Arrays.asList(fieldNames);
|
||||
field.setPath(String.join(".", propertyNames.subList(0, propertyCount - 1)));
|
||||
}
|
||||
|
||||
if (persistentProperty != null) {
|
||||
|
||||
if (persistentProperty.hasPropertyValueConverter()) {
|
||||
PropertyValueConverter propertyValueConverter = Objects
|
||||
.requireNonNull(persistentProperty.getPropertyValueConverter());
|
||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||
|
||||
if (criteriaEntry.getKey().hasValue()) {
|
||||
Object value = criteriaEntry.getValue();
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
|
||||
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
|
||||
|
||||
if (fieldAnnotation != null) {
|
||||
field.setFieldType(fieldAnnotation.type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
Assert.notNull(persistentEntity, "persistentEntity must not be null");
|
||||
|
||||
var properties = propertyPath.split("\\.", 2);
|
||||
|
||||
if (properties.length > 0) {
|
||||
var propertyName = properties[0];
|
||||
var fieldName = propertyToFieldName(persistentEntity, propertyName);
|
||||
|
||||
if (properties.length > 1) {
|
||||
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||
|
||||
if (persistentProperty != null) {
|
||||
ElasticsearchPersistentEntity<?> nestedPersistentEntity = mappingContext
|
||||
.getPersistentEntity(persistentProperty);
|
||||
if (nestedPersistentEntity != null) {
|
||||
return fieldName + '.' + updateFieldNames(properties[1], nestedPersistentEntity);
|
||||
} else {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldName;
|
||||
} else {
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
return new PropertyNamesUpdate(fieldNames, isNested, propertyCount, persistentProperty);
|
||||
}
|
||||
// endregion
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
||||
@@ -859,6 +860,7 @@ public class Criteria {
|
||||
|
||||
// endregion
|
||||
|
||||
// region equals/hashcode
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
@@ -874,6 +876,8 @@ public class Criteria {
|
||||
return false;
|
||||
if (!Objects.equals(field, criteria.field))
|
||||
return false;
|
||||
if (!criteriaChain.filter(this).equals(criteria.criteriaChain.filter(criteria)))
|
||||
return false;
|
||||
if (!queryCriteriaEntries.equals(criteria.queryCriteriaEntries))
|
||||
return false;
|
||||
if (!filterCriteriaEntries.equals(criteria.filterCriteriaEntries))
|
||||
@@ -886,11 +890,16 @@ public class Criteria {
|
||||
int result = field != null ? field.hashCode() : 0;
|
||||
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
|
||||
result = 31 * result + (negating ? 1 : 0);
|
||||
// the criteriaChain contains "this" object, so we need to filter it out
|
||||
// to avoid a stackoverflow here, because the hashcode implementation
|
||||
// uses the element's hashcodes
|
||||
result = 31 * result + criteriaChain.filter(this).hashCode();
|
||||
result = 31 * result + queryCriteriaEntries.hashCode();
|
||||
result = 31 * result + filterCriteriaEntries.hashCode();
|
||||
result = 31 * result + subCriteria.hashCode();
|
||||
return result;
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -938,7 +947,17 @@ public class Criteria {
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public static class CriteriaChain extends LinkedList<Criteria> {}
|
||||
public static class CriteriaChain extends LinkedList<Criteria> {
|
||||
/**
|
||||
* return a copy of this list with the given element filtered out.
|
||||
*
|
||||
* @param criteria the element to filter
|
||||
* @return the filtered list
|
||||
*/
|
||||
List<Criteria> filter(Criteria criteria) {
|
||||
return this.stream().filter(c -> c != criteria).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator to join the entries of the criteria chain
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 5.3.10 (2024.0.10)
|
||||
Spring Data Elasticsearch 5.3.11 (2024.0.11)
|
||||
Copyright (c) [2013-2022] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -30,5 +30,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+104
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.JsonUtils.*;
|
||||
|
||||
@@ -22,6 +23,7 @@ import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -445,6 +447,108 @@ public class CriteriaQueryMappingUnitTests {
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
// the following test failed because of a wrong implementation in Criteria
|
||||
// equals and hscode methods.
|
||||
@Test // #3083
|
||||
@DisplayName("should map correct subcriteria")
|
||||
void shouldMapCorrectSubcriteria() throws JSONException {
|
||||
Criteria criteria = new Criteria("first").is("hello");
|
||||
|
||||
List<Criteria> criterias = new ArrayList<>();
|
||||
criterias.add(new Criteria().or("second").exists());
|
||||
|
||||
List<Criteria> subCriterias = new ArrayList<>();
|
||||
subCriterias.add(new Criteria("third").exists()
|
||||
.and(new Criteria("fourth").is("ciao")));
|
||||
subCriterias.add(new Criteria("third").exists()
|
||||
.and(new Criteria("fourth").is("hi")));
|
||||
|
||||
Criteria result = Criteria.or();
|
||||
|
||||
for (Criteria c : criterias) {
|
||||
result = result.or(c);
|
||||
}
|
||||
|
||||
for (Criteria c : subCriterias) {
|
||||
result = result.subCriteria(c);
|
||||
}
|
||||
criteria = criteria.subCriteria(result);
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(criteria);
|
||||
|
||||
String expected = """
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"first"
|
||||
],
|
||||
"query": "hello"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"should": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "third"
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"fourth"
|
||||
],
|
||||
"query": "ciao"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "third"
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"fourth"
|
||||
],
|
||||
"query": "hi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
|
||||
var queryString = queryToJson(CriteriaQueryProcessor.createQuery(criteriaQuery.getCriteria()), mapper);
|
||||
|
||||
assertEquals(expected, queryString, false);
|
||||
}
|
||||
// endregion
|
||||
// region helper functions
|
||||
|
||||
|
||||
+42
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -27,6 +28,7 @@ import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
@@ -679,6 +681,45 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // #3081
|
||||
@DisplayName("should build sort object with unknown field names")
|
||||
void shouldBuildSortObjectWithUnknownFieldNames() throws NoSuchMethodException, JSONException {
|
||||
|
||||
String methodName = "findByName";
|
||||
Class<?>[] parameterClasses = new Class[] { String.class, Sort.class };
|
||||
Object[] parameters = new Object[] { BOOK_TITLE, Sort.by("sortAuthor.sortName.raw") };
|
||||
|
||||
String query = getQueryString(methodName, parameterClasses, parameters);
|
||||
|
||||
String expected = """
|
||||
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"query_string": {
|
||||
"query": "Title",
|
||||
"fields": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"sort_author.sort_name.raw": {
|
||||
"order": "asc"
|
||||
}
|
||||
}
|
||||
]
|
||||
}""";
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
private String getQueryString(String methodName, Class<?>[] parameterClasses, Object[] parameters)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
@@ -768,6 +809,7 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
|
||||
List<Book> findByNameOrderBySortAuthor_SortName(String name);
|
||||
|
||||
List<Book> findByName(String name, Sort sort);
|
||||
}
|
||||
|
||||
public static class Book {
|
||||
|
||||
Reference in New Issue
Block a user