Skip to content

Commit a159601

Browse files
committed
HSEARCH-4950 Update validation of vector-specific mapping attributes step2
1 parent c378ce6 commit a159601

File tree

3 files changed

+160
-45
lines changed

3 files changed

+160
-45
lines changed

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/validation/impl/PropertyMappingValidator.java

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
import java.util.ArrayList;
1010
import java.util.Collections;
1111
import java.util.List;
12+
import java.util.Map;
1213

1314
import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.DataTypes;
1415
import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.ElasticsearchDenseVectorIndexOptions;
1516
import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.OpenSearchVectorTypeMethod;
1617
import org.hibernate.search.backend.elasticsearch.lowlevel.index.mapping.impl.PropertyMapping;
18+
import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchValidationMessages;
1719
import org.hibernate.search.engine.backend.analysis.AnalyzerNames;
1820
import org.hibernate.search.util.common.impl.CollectionHelper;
1921

22+
import com.google.gson.JsonElement;
23+
2024
abstract class PropertyMappingValidator extends AbstractTypeMappingValidator<PropertyMapping> {
2125

2226
private static final List<String> DEFAULT_DATE_FORMAT;
@@ -196,47 +200,68 @@ protected void validateVectorMapping(ValidationErrorCollector errorCollector, Pr
196200
}
197201

198202
private static class ElasticsearchDenseVectorIndexOptionsValidator
199-
implements Validator<ElasticsearchDenseVectorIndexOptions> {
203+
extends AbstractVectorAttributesValidator<ElasticsearchDenseVectorIndexOptions> {
200204

201205
@Override
202-
public void validate(ValidationErrorCollector errorCollector, ElasticsearchDenseVectorIndexOptions expected,
206+
protected String propertyName() {
207+
return "index_options";
208+
}
209+
210+
@Override
211+
public void doValidate(ValidationErrorCollector errorCollector, ElasticsearchDenseVectorIndexOptions expected,
203212
ElasticsearchDenseVectorIndexOptions actual) {
204213
LeafValidators.EQUAL.validate(
205-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "index_options.type",
214+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "type",
206215
expected.getType(), actual.getType()
207216
);
208217
LeafValidators.EQUAL.validateWithDefault(
209-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "index_options.m",
218+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "m",
210219
expected.getM(), actual.getM(), 16
211220
);
212221
LeafValidators.EQUAL.validateWithDefault(
213-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "index_options.ef_construction",
222+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "ef_construction",
214223
expected.getEfConstruction(), actual.getEfConstruction(), 100
215224
);
216225
}
226+
227+
@Override
228+
protected Map<String, JsonElement> expectedMappingExtraAttributes(ElasticsearchDenseVectorIndexOptions expected) {
229+
return expected.getExtraAttributes();
230+
}
231+
232+
@Override
233+
protected Map<String, JsonElement> actualMappingExtraAttributes(ElasticsearchDenseVectorIndexOptions actual) {
234+
return actual.getExtraAttributes();
235+
}
217236
}
218237

219-
private static class OpenSearchVectorTypeMethodValidator implements Validator<OpenSearchVectorTypeMethod> {
238+
private static class OpenSearchVectorTypeMethodValidator
239+
extends AbstractVectorAttributesValidator<OpenSearchVectorTypeMethod> {
220240

221241
private final OpenSearchVectorTypeMethodParametersValidator parametersValidator =
222242
new OpenSearchVectorTypeMethodParametersValidator();
223243

224244
@Override
225-
public void validate(ValidationErrorCollector errorCollector, OpenSearchVectorTypeMethod expected,
245+
protected String propertyName() {
246+
return "method";
247+
}
248+
249+
@Override
250+
public void doValidate(ValidationErrorCollector errorCollector, OpenSearchVectorTypeMethod expected,
226251
OpenSearchVectorTypeMethod actual) {
227252
LeafValidators.EQUAL.validate(
228-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "method.name",
253+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "name",
229254
expected.getName(), actual.getName()
230255
);
231256

232257
LeafValidators.EQUAL.validateWithDefault(
233-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "method.space_type",
258+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "space_type",
234259
expected.getSpaceType(), actual.getSpaceType(),
235260
"l2"
236261
);
237262

238263
LeafValidators.EQUAL.validate(
239-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "method.engine",
264+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "engine",
240265
expected.getEngine(), actual.getEngine()
241266
);
242267

@@ -245,24 +270,77 @@ public void validate(ValidationErrorCollector errorCollector, OpenSearchVectorTy
245270
parametersValidator.validate( errorCollector, expectedParameters, actual.getParameters() );
246271
}
247272
}
273+
274+
@Override
275+
protected Map<String, JsonElement> expectedMappingExtraAttributes(OpenSearchVectorTypeMethod expected) {
276+
return expected.getExtraAttributes();
277+
}
278+
279+
@Override
280+
protected Map<String, JsonElement> actualMappingExtraAttributes(OpenSearchVectorTypeMethod actual) {
281+
return actual.getExtraAttributes();
282+
}
248283
}
249284

250285
private static class OpenSearchVectorTypeMethodParametersValidator
251-
implements Validator<OpenSearchVectorTypeMethod.Parameters> {
286+
extends AbstractVectorAttributesValidator<OpenSearchVectorTypeMethod.Parameters> {
252287
@Override
253-
public void validate(ValidationErrorCollector errorCollector, OpenSearchVectorTypeMethod.Parameters expected,
288+
protected String propertyName() {
289+
return "parameters";
290+
}
291+
292+
@Override
293+
public void doValidate(ValidationErrorCollector errorCollector, OpenSearchVectorTypeMethod.Parameters expected,
254294
OpenSearchVectorTypeMethod.Parameters actual) {
255295
LeafValidators.EQUAL.validate(
256-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "method.parameters.m",
296+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "m",
257297
expected.getM(), actual.getM()
258298
);
259299

260300
LeafValidators.EQUAL.validate(
261-
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "method.parameters.ef_construction",
301+
errorCollector, ValidationContextType.MAPPING_ATTRIBUTE, "ef_construction",
262302
expected.getEfConstruction(),
263303
actual.getEfConstruction()
264304
);
305+
}
306+
307+
@Override
308+
protected Map<String, JsonElement> expectedMappingExtraAttributes(OpenSearchVectorTypeMethod.Parameters expected) {
309+
return expected.getExtraAttributes();
310+
}
265311

312+
@Override
313+
protected Map<String, JsonElement> actualMappingExtraAttributes(OpenSearchVectorTypeMethod.Parameters actual) {
314+
return actual.getExtraAttributes();
266315
}
267316
}
317+
318+
abstract static class AbstractVectorAttributesValidator<T> implements Validator<T> {
319+
private final Validator<JsonElement> extraAttributeValidator = new JsonElementValidator( new JsonElementEquivalence() );
320+
321+
@Override
322+
public final void validate(ValidationErrorCollector errorCollector, T expected, T actual) {
323+
errorCollector.push( ValidationContextType.MAPPING_ATTRIBUTE, propertyName() );
324+
try {
325+
doValidate( errorCollector, expected, actual );
326+
327+
extraAttributeValidator.validateAllIgnoreUnexpected(
328+
errorCollector, ValidationContextType.CUSTOM_INDEX_MAPPING_ATTRIBUTE,
329+
ElasticsearchValidationMessages.INSTANCE.customIndexMappingAttributeMissing(),
330+
expectedMappingExtraAttributes( expected ), actualMappingExtraAttributes( actual )
331+
);
332+
}
333+
finally {
334+
errorCollector.pop();
335+
}
336+
}
337+
338+
protected abstract String propertyName();
339+
340+
protected abstract void doValidate(ValidationErrorCollector errorCollector, T expected, T actual);
341+
342+
protected abstract Map<String, JsonElement> expectedMappingExtraAttributes(T expected);
343+
344+
protected abstract Map<String, JsonElement> actualMappingExtraAttributes(T actual);
345+
}
268346
}

integrationtest/backend/elasticsearch/src/test/java/org/hibernate/search/integrationtest/backend/elasticsearch/schema/management/ElasticsearchIndexSchemaManagerValidationMappingBaseIT.java

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.OptionalInt;
1818
import java.util.stream.Collectors;
1919

20+
import org.hibernate.search.backend.elasticsearch.ElasticsearchDistributionName;
2021
import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension;
2122
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurationContext;
2223
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer;
@@ -27,6 +28,7 @@
2728
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.extension.SearchSetupHelper;
2829
import org.hibernate.search.util.common.SearchException;
2930
import org.hibernate.search.util.common.impl.Futures;
31+
import org.hibernate.search.util.impl.integrationtest.backend.elasticsearch.dialect.ElasticsearchTestDialect;
3032
import org.hibernate.search.util.impl.integrationtest.backend.elasticsearch.extension.TestElasticsearchClient;
3133
import org.hibernate.search.util.impl.integrationtest.common.reporting.FailureReportChecker;
3234
import org.hibernate.search.util.impl.integrationtest.mapper.stub.StubMappedIndex;
@@ -545,16 +547,30 @@ void vector_invalid_similarity(ElasticsearchIndexSchemaManagerValidationOperatio
545547
)
546548
);
547549

548-
setupAndValidateExpectingFailure( index,
549-
hasValidationFailureReport()
550-
.indexFieldContext( "vectorB" )
551-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "similarity" ) )
552-
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" )
553-
.indexFieldContext( "vectorF" )
554-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "similarity" ) )
555-
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" ),
556-
operation
557-
);
550+
FailureReportChecker reportChecker;
551+
552+
if ( ElasticsearchDistributionName.ELASTIC.equals( ElasticsearchTestDialect.getActualVersion().distribution() ) ) {
553+
reportChecker = hasValidationFailureReport()
554+
.indexFieldContext( "vectorB" )
555+
.mappingAttributeContext( "similarity" )
556+
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" )
557+
.indexFieldContext( "vectorF" )
558+
.mappingAttributeContext( "similarity" )
559+
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" );
560+
}
561+
else {
562+
reportChecker = hasValidationFailureReport()
563+
.indexFieldContext( "vectorB" )
564+
.mappingAttributeContext( "method" )
565+
.mappingAttributeContext( "space_type" )
566+
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" )
567+
.indexFieldContext( "vectorF" )
568+
.mappingAttributeContext( "method" )
569+
.mappingAttributeContext( "space_type" )
570+
.failure( "Invalid value. Expected '" + cosine + "', actual is '" + l2 + "'" );
571+
}
572+
573+
setupAndValidateExpectingFailure( index, reportChecker, operation );
558574
}
559575

