Skip to content

Commit

Permalink
HSEARCH-5235 WIP: draft of injecting field references
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Dec 20, 2024
1 parent 204633f commit 4e062a6
Show file tree
Hide file tree
Showing 49 changed files with 993 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.documentation.mapper.orm.binding.document.model.dsl.injection;

import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

import org.hibernate.search.documentation.testsupport.data.ISBN;
import org.hibernate.search.documentation.testsupport.data.ISBNAttributeConverter;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyBinding;

@Entity
@Indexed
public class Book {

@Id
@GeneratedValue
@GenericField
private Integer id;

@Convert(converter = ISBNAttributeConverter.class)
@PropertyBinding(binder = @PropertyBinderRef(type = ISBNBinder.class))
private ISBN isbn;

public Integer getId() {
return id;
}

public ISBN getIsbn() {
return isbn;
}

public void setIsbn(ISBN isbn) {
this.isbn = isbn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.documentation.mapper.orm.binding.document.model.dsl.injection;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.search.util.impl.integrationtest.mapper.orm.OrmUtils.with;

import java.util.Arrays;
import java.util.List;

import jakarta.persistence.EntityManagerFactory;

import org.hibernate.search.documentation.testsupport.BackendConfigurations;
import org.hibernate.search.documentation.testsupport.DocumentationSetupHelper;
import org.hibernate.search.documentation.testsupport.data.ISBN;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.session.SearchSession;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class DocumentModelDslInjectIT {
@RegisterExtension
public DocumentationSetupHelper setupHelper = DocumentationSetupHelper.withSingleBackend( BackendConfigurations.simple() );

private EntityManagerFactory entityManagerFactory;

@BeforeEach
void setup() {
entityManagerFactory = setupHelper.start().setup( Book.class );
}

@Test
void smoke() {
with( entityManagerFactory ).runInTransaction( entityManager -> {
Book book = new Book();
book.setIsbn( ISBN.parse( "978-0-58-600835-5" ) );
entityManager.persist( book );
} );

with( entityManagerFactory ).runInTransaction( entityManager -> {
SearchSession searchSession = Search.session( entityManager );

List<Book> result = searchSession.search( Arrays.asList( Book.class ) )
.where( f -> f.match().field( "isbn" ).matching( "978-0-58-600835-5" ) )
.fetchHits( 20 );

assertThat( result ).hasSize( 1 );
} );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.documentation.mapper.orm.binding.document.model.dsl.injection;

import org.hibernate.search.documentation.testsupport.data.ISBN;
import org.hibernate.search.engine.backend.document.DocumentElement;
import org.hibernate.search.engine.backend.document.IndexFieldReference;
import org.hibernate.search.mapper.pojo.bridge.PropertyBridge;
import org.hibernate.search.mapper.pojo.bridge.binding.PropertyBindingContext;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
import org.hibernate.search.mapper.pojo.bridge.runtime.PropertyBridgeWriteContext;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;

//tag::bind[]
public class ISBNBinder implements PropertyBinder {

@KeywordField(normalizer = "isbn")
IndexFieldReference<String> isbn;

IndexFieldReference<String> isbnNoAnnotation;

@Override
public void bind(PropertyBindingContext context) {
context.dependencies()
.useRootOnly();

context.bridge( // <6>
ISBN.class, // <7>
new ISBNBridge( isbn ) // <8>
);
}
//end::bind[]

//tag::write[]
private static class ISBNBridge implements PropertyBridge<ISBN> {

private final IndexFieldReference<String> fieldReference;

private ISBNBridge(IndexFieldReference<String> fieldReference) {
this.fieldReference = fieldReference;
}

@Override
public void write(DocumentElement target, ISBN bridgedElement, PropertyBridgeWriteContext context) {
String indexedValue = /* ... (extraction of data, not relevant) ... */
//end::write[]
bridgedElement.getStringValue();
//tag::write[]
target.addValue( this.fieldReference, indexedValue ); // <1>
}
}
//end::write[]
//tag::bind[]
}
//end::bind[]
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.search.mapper.orm.logging.impl.MappingLog;
import org.hibernate.search.mapper.pojo.model.models.spi.AbstractPojoModelsBootstrapIntrospector;
import org.hibernate.search.mapper.pojo.model.models.spi.PojoModelsGenericContextHelper;
import org.hibernate.search.mapper.pojo.model.spi.AbstractPojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext;
import org.hibernate.search.mapper.pojo.model.spi.PojoBootstrapIntrospector;
Expand All @@ -30,6 +29,7 @@
import org.hibernate.search.util.common.reflect.spi.ValueCreateHandle;
import org.hibernate.search.util.common.reflect.spi.ValueHandleFactory;
import org.hibernate.search.util.common.reflect.spi.ValueReadHandle;
import org.hibernate.search.util.common.reflect.spi.ValueReadWriteHandle;

public class HibernateOrmBootstrapIntrospector extends AbstractPojoModelsBootstrapIntrospector
implements PojoBootstrapIntrospector {
Expand All @@ -44,7 +44,6 @@ public static HibernateOrmBootstrapIntrospector create(
}

private final HibernateOrmBasicTypeMetadataProvider basicTypeMetadataProvider;
private final PojoModelsGenericContextHelper genericContextHelper;

/*
* Note: the main purpose of these caches is not to improve performance,
Expand All @@ -64,11 +63,10 @@ private HibernateOrmBootstrapIntrospector(
ValueHandleFactory valueHandleFactory) {
super( classDetailsRegistry, valueHandleFactory );
this.basicTypeMetadataProvider = basicTypeMetadataProvider;
this.genericContextHelper = new PojoModelsGenericContextHelper( this );
}

@Override
public AbstractPojoRawTypeModel<?, ?> typeModel(String name) {
public AbstractPojoRawTypeModel<?, ?, ?> typeModel(String name) {
HibernateOrmBasicDynamicMapTypeMetadata dynamicMapTypeOrmMetadata =
basicTypeMetadataProvider.getBasicDynamicMapTypeMetadata( name );
if ( dynamicMapTypeOrmMetadata != null ) {
Expand Down Expand Up @@ -112,6 +110,12 @@ protected ValueReadHandle<?> createValueReadHandle(Member member) throws Illegal
return super.createValueReadHandle( member );
}

@Override
protected ValueReadWriteHandle<?> createValueReadWriteHandle(Member member) throws IllegalAccessException {
setAccessible( member );
return super.createValueReadWriteHandle( member );
}

ValueReadHandle<?> createValueReadHandle(Class<?> holderClass, Member member,
HibernateOrmBasicClassPropertyMetadata ormPropertyMetadata)
throws IllegalAccessException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.hibernate.search.util.common.reflect.spi.ValueReadHandle;

class HibernateOrmClassPropertyModel<T>
extends AbstractPojoModelsPropertyModel<T, HibernateOrmBootstrapIntrospector> {
extends AbstractPojoModelsPropertyModel<T, HibernateOrmBootstrapIntrospector, ValueReadHandle<T>> {

private final HibernateOrmBasicClassPropertyMetadata ormPropertyMetadata;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.search.mapper.pojo.model.models.spi.AbstractPojoModelsRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext;
import org.hibernate.search.mapper.pojo.model.spi.PojoPropertyModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;

public class HibernateOrmClassRawTypeModel<T>
extends AbstractPojoModelsRawTypeModel<T, HibernateOrmBootstrapIntrospector> {
extends AbstractPojoModelsRawTypeModel<T, HibernateOrmBootstrapIntrospector, PojoPropertyModel<?>> {

private final HibernateOrmBasicClassTypeMetadata ormTypeMetadata;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
import org.hibernate.search.engine.mapper.model.spi.MappableTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.AbstractPojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoConstructorModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoPropertyModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel;

@SuppressWarnings("rawtypes")
public class HibernateOrmDynamicMapRawTypeModel
extends AbstractPojoRawTypeModel<Map, HibernateOrmBootstrapIntrospector> {
extends AbstractPojoRawTypeModel<Map, HibernateOrmBootstrapIntrospector, PojoPropertyModel<?>> {

private final HibernateOrmBasicDynamicMapTypeMetadata ormTypeMetadata;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import org.hibernate.search.engine.mapper.mapping.building.spi.IndexBindingContext;
import org.hibernate.search.mapper.pojo.bridge.PropertyBridge;
import org.hibernate.search.mapper.pojo.bridge.binding.PropertyBindingContext;
import org.hibernate.search.mapper.pojo.bridge.mapping.impl.BeanDelegatingBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
import org.hibernate.search.mapper.pojo.logging.impl.MappingLog;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.impl.PojoInjectableBinderInjector;
import org.hibernate.search.mapper.pojo.model.PojoModelProperty;
import org.hibernate.search.mapper.pojo.model.dependency.PojoPropertyIndexingDependencyConfigurationContext;
import org.hibernate.search.mapper.pojo.model.dependency.impl.PojoPropertyIndexingDependencyConfigurationContextImpl;
Expand All @@ -31,6 +33,7 @@ public class PropertyBindingContextImpl<P> extends AbstractCompositeBindingConte
implements PropertyBindingContext {

private final PojoBootstrapIntrospector introspector;
private final PojoInjectableBinderInjector binderInjector;
private final PojoTypeModel<?> propertyTypeModel;
private final PojoModelPropertyRootElement<P> bridgedElement;
private final PojoPropertyIndexingDependencyConfigurationContextImpl<P> dependencyContext;
Expand All @@ -42,13 +45,15 @@ public class PropertyBindingContextImpl<P> extends AbstractCompositeBindingConte

public PropertyBindingContextImpl(BeanResolver beanResolver,
PojoBootstrapIntrospector introspector,
PojoInjectableBinderInjector binderInjector,
PojoTypeModel<P> propertyTypeModel,
IndexBindingContext indexBindingContext,
PojoModelPropertyRootElement<P> bridgedElement,
PojoPropertyIndexingDependencyConfigurationContextImpl<P> dependencyContext,
Map<String, Object> params) {
super( beanResolver, params );
this.introspector = introspector;
this.binderInjector = binderInjector;
this.propertyTypeModel = propertyTypeModel;
this.bridgedElement = bridgedElement;
this.dependencyContext = dependencyContext;
Expand Down Expand Up @@ -90,6 +95,11 @@ public IndexSchemaElement indexSchemaElement() {
public Optional<BoundPropertyBridge<P>> applyBinder(PropertyBinder binder) {
try {
// This call should set the partial binding
// TODO: we probably should delay the injection of the binder properties till this point
// here we have access to the index schema element and can create the sub-elements we need.
if ( !BeanDelegatingBinder.class.equals( binder.getClass() ) ) {
injectBinderFields( binder );
}
binder.bind( this );
if ( partialBinding == null ) {
throw MappingLog.INSTANCE.missingBridgeForBinder( binder );
Expand Down Expand Up @@ -135,6 +145,13 @@ private <P2> void checkAndBind(BeanHolder<? extends PropertyBridge<P2>> bridgeHo
this.partialBinding = new PartialBinding<>( castedBridgeHolder );
}

// TODO: if users have their delegating binders, those won't get injected with fields ...
// we probably do not care as much about it as in that case we won't get anything out for the static model ...
// but just in case we think of some valid use case then maybe expose this method (or rather whatever it will grow into) through the context to the user.
public void injectBinderFields(PropertyBinder binder) {
binderInjector.injectFields( binder, indexSchemaElement, params() );
}

private static class PartialBinding<P> {
private final BeanHolder<? extends PropertyBridge<? super P>> bridgeHolder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.search.mapper.pojo.bridge.binding.RoutingBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.TypeBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.ValueBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.impl.PropertyBindingContextImpl;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.IdentifierBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.MarkerBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
Expand All @@ -29,9 +30,11 @@ public final class BeanDelegatingBinder
MarkerBinder, IdentifierBinder, ValueBinder {

private final BeanReference<?> delegateReference;
private final Class<?> type;

public BeanDelegatingBinder(BeanReference<?> delegateReference) {
public BeanDelegatingBinder(BeanReference<?> delegateReference, Class<?> type) {
this.delegateReference = delegateReference;
this.type = type;
}

@Override
Expand All @@ -51,7 +54,11 @@ public void bind(TypeBindingContext context) {
public void bind(PropertyBindingContext context) {
try ( BeanHolder<? extends PropertyBinder> delegateHolder =
createDelegate( context.beanResolver(), PropertyBinder.class ) ) {
delegateHolder.get().bind( context );
PropertyBinder binder = delegateHolder.get();
// TODO: injecting into this delegating binder itself will make no sense,
// and we need to inject into the delegate itself ...
( (PropertyBindingContextImpl) context ).injectBinderFields( binder );
binder.bind( context );
}
}

Expand Down Expand Up @@ -91,4 +98,7 @@ private <B> BeanHolder<? extends B> createDelegate(BeanResolver beanResolver, Cl
return delegateReference.asSubTypeOf( expectedType ).resolve( beanResolver );
}

public Class<?> getDelegateType() {
return type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,8 @@ SearchException usingNonDefaultValueConvertAndValueModelNotAllowed(String model,

@Message(id = ID_OFFSET + 170, value = "Property name '%1$s' cannot contain dots.")
IllegalArgumentException propertyNameCannotContainDots(String propertyName);

@Message(id = ID_OFFSET + 179, value = "Unable to retrieve injectable binder model for class '%1$s'.")
SearchException errorRetrievingInjectableBinderModel(@FormatWith(ClassFormatter.class) Class<?> clazz,
@Cause Exception cause);
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public interface PojoMapperLog
* here to the next value.
*/
@LogMessage(level = TRACE)
@Message(id = ID_OFFSET + 179, value = "")
@Message(id = ID_OFFSET + 180, value = "")
void nextLoggerIdForConvenience();

}
Loading

0 comments on commit 4e062a6

Please sign in to comment.