【问题标题】:How to map a Tinkerpop3 graph to a recursive data structure?如何将 Tinkerpop3 图映射到递归数据结构?
【发布时间】:2014-12-19 17:15:35
【问题描述】:

我有一个这样的递归数据结构:

@Canonical
static class Person {
    String name
    Set<Person> knows
}

我有一个 Tinkerpop 图来表示这个结构:

(Jon) -- knows --> (Billy) -- knows --> (Jane) -- ... -->
                           \- knows --> (Suzy) -- ... -->

将任意深度的 Tinkerpop 图映射到数据结构的最有效方法是什么?

我可以想象PathStep 的用法,但似乎应该有更好的方法,而且我对 TP3 的理解还不够好,无法看到它。

def 'build recursive person object'() {
    when:
    def g = TinkerGraph.open()
    def jon = g.addVertex(T.label, 'person', 'name', 'jon')
    def bill = g.addVertex(T.label, 'person', 'name', 'bill')
    def jane = g.addVertex(T.label, 'person', 'name', 'jane')

    jon.addEdge('knows', bill)
    bill.addEdge('knows', jane)

    Multimap<String, Person> relationships = HashMultimap.create()

    g.V().has('name', 'jon')
        .as('a')
        .jump('b', { traverser -> !traverser.get().out('knows').hasNext() })
        .outE('knows')
        .inV()
        .jump('a')
        .as('b')
        .path(
            Function.<Vertex>identity(),
            { Edge e ->
                relationships.put(e.outV().next().value('name') as String,
                        new Person(e.inV().next().value('name') as String))
            } as Function
        )
        .next()

    Person root = new Person('jon')
    Stack<Person> people = new Stack<>()
    Set<String> alreadySeen = new HashSet<>()
    people.push(root)
    alreadySeen.add('jon')

    while(!people.isEmpty()) {
        def person = people.pop()
        person.knows = relationships.get(person.name)
        for(Person related : person.knows) {
            if(alreadySeen.add(related.name))
               people.push(related)
        }
    }

    then:
    root.name == 'jon'
    root.knows*.name == ['bill']
    root.knows[0].knows*.name == ['jane']
}

【问题讨论】:

    标签: titan gremlin tinkerpop3


    【解决方案1】:

    我会利用 Gremlin 的 tree 结构和一些递归方法调用:

    import com.google.common.collect.Iterators;
    import com.tinkerpop.gremlin.process.T;
    import com.tinkerpop.gremlin.process.Traverser;
    import com.tinkerpop.gremlin.process.graph.util.Tree;
    import com.tinkerpop.gremlin.structure.Vertex;
    import com.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    import java.util.function.Function;
    
    /**
     * @author Daniel Kuppitz (daniel at thinkaurelius.com)
     */
    public class App {
    
        static class Person {
    
            private String name;
            private Set<Person> knows;
    
            public static Person fromTree(final String name, final Tree node) {
                final Person person = new Person(name);
                final Tree self = (Tree) node.get(name);
                final Set<String> children = (Set<String>) self.keySet();
                children.forEach(n -> person.knows.add(Person.fromTree(n, self)));
                return person;
            }
    
            public Person(final String name) {
                this.name = name;
                this.knows = new HashSet<>();
            }
    
            public String name() {
                return name;
            }
    
            public Set<Person> knows() {
                return knows;
            }
        }
    
        public static void main(final String[] args) throws Exception {
    
            final TinkerGraph g = TinkerGraph.open();
    
            Vertex jon = g.addVertex(T.label, "person", "name", "jon");
            Vertex bill = g.addVertex(T.label, "person", "name", "bill");
            Vertex jane = g.addVertex(T.label, "person", "name", "jane");
            Vertex suzy = g.addVertex(T.label, "person", "name", "suzy");
    
            jon.addEdge("knows", bill);
            bill.addEdge("knows", jane);
            bill.addEdge("knows", suzy);
    
            final Tree tree = (Tree) g.V().has("name", "jon").as("x").out("knows").until("x", (Traverser<Vertex> t) ->
                    t.get().outE("knows").hasNext(), t -> true).tree((Function<Vertex, String>) v -> v.value("name")).cap().next();
    
            if (!tree.isEmpty()) {
                final Person p1 = Person.fromTree("jon", tree);
                assert p1.name().equals("jon");
                assert p1.knows().size() == 1;
    
                final Person p2 = p1.knows().iterator().next();
                assert p2.name().equals("bill");
                assert p2.knows().size() == 2;
    
                final Iterator<Person> pitty = p2.knows().iterator();
                Iterators.all(pitty, p -> (p.name().equals("jane") || p.name().equals("suzy")) && p1.knows().isEmpty());
            }
        }
    }
    

    这样就可以归结为一条语句:Person.fromTree("jon", tree)

    【讨论】:

    • 很好的回应,丹尼尔。谢谢! until 也很漂亮,我什至不知道它的存在。比我原来的解决方案中丑陋的 while-do 语义要好得多。
    • 为了使这个答案保持最新,当前 TP3 里程碑 (M7) 的 tree 遍历是:g.V().has("name", "jon").repeat(__.out("knows")).emit().tree().by("name").cap().next() - 不再有 lambda,因此它在 Java 和 Groovy 中是相同的。
    猜你喜欢
    • 2020-09-30
    • 1970-01-01
    • 1970-01-01
    • 2017-06-29
    • 2017-05-15
    • 2012-02-06
    • 2015-12-27
    • 2015-05-07
    • 1970-01-01
    相关资源
    最近更新 更多