/** * Hibernate entities might be dirty (their state has changed), but none of these changes would * affect the index state. This method will return {@code true} if any of changed entity * properties identified by their names ({@code dirtyPropertyNames}) will effect the index state. * * @param dirtyPropertyNames array of property names for the changed entity properties, {@code * null} in case the changed properties cannot be specified. * @return {@code true} if the entity changes will effect the index state, {@code false} otherwise * @since 3.4 */ public boolean isDirty(String[] dirtyPropertyNames) { if (dirtyPropertyNames == null || dirtyPropertyNames.length == 0) { return true; // it appears some collection work has no oldState -> reindex } if (!stateInspectionOptimizationsEnabled()) { return true; } for (String dirtyPropertyName : dirtyPropertyNames) { PropertyMetadata propertyMetadata = typeMetadata.getPropertyMetadataForProperty(dirtyPropertyName); if (propertyMetadata != null) { // if there is a property metadata it means that there is at least one @Field. // Fields are either indexed or stored, so we need to re-index return true; } // consider IndexedEmbedded: for (EmbeddedTypeMetadata embeddedTypeMetadata : typeMetadata.getEmbeddedTypeMetadata()) { String name = embeddedTypeMetadata.getEmbeddedFieldName(); if (name.equals(dirtyPropertyName)) { return true; } } } return false; }
/** * If we have a work instance we have to check whether the instance to be indexed is contained in * any other indexed entities for a tenant. * * @param instance the instance to be indexed * @param workPlan the current work plan * @param currentRecursionContext the current {@link * org.hibernate.search.engine.spi.ContainedInRecursionContext} object used to check the graph * traversal * @param tenantIdentifier the identifier of the tenant or null, if there isn't one * @see #appendContainedInWorkForInstance(Object, WorkPlan, ContainedInRecursionContext) */ public void appendContainedInWorkForInstance( Object instance, WorkPlan workPlan, ContainedInRecursionContext currentRecursionContext, String tenantIdentifier) { for (ContainedInMetadata containedInMetadata : typeMetadata.getContainedInMetadata()) { XMember member = containedInMetadata.getContainedInMember(); Object unproxiedInstance = instanceInitializer.unproxy(instance); ContainedInRecursionContext recursionContext = updateContainedInRecursionContext( unproxiedInstance, containedInMetadata, currentRecursionContext); if (recursionContext.isTerminal()) { continue; } Object value = ReflectionHelper.getMemberValue(unproxiedInstance, member); if (value == null) { continue; } if (member.isArray()) { Object[] array = (Object[]) value; for (Object arrayValue : array) { processSingleContainedInInstance( workPlan, arrayValue, recursionContext, tenantIdentifier); } } else if (member.isCollection()) { Collection<?> collection = null; try { collection = getActualCollection(member, value); collection.size(); // load it } catch (Exception e) { if (e.getClass().getName().contains("org.hibernate.LazyInitializationException")) { /* A deleted entity not having its collection initialized * leads to a LIE because the collection is no longer attached to the session * * But that's ok as the collection update event has been processed before * or the fk would have been cleared and thus triggering the cleaning */ collection = null; } } if (collection != null) { for (Object collectionValue : collection) { processSingleContainedInInstance( workPlan, collectionValue, recursionContext, tenantIdentifier); } } } else { processSingleContainedInInstance(workPlan, value, recursionContext, tenantIdentifier); } } }
/** * Verifies entity level preconditions to know if it's safe to skip index updates based on * specific field or collection updates. * * @return true if it seems safe to apply such optimizations */ boolean stateInspectionOptimizationsEnabled() { if (!typeMetadata.areStateInspectionOptimizationsEnabled()) { return false; } if (typeMetadata.areClassBridgesUsed()) { log.tracef( "State inspection optimization disabled as entity %s uses class bridges", this.beanClass.getName()); return false; // can't know what a class bridge is going to look at -> reindex // TODO nice // new feature to have? } BoostStrategy boostStrategy = typeMetadata.getDynamicBoost(); if (boostStrategy != null && !(boostStrategy instanceof DefaultBoostStrategy)) { log.tracef( "State inspection optimization disabled as DynamicBoost is enabled on entity %s", this.beanClass.getName()); return false; // as with class bridge: might be affected by any field // TODO nice new feature // to have? } return true; }
/** * Returns {@code true} if the collection event is going to affect the index state. In this case * the indexing event can be ignored. {@code false} otherwise. * * @param collectionRoleName a {@link java.lang.String} object. * @return {@code true} if an update to the collection identified by the given role name effects * the index state, {@code false} otherwise. */ public boolean collectionChangeRequiresIndexUpdate(String collectionRoleName) { if (collectionRoleName == null) { // collection name will only be non null for PostCollectionUpdateEvents return true; } // don't check stateInspectionOptimizationsEnabled() as it might ignore depth limit: // it will disable optimization even if we have class bridges, but we're too deep // to be reachable. The evaluation of stateInspectionOptimizationsEnabled() was // actually stored in stateInspectionOptimizationsEnabled, but limiting to depth recursion. if (!typeMetadata.areStateInspectionOptimizationsEnabled()) { // if optimizations are not enabled we need to re-index return true; } return this.typeMetadata.containsCollectionRole(collectionRoleName); }
/** * Constructor. * * @param xClass The class for which to build a document builder * @param typeMetadata metadata for the specified class * @param reflectionManager Reflection manager to use for processing the annotations * @param optimizationBlackList keeps track of types on which we need to disable collection events * optimizations * @param instanceInitializer a {@link org.hibernate.search.spi.InstanceInitializer} object. */ public AbstractDocumentBuilder( XClass xClass, TypeMetadata typeMetadata, ReflectionManager reflectionManager, Set<XClass> optimizationBlackList, InstanceInitializer instanceInitializer) { if (xClass == null) { throw new AssertionFailure( "Unable to build a DocumentBuilderContainedEntity with a null class"); } this.instanceInitializer = instanceInitializer; this.entityState = EntityState.CONTAINED_IN_ONLY; this.beanXClass = xClass; this.beanClass = reflectionManager.toClass(xClass); this.typeMetadata = typeMetadata; optimizationBlackList.addAll(typeMetadata.getOptimizationBlackList()); }
/** Closes any resource */ public void close() { typeMetadata.getDefaultAnalyzerReference().close(); }
/** * Makes sure isCollectionRoleExcluded will always return false, so that collection update events * are always processed. * * @see #collectionChangeRequiresIndexUpdate(String) */ public void forceStateInspectionOptimizationsDisabled() { typeMetadata.disableStateInspectionOptimizations(); }
public ScopedAnalyzerReference getAnalyzerReference() { return typeMetadata.getDefaultAnalyzerReference(); }