编写TraversalDescription以排除具有特定属性的边

时间:2015-06-10 21:21:27

标签: neo4j

我想编写一个可以与Walker一起使用的TraversallDescription。我的最终目标是将Walker传递给GraphvizWriter,这样我就可以得到.dot格式的遍历结果。

在我的图表中,我只有一种类型的节点,只有一种类型的关系。每个关系都有一个名为“inCommon”的属性,它具有数值。我只想要在它们之间的关系上具有inCommon> = 15的节点。

以下是我的traversalDescription的样子:

traversalDescription = graphDb.traversalDescription();
traversalDescription = traversalDescription.breadthFirst();
traversalDescription = traversalDescription.evaluator(pathEvaluator);
traversalDescription = traversalDescription.relationships(type, Direction.BOTH);

pathEvaluator是我写的一个类的实例,它试图只返回我想要的边。

public class ReferentEvaluator implements PathEvaluator {
    @Override 
    public Evaluation evaluate(final Path path) {
        if(path!=null){
            Iterator iter=path.relationships().iterator();
            while(iter.hasNext()){
                Relationship r=(Relationship)iter.next();
                if(r!=null){
                    Integer inCommon=(Integer)r.getProperty(r.getPropertyKeys().iterator().next());
                    if(inCommon.intValue() < 15){
                        return Evaluation.EXCLUDE_AND_CONTINUE;
                    }
                }
                else{
                    return Evaluation.EXCLUDE_AND_CONTINUE;
                }
            }
            return Evaluation.INCLUDE_AND_CONTINUE;
        }
    return Evaluation.EXCLUDE_AND_CONTINUE;
    }
}

我所看到的是,这不起作用。与inCommon的关系&lt;包含15个。实际上,这种方法似乎根本不起作用。我完全错误地采取了这种方式吗?或者我在这里做什么接近?

2 个答案:

答案 0 :(得分:3)

我认为在PathExpander中实现关系过滤器比在Evaluator中更好。原因是您可以更早地决定跳过哪些路径,而不需要加载节点并在以后过滤掉。

而不是relationships()您提供自定义PathExpanderPathExpander过滤给定类型与谓词的所有关系。谓词检查inCommon是否为&lt; 15。

由于我们在扩展器中完成了所有脏工作,因此我们可以使用INCLUDE_AND_CONTINUE - Evaluators.all()回复的评估程序来执行此操作。

这是我未经测试的代码段:

import org.neo4j.helpers.Predicate;
import org.neo4j.helpers.collection.Iterables;
import ...

public void traversalSample() {

    TraversalDescription td = graphDatabaseService
            .traversalDescription()
            .expand(new RelationshipPropertyFilterExpander(
                    DynamicRelationshipType.withName("KNOWS"), Direction.BOTH, 
                    "inCommon", 15)
            )
            .evaluator(Evaluators.all());
    td.traverse(.....);
}

class RelationshipPropertyFilterExpander implements PathExpander<Object> {
    public RelationshipType type;
    public Direction direction;
    public String propName;
    int threshold;

    public RelationshipPropertyFilterExpander(RelationshipType type, Direction direction, 
                                              String propName, int threshold) {
        this.type = type;
        this.direction = direction;
        this.propName = propName;
        this.threshold = threshold;
    }

    @Override
    public Iterable<Relationship> expand(Path path, BranchState<Object> state) {
        Node endNode = path.endNode();
        Predicate<? super Relationship> predicate = new Predicate<Relationship>() {
            @Override
            public boolean accept(Relationship relationship) {
                // if relationship property is not set, we assume 0
                return Integer.parseInt(relationship.getProperty(propName, "0").toString()) < threshold;
            }
        };
        return Iterables.filter(predicate, endNode.getRelationships(type, direction));
    }

    @Override
    public PathExpander<Object> reverse() {
        // gets never called in a unidirectional traversal
        throw new UnsupportedOperationException();
    }
}

答案 1 :(得分:2)

关于遍历框架要理解的重要一点是它在路径上运行。评估程序始终获取从起始节点到图形中当前位置的整个路径。因此,要实现的第二个重要的事情是路径之间的区别状态变化是它们的结束节点。您的评估者将在每次调用evaluate()时获得以新节点结尾的路径(有时取决于您对遍历的其他限制,请参阅唯一性等。)

