跳到主要内容

Join-Type 实现

DeepSeek V3 中英对照 Join-Type implementation

Spring Data Elasticsearch 支持 Join 数据类型,用于创建相应的索引映射并存储相关信息。

设置数据

要在父子连接关系中使用一个实体,该实体必须具有一个类型为 JoinField 的属性,并且必须对其进行注解。假设有一个 Statement 实体,其中一条语句可能是一个问题、一个答案、一个评论或一个投票(在这个例子中还展示了一个 Builder,它不是必需的,但在示例代码中稍后会被使用):

@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;
}
}
}
java
  • 有关路由的信息,请参阅 路由值

  • 一个问题可以有答案和评论

  • 一个答案可以有投票

  • JoinField 属性用于将关系的名称(questionanswercommentvote)与父 ID 结合起来。泛型类型必须与 @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"
}
}
}
}
}
json

存储数据

给定该类的一个仓库,以下代码插入一个问题、两个答案、一个评论和一个投票:

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());
}
java
  • 创建一个问题陈述

  • 问题的第一个答案

  • 第二个答案

  • 对问题的评论

  • 为第一个答案投票,这需要将路由设置为天气文档,请参阅路由值

检索数据

目前必须使用原生查询来查询数据,因此标准仓库方法不提供支持。可以使用自定义仓库实现来代替。

以下代码展示了如何使用 ElasticsearchOperations 实例检索所有具有 vote(必须是 answers,因为只有答案才能有投票)的条目:

// 示例代码
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("type", "answer"))
.withFilter(QueryBuilders.existsQuery("vote"))
.build();

List<Answer> answers = elasticsearchOperations.queryForList(searchQuery, Answer.class);
java

在这个示例中,我们使用 NativeSearchQueryBuilder 构建了一个查询,该查询匹配所有类型为 "answer" 的文档,并且这些文档必须包含 "vote" 字段。最后,我们使用 elasticsearchOperations.queryForList 方法执行查询并获取结果列表。

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);
}
java