Skip to content

Commit 75af58d

Browse files
committed
Add routing parameter to ElasticsearchOperations.
1 parent aba14c5 commit 75af58d

35 files changed

+1409
-304
lines changed

Diff for: src/main/asciidoc/index.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ include::reference/elasticsearch-auditing.adoc[]
3333
include::{spring-data-commons-docs}/entity-callbacks.adoc[]
3434
include::reference/elasticsearch-entity-callbacks.adoc[leveloffset=+1]
3535

36+
include::reference/elasticsearch-join-types.adoc[]
37+
include::reference/elasticsearch-routing.adoc[]
3638
include::reference/elasticsearch-misc.adoc[]
3739
:leveloffset: -1
3840

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
[[elasticsearch.jointype]]
2+
= Join-Type implementation
3+
4+
Spring Data Elasticsearch supports the https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html[Join data type] for creating the corresponding index mappings and for storing the relevant information.
5+
6+
== Setting up the data
7+
8+
For an entity to be used in a parent child join relationship, it must have a property of type `JoinField` which must be annotated.
9+
Let's assume a `Statement` entity where a statement may be a _question_, an _answer_, a _comment_ or a _vote_ (a _Builder_ is also shown in this example, it's not necessary, but later used in the sample code):
10+
11+
====
12+
[source,java]
13+
----
14+
@Document(indexName = "statements")
15+
@Routing("routing") <.>
16+
public class Statement {
17+
@Id
18+
private String id;
19+
20+
@Field(type = FieldType.Text)
21+
private String text;
22+
23+
@Field(type = FieldType.Keyword)
24+
private String routing;
25+
26+
@JoinTypeRelations(
27+
relations =
28+
{
29+
@JoinTypeRelation(parent = "question", children = {"answer", "comment"}), <.>
30+
@JoinTypeRelation(parent = "answer", children = "vote") <.>
31+
}
32+
)
33+
private JoinField<String> relation; <.>
34+
35+
private Statement() {
36+
}
37+
38+
public static StatementBuilder builder() {
39+
return new StatementBuilder();
40+
}
41+
42+
public String getId() {
43+
return id;
44+
}
45+
46+
public void setId(String id) {
47+
this.id = id;
48+
}
49+
50+
public String getRouting() {
51+
return routing;
52+
}
53+
54+
public void setRouting(Routing routing) {
55+
this.routing = routing;
56+
}
57+
58+
public String getText() {
59+
return text;
60+
}
61+
62+
public void setText(String text) {
63+
this.text = text;
64+
}
65+
66+
public JoinField<String> getRelation() {
67+
return relation;
68+
}
69+
70+
public void setRelation(JoinField<String> relation) {
71+
this.relation = relation;
72+
}
73+
74+
public static final class StatementBuilder {
75+
private String id;
76+
private String text;
77+
private String routing;
78+
private JoinField<String> relation;
79+
80+
private StatementBuilder() {
81+
}
82+
83+
public StatementBuilder withId(String id) {
84+
this.id = id;
85+
return this;
86+
}
87+
88+
public StatementBuilder withRouting(String routing) {
89+
this.routing = routing;
90+
return this;
91+
}
92+
93+
public StatementBuilder withText(String text) {
94+
this.text = text;
95+
return this;
96+
}
97+
98+
public StatementBuilder withRelation(JoinField<String> relation) {
99+
this.relation = relation;
100+
return this;
101+
}
102+
103+
public Statement build() {
104+
Statement statement = new Statement();
105+
statement.setId(id);
106+
statement.setRouting(routing);
107+
statement.setText(text);
108+
statement.setRelation(relation);
109+
return statement;
110+
}
111+
}
112+
}
113+
----
114+
<.> for routing related info see <<elasticsearch.routing>>
115+
<.> a question can have answers and comments
116+
<.> an answer can have votes
117+
<.> the `JoinField` property is used to combine the name (_question_, _answer_, _comment_ or _vote_) of the relation with the parent id.
118+
The generic type must be the same as the `@Id` annotated property.
119+
====
120+
121+
Spring Data Elasticsearch will build the following mapping for this class:
122+
123+
====
124+
[source,json]
125+
----
126+
{
127+
"statements": {
128+
"mappings": {
129+
"properties": {
130+
"_class": {
131+
"type": "text",
132+
"fields": {
133+
"keyword": {
134+
"type": "keyword",
135+
"ignore_above": 256
136+
}
137+
}
138+
},
139+
"routing": {
140+
"type": "keyword"
141+
},
142+
"relation": {
143+
"type": "join",
144+
"eager_global_ordinals": true,
145+
"relations": {
146+
"question": [
147+
"answer",
148+
"comment"
149+
],
150+
"answer": "vote"
151+
}
152+
},
153+
"text": {
154+
"type": "text"
155+
}
156+
}
157+
}
158+
}
159+
}
160+
----
161+
====
162+
163+
== Storing data
164+
165+
Given a repository for this class the following code inserts a question, two answers, a comment and a vote:
166+
167+
====
168+
[source,java]
169+
----
170+
void init() {
171+
repository.deleteAll();
172+
173+
Statement savedWeather = repository.save(
174+
Statement.builder()
175+
.withText("How is the weather?")
176+
.withRelation(new JoinField<>("question")) <1>
177+
.build());
178+
179+
Statement sunnyAnswer = repository.save(
180+
Statement.builder()
181+
.withText("sunny")
182+
.withRelation(new JoinField<>("answer", savedWeather.getId())) <2>
183+
.build());
184+
185+
repository.save(
186+
Statement.builder()
187+
.withText("rainy")
188+
.withRelation(new JoinField<>("answer", savedWeather.getId())) <3>
189+
.build());
190+
191+
repository.save(
192+
Statement.builder()
193+
.withText("I don't like the rain")
194+
.withRelation(new JoinField<>("comment", savedWeather.getId())) <4>
195+
.build());
196+
197+
repository.save(
198+
Statement.builder()
199+
.withText("+1 for the sun")
200+
,withRouting(savedWeather.getId())
201+
.withRelation(new JoinField<>("vote", sunnyAnswer.getId())) <5>
202+
.build());
203+
}
204+
----
205+
<1> create a question statement
206+
<2> the first answer to the question
207+
<3> the second answer
208+
<4> a comment to the question
209+
<5> a vote for the first answer, this needs to have the routing set to the weather document, see <<elasticsearch.routing>>.
210+
====
211+
212+
== Retrieving data
213+
214+
Currently native search queries must be used to query the data, so there is no support from standard repository methods. <<repositories.custom-implementations>> can be used instead.
215+
216+
The following code shows as an example how to retrieve all entries that have a _vote_ (which must be _answers_, because only answers can have a vote) using an `ElasticsearchOperations` instance:
217+
218+
====
219+
[source,java]
220+
----
221+
SearchHits<Statement> hasVotes() {
222+
NativeSearchQuery query = new NativeSearchQueryBuilder()
223+
.withQuery(hasChildQuery("vote", matchAllQuery(), ScoreMode.None))
224+
.build();
225+
226+
return operations.search(query, Statement.class);
227+
}
228+
----
229+
====

0 commit comments

Comments
 (0)