Spring Data Elasticsearch支持Join数据类型,用于创建相应的索引映射和存储相关信息。
设置数据
对于要在父子连接关系中使用的实体,它必须具有必须进行批注的属性类型。
让我们假设一个实体,其中语句可能是问题、答案、评论或投票(此示例中也显示了 Builder,这不是必需的,但稍后在示例代码中使用):JoinFieldStatement
@Document(indexName = "statements")
@Routing("routing")                                                                       (1)
public class Statement {
    @Id
    private String id;
    @Field(type = FieldType.Text)
    private String text;
    @Field(type = FieldType.Keyword)
    private String routing;
    @JoinTypeRelations(
        relations =
            {
                @JoinTypeRelation(parent = "question", children = {"answer", "comment"}), (2)
                @JoinTypeRelation(parent = "answer", children = "vote")                   (3)
            }
    )
    private JoinField<String> relation;                                                   (4)
    private Statement() {
    }
    public static StatementBuilder builder() {
        return new StatementBuilder();
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getRouting() {
        return routing;
    }
    public void setRouting(String routing) {
        this.routing = routing;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
    public JoinField<String> getRelation() {
        return relation;
    }
    public void setRelation(JoinField<String> relation) {
        this.relation = relation;
    }
    public static final class StatementBuilder {
        private String id;
        private String text;
        private String routing;
        private JoinField<String> relation;
        private StatementBuilder() {
        }
        public StatementBuilder withId(String id) {
            this.id = id;
            return this;
        }
        public StatementBuilder withRouting(String routing) {
            this.routing = routing;
            return this;
        }
        public StatementBuilder withText(String text) {
            this.text = text;
            return this;
        }
        public StatementBuilder withRelation(JoinField<String> relation) {
            this.relation = relation;
            return this;
        }
        public Statement build() {
            Statement statement = new Statement();
            statement.setId(id);
            statement.setRouting(routing);
            statement.setText(text);
            statement.setRelation(relation);
            return statement;
        }
    }
}
| 1 | 有关路由相关信息,请参阅路由值 | 
| 2 | 问题可以有答案和评论 | 
| 3 | 答案可以有票 | 
| 4 | 该属性用于将关系的名称(问题、答案、评论或投票)与父 ID 组合在一起。
泛型类型必须与带批注的属性相同。JoinField@Id | 
Spring Data Elasticsearch 将为此类构建以下映射:
{
  "statements": {
    "mappings": {
      "properties": {
        "_class": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "routing": {
          "type": "keyword"
        },
        "relation": {
          "type": "join",
          "eager_global_ordinals": true,
          "relations": {
            "question": [
              "answer",
              "comment"
            ],
            "answer": "vote"
          }
        },
        "text": {
          "type": "text"
        }
      }
    }
  }
}
| 1 | 有关路由相关信息,请参阅路由值 | 
| 2 | 问题可以有答案和评论 | 
| 3 | 答案可以有票 | 
| 4 | 该属性用于将关系的名称(问题、答案、评论或投票)与父 ID 组合在一起。
泛型类型必须与带批注的属性相同。JoinField@Id | 
存储数据
给定此类的存储库,以下代码插入一个问题、两个答案、一个注释和一个投票:
void init() {
    repository.deleteAll();
    Statement savedWeather = repository.save(
        Statement.builder()
            .withText("How is the weather?")
            .withRelation(new JoinField<>("question"))                          (1)
            .build());
    Statement sunnyAnswer = repository.save(
        Statement.builder()
            .withText("sunny")
            .withRelation(new JoinField<>("answer", savedWeather.getId()))      (2)
            .build());
    repository.save(
        Statement.builder()
            .withText("rainy")
            .withRelation(new JoinField<>("answer", savedWeather.getId()))      (3)
            .build());
    repository.save(
        Statement.builder()
            .withText("I don't like the rain")
            .withRelation(new JoinField<>("comment", savedWeather.getId()))     (4)
            .build());
    repository.save(
        Statement.builder()
            .withText("+1 for the sun")
            .withRouting(savedWeather.getId())
            .withRelation(new JoinField<>("vote", sunnyAnswer.getId()))         (5)
            .build());
}
| 1 | 创建问题陈述 | 
| 2 | 问题的第一个答案 | 
| 3 | 第二个答案 | 
| 4 | 对问题的评论 | 
| 5 | 对第一个答案进行投票,这需要将路由设置为天气文档,请参阅路由值。 | 
| 1 | 创建问题陈述 | 
| 2 | 问题的第一个答案 | 
| 3 | 第二个答案 | 
| 4 | 对问题的评论 | 
| 5 | 对第一个答案进行投票,这需要将路由设置为天气文档,请参阅路由值。 | 
检索数据
目前必须使用本机查询来查询数据,因此不支持标准存储库方法。可以改用自定义存储库实现。
以下代码作为示例演示如何使用实例检索所有具有投票的条目(必须是答案,因为只有答案才能有投票):ElasticsearchOperations
SearchHits<Statement> hasVotes() {
	Query query = NativeQuery.builder()
		.withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query.of(qb -> qb
			.hasChild(hc -> hc
				.type("answer")
				.queryName("vote")
				.query(matchAllQueryAsQuery())
				.scoreMode(ChildScoreMode.None)
			)))
		.build();
	return operations.search(query, Statement.class);
}