Skip to content

Allow to customize the mapped type name for @InnerField and @Field annotations #2950

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 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* @author Morgan Lutz
* @author Sascha Woo
* @author Haibo Liu
* @author Andriy Redko
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD })
Expand Down Expand Up @@ -240,4 +241,11 @@
* @since 5.1
*/
boolean storeEmptyValue() default true;

/**
* overrides the mapping field type which otherwise will be taken from corresponding {@link FieldType}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* overrides the mapping field type which otherwise will be taken from corresponding {@link FieldType}
* overrides the field type in the mapping which otherwise will be taken from corresponding {@link FieldType}

Seems a little clearer for me

*
* @since 5.4
*/
String mappedName() default "";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
String mappedName() default "";
String mappedTypeName() default "";

to not confuse with the field name

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* @author Brian Kimmig
* @author Morgan Lutz
* @author Haibo Liu
* @author Andriy Redko
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
Expand Down Expand Up @@ -171,4 +172,11 @@
* @since 5.4
*/
KnnIndexOptions[] knnIndexOptions() default {};

/**
* overrides the mapping field type which otherwise will be taken from corresponding {@link FieldType}
Copy link
Collaborator

Choose a reason for hiding this comment

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

same as above

*
* @since 5.4
*/
String mappedName() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
* @author Peter-Josef Meisch
* @author Xiao Yu
* @author Subhobrata Dey
* @author Andriy Redko
*/
public class MappingBuilder {

Expand Down Expand Up @@ -175,7 +176,8 @@ protected String buildPropertyMapping(ElasticsearchPersistentEntity<?> entity,
.findAnnotation(org.springframework.data.elasticsearch.annotations.Document.class);
var dynamicMapping = docAnnotation != null ? docAnnotation.dynamic() : null;

mapEntity(objectNode, entity, true, "", false, FieldType.Auto, null, dynamicMapping, runtimeFields);
final FieldType fieldType = FieldType.Auto;
mapEntity(objectNode, entity, true, "", false, fieldType, fieldType.getMappedName(), null, dynamicMapping, runtimeFields);

if (!excludeFromSource.isEmpty()) {
ObjectNode sourceNode = objectNode.putObject(SOURCE);
Expand Down Expand Up @@ -210,7 +212,7 @@ private void writeTypeHintMapping(ObjectNode propertiesNode) throws IOException
}

private void mapEntity(ObjectNode objectNode, @Nullable ElasticsearchPersistentEntity<?> entity,
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType,
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, String mappedName,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, String mappedName,
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, String fieldTypemappedName,

might be mistaken as as mapped name of the entity

@Nullable Field parentFieldAnnotation, @Nullable Dynamic dynamicMapping, @Nullable Document runtimeFields)
throws IOException {

Expand Down Expand Up @@ -244,7 +246,7 @@ private void mapEntity(ObjectNode objectNode, @Nullable ElasticsearchPersistentE
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
if (writeNestedProperties) {

String type = nestedOrObjectField ? fieldType.getMappedName() : FieldType.Object.getMappedName();
String type = nestedOrObjectField ? mappedName : FieldType.Object.getMappedName();

ObjectNode nestedObjectNode = objectMapper.createObjectNode();
nestedObjectNode.put(FIELD_PARAM_TYPE, type);
Expand Down Expand Up @@ -370,7 +372,7 @@ private void buildPropertyMapping(ObjectNode propertiesNode, boolean isRootObjec
nestedPropertyPrefix = nestedPropertyPath;

mapEntity(propertiesNode, persistentEntity, false, property.getFieldName(), true, fieldAnnotation.type(),
fieldAnnotation, dynamicMapping, null);
getMappedName(fieldAnnotation), fieldAnnotation, dynamicMapping, null);

nestedPropertyPrefix = currentNestedPropertyPrefix;
return;
Expand Down Expand Up @@ -473,7 +475,7 @@ private void applyDisabledPropertyMapping(ObjectNode propertiesNode, Elasticsear
}

propertiesNode.set(property.getFieldName(), objectMapper.createObjectNode() //
.put(FIELD_PARAM_TYPE, field.type().getMappedName()) //
.put(FIELD_PARAM_TYPE, getMappedName(field)) //
.put(MAPPING_ENABLED, false) //
);

Expand All @@ -482,6 +484,15 @@ private void applyDisabledPropertyMapping(ObjectNode propertiesNode, Elasticsear
}
}

/**
* Return the mapping type name to be used for the {@link Field}
* @param field field to return the mapping type name for
* @return the mapping type name
*/
private String getMappedName(Field field) {
return StringUtils.hasText(field.mappedName()) ? field.mappedName() : field.type().getMappedName();
}

/**
* Add mapping for @Field annotation
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public final class MappingParameters {
private final boolean store;
private final TermVector termVector;
private final FieldType type;
private final String mappedName;

/**
* extracts the mapping parameters from the relevant annotations.
Expand All @@ -141,6 +142,7 @@ private MappingParameters(Field field) {
store = field.store();
fielddata = field.fielddata();
type = field.type();
mappedName = StringUtils.hasText(field.mappedName()) ? field.mappedName() : type.getMappedName();
dateFormats = field.format();
dateFormatPatterns = field.pattern();
analyzer = field.analyzer();
Expand Down Expand Up @@ -187,6 +189,7 @@ private MappingParameters(InnerField field) {
store = field.store();
fielddata = field.fielddata();
type = field.type();
mappedName = StringUtils.hasText(field.mappedName()) ? field.mappedName() : type.getMappedName();
dateFormats = field.format();
dateFormatPatterns = field.pattern();
analyzer = field.analyzer();
Expand Down Expand Up @@ -245,7 +248,7 @@ public void writeTypeAndParametersTo(ObjectNode objectNode) throws IOException {
}

if (type != FieldType.Auto) {
objectNode.put(FIELD_PARAM_TYPE, type.getMappedName());
objectNode.put(FIELD_PARAM_TYPE, mappedName);

if (type == FieldType.Date || type == FieldType.Date_Nanos || type == FieldType.Date_Range) {
List<String> formats = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
* @author Brian Kimmig
* @author Morgan Lutz
* @author Haibo Liu
* @author Andriy Redko
*/
@SpringIntegrationTest
public abstract class MappingBuilderIntegrationTests extends MappingContextBaseTests {
Expand All @@ -77,6 +78,12 @@ void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
}

@Test
public void shouldSupportAllTypes() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one is not strictly related but EntityWithAllTypes was not used in tests at all, fixing that

IndexOperations indexOperations = operations.indexOps(EntityWithAllTypes.class);
indexOperations.createWithMapping();
}

@Test
public void shouldNotFailOnCircularReference() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* @author Brian Kimmig
* @author Morgan Lutz
* @author Haibo Liu
* @author Andriy Redko
*/
public class MappingBuilderUnitTests extends MappingContextBaseTests {

Expand Down Expand Up @@ -1242,6 +1243,59 @@ void shouldWriteFieldAliasesToTheMapping() throws JSONException {

assertEquals(expected, mapping, true);
}

@Test // #2942
@DisplayName("should use custom mapped name")
void shouldUseCustomMappedName() throws JSONException {

var expected = """
{
"properties": {
"_class": {
"type": "keyword",
"index": false,
"doc_values": false
},
"someText": {
"type": "match_only_text"
}
}
}
""";
String mapping = getMappingBuilder().buildPropertyMapping(FieldMappedNameEntity.class);

assertEquals(expected, mapping, true);
}

@Test // #2942
@DisplayName("should use custom mapped name for multifield")
void shouldUseCustomMappedNameMultiField() throws JSONException {

var expected = """
{
"properties": {
"_class": {
"type": "keyword",
"index": false,
"doc_values": false
},
"description": {
"type": "match_only_text",
"fields": {
"lower_case": {
"type": "constant_keyword",
"normalizer": "lower_case_normalizer"
}
}
}
}
}
""";
String mapping = getMappingBuilder().buildPropertyMapping(MultiFieldMappedNameEntity.class);

assertEquals(expected, mapping, true);
}

// region entities

@Document(indexName = "ignore-above-index")
Expand Down Expand Up @@ -2503,5 +2557,18 @@ private static class FieldAliasEntity {
@Nullable
@Field(type = Text) private String otherText;
}

@SuppressWarnings("unused")
private static class FieldMappedNameEntity {
@Nullable
@Field(type = Text, mappedName = "match_only_text") private String someText;
}

@SuppressWarnings("unused")
private static class MultiFieldMappedNameEntity {
@Nullable
@MultiField(mainField = @Field(type = FieldType.Text, mappedName = "match_only_text"), otherFields = { @InnerField(suffix = "lower_case",
type = FieldType.Keyword, normalizer = "lower_case_normalizer", mappedName = "constant_keyword") }) private String description;
}
// endregion
}