Skip to content

Move merge compatibility logic from MappedFieldType to FieldMapper #56915

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 18 commits into from
May 20, 2020

Conversation

romseygeek
Copy link
Contributor

Merging logic is currently split between FieldMapper, with its merge() method, and
MappedFieldType, which checks for merging compatibility. The compatibility checks
are called from a third class, MappingMergeValidator. This makes it difficult to reason
about what is or is not compatible in updates, and even what is in fact updateable - we
have a number of tests that check compatibility on changes in mapping configuration
that are not in fact possible.

This commit refactors the compatibility logic so that it all sits on FieldMapper, and
makes it called at merge time. It adds a new FieldMapperTestCase base class that
FieldMapper tests can extend, and moves the compatibility testing machinery from
FieldTypeTestCase to here.

Relates to #56814

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

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

@elasticmachine elasticmachine added the Team:Search Meta label for search team label May 18, 2020
Copy link
Contributor

@jtibshirani jtibshirani left a comment

Choose a reason for hiding this comment

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

It's great you're cleaning up FieldMapper! I'm excited about the plan in #56814.

I was wondering if we could simplify the method structure more by combining the compatibility checks and merging together. This seems natural to me -- for each option you validate if it can be changed, and then apply the change if possible. Subclasses would only need to override a single method.

A new call structure would look something like this:
FieldMapper#merge
-> FieldMapper#mergeSharedOptions (handles both compatibility checks and updates field type, copy to, etc.)
-> FieldMapper#mergeOptions (overridden by subclasses to perform both compatibility checks and updates options)

This could be done as a follow-up refactor if we wanted to keep the current PR simple, as a straight reshuffling of methods.


protected abstract T newBuilder();

private static class BogusFieldType extends MappedFieldType {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like we could reuse the existing test classes MockFieldMapper and FakeFieldType here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

++

}
}

public void testFreeze() {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this test was lost in the refactor. Do we have plans to replace it or make it no longer necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The next step will be to decouple FieldType and MappedFieldType entirely, so there won't need to be a freeze() method here at all.

@romseygeek
Copy link
Contributor Author

A new call structure would look something like this:

I like this a lot, it would make things a lot clearer. Let's do it in a follow-up to keep this PR reasonably contained?

Copy link
Contributor

@jtibshirani jtibshirani 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 a last round of comments.

Let's do it in a follow-up to keep this PR reasonably contained?

Sounds good to me.

}
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () ->
mapperService.merge("type", new CompressedXContent(Strings.toString(update)), MapperService.MergeReason.MAPPING_UPDATE));
assertThat(e.getMessage(), containsString("mapper [foo] of different type, current_type [long], merged_type [double]"));
Copy link
Contributor

Choose a reason for hiding this comment

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

To me the error message "mapper [foo] cannot be changed from type [long] to [double]" is a lot clearer, we could prefer that original wording.

On a related note, it looks like MappedFieldType#checkTypeName is now unused and can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

++

ft1 = createNamedDefaultFieldType();
ft2 = createNamedDefaultFieldType();
modifier.modify(ft2);
assertFieldTypeNotEquals(modifier.property, ft1, ft2);
Copy link
Contributor

Choose a reason for hiding this comment

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

I just noticed we've lost test coverage for the equals method, which is used in FieldTypeLookup. This is too bad, I wonder if we could we keep these modifiers around or switch to using EqualsHashCodeTestUtils#checkEqualsAndHashCode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With luck we'll be able to simplify a lot of the MapperService lookup stuff once things have been rearranged, but for now I agree we still need these tests. I've modified FieldTypeTestCase to allow testing of subtypes with modifiers, using EqualsHashCodeTestUtils.

