Skip to content

Commit b3185ef

Browse files
committed
DATAES-462 - add support for mapping max score and document scores
1 parent 69a15c7 commit b3185ef

File tree

11 files changed

+214
-36
lines changed

11 files changed

+214
-36
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: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
* @author Chris White
5454
* @author Mark Paluch
5555
* @author Ilkang Na
56+
* @author Sascha Woo
5657
*/
5758
public class DefaultResultMapper extends AbstractResultMapper {
5859

@@ -62,7 +63,8 @@ public DefaultResultMapper() {
6263
super(new DefaultEntityMapper());
6364
}
6465

65-
public DefaultResultMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
66+
public DefaultResultMapper(
67+
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
6668
super(new DefaultEntityMapper());
6769
this.mappingContext = mappingContext;
6870
}
@@ -81,6 +83,8 @@ public DefaultResultMapper(
8183
@Override
8284
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
8385
long totalHits = response.getHits().getTotalHits();
86+
float maxScore = response.getHits().getMaxScore();
87+
8488
List<T> results = new ArrayList<>();
8589
for (SearchHit hit : response.getHits()) {
8690
if (hit != null) {
@@ -90,14 +94,17 @@ public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz,
9094
} else {
9195
result = mapEntity(hit.getFields().values(), clazz);
9296
}
97+
9398
setPersistentEntityId(result, hit.getId(), clazz);
9499
setPersistentEntityVersion(result, hit.getVersion(), clazz);
100+
setPersistentEntityScore(result, hit.getScore(), clazz);
95101
populateScriptFields(result, hit);
96102
results.add(result);
97103
}
98104
}
99105

100-
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);
101108
}
102109

103110
private <T> void populateScriptFields(T result, SearchHit hit) {
@@ -112,8 +119,8 @@ private <T> void populateScriptFields(T result, SearchHit hit) {
112119
try {
113120
field.set(result, searchHitField.getValue());
114121
} catch (IllegalArgumentException e) {
115-
throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "
116-
+ searchHitField.getValue(), e);
122+
throw new ElasticsearchException(
123+
"failed to set scripted field: " + name + " with value: " + searchHitField.getValue(), e);
117124
} catch (IllegalAccessException e) {
118125
throw new ElasticsearchException("failed to access scripted field: " + name, e);
119126
}
@@ -177,23 +184,19 @@ public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz)
177184
}
178185

179186
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
180-
181187
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
182-
183188
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
184189
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
185190

186191
// Only deal with String because ES generated Ids are strings !
187192
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
188193
persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);
189194
}
190-
191195
}
192196
}
193197

194198
private <T> void setPersistentEntityVersion(T result, long version, Class<T> clazz) {
195199
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
196-
197200
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz);
198201
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
199202

@@ -206,4 +209,16 @@ private <T> void setPersistentEntityVersion(T result, long version, Class<T> cla
206209
}
207210
}
208211
}
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+
}
209224
}
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)