[DATAES-433] Support join datatype.

Original PR: #485
This commit is contained in:
Subhobrata Dey
2020-08-07 06:44:37 -07:00
committed by GitHub
parent 0cfb1b563e
commit 68bdc93a0b
14 changed files with 463 additions and 16 deletions

View File

@@ -15,11 +15,13 @@
*/
package org.springframework.data.elasticsearch;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.join.ParentJoinPlugin;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.transport.Netty4Plugin;
@@ -53,7 +55,7 @@ public class Utils {
.put("cluster.routing.allocation.disk.watermark.high", "1gb")//
.put("cluster.routing.allocation.disk.watermark.flood_stage", "1gb")//
.build(), //
Collections.singletonList(Netty4Plugin.class));
Arrays.asList(Netty4Plugin.class, ParentJoinPlugin.class));
}
public static Client getNodeClient() throws NodeValidationException {

View File

@@ -19,25 +19,28 @@ import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
import lombok.Builder;
import lombok.Data;
import lombok.val;
import lombok.*;
import java.lang.Object;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.SimpleQueryStringBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.JoinTypeRelation;
import org.springframework.data.elasticsearch.annotations.JoinTypeRelations;
import org.springframework.data.elasticsearch.core.join.JoinField;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
@@ -118,5 +121,4 @@ public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
assertThat(fetchSourceContext.includes()).containsExactlyInAnyOrder("incl");
assertThat(fetchSourceContext.excludes()).containsExactlyInAnyOrder("excl");
}
}

View File