@@ -203,8 +179,8 @@ public void testMergeConflicts() {
}
{
FieldMapper mapper = (FieldMapper) newBuilder().build(context);
FieldMapper toMerge = new FieldMapper("bogus", new BogusFieldType(), new BogusFieldType(),
SETTINGS, FieldMapper.MultiFields.empty(), FieldMapper.CopyTo.empty()) {
FieldMapper toMerge = new FieldMapper("bogus", new MockFieldMapper.FakeFieldType(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this could just be FIeldMapper toMerge = new MockFieldMapper("bogus") ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could, thanks!

@romseygeek
Copy link
Contributor Author

A new call structure would look something like this:

This turned out to make the final patch slightly smaller and easier to review, so I added it in. Thanks for the suggestion @jtibshirani !

Copy link
Contributor

@jtibshirani jtibshirani 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 comments about field type tests that were deleted. I think they need to be restored, but once that's done it looks ready to merge.

import org.elasticsearch.index.mapper.MappedFieldType;
import org.junit.Before;

public class GeoShapeWithDocValuesFieldTypeTests extends FieldTypeTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this test have been deleted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should not! Have restored it.

/**
* Merge type-specific options and check for incompatible settings in mappings to be merged
*/
protected abstract void mergeOptions(FieldMapper other, List<String> conflicts);
Copy link
Contributor

Choose a reason for hiding this comment

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

Great that this is abstract, I like how it encourages implementors to think through what can be updated vs. not.

throw new IllegalArgumentException("mapper [" + fieldType.name() + "] cannot be changed from type ["
+ contentType() + "] to [" + mergeWith.getClass().getSimpleName() + "]");
}
merged.mergeSharedOptions((FieldMapper)mergeWith, conflicts);
Copy link
Contributor

Choose a reason for hiding this comment

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

Small comment, calling mergeOptions directly from here could be nice:

FieldMapper fieldMapper = (FieldMapper) mergeWith;
merged.mergeSharedOptions(fieldMapper, conflicts);
merged.mergeOptions(fieldMapper, conflicts);

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've rearranged things here.

@@ -51,22 +50,6 @@ protected MappedFieldType createDefaultFieldType() {
return ft;
}

@Before
public void setupProperties() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we dropped this modifier accidentally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

scaling_factor can't be updated, so we'd never be in a position when FieldTypeLookup would be comparing and old and new types with different scaling factors.


import java.util.Arrays;

public class CompletionFieldTypeTests extends FieldTypeTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this have been deleted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again, none of these values can be updated in a merge, so we don't need an equality check.

@romseygeek romseygeek merged commit f82d74b into elastic:master May 20, 2020
@romseygeek romseygeek deleted the mapper/merge-checks branch May 20, 2020 08:32
romseygeek added a commit that referenced this pull request May 20, 2020
…56915)

Merging logic is currently split between FieldMapper, with its merge() method, and
MappedFieldType, which checks for merging compatibility. The compatibility checks
are called from a third class, MappingMergeValidator. This makes it difficult to reason
about what is or is not compatible in updates, and even what is in fact updateable - we
have a number of tests that check compatibility on changes in mapping configuration
that are not in fact possible.

This commit refactors the compatibility logic so that it all sits on FieldMapper, and
makes it called at merge time. It adds a new FieldMapperTestCase base class that
FieldMapper tests can extend, and moves the compatibility testing machinery from
FieldTypeTestCase to here.

Relates to #56814
original-brownbear added a commit to original-brownbear/elasticsearch that referenced this pull request May 23, 2020
I think this is a left-over from elastic#56915 where a change in assertion
message didn't make it to this very rare-case assertion.
original-brownbear added a commit that referenced this pull request May 23, 2020
I think this is a left-over from #56915 where a change in assertion
message didn't make it to this very rare-case assertion.
original-brownbear added a commit to original-brownbear/elasticsearch that referenced this pull request May 23, 2020
…7085)

I think this is a left-over from elastic#56915 where a change in assertion
message didn't make it to this very rare-case assertion.
original-brownbear added a commit that referenced this pull request May 24, 2020
…57088)

I think this is a left-over from #56915 where a change in assertion
message didn't make it to this very rare-case assertion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>breaking-java >refactoring :Search Foundations/Mapping Index mappings, including merging and defining field types Team:Search Meta label for search team v7.9.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants