| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.tinkerpop.gremlin.process.traversal.step; |
| |
| import org.apache.tinkerpop.gremlin.LoadGraphWith; |
| import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; |
| import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; |
| import org.apache.tinkerpop.gremlin.process.IgnoreEngine; |
| import org.apache.tinkerpop.gremlin.process.traversal.Order; |
| import org.apache.tinkerpop.gremlin.process.traversal.P; |
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal; |
| import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; |
| import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; |
| import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy; |
| import org.apache.tinkerpop.gremlin.structure.Vertex; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import static java.util.Collections.emptyList; |
| import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; |
| import static org.apache.tinkerpop.gremlin.process.traversal.P.eq; |
| import static org.apache.tinkerpop.gremlin.process.traversal.P.gte; |
| import static org.apache.tinkerpop.gremlin.process.traversal.P.lt; |
| import static org.apache.tinkerpop.gremlin.process.traversal.P.neq; |
| import static org.apache.tinkerpop.gremlin.process.traversal.Pop.all; |
| import static org.apache.tinkerpop.gremlin.process.traversal.Pop.first; |
| import static org.apache.tinkerpop.gremlin.process.traversal.Pop.last; |
| import static org.apache.tinkerpop.gremlin.process.traversal.Scope.local; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.branch; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.constant; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.count; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.group; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.project; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.unfold; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.where; |
| import static org.apache.tinkerpop.gremlin.structure.Column.keys; |
| import static org.apache.tinkerpop.gremlin.structure.Column.values; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| /** |
| * @author Marko A. Rodriguez (http://markorodriguez.com) |
| * @author Daniel Kuppitz (http://gremlin.guru) |
| */ |
| @RunWith(GremlinProcessRunner.class) |
| public abstract class ComplexTest extends AbstractGremlinProcessTest { |
| |
| public abstract Traversal<Vertex, String> getClassicRecommendation(); |
| |
| public abstract Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> getCoworkerSummary(); |
| |
| public abstract Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> getCoworkerSummaryOLTP(); |
| |
| public abstract Traversal<Vertex, List<Object>> getAllShortestPaths(); |
| |
| public abstract Traversal<Vertex, Map<String, List<String>>> getPlaylistPaths(); |
| |
| public abstract Traversal<Vertex, Map<String, List<String>>> getAgeComments(); |
| |
| /** |
| * Checks the result of both coworkerSummary tests, which is expected to look as follows: |
| * <p> |
| * marko={peter={numCoCreated=1, coCreated=[lop]}, josh={numCoCreated=1, coCreated=[lop]}} |
| * josh={peter={numCoCreated=1, coCreated=[lop]}, marko={numCoCreated=1, coCreated=[lop]}} |
| * peter={josh={numCoCreated=1, coCreated=[lop]}, marko={numCoCreated=1, coCreated=[lop]}} |
| */ |
| private static void checkCoworkerSummary(final Map<String, Map<String, Map<String, Object>>> summary) { |
| assertNotNull(summary); |
| assertEquals(3, summary.size()); |
| assertTrue(summary.containsKey("marko")); |
| assertTrue(summary.containsKey("josh")); |
| assertTrue(summary.containsKey("peter")); |
| for (final Map.Entry<String, Map<String, Map<String, Object>>> entry : summary.entrySet()) { |
| assertEquals(2, entry.getValue().size()); |
| switch (entry.getKey()) { |
| case "marko": |
| assertTrue(entry.getValue().containsKey("josh") && entry.getValue().containsKey("peter")); |
| break; |
| case "josh": |
| assertTrue(entry.getValue().containsKey("peter") && entry.getValue().containsKey("marko")); |
| break; |
| case "peter": |
| assertTrue(entry.getValue().containsKey("marko") && entry.getValue().containsKey("josh")); |
| break; |
| } |
| for (final Map<String, Object> m : entry.getValue().values()) { |
| assertTrue(m.containsKey("numCoCreated")); |
| assertTrue(m.containsKey("coCreated")); |
| assertTrue(m.get("numCoCreated") instanceof Number); |
| assertTrue(m.get("coCreated") instanceof Collection); |
| assertEquals(1, ((Number) m.get("numCoCreated")).intValue()); |
| assertEquals(1, ((Collection) m.get("coCreated")).size()); |
| assertEquals("lop", ((Collection) m.get("coCreated")).iterator().next()); |
| } |
| } |
| } |
| |
| |
| @Test |
| @LoadGraphWith(LoadGraphWith.GraphData.GRATEFUL) |
| public void classicRecommendation() { |
| final Traversal<Vertex, String> traversal = getClassicRecommendation(); |
| printTraversalForm(traversal); |
| checkResults(Arrays.asList("LET IT GROW", "UNCLE JOHNS BAND", "I KNOW YOU RIDER", "SHIP OF FOOLS", "GOOD LOVING"), traversal); |
| final List<Map<String, Object>> list = new ArrayList<>(traversal.asAdmin().getSideEffects().<BulkSet<Map<String, Object>>>get("m")); |
| assertEquals(5, list.size()); |
| assertFalse(traversal.hasNext()); |
| assertEquals("LET IT GROW", list.get(0).get("x")); |
| assertEquals(276, list.get(0).get("y")); |
| assertEquals(21L, list.get(0).get("z")); |
| // |
| assertEquals("UNCLE JOHNS BAND", list.get(1).get("x")); |
| assertEquals(332, list.get(1).get("y")); |
| assertEquals(20L, list.get(1).get("z")); |
| // |
| assertEquals("I KNOW YOU RIDER", list.get(2).get("x")); |
| assertEquals(550, list.get(2).get("y")); |
| assertEquals(20L, list.get(2).get("z")); |
| // |
| assertEquals("SHIP OF FOOLS", list.get(3).get("x")); |
| assertEquals(225, list.get(3).get("y")); |
| assertEquals(18L, list.get(3).get("z")); |
| // |
| assertEquals("GOOD LOVING", list.get(4).get("x")); |
| assertEquals(428, list.get(4).get("y")); |
| assertEquals(18L, list.get(4).get("z")); |
| // |
| checkSideEffects(traversal.asAdmin().getSideEffects(), "m", BulkSet.class, "stash", BulkSet.class); |
| |
| } |
| |
| @Test |
| @LoadGraphWith(MODERN) |
| public void coworkerSummary() { |
| final Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> traversal = getCoworkerSummary(); |
| printTraversalForm(traversal); |
| assertTrue(traversal.hasNext()); |
| checkCoworkerSummary(traversal.next()); |
| assertFalse(traversal.hasNext()); |
| } |
| |
| @Test |
| @LoadGraphWith(MODERN) |
| @IgnoreEngine(TraversalEngine.Type.COMPUTER) // no mid-traversal V() in computer mode + star-graph limitations |
| public void coworkerSummaryOLTP() { |
| final Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> traversal = getCoworkerSummaryOLTP(); |
| printTraversalForm(traversal); |
| assertTrue(traversal.hasNext()); |
| checkCoworkerSummary(traversal.next()); |
| assertFalse(traversal.hasNext()); |
| } |
| |
| @Test |
| @LoadGraphWith(MODERN) |
| public void allShortestPaths() { |
| final Traversal<Vertex, List<Object>> traversal = getAllShortestPaths(); |
| printTraversalForm(traversal); |
| final Object marko = convertToVertexId(graph, "marko"); |
| final Object vadas = convertToVertexId(graph, "vadas"); |
| final Object lop = convertToVertexId(graph, "lop"); |
| final Object josh = convertToVertexId(graph, "josh"); |
| final Object ripple = convertToVertexId(graph, "ripple"); |
| final Object peter = convertToVertexId(graph, "peter"); |
| final List<List<Object>> allShortestPaths = Arrays.asList( |
| Arrays.asList(josh, lop), |
| Arrays.asList(josh, marko), |
| Arrays.asList(josh, ripple), |
| Arrays.asList(josh, marko, vadas), |
| Arrays.asList(josh, lop, peter), |
| Arrays.asList(lop, marko), |
| Arrays.asList(lop, peter), |
| Arrays.asList(lop, josh), |
| Arrays.asList(lop, josh, ripple), |
| Arrays.asList(lop, marko, vadas), |
| Arrays.asList(marko, lop), |
| Arrays.asList(marko, josh), |
| Arrays.asList(marko, vadas), |
| Arrays.asList(marko, josh, ripple), |
| Arrays.asList(marko, lop, peter), |
| Arrays.asList(peter, lop), |
| Arrays.asList(peter, lop, marko), |
| Arrays.asList(peter, lop, josh), |
| Arrays.asList(peter, lop, josh, ripple), |
| Arrays.asList(peter, lop, marko, vadas), |
| Arrays.asList(ripple, josh), |
| Arrays.asList(ripple, josh, lop), |
| Arrays.asList(ripple, josh, marko), |
| Arrays.asList(ripple, josh, marko, vadas), |
| Arrays.asList(ripple, josh, lop, peter), |
| Arrays.asList(vadas, marko), |
| Arrays.asList(vadas, marko, josh), |
| Arrays.asList(vadas, marko, lop), |
| Arrays.asList(vadas, marko, josh, ripple), |
| Arrays.asList(vadas, marko, lop, peter) |
| ); |
| checkResults(allShortestPaths, traversal); |
| } |
| |
| @Test |
| @LoadGraphWith(LoadGraphWith.GraphData.GRATEFUL) |
| public void playlistPaths() { |
| final Traversal<Vertex, Map<String, List<String>>> traversal = getPlaylistPaths(); |
| printTraversalForm(traversal); |
| assertTrue(traversal.hasNext()); |
| Map<String, List<String>> map = traversal.next(); |
| assertTrue(map.get("artists").contains("Bob_Dylan")); |
| boolean hasJohnnyCash = false; |
| while (traversal.hasNext()) { |
| map = traversal.next(); |
| if (map.get("artists").contains("Johnny_Cash")) |
| hasJohnnyCash = true; |
| } |
| assertTrue(hasJohnnyCash); |
| assertTrue(map.get("artists").contains("Grateful_Dead")); |
| } |
| |
| @Test |
| @LoadGraphWith(LoadGraphWith.GraphData.MODERN) |
| public void ageComments() { |
| final Traversal<Vertex, Map<String, List<String>>> traversal = getAgeComments(); |
| printTraversalForm(traversal); |
| assertTrue(traversal.hasNext()); |
| Map<String, List<String>> map = traversal.next(); |
| assertEquals(4, map.size()); |
| assertTrue(map.containsKey("marko")); |
| assertEquals(2, map.get("marko").size()); |
| assertTrue(map.get("marko").contains("almost old")); |
| assertTrue(map.get("marko").contains("younger than peter")); |
| assertTrue(map.containsKey("vadas")); |
| assertEquals(2, map.get("vadas").size()); |
| assertTrue(map.get("vadas").contains("pretty young")); |
| assertTrue(map.get("vadas").contains("younger than peter")); |
| assertTrue(map.containsKey("josh")); |
| assertEquals(3, map.get("josh").size()); |
| assertTrue(map.get("josh").contains("younger than peter")); |
| assertTrue(map.get("josh").contains("pretty old")); |
| assertTrue(map.get("josh").contains("looks like josh")); |
| assertTrue(map.containsKey("peter")); |
| assertEquals(1, map.get("peter").size()); |
| assertTrue(map.get("peter").contains("pretty old")); |
| } |
| |
| public static class Traversals extends ComplexTest { |
| |
| @Override |
| public Traversal<Vertex, String> getClassicRecommendation() { |
| return g.V().has("name", "DARK STAR").as("a").out("followedBy").aggregate("stash"). |
| in("followedBy").where(neq("a").and(P.not(P.within("stash")))). |
| groupCount(). |
| unfold(). |
| project("x", "y", "z"). |
| by(select(keys).values("name")). |
| by(select(keys).values("performances")). |
| by(select(values)). |
| order(). |
| by(select("z"), Order.desc). |
| by(select("y"), Order.asc). |
| limit(5).store("m").select("x"); |
| } |
| |
| @Override |
| public Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> getCoworkerSummary() { |
| return g.V().hasLabel("person").filter(outE("created")).aggregate("p").as("p1").values("name").as("p1n") |
| .select("p").unfold().where(neq("p1")).as("p2").values("name").as("p2n").select("p2") |
| .out("created").choose(in("created").where(eq("p1")), values("name"), constant(emptyList())) |
| .<String, Map<String, Map<String, Object>>>group().by(select("p1n")). |
| by(group().by(select("p2n")). |
| by(unfold().fold().project("numCoCreated", "coCreated").by(count(local)).by())); |
| } |
| |
| @Override |
| public Traversal<Vertex, Map<String, Map<String, Map<String, Object>>>> getCoworkerSummaryOLTP() { |
| return g.V().hasLabel("person").filter(outE("created")).as("p1") |
| .V().hasLabel("person").where(neq("p1")).filter(outE("created")).as("p2") |
| .map(out("created").where(in("created").as("p1")).values("name").fold()) |
| .<String, Map<String, Map<String, Object>>>group().by(select("p1").by("name")). |
| by(group().by(select("p2").by("name")). |
| by(project("numCoCreated", "coCreated").by(count(local)).by())); |
| } |
| |
| @Override |
| public Traversal<Vertex, List<Object>> getAllShortestPaths() { |
| // TODO: remove .withoutStrategies(PathRetractionStrategy.class) |
| return g.withoutStrategies(ComputerVerificationStrategy.class, PathRetractionStrategy.class). |
| V().as("v").both().as("v"). |
| project("src", "tgt", "p"). |
| by(select(first, "v")). |
| by(select(last, "v")). |
| by(select(all, "v")).as("triple"). |
| group("x"). |
| by(select("src", "tgt")). |
| by(select("p").fold()).select("tgt").barrier(). |
| repeat(both().as("v"). |
| project("src", "tgt", "p"). |
| by(select(first, "v")). |
| by(select(last, "v")). |
| by(select(all, "v")).as("t"). |
| filter(select(all, "p").count(local).as("l"). |
| select(last, "t").select(all, "p").dedup(local).count(local).where(eq("l"))). |
| select(last, "t"). |
| not(select(all, "p").as("p").count(local).as("l"). |
| select(all, "x").unfold().filter(select(keys).where(eq("t")).by(select("src", "tgt"))). |
| filter(select(values).unfold().or(count(local).where(lt("l")), where(eq("p"))))). |
| barrier(). |
| group("x"). |
| by(select("src", "tgt")). |
| by(select(all, "p").fold()).select("tgt").barrier()). |
| cap("x").select(values).unfold().unfold().map(unfold().id().fold()); |
| } |
| |
| @Override |
| public Traversal<Vertex, Map<String, List<String>>> getPlaylistPaths() { |
| return g.V().has("name", "Bob_Dylan").in("sungBy").as("a"). |
| repeat(out().order().by(Order.shuffle).simplePath().from("a")). |
| until(out("writtenBy").has("name", "Johnny_Cash")).limit(1).as("b"). |
| repeat(out().order().by(Order.shuffle).as("c").simplePath().from("b").to("c")). |
| until(out("sungBy").has("name", "Grateful_Dead")).limit(1). |
| path().from("a").unfold(). |
| <List<String>>project("song", "artists"). |
| by("name"). |
| by(__.coalesce(out("sungBy", "writtenBy").dedup().values("name"), constant("Unknown")).fold()); |
| } |
| |
| @Override |
| public Traversal<Vertex, Map<String, List<String>>> getAgeComments() { |
| return g.V().hasLabel("person") |
| .<String, List<String>> group() |
| .by("name") |
| .by(branch(values("age")) |
| .option(29, constant("almost old")) |
| .option(__.is(32), constant("looks like josh")) |
| .option(lt(29), constant("pretty young")) |
| .option(lt(35), constant("younger than peter")) |
| .option(gte(30), constant("pretty old")).fold()); |
| } |
| } |
| } |