560576
@ParameterizedTest(name = "With operation {0}")
@@ -582,21 +598,48 @@ void vector_invalid_m_ef(ElasticsearchIndexSchemaManagerValidationOperation oper
582598
Optional.empty() )
583599
)
584600
);
585-
586-
setupAndValidateExpectingFailure( index,
587-
hasValidationFailureReport()
588-
.indexFieldContext( "vectorB" )
589-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "m" ) )
590-
.failure( "Invalid value. Expected '20', actual is '30'" )
591-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "ef_construction" ) )
592-
.failure( "Invalid value. Expected '2', actual is '3" )
593-
.indexFieldContext( "vectorF" )
594-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "m" ) )
595-
.failure( "Invalid value. Expected '50', actual is '60'" )
596-
.mappingAttributeContext( elasticSearchClient.getDialect().vectorFieldNames().get( "ef_construction" ) )
597-
.failure( "Invalid value. Expected '5', actual is '6'" ),
598-
operation
599-
);
601+
FailureReportChecker reportChecker;
602+
603+
if ( ElasticsearchDistributionName.ELASTIC.equals( ElasticsearchTestDialect.getActualVersion().distribution() ) ) {
604+
FailureReportChecker indexOptionsB = hasValidationFailureReport()
605+
.indexFieldContext( "vectorB" )
606+
.mappingAttributeContext( "index_options" );
607+
indexOptionsB.mappingAttributeContext( "m" )
608+
.failure( "Invalid value. Expected '20', actual is '30'" );
609+
indexOptionsB.mappingAttributeContext( "ef_construction" )
610+
.failure( "Invalid value. Expected '2', actual is '3" );
611+
612+
FailureReportChecker indexOptionsF = indexOptionsB.indexFieldContext( "vectorF" )
613+
.mappingAttributeContext( "index_options" );
614+
indexOptionsF.mappingAttributeContext( "m" )
615+
.failure( "Invalid value. Expected '50', actual is '60'" );
616+
indexOptionsF.mappingAttributeContext( "ef_construction" )
617+
.failure( "Invalid value. Expected '5', actual is '6'" );
618+
619+
reportChecker = indexOptionsF;
620+
}
621+
else {
622+
FailureReportChecker indexOptionsB = hasValidationFailureReport()
623+
.indexFieldContext( "vectorB" )
624+
.mappingAttributeContext( "method" )
625+
.mappingAttributeContext( "parameters" );
626+
indexOptionsB.mappingAttributeContext( "m" )
627+
.failure( "Invalid value. Expected '20', actual is '30'" );
628+
indexOptionsB.mappingAttributeContext( "ef_construction" )
629+
.failure( "Invalid value. Expected '2', actual is '3" );
630+
631+
FailureReportChecker indexOptionsF = indexOptionsB.indexFieldContext( "vectorF" )
632+
.mappingAttributeContext( "method" )
633+
.mappingAttributeContext( "parameters" );
634+
indexOptionsF.mappingAttributeContext( "m" )
635+
.failure( "Invalid value. Expected '50', actual is '60'" );
636+
indexOptionsF.mappingAttributeContext( "ef_construction" )
637+
.failure( "Invalid value. Expected '5', actual is '6'" );
638+
639+
reportChecker = indexOptionsF;
640+
}
641+
642+
setupAndValidateExpectingFailure( index, reportChecker, operation );
600643
}
601644

602645
private void setupAndValidateExpectingFailure(StubMappedIndex index, FailureReportChecker failureReportChecker,

util/internal/integrationtest/backend/elasticsearch/src/main/java/org/hibernate/search/util/impl/integrationtest/backend/elasticsearch/dialect/ElasticsearchTestDialect.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,11 @@ public Map<String, String> vectorFieldNames() {
120120
case ELASTIC:
121121
map.put( "dimension", "dims" );
122122
map.put( "element_type", "element_type" );
123-
map.put( "similarity", "similarity" );
124-
map.put( "m", "index_options.m" );
125-
map.put( "ef_construction", "index_options.ef_construction" );
126123
break;
127124
case OPENSEARCH:
128125
case AMAZON_OPENSEARCH_SERVERLESS:
129126
map.put( "dimension", "dimension" );
130127
map.put( "element_type", "data_type" );
131-
map.put( "similarity", "method.space_type" );
132-
map.put( "m", "method.parameters.m" );
133-
map.put( "ef_construction", "method.parameters.ef_construction" );
134128
break;
135129
default:
136130
throw new IllegalStateException( "Unknown distribution" );

0 commit comments

Comments
 (0)