Skip to content

Commit d996406

Browse files
xhaggiodrotbohm
authored andcommitted
DATAES-462 - Add support for mapping max score and document scores.
Original pull request: #207.
1 parent 1126002 commit d996406

File tree

11 files changed

+212
-35
lines changed

11 files changed

+212
-35
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.springframework.data.elasticsearch.annotations;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Inherited;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
import org.springframework.data.annotation.ReadOnlyProperty;
11+
12+
/**
13+
* Specifies that this field is used for storing the document score.
14+
*
15+
* @author Sascha Woo
16+
*/
17+
@Retention(RetentionPolicy.RUNTIME)
18+
@Target(ElementType.FIELD)
19+
@Documented
20+
@Inherited
21+
@ReadOnlyProperty
22+
public @interface Score {
23+
}

src/main/java/org/springframework/data/elasticsearch/core/DefaultResultMapper.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
* @author Chris White
5555
* @author Mark Paluch
5656
* @author Ilkang Na
57+
* @author Sascha Woo
5758
*/
5859
public class DefaultResultMapper extends AbstractResultMapper {
5960

@@ -82,6 +83,8 @@ public DefaultResultMapper(
8283
@Override
8384
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
8485
long totalHits = response.getHits().getTotalHits();
86+
float maxScore = response.getHits().getMaxScore();
87+
8588
List<T> results = new ArrayList<>();
8689
for (SearchHit hit : response.getHits()) {
8790
if (hit != null) {
@@ -91,14 +94,17 @@ public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz,
9194
} else {
9295
result = mapEntity(hit.getFields().values(), clazz);
9396
}
97+
9498
setPersistentEntityId(result, hit.getId(), clazz);
9599
setPersistentEntityVersion(result, hit.getVersion(), clazz);
100+
setPersistentEntityScore(result, hit.getScore(), clazz);
96101
populateScriptFields(result, hit);
97102
results.add(result);
98103
}
99104
}
100105

101-
return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId());
106+
return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId(),
107+
maxScore);
102108
}
103109

104110
private <T> void populateScriptFields(T result, SearchHit hit) {
@@ -113,8 +119,8 @@ private <T> void populateScriptFields(T result, SearchHit hit) {
113119
try {
114120
field.set(result, searchHitField.getValue());
115121
} catch (IllegalArgumentException e) {
116-
throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "
117-
+ searchHitField.getValue(), e);
122+
throw new ElasticsearchException(
123+
"failed to set scripted field: " + name + " with value: " + searchHitField.getValue(), e);
118124
} catch (IllegalAccessException e) {
119125
throw new ElasticsearchException("failed to access scripted field: " + name, e);
120126
}
@@ -178,23 +184,19 @@ public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz)
178184
}
179185

180186
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
181-
182187
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
183-
184188
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
185189
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
186190

187191
// Only deal with String because ES generated Ids are strings !
188192
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
189193
persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);
190194
}
191-
192195
}
193196
}
194197

195198
private <T> void setPersistentEntityVersion(T result, long version, Class<T> clazz) {
196199
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
197-
198200
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz);
199201
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
200202

@@ -207,4 +209,16 @@ private <T> void setPersistentEntityVersion(T result, long version, Class<T> cla
207209
}
208210
}
209211
}
212+
213+
private <T> void setPersistentEntityScore(T result, float score, Class<T> clazz) {
214+
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
215+
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
216+
ElasticsearchPersistentProperty scoreProperty = persistentEntity.getScoreProperty();
217+
Class<?> type = scoreProperty.getType();
218+
219+
if (scoreProperty != null && (type == Float.class || type == Float.TYPE)) {
220+
persistentEntity.getPropertyAccessor(result).setProperty(scoreProperty, score);
221+
}
222+
}
223+
}
210224
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
package org.springframework.data.elasticsearch.core;
3+
4+
import org.springframework.data.domain.Page;
5+
6+
/**
7+
* A score-aware page gaining information about max score.
8+
*
9+
* @param <T>
10+
* @author Sascha Woo
11+
*/
12+
public interface ScoredPage<T> extends Page<T> {
13+
14+
float getMaxScore();
15+
16+
}

src/main/java/org/springframework/data/elasticsearch/core/aggregation/AggregatedPage.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import org.elasticsearch.search.aggregations.Aggregation;
44
import org.elasticsearch.search.aggregations.Aggregations;
55
import org.springframework.data.elasticsearch.core.FacetedPage;
6+
import org.springframework.data.elasticsearch.core.ScoredPage;
67
import org.springframework.data.elasticsearch.core.ScrolledPage;
78