这里重要的是你的评估者总是迭代遍历当前路径的所有边缘,并且尽快,因为它发现了{{1>的边缘它将停止虽然稍后在路径中仍然可以存在您感兴趣的节点。

但是,作为一个注释,我看不出结果会如何包含不需要的节点;在我的测试中,评估者会正确地拒绝不需要的节点,但是错过了想要的节点。

请参阅此代码,在我的脑海里做你想要的。它源于Neo4j遍历文档(http://neo4j.com/docs/stable/tutorial-traversal-java-api.html#_pathexpander_relationshipexpander),使理解更容易。我刚刚添加了&#39; Nici&#39;有更多的实验空间和展示算法正常工作:

Who-knows-who

以下是代码:

inCommon < 15

}

输出:

public class Neo4jTest {

public static enum EdgeTypes implements RelationshipType {
    KNOWS
}

private static final String PROP_COMMON = "inCommon";
private static final String PROP_NAME = "name";

public static void main(String[] args) {
    GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase("src/main/resources/graph.db");
    try (Transaction tx = graphDb.beginTx()) {
        Node lars = graphDb.createNode();
        Node sara = graphDb.createNode();
        Node ed = graphDb.createNode();
        Node lisa = graphDb.createNode();
        Node peter = graphDb.createNode();
        Node dirk = graphDb.createNode();
        Node joe = graphDb.createNode();
        Node nici = graphDb.createNode();

        lars.setProperty(PROP_NAME, "Lars");
        sara.setProperty(PROP_NAME, "Sara");
        ed.setProperty(PROP_NAME, "Ed");
        lisa.setProperty(PROP_NAME, "Lisa");
        peter.setProperty(PROP_NAME, "Peter");
        dirk.setProperty(PROP_NAME, "Dirk");
        joe.setProperty(PROP_NAME, "Joe");
        nici.setProperty(PROP_NAME, "Nici");

        joe.createRelationshipTo(sara, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 20);
        lisa.createRelationshipTo(joe, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 7);
        peter.createRelationshipTo(sara, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 14);
        dirk.createRelationshipTo(peter, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 19);
        lars.createRelationshipTo(dirk, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 8);
        ed.createRelationshipTo(lars, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 12);
        lisa.createRelationshipTo(lars, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 3);
        nici.createRelationshipTo(lars, EdgeTypes.KNOWS).setProperty(PROP_COMMON, 23);

        ReferentEvaluator pathEvaluator = new ReferentEvaluator();
        EdgeTypes type = EdgeTypes.KNOWS;
        TraversalDescription traversalDescription = graphDb.traversalDescription();
        traversalDescription = traversalDescription.breadthFirst();
        traversalDescription = traversalDescription.evaluator(pathEvaluator);
        traversalDescription = traversalDescription.relationships(type, Direction.BOTH);

        String output = "";
        String names = "";
        for (Path position : traversalDescription.traverse(joe)) {
            output += position + "\n";

        }
        System.out.println("Evaluator traversal:");
        System.out.println(output);
        System.out.println("Nodes with 'inCommon' edges >= 15:");
        for (Node node : traversalDescription.traverse(joe).nodes()) {
            names += node.getProperty("name") + "\n";
        }
        System.out.println(names);
        System.out.println("\n");

    }
    graphDb.shutdown();
}

private static class ReferentEvaluator implements Evaluator {
    @Override
    public Evaluation evaluate(final Path path) {
        // just check for each end node if it fits to the restriction
        Node endNode = path.endNode();
        Iterable<Relationship> relationships = endNode.getRelationships(EdgeTypes.KNOWS);
        for (Relationship rel : relationships) {
            if (rel.hasProperty(PROP_COMMON) && ((int) rel.getProperty(PROP_COMMON) > 15))
                return Evaluation.INCLUDE_AND_CONTINUE;
        }
        return Evaluation.EXCLUDE_AND_CONTINUE;

    }
}

如果我理解正确的话,您可以看到包含的路径以至少一个带有inCommon&gt; = 15的边的节点结束,这就是您想要的。迭代节点会给我们提供那些在我的示例图中与绿色边缘相连的人。