Skip to content

#1218 - Add routing parameter to ElasticsearchOperations. #562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ include::reference/elasticsearch-auditing.adoc[]
include::{spring-data-commons-docs}/entity-callbacks.adoc[]
include::reference/elasticsearch-entity-callbacks.adoc[leveloffset=+1]

include::reference/elasticsearch-join-types.adoc[]
include::reference/elasticsearch-routing.adoc[]
include::reference/elasticsearch-misc.adoc[]
:leveloffset: -1

Expand Down
229 changes: 229 additions & 0 deletions src/main/asciidoc/reference/elasticsearch-join-types.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
[[elasticsearch.jointype]]
= Join-Type implementation

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.

== Setting up the data

For an entity to be used in a parent child join relationship, it must have a property of type `JoinField` which must be annotated.
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):

====
[source,java]
----
@Document(indexName = "statements")
@Routing("routing") <.>
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"}), <.>
@JoinTypeRelation(parent = "answer", children = "vote") <.>
}
)
private JoinField<String> relation; <.>

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(Routing 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;
}
}
}
----
<.> for routing related info see <<elasticsearch.routing>>
<.> a question can have answers and comments
<.> an answer can have votes
<.> the `JoinField` property is used to combine the name (_question_, _answer_, _comment_ or _vote_) of the relation with the parent id.
The generic type must be the same as the `@Id` annotated property.
====

Spring Data Elasticsearch will build the following mapping for this class:

====
[source,json]
----
{
"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"
}
}
}
}
}
----
====

== Storing data

Given a repository for this class the following code inserts a question, two answers, a comment and a vote:

====
[source,java]
----
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> create a question statement
<2> the first answer to the question
<3> the second answer
<4> a comment to the question
<5> a vote for the first answer, this needs to have the routing set to the weather document, see <<elasticsearch.routing>>.
====

== Retrieving data

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.

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:

====
[source,java]
----
SearchHits<Statement> hasVotes() {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(hasChildQuery("vote", matchAllQuery(), ScoreMode.None))
.build();

return operations.search(query, Statement.class);
}
----
====
Loading