Skip to content

Commit 68bdc93

Browse files
authored
[DATAES-433] Support join datatype.
Original PR: spring-projects#485
1 parent 0cfb1b5 commit 68bdc93

File tree

14 files changed

+463
-16
lines changed

14 files changed

+463
-16
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.annotations;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* @author Subhobrata Dey
27+
* @since 4.1
28+
*/
29+
@Documented
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target(ElementType.ANNOTATION_TYPE)
32+
public @interface JoinTypeRelation {
33+
34+
String parent();
35+
36+
String[] children();
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.annotations;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* @author Subhobrata Dey
27+
* @since 4.1
28+
*/
29+
@Documented
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target(ElementType.FIELD)
32+
@Inherited
33+
public @interface JoinTypeRelations {
34+
35+
JoinTypeRelation[] relations();
36+
}

src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import org.springframework.context.ApplicationContextAware;
4040
import org.springframework.data.convert.EntityReader;
4141
import org.springframework.data.elasticsearch.BulkFailureException;
42+
import org.springframework.data.elasticsearch.core.join.JoinField;
43+
import org.springframework.data.elasticsearch.annotations.JoinTypeRelations;
4244
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
4345
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
4446
import org.springframework.data.elasticsearch.core.document.Document;
@@ -532,6 +534,22 @@ private String getEntityId(Object entity) {
532534
return null;
533535
}
534536

537+
@Nullable
538+
private String getEntityRouting(Object entity) {
539+
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
540+
ElasticsearchPersistentProperty joinProperty = persistentEntity.getJoinFieldProperty();
541+
542+
if (joinProperty != null) {
543+
Object joinField = persistentEntity.getPropertyAccessor(entity).getProperty(joinProperty);
544+
if (joinField != null && JoinField.class.isAssignableFrom(joinField.getClass())
545+
&& ((JoinField<?>) joinField).getParent() != null) {
546+
return elasticsearchConverter.convertId(((JoinField<?>) joinField).getParent());
547+
}
548+
}
549+
550+
return null;
551+
}
552+
535553
@Nullable
536554
private Long getEntityVersion(Object entity) {
537555
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
@@ -581,6 +599,11 @@ private <T> IndexQuery getIndexQuery(T entity) {
581599
// version cannot be used together with seq_no and primary_term
582600
builder.withVersion(getEntityVersion(entity));
583601
}
602+
603+
String routing = getEntityRouting(entity);
604+
if (routing != null) {
605+
builder.withRouting(routing);
606+
}
584607
return builder.build();
585608
}
586609

src/main/java/org/springframework/data/elasticsearch/core/RequestFactory.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,10 @@ public DeleteByQueryRequest deleteByQueryRequest(Query query, Class<?> clazz, In
743743
deleteByQueryRequest.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
744744
}
745745

746+
if (query.getRoute() != null) {
747+
deleteByQueryRequest.setRouting(query.getRoute());
748+
}
749+
746750
return deleteByQueryRequest;
747751
}
748752

@@ -798,6 +802,10 @@ public DeleteByQueryRequestBuilder deleteByQueryRequestBuilder(Client client, Qu
798802
source.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
799803
}
800804

805+
if (query.getRoute() != null) {
806+
source.setRouting(query.getRoute());
807+
}
808+
801809
return requestBuilder;
802810
}
803811
// endregion
@@ -888,6 +896,10 @@ public IndexRequest indexRequest(IndexQuery query, IndexCoordinates index) {
888896
indexRequest.setIfPrimaryTerm(query.getPrimaryTerm());
889897
}
890898

899+
if (query.getRouting() != null) {
900+
indexRequest.routing(query.getRouting());
901+
}
902+
891903
return indexRequest;
892904
}
893905

@@ -926,6 +938,9 @@ public IndexRequestBuilder indexRequestBuilder(Client client, IndexQuery query,
926938
if (query.getPrimaryTerm() != null) {
927939
indexRequestBuilder.setIfPrimaryTerm(query.getPrimaryTerm());
928940
}
941+
if (query.getRouting() != null) {
942+
indexRequestBuilder.setRouting(query.getRouting());
943+
}
929944

930945
return indexRequestBuilder;
931946
}

src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.data.elasticsearch.annotations.ScriptedField;
4545
import org.springframework.data.elasticsearch.core.document.Document;
4646
import org.springframework.data.elasticsearch.core.document.SearchDocument;
47+
import org.springframework.data.elasticsearch.core.join.JoinField;
4748
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
4849
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
4950
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter;
@@ -710,6 +711,13 @@ private boolean requiresTypeHint(TypeInformation<?> type, Class<?> actualType,
710711
if (container.equals(type) && type.getType().equals(actualType)) {
711712
return false;
712713
}
714+
715+
if (container.getRawTypeInformation().equals(type)) {
716+
Class<?> containerClass = container.getRawTypeInformation().getType();
717+
if (containerClass.equals(JoinField.class) && type.getType().equals(actualType)) {
718+
return false;
719+
}
720+
}
713721
}
714722

715723
return !conversions.isSimpleType(type.getType()) && !type.isCollectionLike()

