Skip to content

Commit

Permalink
Merge pull request #664 from FgForrest/661-the-hierarchy-exclusion-pr…
Browse files Browse the repository at this point in the history
…edicate-can-be-set-only-once

fix(#661): the hierarchy exclusion predicate can be set only once
  • Loading branch information
novoj authored Sep 6, 2024
2 parents e589a4b + 1694af1 commit c596a9a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,10 @@ public void setRootHierarchyNodesFormula(@Nonnull Formula rootHierarchyNodesForm
* Sets resolved hierarchy having/exclusion predicate to be shared among filter and requirement phase.
*/
public void setHierarchyHavingPredicate(@Nonnull HierarchyFilteringPredicate hierarchyHavingPredicate) {
Assert.isPremiseValid(this.hierarchyHavingPredicate == null, "The hierarchy exclusion predicate can be set only once!");
Assert.isPremiseValid(
this.hierarchyHavingPredicate == null || this.hierarchyHavingPredicate.equals(hierarchyHavingPredicate),
"The hierarchy exclusion predicate can be set only once!"
);
this.hierarchyHavingPredicate = hierarchyHavingPredicate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public abstract class AbstractHierarchyTranslator<T extends FilterConstraint> im
*/
@Nullable
protected static HierarchyFilteringPredicate createAndStoreHavingPredicate(
@Nullable Integer parentId,
@Nullable int[] parentId,
@Nonnull QueryPlanningContext queryContext,
@Nullable FilterBy havingFilter,
@Nullable FilterBy exclusionFilter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.StreamSupport;

import static io.evitadb.api.query.QueryConstraints.entityLocaleEquals;
import static io.evitadb.api.query.QueryConstraints.filterBy;
Expand Down Expand Up @@ -107,30 +107,27 @@ public static Formula createFormulaFromHierarchyIndex(

queryContext.setRootHierarchyNodesFormula(hierarchyParentFormula);

final int[] nodeIds = hierarchyParentFormula.compute().stream().toArray();
return FormulaFactory.or(
StreamSupport.stream(hierarchyParentFormula.compute().spliterator(), false)
.map(
nodeId -> createFormulaFromHierarchyIndex(
nodeId,
createAndStoreHavingPredicate(
nodeId,
queryContext,
of(new FilterBy(hierarchyWithin.getHavingChildrenFilter()))
.filter(ConstraintContainer::isApplicable)
.orElse(null),
of(new FilterBy(hierarchyWithin.getExcludedChildrenFilter()))
.filter(ConstraintContainer::isApplicable)
.orElse(null),
referenceSchema
),
hierarchyWithin.isDirectRelation(),
hierarchyWithin.isExcludingRoot(),
targetEntitySchema,
targetEntityIndex,
queryContext
)
)
.toArray(Formula[]::new)
createFormulaFromHierarchyIndex(
nodeIds,
createAndStoreHavingPredicate(
nodeIds,
queryContext,
of(new FilterBy(hierarchyWithin.getHavingChildrenFilter()))
.filter(ConstraintContainer::isApplicable)
.orElse(null),
of(new FilterBy(hierarchyWithin.getExcludedChildrenFilter()))
.filter(ConstraintContainer::isApplicable)
.orElse(null),
referenceSchema
),
hierarchyWithin.isDirectRelation(),
hierarchyWithin.isExcludingRoot(),
targetEntitySchema,
targetEntityIndex,
queryContext
)
);
}
))
Expand All @@ -145,7 +142,7 @@ private static FilterBy createFilter(QueryPlanningContext queryContext, FilterCo
}

private static Formula createFormulaFromHierarchyIndex(
int parentId,
@Nonnull int[] parentIds,
@Nullable HierarchyFilteringPredicate excludedChildren,
boolean directRelation,
boolean excludingRoot,
Expand All @@ -157,26 +154,54 @@ private static Formula createFormulaFromHierarchyIndex(
// if the hierarchy entity is the same as queried entity
if (Objects.equals(queryContext.getSchema().getName(), targetEntitySchema.getName())) {
if (excludedChildren == null) {
return entityIndex.getHierarchyNodesForParentFormula(parentId);
return FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
entityIndex::getHierarchyNodesForParentFormula
).toArray(Formula[]::new)
);
} else {
return entityIndex.getHierarchyNodesForParentFormula(parentId, excludedChildren);
return FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
parentId -> entityIndex.getHierarchyNodesForParentFormula(parentId, excludedChildren)
).toArray(Formula[]::new)
);
}
} else {
if (excludedChildren == null) {
return new ConstantFormula(new BaseBitmap(parentId));
return new ConstantFormula(new BaseBitmap(parentIds));
} else {
return excludedChildren.test(parentId) ? EmptyFormula.INSTANCE : new ConstantFormula(new BaseBitmap(parentId));
final int[] filteredParents = Arrays.stream(parentIds)
.filter(excludedChildren::test)
.toArray();
return filteredParents.length == 0 ?
EmptyFormula.INSTANCE : new ConstantFormula(new BaseBitmap(filteredParents));
}
}
} else {
if (excludedChildren == null) {
return excludingRoot ?
entityIndex.getListHierarchyNodesFromParentFormula(parentId) :
entityIndex.getListHierarchyNodesFromParentIncludingItselfFormula(parentId);
FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
entityIndex::getListHierarchyNodesFromParentFormula
).toArray(Formula[]::new)
) :
FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
entityIndex::getListHierarchyNodesFromParentIncludingItselfFormula
).toArray(Formula[]::new)
);
} else {
return excludingRoot ?
entityIndex.getListHierarchyNodesFromParentFormula(parentId, excludedChildren) :
entityIndex.getListHierarchyNodesFromParentIncludingItselfFormula(parentId, excludedChildren);
FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
parentId -> entityIndex.getListHierarchyNodesFromParentFormula(parentId, excludedChildren)
).toArray(Formula[]::new)
) :
FormulaFactory.or(
Arrays.stream(parentIds).mapToObj(
parentId -> entityIndex.getListHierarchyNodesFromParentIncludingItselfFormula(parentId, excludedChildren)
).toArray(Formula[]::new)
);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Stream;

