-
Notifications
You must be signed in to change notification settings - Fork 1.3k
DATAES-462 - Add score mapping support to DefaultResultMapper #207
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
Conversation
|
||
float getScore(); | ||
|
||
void setScore(float score); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that an interface that's supposed to be implemented by user code? If so, does it makes sense that the score can be modified? That feels quite invasive. Should we provide an annotation based approach instead, so that users can simply annotate a field in their domain class and we invoke this reflectively (via PersistentEntity.getPropertyAccessor(…).setProperty(scoreProperty, value);
in DefaultResultMapper? I guess, that would need
ElasticsearchPersistentEntity` to expose a score property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After talking to @christophstrobl, it looks like the Solr module has an @Score
annotation that's a @ReadOnlyProperty
. Would be cool if you could follow that design. A good starting point for the integration into the metamodel is probably SolrPersistentEntity
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought exactly the same before I saw this comment ;) Will address your comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to ping me in Gitter if you need help regarding the meta-model and property access.
/** | ||
* @author Sascha Woo | ||
*/ | ||
public interface ScoredPage<T> extends Page<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit of Javadoc highly appreciated :).
return trackScores; | ||
} | ||
|
||
public void setTrackScores(boolean trackScores) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At a quick glance, this setter doesn't seem to be used anywhere, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it is. From NativeSearchQueryBuilder
. However, it should be in package scope then to limit the API surface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This setter needs to be public for cases where NativeSearchQueryBuilder
is not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am probably too unfamiliar with the ES specifics here. I am merely looking at this from an "How much API do we expose?" point of view and API that assumes or exposes mutability is usually candidate for extra scrutiny.
Can you elaborate on the use case? I guess you're referring to the usage via CriteriaQuery
, StringQuery
and the likes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are cases in which the query is created using the constructors of NativeSearchQuery
and then additional setters are called. If we want to enforce the use of the NativeSearchQueryBuilder
, all constructors and setters should be package scoped. But we should not do that, as it would break existing code. So in my opinion, this setter should be public analog to the other setters. In our internal projects, we implemented a less complex search query builder implementation which needs to have access to the setTrackScores
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood, thanks. Looks like Sascha opened up the visibility to public
again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we discussed it before.
@olivergierke pushed some fixups to address your comments and drop the interface |
@Target(ElementType.FIELD) | ||
@Documented | ||
@Inherited | ||
public @interface Score { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's meta-annotate this with @ReadOnlyProperty
so that @Transient
is not needed in the domain model as we always consider a score property metadata, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thx, don't know that this is possible ;)
Let me take over quickly. I'll add a commit that takes care of proper integration with the |
I'm on it. I need some time to push another fixup that address it. |
Okay, ping me if I should take over again. I was planning to literally copy the code of Spring Data Solr in |
@olivergierke force-pushed with everything addressed. Now it use the mapping context + meta model to determine the score property etc. |
@@ -206,4 +209,16 @@ private String buildJSONFromFields(Collection<DocumentField> values) { | |||
} | |||
} | |||
} | |||
|
|||
private <T> void setPersistentEntityScore(T result, float score, Class<T> clazz) { | |||
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need that guard? Shouldn't the constructor of DefaultResultMapper
already make sure that it's never null
? What's the second guard forI'd vote to avoid any Java-native reflection in the context of actually working with the metamodel.
private <T> void setPersistentEntityScore(T result, float score, Class<T> clazz) { | ||
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) { | ||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz); | ||
ElasticsearchPersistentProperty scoreProperty = persistentEntity.getScoreProperty(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we use ….hasScoreProperty()
here to avoid the downstream null
check?
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) { | ||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz); | ||
ElasticsearchPersistentProperty scoreProperty = persistentEntity.getScoreProperty(); | ||
Class<?> type = scoreProperty.getType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to already reject score properties with other types than float
/Float
when the ElasticsearchPersistentProperty
is created? That would fail much faster and not let the application bootstrap and the score not being mapped without any indication that the type doesn't match.
* Returns whether the {@link ElasticsearchPersistentEntity} has an score property. If this call returns | ||
* {@literal true}, {@link #getScoreProperty()} will return a non-{@literal null} value. | ||
* | ||
* @return false when {@link ElasticsearchPersistentEntity} does not define a score property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add @since 3.1
for newly introduced API. Probably also for the method below, and ScoredPage
etc.
+ "as version. Check your mapping configuration!", property.getField(), scoreProperty.getField())); | ||
} | ||
|
||
Assert.isTrue(property.getType() == Float.class || property.getType() == Float.TYPE, "Score property must be of type float!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move that check into the creation of the ElasticsearchPersistentProperty
.
@Target(ElementType.FIELD) | ||
@Documented | ||
@Inherited | ||
@ReadOnlyProperty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just checked the EntityWriter
implementation and it looks like it's solely based on Jackson. This unfortunately makes it impossible that our mapping annotations are considered, as we'd effectively have to translate @ReadOnlyProperty
into @JsonIgnore
.
This seems to be a bigger issue, and ideally I'd like to move off Jackson at some point entirely (as Jackson annotations might be used to customize the web representation of domain types and thus couple the store representation to the external one). However, do you think we can register a Jackson module that registers a BeanSerializerModifier
and in ….changeProperties(…)
it uses the PersistentEntity
metamodel to drop the properties that Jackson has discovered in case a PersistentProperty
is annotated with @ReadOnlyProperty
?
Feel free to create a separate ticket for that and assign to me. I guess it's quite a bit involved additional code that's needed. However, without that, what we have is not going to work properly as an @Transient
on the property will make the property disappear from the mapping context, i.e. the code that's trying to populate the property is never going to find a score property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just filed DATAES-464 and am going to push a fix for that. With that in place, the read-only properties shouldn't be written to Elasticsearch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, will rebase the PR tomorrow and address the other stuff as well.
I'll take it from here. |
I'm pretty much done with polish and merge. |
Original pull request: #207.
Original pull request: #207.
SimpleElasticsearchPersistentProperty now already checks for the correct type of score properties. Added unit tests for that. Also added unit tests for SimpleElasticsearchPersistentEntity rejecting more than one score property being present. Additional non-null assertions on components that are required so that we can remove superfluous null checks. A bit o formatting, Javadoc, missing @SInCE tags and license headers. Original pull request: #207.
Thanks for taking over. |
SimpleElasticsearchPersistentProperty now already checks for the correct type of score properties. Added unit tests for that. Also added unit tests for SimpleElasticsearchPersistentEntity rejecting more than one score property being present. Additional non-null assertions on components that are required so that we can remove superfluous null checks. A bit o formatting, Javadoc, missing @SInCE tags and license headers. Original pull request: spring-projects#207.
See https://jira.spring.io/browse/DATAES-462