Skip to content

Commit

Permalink
HSEARCH-4253 Implement merging of custom mapping as a post-processing…
Browse files Browse the repository at this point in the history
… step

To separate concerns. No need to think about it while building the
index model, nor while adding Hibernate-specific metadata: it's just a
post-processing step.
  • Loading branch information
yrodiere authored and fax4ever committed Oct 14, 2021
1 parent 33794df commit ef540b5
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,7 @@ public void contribute(ElasticsearchIndexNodeCollector collector,

PropertyMapping mapping = fieldNode.type().createMapping( dynamicType );

if ( parentMapping.getProperties() != null && parentMapping.getProperties().containsKey( relativeFieldName ) ) {
// If the object (node) property is already present on both sides,
// we will take the one from the user:
mapping = parentMapping.getProperties().get( relativeFieldName );
}
else if ( IndexFieldInclusion.INCLUDED.equals( fieldNode.inclusion() ) ) {
if ( IndexFieldInclusion.INCLUDED.equals( fieldNode.inclusion() ) ) {
parentMapping.addProperty( relativeFieldName, mapping );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,17 @@ public void addSchemaRootContributor(IndexSchemaRootContributor schemaRootContri
public ElasticsearchIndexModel build() {
IndexIdentifier identifier = new IndexIdentifier( idDslConverter, idProjectionConverter );

RootTypeMapping mapping = rootTypeMapping();
RootTypeMapping mapping = new RootTypeMapping();
if ( routing != null ) {
mapping.setRouting( routing );
}

for ( IndexSchemaRootContributor schemaRootContributor : schemaRootContributors ) {
schemaRootContributor.contribute( mapping );
}

mapping.setDynamic( resolveSelfDynamicType( defaultDynamicType ) );

Map<String, ElasticsearchIndexField> staticFields = new HashMap<>();
List<AbstractElasticsearchIndexFieldTemplate<?>> fieldTemplates = new ArrayList<>();

Expand Down Expand Up @@ -151,7 +157,7 @@ public void collect(NamedDynamicTemplate templateForMapping) {

return new ElasticsearchIndexModel( indexNames, mappedTypeName, identifier,
rootNode, staticFields, fieldTemplates,
analysisDefinitionRegistry, customIndexSettings, mapping );
analysisDefinitionRegistry, customIndexSettings, mapping, customIndexMapping );
}

@Override
Expand All @@ -167,17 +173,4 @@ String getAbsolutePath() {
EventContext getIndexEventContext() {
return indexEventContext;
}

private RootTypeMapping rootTypeMapping() {
if ( customIndexMapping != null ) {
return customIndexMapping;
}

RootTypeMapping mapping = new RootTypeMapping();
if ( routing != null ) {
mapping.setRouting( routing );
}
mapping.setDynamic( resolveSelfDynamicType( defaultDynamicType ) );
return mapping;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,7 @@ public void contribute(ElasticsearchIndexNodeCollector collector,
staticChildrenByNameForParent.put( relativeFieldName, fieldNode );
collector.collect( absoluteFieldPath, fieldNode );

if ( IndexFieldInclusion.INCLUDED.equals( fieldNode.inclusion() ) &&
// If the value (leaf) property is already present on both sides,
// we will take the one from the user:
( parentMapping.getProperties() == null ||
!parentMapping.getProperties().containsKey( relativeFieldName ) ) ) {
if ( IndexFieldInclusion.INCLUDED.equals( fieldNode.inclusion() ) ) {
parentMapping.addProperty( relativeFieldName, type.mapping() );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,20 @@ public class ElasticsearchIndexModel
private final ElasticsearchAnalysisDefinitionRegistry analysisDefinitionRegistry;
private final IndexSettings customIndexSettings;
private final RootTypeMapping mapping;
private final RootTypeMapping customMapping;

public ElasticsearchIndexModel(IndexNames names, String mappedTypeName,
IndexIdentifier identifier,
ElasticsearchIndexRoot rootNode, Map<String, ElasticsearchIndexField> staticFields,
List<AbstractElasticsearchIndexFieldTemplate<?>> fieldTemplates,
ElasticsearchAnalysisDefinitionRegistry analysisDefinitionRegistry, IndexSettings customIndexSettings,
RootTypeMapping mapping) {
RootTypeMapping mapping, RootTypeMapping customMapping) {
super( names.hibernateSearchIndex(), mappedTypeName, identifier, rootNode, staticFields, fieldTemplates );
this.names = names;
this.analysisDefinitionRegistry = analysisDefinitionRegistry;
this.customIndexSettings = customIndexSettings;
this.mapping = mapping;
this.customMapping = customMapping;
}

@Override
Expand Down Expand Up @@ -68,6 +70,7 @@ public void contributeLowLevelMetadata(LowLevelIndexMetadataBuilder builder) {
builder.setAnalysisDefinitionRegistry( analysisDefinitionRegistry );
builder.setCustomIndexSettings( customIndexSettings );
builder.setMapping( mapping );
builder.setCustomMapping( customMapping );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.LinkedHashMap;
import java.util.Map;

import org.hibernate.search.backend.elasticsearch.gson.impl.GsonUtils;
import org.hibernate.search.backend.elasticsearch.gson.spi.GsonProvider;
import org.hibernate.search.backend.elasticsearch.index.layout.impl.IndexNames;
import org.hibernate.search.backend.elasticsearch.lowlevel.index.aliases.impl.IndexAliasDefinition;
import org.hibernate.search.backend.elasticsearch.lowlevel.index.impl.IndexMetadata;
Expand All @@ -21,13 +23,16 @@

public class LowLevelIndexMetadataBuilder {

private final GsonProvider gsonProvider;
private final ElasticsearchIndexMetadataSyntax syntax;
private final IndexNames indexNames;
private ElasticsearchAnalysisDefinitionRegistry analysisDefinitionRegistry;
private IndexSettings customIndexSettings;
private RootTypeMapping mapping;
private RootTypeMapping customMapping;

public LowLevelIndexMetadataBuilder(ElasticsearchIndexMetadataSyntax syntax, IndexNames indexNames) {
public LowLevelIndexMetadataBuilder(GsonProvider gsonProvider, ElasticsearchIndexMetadataSyntax syntax, IndexNames indexNames) {
this.gsonProvider = gsonProvider;
this.syntax = syntax;
this.indexNames = indexNames;
}
Expand All @@ -44,13 +49,17 @@ public void setMapping(RootTypeMapping mapping) {
this.mapping = mapping;
}

public void setCustomMapping(RootTypeMapping customMapping) {
this.customMapping = customMapping;
}

public IndexMetadata build() {
IndexMetadata indexMetadata = new IndexMetadata();
indexMetadata.setAliases( buildAliases() );

indexMetadata.setSettings( buildSettings() );

indexMetadata.setMapping( mapping );
indexMetadata.setMapping( buildMapping() );
return indexMetadata;
}

Expand Down Expand Up @@ -90,6 +99,20 @@ private IndexSettings buildSettings() {
return settings;
}

private RootTypeMapping buildMapping() {
if ( customMapping == null ) {
return mapping;
}
// Avoid side effects: we copy the mappings before modifying them.
RootTypeMapping customMappingCopy =
GsonUtils.deepCopy( gsonProvider.getGsonNoSerializeNulls(), RootTypeMapping.class, customMapping );
RootTypeMapping hibernateSearchMappingCopy =
GsonUtils.deepCopy( gsonProvider.getGsonNoSerializeNulls(), RootTypeMapping.class, mapping );
// The custom mapping takes precedence over the Hibernate Search one.
customMappingCopy.merge( hibernateSearchMappingCopy );
return customMappingCopy;
}

/*
* Allows lazy initialization of analysis settings
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.backend.elasticsearch.gson.impl;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand Down Expand Up @@ -48,4 +49,16 @@ else if ( JsonElementTypes.ARRAY.isInstance( currentValue ) ) {
}
}

/**
* Efficiently performs a deep copy of a given object using Gson serialization/deserialization.
* @param gson The {@link Gson} object.
* @param objectType The type of the object to copy.
* @param object The object to copy.
* @param <T> The type of the object to copy.
* @return A deep copy of {@code object}.
*/
public static <T> T deepCopy(Gson gson, Class<T> objectType, T object) {
return gson.fromJson( gson.toJsonTree( object ), objectType );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ EventContext getEventContext() {
ElasticsearchIndexSchemaManager createSchemaManager(ElasticsearchIndexModel model,
ElasticsearchIndexLifecycleExecutionOptions lifecycleExecutionOptions) {
LowLevelIndexMetadataBuilder builder = new LowLevelIndexMetadataBuilder(
link.getGsonProvider(),
link.getIndexMetadataSyntax(),
model.names()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,43 @@ public void setExtraAttributes(Map<String, JsonElement> extraAttributes) {
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson( this );
}

/**
* Merge this mapping with another mapping generated by Hibernate Search:
*
* <ol>
* <li>Mapping parameters other than {@code properties} will be
* those from {@code this}; those from {@code other} will be ignored.
* <li>The mapping parameter {@code properties} will be merged, using properties defined in both {@code this}
* and {@code other}.
* <li>If a property is defined on both sides, it will be merged recursively as described in item 1 and 2.
* </ol>
*
* @param other The other mapping.
*/
public void merge(AbstractTypeMapping other) {
if ( other == null ) {
// nothing to do
return;
}

Map<String, PropertyMapping> otherProperties = other.properties;
if ( otherProperties == null ) {
return;
}

for ( Map.Entry<String, PropertyMapping> entry : otherProperties.entrySet() ) {
String name = entry.getKey();
PropertyMapping thisProperty = properties == null ? null : properties.get( name );
PropertyMapping otherProperty = entry.getValue();
if ( thisProperty != null ) {
// Merge common properties recursively
thisProperty.merge( otherProperty );
}
else {
// Add missing properties
addProperty( name, otherProperty );
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ private static final class TypeNameDiscriminatorSchemaRootContributor
implements IndexSchemaRootContributor {
@Override
public void contribute(RootTypeMapping rootTypeMapping) {
if ( rootTypeMapping.getProperties() != null && rootTypeMapping.getProperties().containsKey( MAPPED_TYPE_FIELD_NAME ) ) {
// do not override if the property is defined in the user custom mapping
return;
}

PropertyMapping mappedTypePropertyMapping = new PropertyMapping();
mappedTypePropertyMapping.setType( DataTypes.KEYWORD );
mappedTypePropertyMapping.setIndex( false );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,17 @@ public DiscriminatorMultiTenancyIdProjectionExtractionHelper idProjectionExtract
private static class DiscriminatorMultiTenancyIndexSchemaRootContributor implements IndexSchemaRootContributor {
@Override
public void contribute(RootTypeMapping rootTypeMapping) {
// do not override if the property is defined in the user custom mapping
if ( rootTypeMapping.getProperties() == null || !rootTypeMapping.getProperties().containsKey( ID_FIELD_NAME ) ) {
PropertyMapping idPropertyMapping = new PropertyMapping();
idPropertyMapping.setType( DataTypes.KEYWORD );
idPropertyMapping.setIndex( false );
idPropertyMapping.setDocValues( true );
rootTypeMapping.addProperty( ID_FIELD_NAME, idPropertyMapping );
}

// do not override if the property is defined in the user custom mapping
if ( rootTypeMapping.getProperties() == null || !rootTypeMapping.getProperties().containsKey( TENANT_ID_FIELD_NAME ) ) {
PropertyMapping tenantIdPropertyMapping = new PropertyMapping();
tenantIdPropertyMapping.setType( DataTypes.KEYWORD );
tenantIdPropertyMapping.setIndex( true );
tenantIdPropertyMapping.setDocValues( true );
rootTypeMapping.addProperty( TENANT_ID_FIELD_NAME, tenantIdPropertyMapping );
}
PropertyMapping idPropertyMapping = new PropertyMapping();
idPropertyMapping.setType( DataTypes.KEYWORD );
idPropertyMapping.setIndex( false );
idPropertyMapping.setDocValues( true );
rootTypeMapping.addProperty( ID_FIELD_NAME, idPropertyMapping );

PropertyMapping tenantIdPropertyMapping = new PropertyMapping();
tenantIdPropertyMapping.setType( DataTypes.KEYWORD );
tenantIdPropertyMapping.setIndex( true );
tenantIdPropertyMapping.setDocValues( true );
rootTypeMapping.addProperty( TENANT_ID_FIELD_NAME, tenantIdPropertyMapping );
}
}

Expand Down

0 comments on commit ef540b5

Please sign in to comment.