@@ -41,6 +41,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.assertj.core.util.Lists;
@@ -49,6 +50,8 @@ import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.SimpleQueryStringBuilder;
import org.elasticsearch.join.query.ParentIdQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
@@ -68,20 +71,17 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.join.JoinField;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
@@ -117,9 +117,10 @@ public abstract class ElasticsearchTemplateTests {
private static final String INDEX_3_NAME = "test-index-3";
protected final IndexCoordinates index = IndexCoordinates.of(INDEX_NAME_SAMPLE_ENTITY);
protected static final String INDEX_NAME_JOIN_SAMPLE_ENTITY = "test-index-sample-join-template";
@Autowired protected ElasticsearchOperations operations;
private IndexOperations indexOperations;
protected IndexOperations indexOperations;
@BeforeEach
public void before() {
@@ -136,6 +137,10 @@ public abstract class ElasticsearchTemplateTests {
IndexOperations indexOpsSearchHitsEntity = operations.indexOps(SearchHitsEntity.class);
indexOpsSearchHitsEntity.create();
indexOpsSearchHitsEntity.putMapping(SearchHitsEntity.class);
IndexOperations indexOpsJoinEntity = operations.indexOps(ElasticsearchRestTemplateTests.SampleJoinEntity.class);
indexOpsJoinEntity.create();
indexOpsJoinEntity.putMapping(ElasticsearchRestTemplateTests.SampleJoinEntity.class);
}
@AfterEach
@@ -158,6 +163,7 @@ public abstract class ElasticsearchTemplateTests {
operations.indexOps(HighlightEntity.class).delete();
operations.indexOps(OptimisticEntity.class).delete();
operations.indexOps(OptimisticAndVersionedEntity.class).delete();
operations.indexOps(IndexCoordinates.of(INDEX_NAME_JOIN_SAMPLE_ENTITY)).delete();
}
@Test // DATAES-106
@@ -171,7 +177,6 @@ public abstract class ElasticsearchTemplateTests {
IndexQuery indexQuery = getIndexQuery(sampleEntity);
operations.index(indexQuery, index);
indexOperations.refresh();
;
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
// when
@@ -3302,6 +3307,135 @@ public abstract class ElasticsearchTemplateTests {
operations.save(forEdit);
}
@Test
void shouldSupportCRUDOpsForEntityWithJoinFields() throws Exception {
String qId1 = java.util.UUID.randomUUID().toString();
String qId2 = java.util.UUID.randomUUID().toString();
String aId1 = java.util.UUID.randomUUID().toString();
String aId2 = java.util.UUID.randomUUID().toString();
shouldSaveEntityWithJoinFields(qId1, qId2, aId1, aId2);
shouldUpdateEntityWithJoinFields(qId1, qId2, aId1, aId2);
shouldDeleteEntityWithJoinFields(qId2, aId2);
}
void shouldSaveEntityWithJoinFields(String qId1, String qId2, String aId1, String aId2) throws Exception {
SampleJoinEntity sampleQuestionEntity1 = new SampleJoinEntity();
sampleQuestionEntity1.setUuid(qId1);
sampleQuestionEntity1.setText("This is a question");
JoinField<String> myQJoinField1 = new JoinField<>("question");
sampleQuestionEntity1.setMyJoinField(myQJoinField1);
SampleJoinEntity sampleQuestionEntity2 = new SampleJoinEntity();
sampleQuestionEntity2.setUuid(qId2);
sampleQuestionEntity2.setText("This is another question");
JoinField<String> myQJoinField2 = new JoinField<>("question");
sampleQuestionEntity2.setMyJoinField(myQJoinField2);
SampleJoinEntity sampleAnswerEntity1 = new SampleJoinEntity();
sampleAnswerEntity1.setUuid(aId1);
sampleAnswerEntity1.setText("This is an answer");
JoinField<String> myAJoinField1 = new JoinField<>("answer");
myAJoinField1.setParent(qId1);
sampleAnswerEntity1.setMyJoinField(myAJoinField1);
SampleJoinEntity sampleAnswerEntity2 = new SampleJoinEntity();
sampleAnswerEntity2.setUuid(aId2);
sampleAnswerEntity2.setText("This is another answer");
JoinField<String> myAJoinField2 = new JoinField<>("answer");
myAJoinField2.setParent(qId1);
sampleAnswerEntity2.setMyJoinField(myAJoinField2);
operations.save(Arrays.asList(sampleQuestionEntity1, sampleQuestionEntity2,
sampleAnswerEntity1, sampleAnswerEntity2), IndexCoordinates.of(INDEX_NAME_JOIN_SAMPLE_ENTITY));
indexOperations.refresh();
Thread.sleep(5000);
SearchHits<SampleJoinEntity> hits = operations.search(new NativeSearchQueryBuilder().withQuery(
new ParentIdQueryBuilder("answer", qId1))
.build(), SampleJoinEntity.class);
List<String> hitIds = hits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
@Override
public String apply(SearchHit<SampleJoinEntity> sampleJoinEntitySearchHit) {
return sampleJoinEntitySearchHit.getId();
}
}).collect(Collectors.toList());
assertThat(hitIds.size()).isEqualTo(2);
assertThat(hitIds.containsAll(Arrays.asList(aId1, aId2))).isTrue();
}
void shouldUpdateEntityWithJoinFields(String qId1, String qId2, String aId1, String aId2) throws Exception {
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
.create();
document.put("myJoinField", new JoinField<>("answer", qId2).getAsMap());
UpdateQuery updateQuery = UpdateQuery.builder(aId2) //
.withDocument(document) //
.withRouting(qId2)
.build();
List<UpdateQuery> queries = new ArrayList<>();
queries.add(updateQuery);
// when
operations.bulkUpdate(queries, IndexCoordinates.of(INDEX_NAME_JOIN_SAMPLE_ENTITY));
indexOperations.refresh();
Thread.sleep(5000);
SearchHits<SampleJoinEntity> updatedHits = operations.search(new NativeSearchQueryBuilder().withQuery(
new ParentIdQueryBuilder("answer", qId2))
.build(), SampleJoinEntity.class);
List<String> hitIds = updatedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
@Override
public String apply(SearchHit<SampleJoinEntity> sampleJoinEntitySearchHit) {
return sampleJoinEntitySearchHit.getId();
}
}).collect(Collectors.toList());
assertThat(hitIds.size()).isEqualTo(1);
assertThat(hitIds.get(0)).isEqualTo(aId2);
updatedHits = operations.search(new NativeSearchQueryBuilder().withQuery(
new ParentIdQueryBuilder("answer", qId1))
.build(), SampleJoinEntity.class);
hitIds = updatedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
@Override
public String apply(SearchHit<SampleJoinEntity> sampleJoinEntitySearchHit) {
return sampleJoinEntitySearchHit.getId();
}
}).collect(Collectors.toList());
assertThat(hitIds.size()).isEqualTo(1);
assertThat(hitIds.get(0)).isEqualTo(aId1);
}
void shouldDeleteEntityWithJoinFields(String qId2, String aId2) throws Exception {
Query query = new NativeSearchQueryBuilder()
.withQuery(new ParentIdQueryBuilder("answer", qId2))
.withRoute(qId2)
.build();
operations.delete(query, SampleJoinEntity.class, IndexCoordinates.of(INDEX_NAME_JOIN_SAMPLE_ENTITY));
indexOperations.refresh();
Thread.sleep(5000);
SearchHits<SampleJoinEntity> deletedHits = operations.search(new NativeSearchQueryBuilder().withQuery(
new ParentIdQueryBuilder("answer", qId2))
.build(), SampleJoinEntity.class);
List<String> hitIds = deletedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
@Override
public String apply(SearchHit<SampleJoinEntity> sampleJoinEntitySearchHit) {
return sampleJoinEntitySearchHit.getId();
}
}).collect(Collectors.toList());
assertThat(hitIds.size()).isEqualTo(0);
}
protected RequestFactory getRequestFactory() {
return ((AbstractElasticsearchTemplate) operations).getRequestFactory();
}
@@ -3482,4 +3616,18 @@ public abstract class ElasticsearchTemplateTests {
private SeqNoPrimaryTerm seqNoPrimaryTerm;
@Version private Long version;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(indexName = INDEX_NAME_JOIN_SAMPLE_ENTITY)
static class SampleJoinEntity {
@Id @Field(type = Keyword) private String uuid;
@JoinTypeRelations(relations = {
@JoinTypeRelation(parent = "question", children = {"answer"})
})
private JoinField<String> myJoinField;
@Field(type = Text) private String text;
}
}