src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@
4040
import org.springframework.data.elasticsearch.annotations.FieldType;
4141
import org.springframework.data.elasticsearch.annotations.GeoPointField;
4242
import org.springframework.data.elasticsearch.annotations.InnerField;
43+
import org.springframework.data.elasticsearch.annotations.JoinTypeRelation;
44+
import org.springframework.data.elasticsearch.annotations.JoinTypeRelations;
4345
import org.springframework.data.elasticsearch.annotations.Mapping;
4446
import org.springframework.data.elasticsearch.annotations.MultiField;
4547
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
4648
import org.springframework.data.elasticsearch.core.ResourceUtil;
4749
import org.springframework.data.elasticsearch.core.completion.Completion;
4850
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
4951
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
52+
import org.springframework.data.elasticsearch.core.join.JoinField;
5053
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
5154
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
5255
import org.springframework.data.mapping.MappingException;
@@ -73,6 +76,7 @@
7376
* @author Petr Kukral
7477
* @author Peter-Josef Meisch
7578
* @author Xiao Yu
79+
* @author Subhobrata Dey
7680
*/
7781
public class MappingBuilder {
7882

@@ -93,8 +97,11 @@ public class MappingBuilder {
9397
private static final String TYPE_DYNAMIC = "dynamic";
9498
private static final String TYPE_VALUE_KEYWORD = "keyword";
9599
private static final String TYPE_VALUE_GEO_POINT = "geo_point";
100+
private static final String TYPE_VALUE_JOIN = "join";
96101
private static final String TYPE_VALUE_COMPLETION = "completion";
97102

103+
private static final String JOIN_TYPE_RELATIONS = "relations";
104+
98105
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
99106

100107
private final ElasticsearchConverter elasticsearchConverter;
@@ -212,6 +219,10 @@ private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject,
212219
return;
213220
}
214221

222+
if (isJoinFieldProperty(property)) {
223+
addJoinFieldMapping(builder, property);
224+
}
225+
215226
Field fieldAnnotation = property.findAnnotation(Field.class);
216227
boolean isCompletionProperty = isCompletionProperty(property);
217228
boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property);
@@ -336,6 +347,36 @@ private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersist
336347
builder.endObject();
337348
}
338349

350+
private void addJoinFieldMapping(XContentBuilder builder,
351+
ElasticsearchPersistentProperty property) throws IOException {
352+
JoinTypeRelation[] joinTypeRelations = property.getRequiredAnnotation(JoinTypeRelations.class).relations();
353+
354+
if (joinTypeRelations.length == 0) {
355+
logger.warn("Property {}s type is JoinField but its annotation JoinTypeRelation is " + //
356+
"not properly maintained", //
357+
property.getFieldName());
358+
return;
359+
}
360+
builder.startObject(property.getFieldName());
361+
362+
builder.field(FIELD_PARAM_TYPE, TYPE_VALUE_JOIN);
363+
364+
builder.startObject(JOIN_TYPE_RELATIONS);
365+
366+
for (JoinTypeRelation joinTypeRelation: joinTypeRelations) {
367+
String parent = joinTypeRelation.parent();
368+
String[] children = joinTypeRelation.children();
369+
370+
if (children.length > 1) {
371+
builder.array(parent, children);
372+
} else if (children.length == 1) {
373+
builder.field(parent, children[0]);
374+
}
375+
}
376+
builder.endObject();
377+
builder.endObject();
378+
}
379+
339380
/**
340381
* Add mapping for @MultiField annotation
341382
*
@@ -423,6 +464,10 @@ private boolean isGeoPointProperty(ElasticsearchPersistentProperty property) {
423464
return property.getActualType() == GeoPoint.class || property.isAnnotationPresent(GeoPointField.class);
424465
}
425466

467+
private boolean isJoinFieldProperty(ElasticsearchPersistentProperty property) {
468+
return property.getActualType() == JoinField.class;
469+
}
470+
426471
private boolean isCompletionProperty(ElasticsearchPersistentProperty property) {
427472
return property.getActualType() == Completion.class;
428473
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.join;
17+
18+
import org.springframework.lang.Nullable;
19+
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
/**
25+
* @author Subhobrata Dey
26+
* @since 4.1
27+
*/
28+
public class JoinField<ID> {
29+
30+
private final String name;
31+
32+
@Nullable private ID parent;
33+
34+
public JoinField() {
35+
this("default", null);
36+
}
37+
38+
public JoinField(String name) {
39+
this(name, null);
40+
}
41+
42+
public JoinField(String name, @Nullable ID parent) {
43+
this.name = name;
44+
this.parent = parent;
45+
}
46+
47+
public void setParent(@Nullable ID parent) {
48+
this.parent = parent;
49+
}
50+
51+
@Nullable
52+
public ID getParent() {
53+
return parent;
54+
}
55+
56+
public String getName() {
57+
return name;
58+
}
59+
60+
public Map<String, Object> getAsMap() {
61+
Map<String, Object> joinMap = new HashMap<>();
62+
joinMap.put("name", getName());
63+
joinMap.put("parent", getParent());
64+
65+
return Collections.unmodifiableMap(joinMap);
66+
}
67+
}

0 commit comments

Comments
 (0)