89
/**
910
* @author Petar Tahchiev
11+
* @author Sascha Woo
1012
*/
11-
public interface AggregatedPage<T> extends FacetedPage<T>, ScrolledPage<T> {
13+
public interface AggregatedPage<T> extends FacetedPage<T>, ScrolledPage<T>, ScoredPage<T> {
1214

1315
boolean hasAggregations();
1416

src/main/java/org/springframework/data/elasticsearch/core/aggregation/impl/AggregatedPageImpl.java

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
*/
1616
package org.springframework.data.elasticsearch.core.aggregation.impl;
1717

18-
import java.util.HashMap;
1918
import java.util.List;
20-
import java.util.Map;
2119

2220
import org.elasticsearch.search.aggregations.Aggregation;
2321
import org.elasticsearch.search.aggregations.Aggregations;
@@ -29,55 +27,77 @@
2927
* @author Petar Tahchiev
3028
* @author Artur Konczak
3129
* @author Mohsin Husen
30+
* @author Sascha Woo
3231
*/
3332
public class AggregatedPageImpl<T> extends FacetedPageImpl<T> implements AggregatedPage<T> {
3433

3534
private Aggregations aggregations;
36-
private Map<String, Aggregation> mapOfAggregations = new HashMap<>();
37-
private String scrollId;
35+
private String scrollId;
36+
private float maxScore;
3837

3938
public AggregatedPageImpl(List<T> content) {
4039
super(content);
4140
}
4241

42+
public AggregatedPageImpl(List<T> content, float maxScore) {
43+
super(content);
44+
this.maxScore = maxScore;
45+
}
46+
4347
public AggregatedPageImpl(List<T> content, String scrollId) {
4448
super(content);
4549
this.scrollId = scrollId;
4650
}
4751

52+
public AggregatedPageImpl(List<T> content, String scrollId, float maxScore) {
53+
this(content, scrollId);
54+
this.maxScore = maxScore;
55+
}
56+
4857
public AggregatedPageImpl(List<T> content, Pageable pageable, long total) {
4958
super(content, pageable, total);
5059
}
5160

61+
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, float maxScore) {
62+
super(content, pageable, total);
63+
this.maxScore = maxScore;
64+
}
65+
5266
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, String scrollId) {
5367
super(content, pageable, total);
5468
this.scrollId = scrollId;
5569
}
5670

71+
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, String scrollId, float maxScore) {
72+
this(content, pageable, total, scrollId);
73+
this.maxScore = maxScore;
74+
}
75+
5776
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations) {
5877
super(content, pageable, total);
5978
this.aggregations = aggregations;
60-
if (aggregations != null) {
61-
for (Aggregation aggregation : aggregations) {
62-
mapOfAggregations.put(aggregation.getName(), aggregation);
63-
}
64-
}
6579
}
6680

67-
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, String scrollId) {
68-
super(content, pageable, total);
69-
this.aggregations = aggregations;
81+
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, float maxScore) {
82+
this(content, pageable, total, aggregations);
83+
this.maxScore = maxScore;
84+
}
85+
86+
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations,
87+
String scrollId) {
88+
this(content, pageable, total, aggregations);
7089
this.scrollId = scrollId;
71-
if (aggregations != null) {
72-
for (Aggregation aggregation : aggregations) {
73-
mapOfAggregations.put(aggregation.getName(), aggregation);
74-
}
75-
}
90+
}
91+
92+
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, String scrollId,
93+
float maxScore) {
94+
this(content, pageable, total, aggregations, scrollId);
95+
this.maxScore = maxScore;
7696
}
7797

7898
@Override
7999
public boolean hasAggregations() {
80-
return aggregations != null && mapOfAggregations.size() > 0;
100+
return aggregations != null;
81101
}
82102

83103
@Override
@@ -94,4 +114,9 @@ public Aggregation getAggregation(String name) {
94114
public String getScrollId() {
95115
return scrollId;
96116
}
117+
118+
@Override
119+
public float getMaxScore() {
120+
return maxScore;
121+
}
97122
}

src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
package org.springframework.data.elasticsearch.core.mapping;
1717

1818
import org.springframework.data.mapping.PersistentEntity;
19+
import org.springframework.lang.Nullable;
1920

2021
/**
2122
* ElasticsearchPersistentEntity
2223
*
2324
* @author Rizwan Idrees
2425
* @author Mohsin Husen
2526
* @author Mark Paluch
27+
* @author Sascha Woo
2628
*/
2729
public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, ElasticsearchPersistentProperty> {
2830

@@ -49,4 +51,22 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
4951
String settingPath();
5052

5153
boolean isCreateIndexAndMapping();
54+
55+
/**
56+
* Returns whether the {@link ElasticsearchPersistentEntity} has an score property. If this call returns
57+
* {@literal true}, {@link #getScoreProperty()} will return a non-{@literal null} value.
58+
*
59+
* @return false when {@link ElasticsearchPersistentEntity} does not define a score property.
60+
*/
61+
boolean hasScoreProperty();
62+
63+
/**
64+
* Returns the score property of the {@link ElasticsearchPersistentEntity}. Can be {@literal null} in case no score
65+
* property is available on the entity.
66+
*
67+
* @return the score {@link ElasticsearchPersistentProperty} of the {@link PersistentEntity} or {@literal null} if not
68+
* defined.
69+
*/
70+
@Nullable
71+
ElasticsearchPersistentProperty getScoreProperty();
5272
}

src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentProperty.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,24 @@
2323
*
2424
* @author Rizwan Idrees
2525
* @author Mohsin Husen
26+
* @author Sascha Woo
2627
*/
2728

2829
public interface ElasticsearchPersistentProperty extends PersistentProperty<ElasticsearchPersistentProperty> {
2930

3031
String getFieldName();
3132

33+
/**
34+
* Returns whether the current property is a <em>potential</em> score property of the owning
35+
* {@link ElasticsearchPersistentEntity}. This method is mainly used by {@link ElasticsearchPersistentEntity}
36+
* implementation to discover score property candidates on {@link ElasticsearchPersistentEntity} creation you should
37+
* rather call {@link ElasticsearchPersistentEntity#isScoreProperty(PersistentProperty)} to determine whether the
38+
* current property is the version property of that {@link ElasticsearchPersistentEntity} under consideration.
39+
*
40+
* @return
41+
*/
42+
boolean isScoreProperty();
43+
3244
public enum PropertyToFieldNameConverter implements Converter<ElasticsearchPersistentProperty, String> {
3345

3446
INSTANCE;

0 commit comments

Comments
 (0)