Skip to content
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

Move FieldMapper#valueFetcher to MappedFieldType #62974

Merged
merged 12 commits into from
Oct 4, 2020

Conversation

romseygeek
Copy link
Contributor

For runtime fields, we will want to do all search-time interaction with
a field definition via a MappedFieldType, rather than a FieldMapper, to
avoid interfering with the logic of document parsing. Currently, fetching
values for runtime scripts and for building top hits responses need to
call a method on FieldMapper. This commit moves this method to
MappedFieldType, incidentally simplifying the current call sites and freeing
us up to implement runtime fields as pure MappedFieldType objects.

@romseygeek romseygeek added :Search Foundations/Mapping Index mappings, including merging and defining field types >refactoring v8.0.0 v7.10.0 labels Sep 28, 2020
@romseygeek romseygeek self-assigned this Sep 28, 2020
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-search (:Search/Mapping)

@elasticmachine elasticmachine added the Team:Search Meta label for search team label Sep 28, 2020
@romseygeek
Copy link
Contributor Author

Some more refactoring from me :). We are getting some fairly unwieldy telescoping MappedFieldType constructors now, and there are also quite a few places where we end up sharing object parsing logic between the FieldMapper and the MappedFieldType which I think can be cleaned up, but I wanted to keep this PR as simple as possible and so this is just doing the absolute basics. Further improvements will follow.