Expand All @@ -60,7 +60,7 @@ public class FilteringFormulaHierarchyEntityPredicate implements HierarchyFilter
/**
* Identification of the root node this predicate relates to.
*/
@Nullable private final Integer parent;
@Nullable private final int[] parent;
/**
* The result that should be returned for parent node (constant).
*/
Expand Down Expand Up @@ -100,7 +100,7 @@ public class FilteringFormulaHierarchyEntityPredicate implements HierarchyFilter
* @param referenceSchema the optional reference schema if the entity targets itself hierarchy tree
*/
public FilteringFormulaHierarchyEntityPredicate(
@Nullable Integer parent,
@Nullable int[] parent,
boolean parentResult,
@Nonnull QueryPlanningContext queryContext,
@Nonnull FilterBy filterBy,
Expand Down Expand Up @@ -287,7 +287,7 @@ public boolean test(int hierarchyNodeId, int level, int distance) {

@Override
public void traverse(int hierarchyNodeId, int level, int distance, @Nonnull Runnable traverser) {
if (Objects.equals(hierarchyNodeId, parent)) {
if (parent != null && Arrays.stream(parent).anyMatch(it -> it == hierarchyNodeId)) {
traverser.run();
} else if (filteringFormula.compute().contains(hierarchyNodeId)) {
try {
Expand All @@ -303,16 +303,34 @@ public void traverse(int hierarchyNodeId, int level, int distance, @Nonnull Runn

@Override
public boolean test(int hierarchyNodeId) {
if (Objects.equals(hierarchyNodeId, parent)) {
if (parent != null && Arrays.stream(parent).anyMatch(it -> it == hierarchyNodeId)) {
return parentResult;
}
return filteringFormula.compute().contains(hierarchyNodeId);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

FilteringFormulaHierarchyEntityPredicate that = (FilteringFormulaHierarchyEntityPredicate) o;
return parentResult == that.parentResult && Arrays.equals(parent, that.parent) && filterBy.equals(that.filterBy) && filteringFormula.equals(that.filteringFormula);
}

@Override
public int hashCode() {
int result = Arrays.hashCode(parent);
result = 31 * result + Boolean.hashCode(parentResult);
result = 31 * result + filterBy.hashCode();
result = 31 * result + filteringFormula.hashCode();
return result;
}

@Override
public String toString() {
return "HIERARCHY (" +
"parent=" + parent +
"parent=" + Arrays.toString(parent) +
", filterBy=" + filterBy +
", filteringFormula=" + filteringFormula +
')';
Expand Down

0 comments on commit c596a9a

Please sign in to comment.