Skip to content

Commit

Permalink
HSEARCH-4139 Use bitsets and ordinals in POJO path filters
Browse files Browse the repository at this point in the history
Signed-off-by: Yoann Rodière <yoann@hibernate.org>
  • Loading branch information
yrodiere committed Jan 22, 2021
1 parent 11e9ada commit 4fb5e31
Show file tree
Hide file tree
Showing 28 changed files with 309 additions and 236 deletions.
Expand Up @@ -6,7 +6,7 @@
*/
package org.hibernate.search.mapper.javabean.impl;

import org.hibernate.search.mapper.javabean.model.impl.JavaBeanSimpleStringSetPojoPathFilterFactory;
import org.hibernate.search.mapper.javabean.model.impl.JavaBeanPojoPathsDefinition;
import org.hibernate.search.mapper.pojo.mapping.building.spi.PojoMappingCollectorTypeNode;
import org.hibernate.search.mapper.pojo.mapping.building.spi.PojoTypeMetadataContributor;
import org.hibernate.search.mapper.pojo.model.additionalmetadata.building.spi.PojoAdditionalMetadataCollectorTypeNode;
Expand All @@ -29,7 +29,7 @@ public void contributeAdditionalMetadata(PojoAdditionalMetadataCollectorTypeNode
// Entity metadata is not inherited; only contribute it to the exact type.
return;
}
collector.markAsEntity( entityName, new JavaBeanSimpleStringSetPojoPathFilterFactory() );
collector.markAsEntity( entityName, new JavaBeanPojoPathsDefinition() );
}
catch (RuntimeException e) {
collector.failureCollector().add( e );
Expand Down
@@ -0,0 +1,36 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.mapper.javabean.model.impl;

import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.hibernate.search.mapper.pojo.model.path.PojoModelPathPropertyNode;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathsDefinition;

/**
* A factory for filters expecting a simple string representation of dirty paths,
* in the form "propertyA.propertyB.propertyC".
* <p>
* See {@link PojoModelPathPropertyNode#toPropertyString()}.
*/
public class JavaBeanPojoPathsDefinition implements PojoPathsDefinition {

@Override
public List<String> preDefinedOrdinals() {
return Collections.emptyList(); // No pre-defined ordinals
}

@Override
public void interpretPaths(Set<String> target, Set<PojoModelPathValueNode> source) {
for ( PojoModelPathValueNode path : source ) {
target.add( path.parent().toPropertyString() );
}
}
}

This file was deleted.

Expand Up @@ -9,7 +9,7 @@
import java.util.Optional;

import org.hibernate.mapping.PersistentClass;
import org.hibernate.search.mapper.orm.model.impl.HibernateOrmPathFilterFactory;
import org.hibernate.search.mapper.orm.model.impl.HibernateOrmPathsDefinition;
import org.hibernate.search.mapper.pojo.mapping.building.spi.PojoMappingCollectorTypeNode;
import org.hibernate.search.mapper.pojo.mapping.building.spi.PojoTypeMetadataContributor;
import org.hibernate.search.mapper.pojo.model.additionalmetadata.building.spi.PojoAdditionalMetadataCollectorTypeNode;
Expand All @@ -36,7 +36,7 @@ public void contributeAdditionalMetadata(PojoAdditionalMetadataCollectorTypeNode
}
collector.markAsEntity(
persistentClass.getJpaEntityName(),
new HibernateOrmPathFilterFactory( persistentClass )
new HibernateOrmPathsDefinition( persistentClass )
)
.entityIdPropertyName( identifierPropertyNameOptional.orElse( null ) );
}
Expand Down
Expand Up @@ -7,6 +7,7 @@
package org.hibernate.search.mapper.orm.model.impl;

import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
Expand All @@ -27,19 +28,20 @@
import org.hibernate.search.mapper.pojo.extractor.builtin.BuiltinContainerExtractors;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathPropertyNode;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.spi.StringSetPojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathsDefinition;
import org.hibernate.search.util.common.AssertionFailure;
import org.hibernate.search.util.common.SearchException;
import org.hibernate.search.util.common.impl.CollectionHelper;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

