Skip to content
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

HSEARCH-5235 WIP: draft of injecting field references #4451

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
@@ -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();
Comment on lines +26 to +27
Copy link
Member

Choose a reason for hiding this comment

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

Interestingly, if we found a way to declare dependencies through annotations too... we could get rid of the binder altogether, and declare everything directly in the bridge.

In fact if we do that, I wonder if there's even any reason to keep binders when using annotations to configure the binding? They might only be useful for the old, very-dynamic API, which we'll probably want to deprecate anyway.


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,12 @@ 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);

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = ID_OFFSET + 180, value = "Unable to retrieve injectable binder model for class '%1$s': %2$s")
void cannotLoadClassDetailsButIgnore(@FormatWith(ClassFormatter.class) Class<?> binderType, String message);
}
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 + 181, value = "")
void nextLoggerIdForConvenience();

}
Loading
Loading