{-# LANGUAGE OverloadedStrings, TypeFamilies #-} module Main (main,spec) where import Control.Category ((<<<), (>>>)) import qualified Data.Aeson as Aeson import Data.Either (isRight) import Data.Foldable (toList) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import Data.List (sort) import Data.Monoid (mempty, (<>)) import Data.Scientific (Scientific) import Data.Text (unpack, Text) import Data.Traversable (traverse) import qualified Data.Vector as V import qualified Network.Greskell.WebSocket.Client as WS import Test.Hspec import Data.Greskell.AsLabel (AsLabel(..), lookupAsM) import qualified Data.Greskell.AsLabel as As import Data.Greskell.AsIterator ( AsIterator(IteratorItem) ) import Data.Greskell.Binder (newBind, runBinder) import Data.Greskell.Extra (gWhenEmptyInput) import Data.Greskell.GMap (GMapEntry, unGMapEntry) import Data.Greskell.Gremlin ( oIncr, oDecr, cCompare, Order, Predicate(..), pLt, pAnd, pGte, pNot, pEq, pTest, P ) import Data.Greskell.Greskell ( toGremlin, Greskell, toGreskell, ToGreskell(..), true, false, list, value, single, number, gvalueInt, unsafeMethodCall, unsafeGreskell ) import Data.Greskell.Graph ( AVertex(..), AEdge(..), AProperty(..), AVertexProperty(..), Key, Keys(..), (-:), singletonKeys, T, tId, tLabel, tKey, tValue, cList, (=:), ElementID(..), Path(..), makePathEntry ) import Data.Greskell.GraphSON ( FromGraphSON, nonTypedGValue, GValue, parseEither ) import Data.Greskell.GTraversal ( Walk, GTraversal, SideEffect, source, sV', sE', gV', sAddV', gAddE', gTo, ($.), gOrder, gBy1, gBy, gByL, Transform, unsafeWalk, unsafeGTraversal, gProperties, gProperty, gPropertyV, liftWalk, gValues, gAs, gSelect1, gSelectN, gSelectBy1, gSelectByN, gFilter, gOut', gOutV, gOutV', gInV, gInV', gId, gLabel, gProject, gValueMap, gProject, gByL, gRepeat, gTimes, gEmitHead, gUntilTail, gLoops, gIsP, gIsP', gHasLabel, gHas2, gAddV, gIterate, gPath, gPathBy, gWhereP1, gChoose3, gIdentity, gWhereP2, gMatch, mPattern ) import Data.Greskell.Logic (Logic(..)) import Data.Greskell.PMap (lookupAsM, lookupListAs, pMapToThrow) import ServerTest.Common (withEnv, withClient) main :: IO () main = hspec spec spec :: Spec spec = withEnv $ do spec_basics spec_comparator spec_predicate spec_T spec_P spec_graph spec_as spec_selectBy spec_project spec_repeat spec_upsert spec_path spec_where spec_match spec_basics :: SpecWith (String,Int) spec_basics = do describe "Num" $ do let checkInt :: Greskell Int -> Int -> SpecWith (String,Int) checkInt = checkOne checkInt 100 100 checkInt (20 + 30) (20 + 30) checkInt (10 - 3 * 6) (10 - 3 * 6) checkInt (-99) (-99) checkInt (abs (-53)) (abs (-53)) checkInt (signum 0) (signum 0) checkInt (signum 99) (signum 99) checkInt (signum (-12)) (signum (-12)) describe "Fractional" $ do let checkFrac :: Greskell Scientific -> Scientific -> SpecWith (String,Int) checkFrac = checkOne checkFrac (20.5) (20.5) checkFrac (20.123) (20.123) checkFrac (32.25 / 2.5) (32.25 / 2.5) checkFrac (19.2 * recip 12.5) (19.2 * recip 12.5) describe "Monoid" $ do let checkT :: Greskell Text -> Text -> SpecWith (String,Int) checkT = checkOne checkT mempty mempty checkT ("hello, " <> "world!") ("hello, " <> "world!") checkT ("!\"#$%&'()=~\\|><+*;:@{}[]/?_\r\n\t ") ("!\"#$%&'()=~\\|><+*;:@{}[]/?_\r\n\t ") describe "Bool" $ do let checkB :: Greskell Bool -> Bool -> SpecWith (String,Int) checkB = checkOne checkB true True checkB false False describe "list" $ do let checkL :: Greskell [Int] -> [Int] -> SpecWith (String,Int) checkL = checkRaw checkL (list []) [] checkL (list [20,30,20,10]) [20,30,20,10] describe "number" $ do let checkN :: Greskell Scientific -> Scientific -> SpecWith (String,Int) checkN = checkOne checkN (number 3.1415) (3.1415) checkN (number 2.31e12) (2.31e12) checkN (number (-434.23e-19)) (-434.23e-19) describe "nested map" $ do let check :: Greskell (HashMap Int (HashMap Text Int)) -> [(Int, (HashMap Text Int))] -> SpecWith (String,Int) check = checkRawMapped unGMapEntry check (unsafeGreskell "[:]") [] check (unsafeGreskell "[100: [\"foo\": 55], 200: [:], 300: [\"bar\": 60, \"buzz\": 65]]") [ (100, HM.fromList [("foo", 55)]), (200, mempty), (300, HM.fromList [("bar", 60), ("buzz", 65)]) ] describe "array in map" $ do let check :: Greskell (HashMap Text [Int]) -> [(Text, [Int])] -> SpecWith (String,Int) check = checkRawMapped unGMapEntry check (unsafeGreskell "[:]") [] check (unsafeGreskell "[\"foo\": [], \"bar\": [1,2,3]]") [ ("foo", []), ("bar", [1,2,3]) ] checkRawMapped :: (AsIterator a, b ~ IteratorItem a, FromGraphSON b, Eq c, Show c) => (b -> c) -> Greskell a -> [c] -> SpecWith (String, Int) checkRawMapped mapResult input expected = specify label $ withClient $ \client -> do got <- WS.slurpResults =<< WS.submit client input Nothing fmap mapResult got `shouldBe` V.fromList expected where label = unpack $ toGremlin input checkRaw :: (AsIterator a, b ~ IteratorItem a, FromGraphSON b, Eq b, Show b) => Greskell a -> [b] -> SpecWith (String, Int) checkRaw = checkRawMapped id checkOne :: (AsIterator a, b ~ IteratorItem a, FromGraphSON b, Eq b, Show b) => Greskell a -> b -> SpecWith (String, Int) checkOne input expected = checkRaw input [expected] spec_comparator :: SpecWith (String,Int) spec_comparator = do let oIncr' :: Greskell (Order Int) oIncr' = oIncr oDecr' :: Greskell (Order Int) oDecr' = oDecr checkOne (cCompare oIncr' 20 20) 0 checkOne (cCompare oIncr' 10 20) (-1) checkOne (cCompare oIncr' 20 10) 1 checkOne (cCompare oDecr' 20 20) 0 checkOne (cCompare oDecr' 10 20) 1 checkOne (cCompare oDecr' 20 10) (-1) spec_predicate :: SpecWith (String,Int) spec_predicate = do checkOne (pTest the_p 5) False checkOne (pTest the_p 10) True checkOne (pTest the_p 15) True checkOne (pTest the_p 20) False where the_p :: Greskell (P Int) the_p = pLt 20 `pAnd` pGte 10 iterateTraversal :: GTraversal c s e -> Greskell () iterateTraversal gt = unsafeMethodCall (toGreskell gt) "iterate" [] spec_T :: SpecWith (String,Int) spec_T = describe "T enum" $ do specFor' "tId" (gMapT tId) (parseEither . unElementID) [(Right 10 :: Either String Int)] specFor "tLabel" (gMapT tLabel) ["VLABEL"] specFor "tKey" (gMapT tKey <<< gProperties ["vprop"]) ["vprop"] specFor' "tValue" (gMapT tValue <<< gProperties ["vprop"]) parseEither [(Right 400 :: Either String Int)] where gMapT :: Greskell (T a b) -> Walk Transform a b gMapT t = unsafeWalk "map" ["{ " <> toGremlin (unsafeMethodCall t "apply" ["it.get()"]) <> " }"] prefixedTraversal :: Walk Transform AVertex a -> GTraversal Transform () a prefixedTraversal mapper = unsafeGTraversal (prelude <> body) where prelude = ( "graph = org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph.open(); " <> "g = graph.traversal(); " <> "graph.addVertex(id, 10, label, \"VLABEL\"); " <> ( toGremlin $ iterateTraversal $ gPropertyV Nothing "vprop" (gvalueInt $ (400 :: Int)) ["a" =: ("A" :: Greskell Text), "b" =: ("B" :: Greskell Text)] $. liftWalk $ sV' [] $ source "g" ) <> "; " ) body = toGremlin $ mapper $. sV' [] $ source "g" specFor' :: (FromGraphSON a, Eq b, Show b) => String -> Walk Transform AVertex a -> (a -> b) -> [b] -> SpecWith (String,Int) specFor' desc mapper convResult expected = specify desc $ withClient $ \client -> do got <- WS.slurpResults =<< WS.submit client (prefixedTraversal mapper) Nothing (fmap convResult got) `shouldBe` V.fromList expected specFor :: (FromGraphSON a, Eq a, Show a) => String -> Walk Transform AVertex a -> [a] -> SpecWith (String,Int) specFor desc mapper expected = specFor' desc mapper id expected spec_P :: SpecWith (String,Int) spec_P = describe "P class" $ specify "pNot, pEq, pTest" $ withClient $ \client -> do let p :: Greskell (P Scientific) p = pNot $ pEq $ number 10 test v = WS.slurpResults =<< WS.submit client (pTest p $ v) Nothing test (number 10) `shouldReturn` V.fromList [False] test (number 15) `shouldReturn` V.fromList [True] withPrelude :: (ToGreskell a) => Greskell () -> a -> Greskell (GreskellReturn a) withPrelude prelude orig = unsafeGreskell (toGremlin prelude <> toGremlin orig) statements :: [Text] -> Greskell () statements = unsafeGreskell . mconcat . map (<> "; ") -- | This test is supported TinkerPop 3.1.0 and above, because it uses -- 'gAddE'' function. spec_graph :: SpecWith (String,Int) spec_graph = do specify "AProperty (edge properties)" $ withClient $ \client -> do let trav = gProperties [] $. sE' [] $ source "g" prop t = AProperty "condition" $ Right (t :: Text) expected = map prop [ ">=0.11.2.1", ">=1.2.2.1", ">=1.2.3" ] got <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing (map (fmap parseEither) $ V.toList got) `shouldMatchList` expected specify "AProperty (vertex property meta-properties)" $ withClient $ \client -> do let trav = gProperties [] $. gProperties [] $. sV' [] $ source "g" prop t = AProperty "date" $ Right (t :: Text) expected = map prop [ "2018-04-08", "2018-05-10", "2017-09-20", "2017-12-27", "2017-12-23" ] got <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing (map (fmap parseEither) $ V.toList got) `shouldMatchList` expected specify "AEdge" $ withClient $ \client -> do let lOutV :: AsLabel Text lOutV = "outV_name" lInV :: AsLabel Text lInV = "inV_name" lProps = "props" lEdge = "edge" lProj = "projections" kCond :: Key AEdge Text kCond = "condition" trav = gSelectN lEdge lProj [] $. gAs lProj $. ( gProject ( gByL lOutV (gOutV' >>> gValues ["name"]) ) [ gByL lInV (gInV' >>> gValues ["name"]), gByL lProps (gValueMap KeysNil) ] ) $. gAs lEdge $. sE' [] $ source "g" parseResult pm = do edge <- lookupAsM lEdge pm pj <- lookupAsM lProj pm (,,,) (aeLabel edge) <$> (lookupAsM lOutV pj) <*> (lookupAsM lInV pj) <*> (lookupAsM kCond =<< lookupAsM lProps pj) expected = [ ("depends_on", "greskell", "aeson", ">=0.11.2.1"), ("depends_on", "greskell", "text", ">=1.2.2.1"), ("depends_on", "aeson", "text", ">=1.2.3") ] got <- traverse parseResult =<< WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing V.toList got `shouldMatchList` expected specify "AVertexProperty" $ withClient $ \client -> do let lAV = "vertex_property" lProps = "props" kDate :: Key (AVertexProperty GValue) Text kDate = "date" trav = gSelectN lAV lProps [] $. gAs lProps $. gValueMap (singletonKeys kDate) $. gAs lAV $. gProperties [] $. sV' [] $ source "g" parseResult pm = do av <- lookupAsM lAV pm let label = avpLabel av e_val = parseEither $ avpValue av m_date <- if label == "version" then fmap Just (lookupAsM kDate =<< lookupAsM lProps pm) else return Nothing return (label, e_val, m_date) expected :: [(Text, Either String Text, Maybe Text)] expected = [ ("name", Right "greskell", Nothing), ("name", Right "aeson", Nothing), ("name", Right "text", Nothing), ("version", Right "0.1.1.0", Just "2018-04-08"), ("version", Right "1.3.1.1", Just "2018-05-10"), ("version", Right "1.2.2.0", Just "2017-09-20"), ("version", Right "1.2.3.0", Just "2017-12-27"), ("version", Right "1.2.2.0", Just "2017-12-23") ] got <- traverse parseResult =<< WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing V.toList got `shouldMatchList` expected specify "AVertex" $ withClient $ \client -> do let lVertex = "vertex" kName :: Key AVertex Text kName = "name" kVer :: Key AVertex Text kVer = "version" lProps = "props" trav = gSelectN lVertex lProps [] $. gAs lProps $. gValueMap (kName -: kVer -: KeysNil) $. gAs lVertex $. sV' [] $ source "g" parseResult pm = do v <- lookupAsM lVertex pm let label = avLabel v evid = parseEither $ unElementID $ avId v props <- lookupAsM lProps pm names <- fmap toList $ pMapToThrow $ lookupListAs kName props vers <- fmap (sort . toList) $ pMapToThrow $ lookupListAs kVer props return (evid, label, names, vers) expected :: [(Either String Int, Text, [Text], [Text])] expected = [ (Right 1, "package", ["greskell"], ["0.1.1.0"]), (Right 2, "package", ["aeson"], ["1.2.2.0", "1.3.1.1"]), (Right 3, "package", ["text"], ["1.2.2.0", "1.2.3.0"]) ] got <- traverse parseResult =<< WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing V.toList got `shouldMatchList` expected where withPrelude' :: (ToGreskell a) => a -> Greskell (GreskellReturn a) withPrelude' = withPrelude prelude prelude :: Greskell () prelude = statements ( [ "graph = org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph.open()", "g = graph.traversal()", "graph.addVertex(id, 1, label, 'package')", "graph.addVertex(id, 2, label, 'package')", "graph.addVertex(id, 3, label, 'package')", finalize $ setName 1 "greskell", finalize $ setName 2 "aeson", finalize $ setName 3 "text", finalize $ dependsOn 1 2 ">=0.11.2.1", finalize $ dependsOn 1 3 ">=1.2.2.1", finalize $ dependsOn 2 3 ">=1.2.3" ] ++ addVersion 1 "0.1.1.0" "2018-04-08" ++ addVersion 2 "1.3.1.1" "2018-05-10" ++ addVersion 2 "1.2.2.0" "2017-09-20" ++ addVersion 3 "1.2.3.0" "2017-12-27" ++ addVersion 3 "1.2.2.0" "2017-12-23" ) finalize :: GTraversal c s e -> Text finalize gt = toGremlin $ iterateTraversal gt num :: Integer -> Greskell (ElementID AVertex) num = fmap ElementID . gvalueInt setName :: Integer -> Greskell Text -> GTraversal SideEffect () AVertex setName vid name = gProperty "name" name $. liftWalk $ sV' [num vid] $ source "g" dependsOn :: Integer -> Integer -> Greskell Text -> GTraversal SideEffect () AEdge dependsOn from_id to_id version_cond = gProperty "condition" version_cond $. (gAddE' "depends_on" $ gTo (gV' [num to_id])) $. liftWalk $ sV' [num from_id] $ source "g" addVersion :: Integer -> Greskell Text -> Greskell Text -> [Text] addVersion vid ver date = [ finalize $ gPropertyV (Just cList) "version" ver ["date" =: date] $. liftWalk $ sV' [num vid] $ source "g" ] multiplyWalk :: Greskell Int -> Walk Transform Int Int multiplyWalk factor = unsafeWalk "map" ["{ it.get() * " <> toGremlin factor <> " }"] plusWalk :: Greskell Int -> Walk Transform Int Int plusWalk n = unsafeWalk "map" ["{ it.get() + " <> toGremlin n <> " }"] squareWalk :: Walk Transform Int Int squareWalk = unsafeWalk "map" ["{ it.get() * it.get() }"] appendWalk :: Greskell Text -> Walk Transform Text Text appendWalk t = unsafeWalk "map" ["{ it.get() + " <> toGremlin t <> " }"] -- toLowerWalk :: Walk Transform Text Text -- toLowerWalk = unsafeWalk "map" ["{ it.get().toLowerCase() }"] lengthWalk :: Walk Transform Text Int lengthWalk = unsafeWalk "map" ["{ it.get().length() }"] substrWalk :: Greskell Int -> Greskell Int -> Walk Transform Text Text substrWalk s e = unsafeWalk "map" ["{ it.get().substring(" <> toGremlin s <> ", " <> toGremlin e <> ") }"] spec_as :: SpecWith (String,Int) spec_as = do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)" mult = multiplyWalk specify "gAs and gSelect1" $ withClient $ \client -> do let label :: AsLabel Int label = AsLabel "a" got <- WS.slurpResults =<< WS.submit client (gSelect1 label $. mult 100 $. gAs label $. start) Nothing V.toList got `shouldBe` [1,2,3] specify "gAs and gSelectN" $ withClient $ \client -> do let lorig, lmul :: AsLabel Int lorig = AsLabel "a" lmul = AsLabel "b" gt = gSelectN lorig lmul [] $. mult 5 $. gAs lmul $. mult 100 $. gAs lorig $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client gt Nothing mapM (lookupAsM lorig) got `shouldReturn` [1,2,3] mapM (lookupAsM lmul) got `shouldReturn` [100,200,300] spec_selectBy :: SpecWith (String,Int) spec_selectBy = do let prelude :: Greskell () prelude = statements [ "graph = org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph.open()", "g = graph.traversal()", "graph.addVertex(id, 1, label, 'person')", "graph.addVertex(id, 2, label, 'person')", "g.V(1).property('name', 'ito').property('age', 23).iterate()", "g.V(2).property('name', 'tanaka').property('age', 18).iterate()", "g.V(1).addE('knows').to(V(2)).iterate()" ] specify "gAs and gSelectBy1" $ withClient $ \client -> do let src :: AsLabel AVertex src = AsLabel "s" gt = gSelectBy1 src (gBy ("name" :: Key AVertex Text)) $. gAs src $. gFilter (gOut' ["knows"]) $. sV' [] $ source "g" got <- fmap V.toList $ WS.slurpResults =<< WS.submit client (withPrelude prelude gt) Nothing got `shouldBe` ["ito"] specify "gAs and gSelectByN" $ withClient $ \client -> do let src, dest :: AsLabel AVertex src = AsLabel "s" dest = AsLabel "d" gt = gSelectByN src dest [] (gBy ("age" :: Key AVertex Int)) $. gAs dest $. gOut' ["knows"] $. gAs src $. gFilter (gOut' ["knows"]) $. sV' [] $ source "g" got <- fmap V.toList $ WS.slurpResults =<< WS.submit client (withPrelude prelude gt) Nothing map (As.lookup src) got `shouldBe` [Just 23] map (As.lookup dest) got `shouldBe` [Just 18] spec_project :: SpecWith (String,Int) spec_project = do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)" specify "gProject with single item" $ withClient $ \client -> do let l2 = "l2" trav = gProject (gByL l2 $ multiplyWalk 2) [] $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing got_l2 <- traverse (lookupAsM l2) got got_l2 `shouldBe` [2,4,6] specify "gProject with two items" $ withClient $ \client -> do let l2 = "l2" l3 = "l3" trav = gProject (gByL l2 $ multiplyWalk 2) [gByL l3 $ multiplyWalk 3] $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing got_l2 <- traverse (lookupAsM l2) got got_l2 `shouldBe` [2,4,6] got_l3 <- traverse (lookupAsM l3) got got_l3 `shouldBe` [3,6,9] specify "gProject with more than one items in 'by' traversal" $ withClient $ \client -> do let rep_mul3 :: Walk Transform Int Int rep_mul3 = unsafeWalk "flatMap" ["{ def a = it.get(); return [a * 3, a * 4, a * 5].iterator() }"] lab = "rep_mul3" trav = gProject (gByL lab $ rep_mul3) [] $. start got_simple <- fmap V.toList $ WS.slurpResults =<< WS.submit client (rep_mul3 $. start) Nothing got_simple `shouldBe` [3, 4, 5, 6, 8, 10, 9, 12, 15] got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing got_l <- traverse (lookupAsM lab) got got_l `shouldBe` [3, 6, 9] -- only the first item from the by-projection traversal. specify "gProject with gSelect1 in by-projection" $ withClient $ \client -> do let as_orig = "as_orig" l_orig = "orig" l_mapped = "mapped" trav = gProject (gByL l_mapped $ multiplyWalk 4) [gByL l_orig $ gSelect1 as_orig ] $. gAs as_orig $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing traverse (lookupAsM l_mapped) got `shouldReturn` [4, 8, 12] traverse (lookupAsM l_orig) got `shouldReturn` [1, 2, 3] spec_repeat :: SpecWith (String,Int) spec_repeat = do specify "gRepeat and gTimes" $ withClient $ \client -> do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)" trav = gRepeat Nothing (gTimes 3) Nothing (multiplyWalk 2) $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing got `shouldBe` [8, 16, 24] specify "gRepeat, gTimes and gEmitHead" $ withClient $ \client -> do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1, 10, 100)" trav = gRepeat Nothing (gTimes 3) gEmitHead (multiplyWalk 2) $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing sort got `shouldBe` [1, 2, 4, 8, 10, 20, 40, 80, 100, 200, 400, 800] specify "gRepeat, gUntilTail and gLoops" $ withClient $ \client -> do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1, 10, 100)" trav = gRepeat Nothing (gUntilTail $ gIsP (pGte 4) <<< gLoops Nothing) Nothing (multiplyWalk 2) $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client trav Nothing sort got `shouldBe` [16, 160, 1600] spec_upsert :: SpecWith (String,Int) spec_upsert = do describe "upsert vertex" $ do specify "upsert outputs the vertex" $ withClient $ \client -> do let body = liftWalk getName $. upsert "foo" pre = statements prelude got <- WS.slurpResults =<< WS.submit client (withPrelude pre body) Nothing V.toList got `shouldBe` ["foo"] specify "upsert adds a vertex" $ withClient $ \client -> do let pre = statements $ prelude ++ [ toGremlin (gIterate $ upsert "foo") ] body = getName $. getAllPersons got <- WS.slurpResults =<< WS.submit client (withPrelude pre body) Nothing V.toList got `shouldBe` ["foo"] specify "upsert adds different vertices" $ withClient $ \client -> do let pre = statements $ prelude ++ [ toGremlin (gIterate $ upsert "foo"), toGremlin (gIterate $ upsert "bar"), toGremlin (gIterate $ upsert "foo"), toGremlin (gIterate $ upsert "buzz"), toGremlin (gIterate $ upsert "bar") ] body = getName $. getAllPersons got <- WS.slurpResults =<< WS.submit client (withPrelude pre body) Nothing V.toList got `shouldMatchList` ["foo", "bar", "buzz"] specify "upsert returns existing vertex" $ withClient $ \client -> do let pre = statements $ prelude ++ [ toGremlin (gIterate $ upsert "foo"), toGremlin (gIterate $ upsert "foo") ] body = liftWalk getName $. upsert "foo" got <- WS.slurpResults =<< WS.submit client (withPrelude pre body) Nothing V.toList got `shouldBe` ["foo"] where prelude :: [Text] prelude = [ "graph = org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph.open()", "g = graph.traversal()" ] getAllPersons :: GTraversal Transform () AVertex getAllPersons = gHasLabel "person" $. sV' [] $ source "g" getPerson :: Greskell Text -> GTraversal Transform () AVertex getPerson name = gHas2 "name" name $. getAllPersons insert :: Greskell Text -> Walk SideEffect a AVertex insert name = gProperty "name" name <<< gAddV "person" upsert :: Greskell Text -> GTraversal SideEffect () AVertex upsert name = gWhenEmptyInput (insert name) $. liftWalk $ getPerson name getName :: Walk Transform AVertex Text getName = gValues ["name"] spec_path :: SpecWith (String,Int) spec_path = do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)" mult = multiplyWalk specify "gPath" $ withClient $ \client -> do let g = gPath $. gAs "c" $. mult 10 $. mult 10 $. gAs "b" $. gAs "a" $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing let parsed = traverse (traverse parseEither) got expected :: [Path Int] expected = [ Path [ makePathEntry ["a", "b"] 1, makePathEntry [] 10, makePathEntry ["c"] 100 ], Path [ makePathEntry ["a", "b"] 2, makePathEntry [] 20, makePathEntry ["c"] 200 ], Path [ makePathEntry ["a", "b"] 3, makePathEntry [] 30, makePathEntry ["c"] 300 ] ] parsed `shouldBe` Right expected specify "gPathBy" $ withClient $ \client -> do let g = gPathBy (gBy $ mult 50) [gBy $ mult 800] $. mult 10 $. gAs "b" $. mult 10 $. gAs "a" $. start got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing let expected :: [Path Int] expected = [ Path [ makePathEntry ["a"] 50, makePathEntry ["b"] 8000, makePathEntry [] 5000 ], Path [ makePathEntry ["a"] 100, makePathEntry ["b"] 16000, makePathEntry [] 10000 ], Path [ makePathEntry ["a"] 150, makePathEntry ["b"] 24000, makePathEntry [] 15000 ] ] got `shouldBe` expected spec_where :: SpecWith (String,Int) spec_where = do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)" specify "gWhereP1 (without modulation)" $ withClient $ \client -> do let g = gWhereP1 (pEq label_s) Nothing $. squareWalk $. gAs label_s $. start label_s = "s" got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing got `shouldBe` [1] specify "gWhereP1 (with modulation)" $ withClient $ \client -> do let g = gWhereP1 (pEq label_s) (Just $ gBy $ mapper) $. multiplyWalk 5 $. gAs label_s $. start label_s = "s" mapper = gChoose3 (gIsP' $ pGte 12) (plusWalk (-12)) gIdentity got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing got `shouldBe` [15] specify "gWhereP2 (without modulation)" $ withClient $ \client -> do let g = gWhereP2 label_s (pEq label_m) Nothing $. plusWalk 10 $. gAs label_m $. squareWalk $. gAs label_s $. start label_s = "s" label_m = "m" got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing got `shouldBe` [11] specify "gWhereP2 (with modulation)" $ withClient $ \client -> do let start_str :: GTraversal Transform () Text start_str = unsafeGTraversal "__(\"foo\", \"bar\", \"quux\", \"hoge\")" g = gWhereP2 label_s (pEq label_a) (Just $ gBy $ mapper) $. gAs label_a $. appendWalk "FOO" $. gAs label_s $. start_str label_s = "s" label_a = "a" mapper = gChoose3 (gIsP (pGte 6) <<< lengthWalk) (substrWalk 0 3) gIdentity got <- fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing got `shouldBe` ["fooFOO", "barFOO"] spec_match :: SpecWith (String,Int) spec_match = do specify "gMatch" $ withClient $ \client -> do let start_str :: GTraversal Transform () Text start_str = unsafeGTraversal "__(\"foo\", \"hoge\", \"bar\", \"quux\")" g = gSelectN label_s label_ss [label_len, label_dlen] $. gMatch pat $. start_str pat = And ( mPattern label_s (gAs label_len <<< lengthWalk) ) [ mPattern label_s (gAs label_ss <<< appendWalk "HOGE"), mPattern label_len (gAs label_dlen <<< squareWalk), mPattern label_ss (gWhereP1 (pLt label_dlen) Nothing <<< plusWalk 5 <<< lengthWalk) ] label_s = "s" label_ss = "ss" label_len = "len" label_dlen = "dlen" extract sm = (,,,) <$> lookupAsM label_s sm <*> lookupAsM label_ss sm <*> lookupAsM label_len sm <*> lookupAsM label_dlen sm got <- traverse extract =<< (fmap V.toList $ WS.slurpResults =<< WS.submit client g Nothing) got `shouldBe` [ ("hoge", "hogeHOGE", 4, 16), ("quux", "quuxHOGE", 4, 16) ]