/**
* A {@link PojoPathFilterFactory} suitable for use with Hibernate ORM,
* A {@link PojoPathsDefinition} suitable for use with Hibernate ORM,
* in particular with its event system.
* <p>
* Paths passed to this factory are assigned a string representation as follows:
* Paths passed to this factory are assigned a string representation so as to match the property names
* and collection roles from Hibernate ORM.
* <p>
* Implementation is as follows:
* <ul>
* <li>
* If the whole path does not contain any multi-valued {@link Value}
Expand Down Expand Up @@ -143,7 +145,7 @@
* </li>
* </ul>
*/
public class HibernateOrmPathFilterFactory implements PojoPathFilterFactory {
public class HibernateOrmPathsDefinition implements PojoPathsDefinition {

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );
private static final Set<String> PRIMITIVE_EXTRACTOR_NAMES = CollectionHelper.asImmutableSet(
Expand All @@ -159,18 +161,26 @@ public class HibernateOrmPathFilterFactory implements PojoPathFilterFactory {

private final PersistentClass persistentClass;

public HibernateOrmPathFilterFactory(PersistentClass persistentClass) {
public HibernateOrmPathsDefinition(PersistentClass persistentClass) {
this.persistentClass = persistentClass;
}

@Override
public PojoPathFilter create(Set<PojoModelPathValueNode> paths) {
// Use a LinkedHashSet for deterministic iteration
Set<String> pathsAsStrings = CollectionHelper.newLinkedHashSet( paths.size() );
for ( PojoModelPathValueNode path : paths ) {
addDirtyPathStringRepresentations( pathsAsStrings, path );
@SuppressWarnings("unchecked")
public List<String> preDefinedOrdinals() {
List<String> preDefinedOrdinals = new ArrayList<>();
for ( Iterator<Property> iterator = persistentClass.getPropertyClosureIterator(); iterator.hasNext(); ) {
Property property = iterator.next();
preDefinedOrdinals.add( property.getName() );
}
return preDefinedOrdinals;
}

@Override
public void interpretPaths(Set<String> target, Set<PojoModelPathValueNode> source) {
for ( PojoModelPathValueNode path : source ) {
addDirtyPathStringRepresentations( target, path );
}
return new StringSetPojoPathFilter( pathsAsStrings );
}

private void addDirtyPathStringRepresentations(Set<String> pathsAsStrings, PojoModelPathValueNode path) {
Expand Down Expand Up @@ -222,7 +232,7 @@ private Optional<Value> resolveValueNode(Set<String> pathsAsStrings, PojoModelPa
if ( extractorPath.isDefault() ) {
throw new AssertionFailure(
"Expected a non-default extractor path as per the "
+ PojoPathFilterFactory.class.getSimpleName() + " contract"
+ HibernateOrmPathsDefinition.class.getSimpleName() + " contract"
);
}

Expand Down
Expand Up @@ -14,10 +14,10 @@
import org.hibernate.search.mapper.pojo.automaticindexing.impl.PojoImplicitReindexingResolverMultiNode;
import org.hibernate.search.mapper.pojo.automaticindexing.impl.PojoImplicitReindexingResolverNode;
import org.hibernate.search.mapper.pojo.automaticindexing.impl.PojoImplicitReindexingResolverDirtinessFilterNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPath;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilterProvider;
import org.hibernate.search.util.common.AssertionFailure;

abstract class AbstractPojoImplicitReindexingResolverNodeBuilder<T> {
Expand Down Expand Up @@ -76,11 +76,11 @@ final Set<PojoModelPathValueNode> getDirtyPathsTriggeringReindexingIncludingNest
}

/**
* @param pathFilterFactory A factory for path filters that will be used in the resolver (and its nested resolvers)
* @param pathFilterProvider A provider for path filters that will be used in the resolver (and its nested resolvers)
* @param allPotentialDirtyPaths A comprehensive list of all paths that may be dirty
* when the built resolver will be called. {@code null} if unknown.
*/
final Optional<PojoImplicitReindexingResolverNode<T>> build(PojoPathFilterFactory pathFilterFactory,
final Optional<PojoImplicitReindexingResolverNode<T>> build(PojoPathFilterProvider pathFilterProvider,
Set<PojoModelPathValueNode> allPotentialDirtyPaths) {
freeze();

Expand Down Expand Up @@ -109,10 +109,10 @@ final Optional<PojoImplicitReindexingResolverNode<T>> build(PojoPathFilterFactor
*
* Thus we need to filter out all the paths that are not tied to this node.
*/
result = doBuild( pathFilterFactory, immutableDirtyPathsAcceptedByFilter );
result = doBuild( pathFilterProvider, immutableDirtyPathsAcceptedByFilter );
if ( result.isPresent() ) {
result = Optional.of(
wrapWithFilter( result.get(), pathFilterFactory, immutableDirtyPathsAcceptedByFilter )
wrapWithFilter( result.get(), pathFilterProvider, immutableDirtyPathsAcceptedByFilter )
);
}
}
Expand All @@ -132,7 +132,7 @@ final Optional<PojoImplicitReindexingResolverNode<T>> build(PojoPathFilterFactor
* Thus we do not need to add our own dirty check: no filter node wrapping the node we are building
* is necessary.
*/
result = doBuild( pathFilterFactory, allPotentialDirtyPaths );
result = doBuild( pathFilterProvider, allPotentialDirtyPaths );
}

if ( !result.isPresent() ) {
Expand All @@ -143,13 +143,13 @@ final Optional<PojoImplicitReindexingResolverNode<T>> build(PojoPathFilterFactor
return result;
}

abstract Optional<PojoImplicitReindexingResolverNode<T>> doBuild(PojoPathFilterFactory pathFilterFactory,
abstract Optional<PojoImplicitReindexingResolverNode<T>> doBuild(PojoPathFilterProvider pathFilterProvider,
Set<PojoModelPathValueNode> allPotentialDirtyPaths);

private PojoImplicitReindexingResolverNode<T> wrapWithFilter(PojoImplicitReindexingResolverNode<T> resolver,
PojoPathFilterFactory pathFilterFactory,
PojoPathFilterProvider pathFilterProvider,
Set<PojoModelPathValueNode> immutableDirtyPathsTriggeringReindexing) {
PojoPathFilter filter = pathFilterFactory.create( immutableDirtyPathsTriggeringReindexing );
PojoPathFilter filter = pathFilterProvider.create( immutableDirtyPathsTriggeringReindexing );
return new PojoImplicitReindexingResolverDirtinessFilterNode<>(
filter, resolver
);
Expand Down
Expand Up @@ -17,7 +17,7 @@
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPathTypeNode;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilterProvider;
import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel;
import org.hibernate.search.util.common.impl.Closer;

Expand Down Expand Up @@ -78,15 +78,15 @@ void onFreeze(Set<PojoModelPathValueNode> dirtyPathsTriggeringReindexingCollecto
}

@Override
final Optional<PojoImplicitReindexingResolverNode<T>> doBuild(PojoPathFilterFactory pathFilterFactory,
final Optional<PojoImplicitReindexingResolverNode<T>> doBuild(PojoPathFilterProvider pathFilterProvider,
Set<PojoModelPathValueNode> allPotentialDirtyPaths) {
checkFrozen();

Collection<PojoImplicitReindexingResolverNode<? super U>> immutableNestedNodes = new ArrayList<>();
markingNodeBuilder.build( pathFilterFactory, allPotentialDirtyPaths )
markingNodeBuilder.build( pathFilterProvider, allPotentialDirtyPaths )
.ifPresent( immutableNestedNodes::add );
propertyNodeBuilders.values().stream()
.map( builder -> builder.build( pathFilterFactory, allPotentialDirtyPaths ) )
.map( builder -> builder.build( pathFilterProvider, allPotentialDirtyPaths ) )
.filter( Optional::isPresent )
.map( Optional::get )
.forEach( immutableNestedNodes::add );
Expand Down
Expand Up @@ -18,8 +18,8 @@
import org.hibernate.search.mapper.pojo.model.path.binding.impl.PojoModelPathWalker;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPath;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilterProvider;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel;
import org.hibernate.search.util.common.AssertionFailure;

Expand Down Expand Up @@ -65,15 +65,15 @@ PojoImplicitReindexingResolverOriginalTypeNodeBuilder<T> containingEntitiesResol
}

/**
* @param pathFilterFactory A factory for path filters that will be used in the resolver (and its nested resolvers)
* @param pathFilterProvider A provider for path filters that will be used in the resolver (and its nested resolvers)
*/
final Optional<PojoImplicitReindexingResolver<T>> build(PojoPathFilterFactory pathFilterFactory) {
final Optional<PojoImplicitReindexingResolver<T>> build(PojoPathFilterProvider pathFilterProvider) {
freeze();

Set<PojoModelPathValueNode> immutableDirtyPathsAcceptedByFilter = dirtyPathsTriggeringSelfReindexing;

Optional<PojoImplicitReindexingResolverNode<T>> containingEntitiesResolverRootOptional =
containingEntitiesResolverRootBuilder.build( pathFilterFactory, null );
containingEntitiesResolverRootBuilder.build( pathFilterProvider, null );

if ( immutableDirtyPathsAcceptedByFilter.isEmpty() && !containingEntitiesResolverRootOptional.isPresent() ) {
/*
Expand All @@ -82,8 +82,7 @@ final Optional<PojoImplicitReindexingResolver<T>> build(PojoPathFilterFactory pa
return Optional.empty();
}
else {
PojoPathFilter filter = immutableDirtyPathsAcceptedByFilter.isEmpty()
? PojoPathFilter.empty() : pathFilterFactory.create( immutableDirtyPathsAcceptedByFilter );
PojoPathFilter filter = pathFilterProvider.create( immutableDirtyPathsAcceptedByFilter );
PojoImplicitReindexingResolverNode<T> containingEntitiesResolverRoot =
containingEntitiesResolverRootOptional.orElseGet( PojoImplicitReindexingResolverNode::noOp );

Expand Down
Expand Up @@ -24,8 +24,8 @@
import org.hibernate.search.mapper.pojo.model.additionalmetadata.building.impl.PojoTypeAdditionalMetadataProvider;
import org.hibernate.search.mapper.pojo.model.additionalmetadata.impl.PojoTypeAdditionalMetadata;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilter;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilterProvider;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathsDefinition;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel;
import org.hibernate.search.util.common.impl.Closer;
Expand Down Expand Up @@ -85,23 +85,28 @@ public void closeOnFailure() {
}

public <T> PojoImplicitReindexingResolver<T> build(PojoRawTypeModel<T> typeModel,
PojoPathFilterFactory pathFilterFactory) {
return buildOptional( typeModel, pathFilterFactory )
.orElseGet( () -> new PojoImplicitReindexingResolverImpl<>(
PojoPathFilter.empty(), PojoImplicitReindexingResolverNode.noOp()
) );
PojoPathsDefinition pathsDefinition) {
return buildOptional( typeModel, pathsDefinition )
.orElseGet( () -> {
PojoPathFilterProvider pathFilterProvider = new PojoPathFilterProvider( pathsDefinition );
return new PojoImplicitReindexingResolverImpl<>(
pathFilterProvider.create( Collections.emptySet() ),
PojoImplicitReindexingResolverNode.noOp()
);
} );
}

public <T> Optional<PojoImplicitReindexingResolver<T>> buildOptional(PojoRawTypeModel<T> typeModel,
PojoPathFilterFactory pathFilterFactory) {
PojoPathsDefinition pathsDefinition) {
@SuppressWarnings("unchecked") // We know builders have this type, by construction
PojoImplicitReindexingResolverBuilder<T> builder =
(PojoImplicitReindexingResolverBuilder<T>) builderByType.get( typeModel );
if ( builder == null ) {
return Optional.empty();
}
else {
return builder.build( pathFilterFactory );
PojoPathFilterProvider pathFilterProvider = new PojoPathFilterProvider( pathsDefinition );
return builder.build( pathFilterProvider );
}
}

Expand Down
Expand Up @@ -15,7 +15,7 @@
import org.hibernate.search.mapper.pojo.extractor.impl.ContainerExtractorHolder;
import org.hibernate.search.mapper.pojo.model.path.PojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.impl.BoundPojoModelPathValueNode;
import org.hibernate.search.mapper.pojo.model.path.spi.PojoPathFilterFactory;
import org.hibernate.search.mapper.pojo.model.path.impl.PojoPathFilterProvider;
import org.hibernate.search.util.common.impl.Closer;

class PojoImplicitReindexingResolverContainerElementNodeBuilder<C, V>
Expand Down Expand Up @@ -58,12 +58,12 @@ void onFreeze(Set<PojoModelPathValueNode> dirtyPathsTriggeringReindexingCollecto
}

@Override
Optional<PojoImplicitReindexingResolverNode<C>> doBuild(PojoPathFilterFactory pathFilterFactory,
Optional<PojoImplicitReindexingResolverNode<C>> doBuild(PojoPathFilterProvider pathFilterProvider,
Set<PojoModelPathValueNode> allPotentialDirtyPaths) {
checkFrozen();

Collection<PojoImplicitReindexingResolverNode<V>> valueTypeNodes =
valueBuilderDelegate.buildTypeNodes( pathFilterFactory, allPotentialDirtyPaths );
valueBuilderDelegate.buildTypeNodes( pathFilterProvider, allPotentialDirtyPaths );

if ( valueTypeNodes.isEmpty() ) {
/*
Expand Down

0 comments on commit 4fb5e31

Please sign in to comment.