diff --git a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/predicate/NestedSearchPredicateIT.java b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/predicate/NestedSearchPredicateIT.java index 7a429e0a20f..8490ce287d5 100644 --- a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/predicate/NestedSearchPredicateIT.java +++ b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/predicate/NestedSearchPredicateIT.java @@ -6,6 +6,7 @@ */ package org.hibernate.search.integrationtest.backend.tck.search.predicate; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hibernate.search.util.impl.integrationtest.common.assertion.SearchResultAssert.assertThat; import static org.hibernate.search.util.impl.integrationtest.mapper.stub.StubMapperUtils.referenceProvider; @@ -16,6 +17,7 @@ import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaObjectField; import org.hibernate.search.engine.backend.document.model.dsl.ObjectFieldStorage; import org.hibernate.search.engine.backend.work.execution.spi.IndexIndexingPlan; +import org.hibernate.search.util.common.SearchException; import org.hibernate.search.util.impl.integrationtest.mapper.stub.StubMappingIndexManager; import org.hibernate.search.util.impl.integrationtest.mapper.stub.StubMappingScope; import org.hibernate.search.integrationtest.backend.tck.testsupport.util.rule.SearchSetupHelper; @@ -219,6 +221,64 @@ public void search_nestedOnTwoLevels_separatePredicates() { .hasTotalHitCount( 1 ); } + @Test + public void invalidNestedPath_parent() { + StubMappingScope scope = indexManager.createScope(); + + String objectFieldPath = "nestedObject"; + String fieldInParentPath = "string"; + + assertThatThrownBy( () -> scope.query() + .where( f -> f.nested().objectField( objectFieldPath ) + .nest( f.bool() + .must( f.match() + .field( fieldInParentPath ) + .matching( "irrelevant_because_this_will_fail" ) + ) + .must( f.match() + .field( fieldInParentPath ) + .matching( "irrelevant_because_this_will_fail" ) + ) + ) + ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInParentPath + "]", + "Only fields that are contained in the nested object with path '" + objectFieldPath + "'" + + " are allowed here." + ); + } + + @Test + public void invalidNestedPath_sibling() { + StubMappingScope scope = indexManager.createScope(); + + String objectFieldPath = "nestedObject"; + String fieldInSiblingPath = "nestedObject2.string"; + + assertThatThrownBy( () -> scope.query() + .where( f -> f.nested().objectField( objectFieldPath ) + .nest( f.bool() + .must( f.match() + .field( fieldInSiblingPath ) + .matching( "irrelevant_because_this_will_fail" ) + ) + .must( f.match() + .field( fieldInSiblingPath ) + .matching( "irrelevant_because_this_will_fail" ) + ) + ) + ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInSiblingPath + "]", + "Only fields that are contained in the nested object with path '" + objectFieldPath + "'" + + " are allowed here." + ); + } + private void initData() { IndexIndexingPlan plan = indexManager.createIndexingPlan(); plan.add( referenceProvider( DOCUMENT_1 ), document -> { @@ -348,12 +408,19 @@ private void initData() { } private static class IndexMapping { + final IndexFieldReference string; final ObjectMapping nestedObject; + final ObjectMapping nestedObject2; IndexMapping(IndexSchemaElement root) { + string = root.field( "string", f -> f.asString() ).toReference(); + IndexSchemaObjectField nestedObjectField = root.objectField( "nestedObject", ObjectFieldStorage.NESTED ) .multiValued(); nestedObject = new ObjectMapping( nestedObjectField ); + IndexSchemaObjectField nestedObject2Field = root.objectField( "nestedObject2", ObjectFieldStorage.NESTED ) + .multiValued(); + nestedObject2 = new ObjectMapping( nestedObject2Field ); } } diff --git a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/DistanceSearchSortTypeFilteringSpecificsIT.java b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/DistanceSearchSortTypeFilteringSpecificsIT.java index 5404c6b7e37..b8fa0bc695f 100644 --- a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/DistanceSearchSortTypeFilteringSpecificsIT.java +++ b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/DistanceSearchSortTypeFilteringSpecificsIT.java @@ -71,6 +71,40 @@ public void nonNested() { ); } + @Test + public void invalidNestedPath_parent() { + String fieldPath = indexMapping.nestedObject1.relativeFieldName + ".geoPoint"; + String fieldInParentPath = "geoPoint"; + + assertThatThrownBy( + () -> matchAllQuery( f -> f.distance( fieldPath, GeoPoint.of( 42.0, 42.0 ) ) + .filter( pf -> pf.exists().field( fieldInParentPath ) ) ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInParentPath + "]", + "Only fields that are contained in the nested object with path '" + indexMapping.nestedObject1.relativeFieldName + "'" + + " are allowed here." + ); + } + + @Test + public void invalidNestedPath_sibling() { + String fieldPath = indexMapping.nestedObject1.relativeFieldName + ".geoPoint"; + String fieldInSiblingPath = indexMapping.nestedObject2.relativeFieldName + ".geoPoint"; + + assertThatThrownBy( + () -> matchAllQuery( f -> f.distance( fieldPath, GeoPoint.of( 42.0, 42.0 ) ) + .filter( pf -> pf.exists().field( fieldInSiblingPath ) ) ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInSiblingPath + "]", + "Only fields that are contained in the nested object with path '" + indexMapping.nestedObject1.relativeFieldName + "'" + + " are allowed here." + ); + } + private SearchQuery matchAllQuery( Function sortContributor) { return matchAllQuery( sortContributor, indexManager.createScope() ); @@ -95,12 +129,18 @@ private static class AbstractObjectMapping { private static class IndexMapping extends AbstractObjectMapping { final FirstLevelObjectMapping flattenedObject; + final FirstLevelObjectMapping nestedObject1; + final FirstLevelObjectMapping nestedObject2; IndexMapping(IndexSchemaElement root) { super( root ); flattenedObject = FirstLevelObjectMapping.create( root, "flattenedObject", ObjectFieldStorage.FLATTENED ); + nestedObject1 = FirstLevelObjectMapping.create( root, "nestedObject1", + ObjectFieldStorage.NESTED ); + nestedObject2 = FirstLevelObjectMapping.create( root, "nestedObject2", + ObjectFieldStorage.NESTED ); } } diff --git a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/FieldSearchSortTypeFilteringSpecificsIT.java b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/FieldSearchSortTypeFilteringSpecificsIT.java index e388f10f63f..226b9ecbe7d 100644 --- a/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/FieldSearchSortTypeFilteringSpecificsIT.java +++ b/integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/sort/FieldSearchSortTypeFilteringSpecificsIT.java @@ -98,6 +98,41 @@ public void nonNested() { ); } + @Test + public void invalidNestedPath_parent() { + String fieldPath = indexMapping.nestedObject1.relativeFieldName + "." + + indexMapping.nestedObject1.fieldModels.get( fieldTypeDescriptor ).relativeFieldName; + String fieldInParentPath = indexMapping.fieldModels.get( fieldTypeDescriptor ).relativeFieldName; + + assertThatThrownBy( + () -> matchAllQuery( f -> f.field( fieldPath ).filter( pf -> pf.exists().field( fieldInParentPath ) ) ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInParentPath + "]", + "Only fields that are contained in the nested object with path '" + indexMapping.nestedObject1.relativeFieldName + "'" + + " are allowed here." + ); + } + + @Test + public void invalidNestedPath_sibling() { + String fieldPath = indexMapping.nestedObject1.relativeFieldName + "." + + indexMapping.nestedObject1.fieldModels.get( fieldTypeDescriptor ).relativeFieldName; + String fieldInSiblingPath = indexMapping.nestedObject2.relativeFieldName + "." + + indexMapping.nestedObject2.fieldModels.get( fieldTypeDescriptor ).relativeFieldName; + + assertThatThrownBy( + () -> matchAllQuery( f -> f.field( fieldPath ).filter( pf -> pf.exists().field( fieldInSiblingPath ) ) ) + ) + .isInstanceOf( SearchException.class ) + .hasMessageContainingAll( + "Predicate targets unexpected fields [" + fieldInSiblingPath + "]", + "Only fields that are contained in the nested object with path '" + indexMapping.nestedObject1.relativeFieldName + "'" + + " are allowed here." + ); + } + private SearchQuery matchAllQuery( Function sortContributor) { return matchAllQuery( sortContributor, indexManager.createScope() ); @@ -122,12 +157,18 @@ private static class AbstractObjectMapping { private static class IndexMapping extends AbstractObjectMapping { final FirstLevelObjectMapping flattenedObject; + final FirstLevelObjectMapping nestedObject1; + final FirstLevelObjectMapping nestedObject2; IndexMapping(IndexSchemaElement root) { super( root ); flattenedObject = FirstLevelObjectMapping.create( root, "flattenedObject", ObjectFieldStorage.FLATTENED ); + nestedObject1 = FirstLevelObjectMapping.create( root, "nestedObject1", + ObjectFieldStorage.NESTED ); + nestedObject2 = FirstLevelObjectMapping.create( root, "nestedObject2", + ObjectFieldStorage.NESTED ); } }