@@ -122,7 +122,7 @@ Builder nullValue(double nullValue) {
@Override
public ScaledFloatFieldMapper build(BuilderContext context) {
ScaledFloatFieldType type = new ScaledFloatFieldType(buildFullName(context), indexed.getValue(), stored.getValue(),
hasDocValues.getValue(), meta.getValue(), scalingFactor.getValue());
hasDocValues.getValue(), meta.getValue(), scalingFactor.getValue(), nullValue.getValue());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These telescoping constructors are getting too complicated now - one idea I have for a follow-up is to add a FieldCharacteristics wrapper object that takes the (indexed, docvales, stored, meta) tuple, analogous to TextSearchInfo, and give both of these 'holder' objects constructors that take Parameter sets directly, so that you can go FieldCharacteristics.build(index, hasDocValues, stored, meta) directly in build and remove some of the ceremony.


public CollationFieldType(String name, boolean isSearchable, boolean isStored, boolean hasDocValues,
Collator collator, Map<String, String> meta) {
Collator collator, String nullValue, int ignoreAbove, Map<String, String> meta) {
super(name, isSearchable, isStored, hasDocValues, TextSearchInfo.SIMPLE_MATCH_ONLY, meta);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can wrap this up more nicely as well in a followup, passing a Function<Object, String> parsing object here that is shared with the FieldMapper impl so that we don't need to a) pass nullValue and ignoreAbove and b) duplicate the relevant logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And indeed, we can probably add a factory function to SourceValueFetcher that takes a parsing function as well and further reduce the boilerplate.

Copy link
Contributor

@jtibshirani jtibshirani Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this out when initially designing the change, and it proved difficult to share the parsing logic. I wrote up the details in this draft PR: #56473. But I would love to discuss further if you find a way forward!

Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some questions, thanks for taking care of this change!


@Override
public ValueFetcher valueFetcher(MapperService mapperService, SearchLookup searchLookup, String format) {
throw new UnsupportedOperationException(); // TODO can we implement this?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering the same, or even if we should do so. when would this field type be looked up compared to the other one that implements valueFetcher?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good question, I'll propose an idea in a follow-up PR.

@romseygeek
Copy link
Contributor Author

Have updated @javanna and ready for another go-round.

Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@romseygeek romseygeek merged commit ce649d0 into elastic:master Oct 4, 2020
@romseygeek romseygeek deleted the mapper/valuefetcher-ft branch October 4, 2020 09:47
romseygeek added a commit to romseygeek/elasticsearch that referenced this pull request Oct 4, 2020
For runtime fields, we will want to do all search-time interaction with
a field definition via a MappedFieldType, rather than a FieldMapper, to
avoid interfering with the logic of document parsing. Currently, fetching
values for runtime scripts and for building top hits responses need to
call a method on FieldMapper. This commit moves this method to
MappedFieldType, incidentally simplifying the current call sites and freeing
us up to implement runtime fields as pure MappedFieldType objects.
romseygeek added a commit that referenced this pull request Oct 4, 2020
For runtime fields, we will want to do all search-time interaction with
a field definition via a MappedFieldType, rather than a FieldMapper, to
avoid interfering with the logic of document parsing. Currently, fetching
values for runtime scripts and for building top hits responses need to
call a method on FieldMapper. This commit moves this method to
MappedFieldType, incidentally simplifying the current call sites and freeing
us up to implement runtime fields as pure MappedFieldType objects.
@@ -129,20 +130,6 @@ protected void parseCreateField(ParseContext context) throws IOException {
);
}

@Override
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this change, I think value parsing will always fail -- since it uses NumberFieldType, it will attempt to parse a piece of text as a number.

continue;
}

if (mapper instanceof FieldAliasMapper) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very happy we removed this 🎉

@jtibshirani
Copy link
Contributor

Thanks @romseygeek for this refactor! Overall it turned out nicely -- it feels better conceptually for field types to own value fetching, since they're in charge of all other search-time operations.

if (format != null) {
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] doesn't support formats.");
}
return new SourceValueFetcher(name(), mapperService, false) {
Copy link
Contributor

@jtibshirani jtibshirani Oct 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other change I noticed -- previously we always passed the result of FieldMapper#parsesArrayValue instead of hard-coding the booleans. Now there is nothing really tying this to parsesArrayValue and it could be easily overlooked. I wonder how we could make this more robust.

An update: I opened #63354 with a small improvement.

jtibshirani added a commit to jtibshirani/elasticsearch that referenced this pull request Oct 6, 2020
When constructing a value fetcher, the 'parsesArrayValue' flag must match
`FieldMapper#parsesArrayValue`. However there is nothing in code or tests to
help enforce this.

This PR reworks the value fetcher constructors so that `parsesArrayValue` is
'false' by default. Just as for `FieldMapper#parsesArrayValue`, field types must
explicitly set it to true and ensure the behavior is covered by tests.

Follow-up to elastic#62974.
jtibshirani added a commit that referenced this pull request Oct 6, 2020
When constructing a value fetcher, the 'parsesArrayValue' flag must match
`FieldMapper#parsesArrayValue`. However there is nothing in code or tests to
help enforce this.

This PR reworks the value fetcher constructors so that `parsesArrayValue` is
'false' by default. Just as for `FieldMapper#parsesArrayValue`, field types must
explicitly set it to true and ensure the behavior is covered by tests.

Follow-up to #62974.
jtibshirani added a commit to jtibshirani/elasticsearch that referenced this pull request Oct 6, 2020
When constructing a value fetcher, the 'parsesArrayValue' flag must match
`FieldMapper#parsesArrayValue`. However there is nothing in code or tests to
help enforce this.

This PR reworks the value fetcher constructors so that `parsesArrayValue` is
'false' by default. Just as for `FieldMapper#parsesArrayValue`, field types must
explicitly set it to true and ensure the behavior is covered by tests.

Follow-up to elastic#62974.
jtibshirani added a commit that referenced this pull request Oct 7, 2020
When constructing a value fetcher, the 'parsesArrayValue' flag must match
`FieldMapper#parsesArrayValue`. However there is nothing in code or tests to
help enforce this.

This PR reworks the value fetcher constructors so that `parsesArrayValue` is
'false' by default. Just as for `FieldMapper#parsesArrayValue`, field types must
explicitly set it to true and ensure the behavior is covered by tests.

Follow-up to #62974.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>refactoring :Search Foundations/Mapping Index mappings, including merging and defining field types Team:Search Meta label for search team v7.10.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants