
org.neo4j.graphdb.traversal.Evaluators Maven / Gradle / Ivy
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.graphdb.traversal;
import static java.util.Arrays.asList;
import static org.neo4j.graphdb.traversal.Evaluation.INCLUDE_AND_CONTINUE;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.annotations.api.PublicApi;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
/**
* Common {@link Evaluator}s useful during common traversals.
*
* @see Evaluator
* @see TraversalDescription
*/
@PublicApi
public abstract class Evaluators {
@SuppressWarnings("rawtypes")
private static final PathEvaluator> ALL = new PathEvaluator.Adapter() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
return INCLUDE_AND_CONTINUE;
}
};
private static final PathEvaluator ALL_BUT_START_POSITION = fromDepth(1);
/**
* @param the type of the state object.
* @return an evaluator which includes everything it encounters and doesn't prune
* anything.
*/
public static PathEvaluator all() {
//noinspection unchecked
return (PathEvaluator) ALL;
}
/**
* @return an evaluator which never prunes and includes everything except
* the first position, i.e. the the start node.
*/
public static PathEvaluator excludeStartPosition() {
return ALL_BUT_START_POSITION;
}
/**
* Returns an {@link Evaluator} which includes positions down to {@code depth}
* and prunes everything deeper than that.
*
* @param depth the max depth to traverse to.
* @param the type of the state object.
* @return Returns an {@link Evaluator} which includes positions down to
* {@code depth} and prunes everything deeper than that.
*/
public static PathEvaluator toDepth(final int depth) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
int pathLength = path.length();
return Evaluation.of(pathLength <= depth, pathLength < depth);
}
};
}
/**
* Returns an {@link Evaluator} which only includes positions from {@code depth}
* and deeper and never prunes anything.
*
* @param depth the depth to start include positions from.
* @param the type of the state object.
* @return Returns an {@link Evaluator} which only includes positions from
* {@code depth} and deeper and never prunes anything.
*/
public static PathEvaluator fromDepth(final int depth) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
return Evaluation.ofIncludes(path.length() >= depth);
}
};
}
/**
* Returns an {@link Evaluator} which only includes positions at {@code depth}
* and prunes everything deeper than that.
*
* @param depth the depth to start include positions from.
* @param the type of the state object.
* @return Returns an {@link Evaluator} which only includes positions at
* {@code depth} and prunes everything deeper than that.
*/
public static PathEvaluator atDepth(final int depth) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
return path.length() == depth ? Evaluation.INCLUDE_AND_PRUNE : Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
/**
* Returns an {@link Evaluator} which only includes positions between
* depths {@code minDepth} and {@code maxDepth}. It prunes everything deeper
* than {@code maxDepth}.
*
* @param minDepth minimum depth a position must have to be included.
* @param maxDepth maximum depth a position must have to be included.
* @param the type of the state object.
* @return Returns an {@link Evaluator} which only includes positions between
* depths {@code minDepth} and {@code maxDepth}. It prunes everything deeper
* than {@code maxDepth}.
*/
public static PathEvaluator includingDepths(final int minDepth, final int maxDepth) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
int length = path.length();
return Evaluation.of(length >= minDepth && length <= maxDepth, length < maxDepth);
}
};
}
/**
* Returns an {@link Evaluator} which compares the type of the last relationship
* in a {@link Path} to a given set of relationship types (one or more).If the type of
* the last relationship in a path is of one of the given types then
* {@code evaluationIfMatch} will be returned, otherwise
* {@code evaluationIfNoMatch} will be returned.
*
* @param evaluationIfMatch the {@link Evaluation} to return if the type of the
* last relationship in the path matches any of the given types.
* @param evaluationIfNoMatch the {@link Evaluation} to return if the type of the
* last relationship in the path doesn't match any of the given types.
* @param type the (first) type (of possibly many) to match the last relationship
* in paths with.
* @param orAnyOfTheseTypes additional types to match the last relationship in
* paths with.
* @param the type of the state object.
* @return an {@link Evaluator} which compares the type of the last relationship
* in a {@link Path} to a given set of relationship types.
*/
public static PathEvaluator lastRelationshipTypeIs(
final Evaluation evaluationIfMatch,
final Evaluation evaluationIfNoMatch,
final RelationshipType type,
RelationshipType... orAnyOfTheseTypes) {
if (orAnyOfTheseTypes.length == 0) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
Relationship rel = path.lastRelationship();
return rel != null && rel.isType(type) ? evaluationIfMatch : evaluationIfNoMatch;
}
};
}
final Set expectedTypes = new HashSet<>();
expectedTypes.add(type.name());
for (RelationshipType otherType : orAnyOfTheseTypes) {
expectedTypes.add(otherType.name());
}
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
Relationship lastRelationship = path.lastRelationship();
if (lastRelationship == null) {
return evaluationIfNoMatch;
}
return expectedTypes.contains(lastRelationship.getType().name())
? evaluationIfMatch
: evaluationIfNoMatch;
}
};
}
/**
* @param type the (first) type (of possibly many) to match the last relationship
* in paths with.
* @param orAnyOfTheseTypes types to match the last relationship in paths with. If any matches
* it's considered a match.
* @param the type of the state object.
* @return an {@link Evaluator} which compares the type of the last relationship
* in a {@link Path} to a given set of relationship types.
* @see #lastRelationshipTypeIs(Evaluation, Evaluation, RelationshipType, RelationshipType...)
* Uses {@link Evaluation#INCLUDE_AND_CONTINUE} for {@code evaluationIfMatch}
* and {@link Evaluation#EXCLUDE_AND_CONTINUE} for {@code evaluationIfNoMatch}.
*/
public static PathEvaluator includeWhereLastRelationshipTypeIs(
RelationshipType type, RelationshipType... orAnyOfTheseTypes) {
return lastRelationshipTypeIs(
Evaluation.INCLUDE_AND_CONTINUE, Evaluation.EXCLUDE_AND_CONTINUE, type, orAnyOfTheseTypes);
}
/**
* @param type the (first) type (of possibly many) to match the last relationship
* in paths with.
* @param orAnyOfTheseTypes types to match the last relationship in paths with. If any matches
* it's considered a match.
* @param the type of the state object.
* @return an {@link Evaluator} which compares the type of the last relationship
* in a {@link Path} to a given set of relationship types.
* @see #lastRelationshipTypeIs(Evaluation, Evaluation, RelationshipType, RelationshipType...)
* Uses {@link Evaluation#INCLUDE_AND_PRUNE} for {@code evaluationIfMatch}
* and {@link Evaluation#INCLUDE_AND_CONTINUE} for {@code evaluationIfNoMatch}.
*/
public static PathEvaluator pruneWhereLastRelationshipTypeIs(
RelationshipType type, RelationshipType... orAnyOfTheseTypes) {
return lastRelationshipTypeIs(
Evaluation.INCLUDE_AND_PRUNE, Evaluation.EXCLUDE_AND_CONTINUE, type, orAnyOfTheseTypes);
}
/**
* An {@link Evaluator} which will return {@code evaluationIfMatch} if {@link Path#endNode()}
* for a given path is any of {@code nodes}, else {@code evaluationIfNoMatch}.
*
* @param evaluationIfMatch the {@link Evaluation} to return if the {@link Path#endNode()}
* is any of the nodes in {@code nodes}.
* @param evaluationIfNoMatch the {@link Evaluation} to return if the {@link Path#endNode()}
* doesn't match any of the nodes in {@code nodes}.
* @param possibleEndNodes a set of nodes to match to end nodes in paths.
* @param the type of the state object.
* @return an {@link Evaluator} which will return {@code evaluationIfMatch} if
* {@link Path#endNode()} for a given path is any of {@code nodes},
* else {@code evaluationIfNoMatch}.
*/
public static PathEvaluator endNodeIs(
final Evaluation evaluationIfMatch, final Evaluation evaluationIfNoMatch, Node... possibleEndNodes) {
if (possibleEndNodes.length == 1) {
final Node target = possibleEndNodes[0];
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
return target.equals(path.endNode()) ? evaluationIfMatch : evaluationIfNoMatch;
}
};
}
final Set endNodes = new HashSet<>(asList(possibleEndNodes));
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
return endNodes.contains(path.endNode()) ? evaluationIfMatch : evaluationIfNoMatch;
}
};
}
/**
* Include paths with the specified end nodes.
*
* Uses Evaluators#endNodeIs(Evaluation, Evaluation, Node...) with
* {@link Evaluation#INCLUDE_AND_CONTINUE} for {@code evaluationIfMatch} and
* {@link Evaluation#EXCLUDE_AND_CONTINUE} for {@code evaluationIfNoMatch}.
*
* @param nodes end nodes for paths to be included in the result.
* @param the type of the state object.
* @return paths where the end node is one of {@code nodes}
*/
public static PathEvaluator includeWhereEndNodeIs(Node... nodes) {
return endNodeIs(Evaluation.INCLUDE_AND_CONTINUE, Evaluation.EXCLUDE_AND_CONTINUE, nodes);
}
public static PathEvaluator pruneWhereEndNodeIs(Node... nodes) {
return endNodeIs(Evaluation.INCLUDE_AND_PRUNE, Evaluation.EXCLUDE_AND_CONTINUE, nodes);
}
/**
* Evaluator which decides to include a {@link Path} if all the
* {@code nodes} exist in it.
*
* @param nodes {@link Node}s that must exist in a {@link Path} for it
* to be included.
* @param the type of the state object.
* @return {@link Evaluation#INCLUDE_AND_CONTINUE} if all {@code nodes}
* exist in a given {@link Path}, otherwise
* {@link Evaluation#EXCLUDE_AND_CONTINUE}.
*/
public static PathEvaluator includeIfContainsAll(final Node... nodes) {
if (nodes.length == 1) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
for (Node node : path.reverseNodes()) {
if (node.equals(nodes[0])) {
return Evaluation.INCLUDE_AND_CONTINUE;
}
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
final Set fullSet = new HashSet<>(asList(nodes));
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
Set set = new HashSet<>(fullSet);
for (Node node : path.reverseNodes()) {
if (set.remove(node) && set.isEmpty()) {
return Evaluation.INCLUDE_AND_CONTINUE;
}
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
/**
* Whereas adding {@link Evaluator}s to a {@link TraversalDescription} puts those
* evaluators in {@code AND-mode} this can group many evaluators in {@code OR-mode}.
*
* @param evaluators represented as one evaluators. If any of the evaluators decides
* to include a path it will be included.
* @param the type of the state object.
* @return an {@link Evaluator} which decides to include a path if any of the supplied
* evaluators wants to include it.
*/
public static PathEvaluator includeIfAcceptedByAny(final PathEvaluator... evaluators) {
return new PathEvaluator.Adapter<>() {
@SuppressWarnings("unchecked")
@Override
public Evaluation evaluate(Path path, BranchState state) {
for (PathEvaluator evaluator : evaluators) {
if (evaluator.evaluate(path, state).includes()) {
return Evaluation.INCLUDE_AND_CONTINUE;
}
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
/**
* Whereas adding {@link Evaluator}s to a {@link TraversalDescription} puts those
* evaluators in {@code AND-mode} this can group many evaluators in {@code OR-mode}.
*
* @param evaluators represented as one evaluators. If any of the evaluators decides
* to include a path it will be included.
* @param the type of the state object.
* @return an {@link Evaluator} which decides to include a path if any of the supplied
* evaluators wants to include it.
*/
public static PathEvaluator includeIfAcceptedByAny(final Evaluator... evaluators) {
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
for (Evaluator evaluator : evaluators) {
if (evaluator.evaluate(path).includes()) {
return Evaluation.INCLUDE_AND_CONTINUE;
}
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
/**
* Returns {@link Evaluator}s for paths with the specified depth and with an end node from the list of
* possibleEndNodes.
* @param depth The exact depth to filter the returned path evaluators.
* @param possibleEndNodes Filter for the possible nodes to end the path on.
* @param the type of the state object.
* @return {@link Evaluator}s for paths with the specified depth and with an end node from the list of
* possibleEndNodes.
*/
public static PathEvaluator endNodeIsAtDepth(final int depth, Node... possibleEndNodes) {
if (possibleEndNodes.length == 1) {
final Node target = possibleEndNodes[0];
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
if (path.length() == depth) {
return target.equals(path.endNode())
? Evaluation.INCLUDE_AND_PRUNE
: Evaluation.EXCLUDE_AND_PRUNE;
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
final Set endNodes = new HashSet<>(asList(possibleEndNodes));
return new PathEvaluator.Adapter<>() {
@Override
public Evaluation evaluate(Path path, BranchState state) {
if (path.length() == depth) {
return endNodes.contains(path.endNode())
? Evaluation.INCLUDE_AND_PRUNE
: Evaluation.EXCLUDE_AND_PRUNE;
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy