greskell 0.2.3.1 → 1.0.0.0
raw patch · 17 files changed
+1489/−671 lines, 17 filesdep +hashabledep ~greskell-corePVP ok
version bump matches the API change (PVP)
Dependencies added: hashable
Dependency ranges changed: greskell-core
API changes (from Hackage documentation)
- Data.Greskell.AsLabel: NoSuchAsLabel :: AsLookupException
- Data.Greskell.AsLabel: ParseError :: String -> AsLookupException
- Data.Greskell.AsLabel: data AsLookupException
- Data.Greskell.AsLabel: data SelectedMap a
- Data.Greskell.AsLabel: instance Data.Foldable.Foldable Data.Greskell.AsLabel.SelectedMap
- Data.Greskell.AsLabel: instance Data.Greskell.GraphSON.FromGraphSON a => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.AsLabel.SelectedMap a)
- Data.Greskell.AsLabel: instance Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (Data.Greskell.AsLabel.SelectedMap a)
- Data.Greskell.AsLabel: instance Data.Traversable.Traversable Data.Greskell.AsLabel.SelectedMap
- Data.Greskell.AsLabel: instance GHC.Base.Functor Data.Greskell.AsLabel.SelectedMap
- Data.Greskell.AsLabel: instance GHC.Classes.Eq Data.Greskell.AsLabel.AsLookupException
- Data.Greskell.AsLabel: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Greskell.AsLabel.SelectedMap a)
- Data.Greskell.AsLabel: instance GHC.Classes.Ord Data.Greskell.AsLabel.AsLookupException
- Data.Greskell.AsLabel: instance GHC.Exception.Type.Exception Data.Greskell.AsLabel.AsLookupException
- Data.Greskell.AsLabel: instance GHC.Show.Show Data.Greskell.AsLabel.AsLookupException
- Data.Greskell.AsLabel: instance GHC.Show.Show a => GHC.Show.Show (Data.Greskell.AsLabel.SelectedMap a)
- Data.Greskell.Extra: writeAllProperties :: (PropertyMap m, Property p, ToJSON v, Element e) => m p v -> Binder (Walk SideEffect e e)
- Data.Greskell.Graph: -- <tt>e</tt> is an <a>Edge</a> or VertexProperty.
- Data.Greskell.Graph: -- implementation and its settings.
- Data.Greskell.Graph: [aeInVLabel] :: AEdge -> Text
- Data.Greskell.Graph: [aeInV] :: AEdge -> GValue
- Data.Greskell.Graph: [aeOutVLabel] :: AEdge -> Text
- Data.Greskell.Graph: [aeOutV] :: AEdge -> GValue
- Data.Greskell.Graph: [aeProperties] :: AEdge -> PropertyMapSingle AProperty GValue
- Data.Greskell.Graph: [avProperties] :: AVertex -> PropertyMapList AVertexProperty GValue
- Data.Greskell.Graph: [avpProperties] :: AVertexProperty v -> PropertyMapSingle AProperty GValue
- Data.Greskell.Graph: allProperties :: PropertyMap m => m p v -> [p v]
- Data.Greskell.Graph: class FromGraphSONWithKey a
- Data.Greskell.Graph: class PropertyMap m
- Data.Greskell.Graph: data PropertyMapList p v
- Data.Greskell.Graph: data PropertyMapSingle p v
- Data.Greskell.Graph: fromProperties :: (PropertyMap m, Property p, Monoid (m p v)) => [p v] -> m p v
- Data.Greskell.Graph: instance (Data.Foldable.Foldable t, Data.Foldable.Foldable p) => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMapGeneric t p)
- Data.Greskell.Graph: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.FromGraphSONWithKey (p v)) => Data.Aeson.Types.FromJSON.FromJSON (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.FromGraphSONWithKey (p v)) => Data.Aeson.Types.FromJSON.FromJSON (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.FromGraphSONWithKey (p v)) => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.FromGraphSONWithKey (p v)) => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance (Data.Traversable.Traversable t, Data.Traversable.Traversable p) => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMapGeneric t p)
- Data.Greskell.Graph: instance (GHC.Base.Functor t, GHC.Base.Functor p) => GHC.Base.Functor (Data.Greskell.Graph.PropertyMapGeneric t p)
- Data.Greskell.Graph: instance Data.Foldable.Foldable p => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMapList p)
- Data.Greskell.Graph: instance Data.Foldable.Foldable p => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMapSingle p)
- Data.Greskell.Graph: instance Data.Greskell.Graph.PropertyMap Data.Greskell.Graph.PropertyMapList
- Data.Greskell.Graph: instance Data.Greskell.Graph.PropertyMap Data.Greskell.Graph.PropertyMapSingle
- Data.Greskell.Graph: instance Data.Greskell.GraphSON.FromGraphSON v => Data.Greskell.Graph.FromGraphSONWithKey (Data.Greskell.Graph.AProperty v)
- Data.Greskell.Graph: instance Data.Greskell.GraphSON.FromGraphSON v => Data.Greskell.Graph.FromGraphSONWithKey (Data.Greskell.Graph.AVertexProperty v)
- Data.Greskell.Graph: instance Data.Traversable.Traversable p => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMapList p)
- Data.Greskell.Graph: instance Data.Traversable.Traversable p => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMapSingle p)
- Data.Greskell.Graph: instance GHC.Base.Functor p => GHC.Base.Functor (Data.Greskell.Graph.PropertyMapList p)
- Data.Greskell.Graph: instance GHC.Base.Functor p => GHC.Base.Functor (Data.Greskell.Graph.PropertyMapSingle p)
- Data.Greskell.Graph: instance GHC.Base.Monoid (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance GHC.Base.Monoid (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance GHC.Base.Semigroup (t (p v)) => GHC.Base.Monoid (Data.Greskell.Graph.PropertyMapGeneric t p v)
- Data.Greskell.Graph: instance GHC.Base.Semigroup (t (p v)) => GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMapGeneric t p v)
- Data.Greskell.Graph: instance GHC.Classes.Eq (p v) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance GHC.Classes.Eq (p v) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance GHC.Classes.Eq (t (p v)) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMapGeneric t p v)
- Data.Greskell.Graph: instance GHC.Show.Show (p v) => GHC.Show.Show (Data.Greskell.Graph.PropertyMapList p v)
- Data.Greskell.Graph: instance GHC.Show.Show (p v) => GHC.Show.Show (Data.Greskell.Graph.PropertyMapSingle p v)
- Data.Greskell.Graph: instance GHC.Show.Show (t (p v)) => GHC.Show.Show (Data.Greskell.Graph.PropertyMapGeneric t p v)
- Data.Greskell.Graph: lookupList :: PropertyMap m => Text -> m p v -> [p v]
- Data.Greskell.Graph: lookupListValues :: (PropertyMap m, Property p) => Text -> m p v -> [v]
- Data.Greskell.Graph: lookupOne :: PropertyMap m => Text -> m p v -> Maybe (p v)
- Data.Greskell.Graph: lookupOneValue :: (PropertyMap m, Property p) => Text -> m p v -> Maybe v
- Data.Greskell.Graph: parseListValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser [v]
- Data.Greskell.Graph: parseNonEmptyValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser (NonEmpty v)
- Data.Greskell.Graph: parseOneValue :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser v
- Data.Greskell.Graph: putProperty :: (PropertyMap m, Property p) => p v -> m p v -> m p v
- Data.Greskell.Graph: removeProperty :: PropertyMap m => Text -> m p v -> m p v
+ Data.Greskell.AsLabel: PMapNoSuchKey :: Text -> PMapLookupException
+ Data.Greskell.AsLabel: PMapParseError :: Text -> String -> PMapLookupException
+ Data.Greskell.AsLabel: data PMapLookupException
+ Data.Greskell.AsLabel: instance Data.Greskell.PMap.PMapKey (Data.Greskell.AsLabel.AsLabel a)
+ Data.Greskell.AsLabel: instance Data.String.IsString (Data.Greskell.AsLabel.AsLabel a)
+ Data.Greskell.AsLabel: type SelectedMap = PMap Single
+ Data.Greskell.Extra: (<=:>) :: ToJSON b => Key a b -> b -> Binder (KeyValue a)
+ Data.Greskell.Extra: (<=?>) :: ToJSON b => Key a (Maybe b) -> Maybe b -> Binder (KeyValue a)
+ Data.Greskell.Extra: lookupAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException a
+ Data.Greskell.Extra: lookupAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ Maybe a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException (Maybe a)
+ Data.Greskell.Extra: lookupListAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException (NonEmpty a)
+ Data.Greskell.Extra: lookupListAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ Maybe a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException [Maybe a]
+ Data.Greskell.Extra: pMapToFail :: MonadFail m => Either PMapLookupException a -> m a
+ Data.Greskell.Extra: writeKeyValues :: Element e => [KeyValue e] -> Walk SideEffect e e
+ Data.Greskell.Extra: writePMapProperties :: (Foldable c, ToJSON v, Element e) => PMap c v -> Binder (Walk SideEffect e e)
+ Data.Greskell.GTraversal: [LabeledByProjection] :: AsLabel a -> ByProjection s a -> LabeledByProjection s
+ Data.Greskell.GTraversal: data LabeledByProjection s
+ Data.Greskell.GTraversal: gByL :: (ProjectionLike p, ToGreskell p) => AsLabel (ProjectionLikeEnd p) -> p -> LabeledByProjection (ProjectionLikeStart p)
+ Data.Greskell.GTraversal: gProject :: LabeledByProjection s -> [LabeledByProjection s] -> Walk Transform s (PMap Single GValue)
+ Data.Greskell.GTraversal: gValueMap :: Element s => Keys s -> Walk Transform s (PMap (ElementPropertyContainer s) GValue)
+ Data.Greskell.GTraversal: unsafeCastEnd :: (ToGTraversal g, WalkType c) => g c s e1 -> g c s e2
+ Data.Greskell.GTraversal: unsafeCastStart :: (ToGTraversal g, WalkType c) => g c s1 e -> g c s2 e
+ Data.Greskell.Graph: (-:) :: Key a b -> Keys a -> Keys a
+ Data.Greskell.Graph: -- of <a>NonEmptyLike</a> class.
+ Data.Greskell.Graph: ElementID :: GValue -> ElementID e
+ Data.Greskell.Graph: [KeyNoValue] :: Key a b -> KeyValue a
+ Data.Greskell.Graph: [KeysCons] :: Key a b -> Keys a -> Keys a
+ Data.Greskell.Graph: [KeysNil] :: Keys a
+ Data.Greskell.Graph: [unElementID] :: ElementID e -> GValue
+ Data.Greskell.Graph: class ElementData e
+ Data.Greskell.Graph: data Keys a
+ Data.Greskell.Graph: elementId :: ElementData e => e -> ElementID e
+ Data.Greskell.Graph: elementLabel :: ElementData e => e -> Text
+ Data.Greskell.Graph: infixr 5 -:
+ Data.Greskell.Graph: instance Data.Aeson.Types.FromJSON.FromJSON (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance Data.Aeson.Types.ToJSON.ToJSON (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance Data.Greskell.Graph.ElementData (Data.Greskell.Graph.AVertexProperty v)
+ Data.Greskell.Graph: instance Data.Greskell.Graph.ElementData Data.Greskell.Graph.AEdge
+ Data.Greskell.Graph: instance Data.Greskell.Graph.ElementData Data.Greskell.Graph.AVertex
+ Data.Greskell.Graph: instance Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance Data.Greskell.PMap.PMapKey (Data.Greskell.Graph.Key a b)
+ Data.Greskell.Graph: instance Data.Hashable.Class.Hashable (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance GHC.Base.Functor Data.Greskell.Graph.ElementID
+ Data.Greskell.Graph: instance GHC.Base.Monoid (Data.Greskell.Graph.Keys a)
+ Data.Greskell.Graph: instance GHC.Base.Semigroup (Data.Greskell.Graph.Keys a)
+ Data.Greskell.Graph: instance GHC.Classes.Eq (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance GHC.Generics.Generic (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: instance GHC.Show.Show (Data.Greskell.Graph.ElementID e)
+ Data.Greskell.Graph: newtype ElementID e
+ Data.Greskell.Graph: singletonKeys :: Key a b -> Keys a
+ Data.Greskell.Graph: unsafeCastElementID :: ElementID a -> ElementID b
+ Data.Greskell.Graph: unsafeCastKey :: Key a1 b1 -> Key a2 b2
+ Data.Greskell.Graph.PropertyMap: AProperty :: Text -> v -> AProperty v
+ Data.Greskell.Graph.PropertyMap: AVertexProperty :: ElementID (AVertexProperty v) -> Text -> v -> AVertexProperty v
+ Data.Greskell.Graph.PropertyMap: [apKey] :: AProperty v -> Text
+ Data.Greskell.Graph.PropertyMap: [apValue] :: AProperty v -> v
+ Data.Greskell.Graph.PropertyMap: [avpId] :: AVertexProperty v -> ElementID (AVertexProperty v)
+ Data.Greskell.Graph.PropertyMap: [avpLabel] :: AVertexProperty v -> Text
+ Data.Greskell.Graph.PropertyMap: [avpValue] :: AVertexProperty v -> v
+ Data.Greskell.Graph.PropertyMap: allProperties :: PropertyMap m => m p v -> [p v]
+ Data.Greskell.Graph.PropertyMap: class FromGraphSONWithKey a
+ Data.Greskell.Graph.PropertyMap: class PropertyMap m
+ Data.Greskell.Graph.PropertyMap: data AProperty v
+ Data.Greskell.Graph.PropertyMap: data AVertexProperty v
+ Data.Greskell.Graph.PropertyMap: data PropertyMapList p v
+ Data.Greskell.Graph.PropertyMap: data PropertyMapSingle p v
+ Data.Greskell.Graph.PropertyMap: fromProperties :: (PropertyMap m, Property p, Monoid (m p v)) => [p v] -> m p v
+ Data.Greskell.Graph.PropertyMap: instance (Data.Foldable.Foldable t, Data.Foldable.Foldable p) => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p)
+ Data.Greskell.Graph.PropertyMap: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (p v)) => Data.Aeson.Types.FromJSON.FromJSON (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (p v)) => Data.Aeson.Types.FromJSON.FromJSON (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (p v)) => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance (Data.Greskell.Graph.Property p, Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (p v), Data.Greskell.GraphSON.FromGraphSON (p v), Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (p v)) => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance (Data.Traversable.Traversable t, Data.Traversable.Traversable p) => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p)
+ Data.Greskell.Graph.PropertyMap: instance (GHC.Base.Functor t, GHC.Base.Functor p) => GHC.Base.Functor (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p)
+ Data.Greskell.Graph.PropertyMap: instance Data.Foldable.Foldable p => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMap.PropertyMapList p)
+ Data.Greskell.Graph.PropertyMap: instance Data.Foldable.Foldable p => Data.Foldable.Foldable (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p)
+ Data.Greskell.Graph.PropertyMap: instance Data.Greskell.Graph.PropertyMap.PropertyMap Data.Greskell.Graph.PropertyMap.PropertyMapList
+ Data.Greskell.Graph.PropertyMap: instance Data.Greskell.Graph.PropertyMap.PropertyMap Data.Greskell.Graph.PropertyMap.PropertyMapSingle
+ Data.Greskell.Graph.PropertyMap: instance Data.Greskell.GraphSON.FromGraphSON v => Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (Data.Greskell.Graph.AProperty v)
+ Data.Greskell.Graph.PropertyMap: instance Data.Greskell.GraphSON.FromGraphSON v => Data.Greskell.Graph.PropertyMap.FromGraphSONWithKey (Data.Greskell.Graph.AVertexProperty v)
+ Data.Greskell.Graph.PropertyMap: instance Data.Traversable.Traversable p => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMap.PropertyMapList p)
+ Data.Greskell.Graph.PropertyMap: instance Data.Traversable.Traversable p => Data.Traversable.Traversable (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Functor p => GHC.Base.Functor (Data.Greskell.Graph.PropertyMap.PropertyMapList p)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Functor p => GHC.Base.Functor (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Monoid (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Monoid (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Semigroup (t (p v)) => GHC.Base.Monoid (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Base.Semigroup (t (p v)) => GHC.Base.Semigroup (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Classes.Eq (p v) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Classes.Eq (p v) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Classes.Eq (t (p v)) => GHC.Classes.Eq (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Show.Show (p v) => GHC.Show.Show (Data.Greskell.Graph.PropertyMap.PropertyMapList p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Show.Show (p v) => GHC.Show.Show (Data.Greskell.Graph.PropertyMap.PropertyMapSingle p v)
+ Data.Greskell.Graph.PropertyMap: instance GHC.Show.Show (t (p v)) => GHC.Show.Show (Data.Greskell.Graph.PropertyMap.PropertyMapGeneric t p v)
+ Data.Greskell.Graph.PropertyMap: lookupList :: PropertyMap m => Text -> m p v -> [p v]
+ Data.Greskell.Graph.PropertyMap: lookupListValues :: (PropertyMap m, Property p) => Text -> m p v -> [v]
+ Data.Greskell.Graph.PropertyMap: lookupOne :: PropertyMap m => Text -> m p v -> Maybe (p v)
+ Data.Greskell.Graph.PropertyMap: lookupOneValue :: (PropertyMap m, Property p) => Text -> m p v -> Maybe v
+ Data.Greskell.Graph.PropertyMap: parseListValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser [v]
+ Data.Greskell.Graph.PropertyMap: parseNonEmptyValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser (NonEmpty v)
+ Data.Greskell.Graph.PropertyMap: parseOneValue :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser v
+ Data.Greskell.Graph.PropertyMap: putProperty :: (PropertyMap m, Property p) => p v -> m p v -> m p v
+ Data.Greskell.Graph.PropertyMap: removeProperty :: PropertyMap m => Text -> m p v -> m p v
+ Data.Greskell.NonEmptyLike: append :: NonEmptyLike t => t a -> t a -> t a
+ Data.Greskell.NonEmptyLike: class Foldable t => NonEmptyLike t
+ Data.Greskell.NonEmptyLike: instance Data.Greskell.NonEmptyLike.NonEmptyLike Data.Semigroup.First
+ Data.Greskell.NonEmptyLike: instance Data.Greskell.NonEmptyLike.NonEmptyLike Data.Semigroup.Last
+ Data.Greskell.NonEmptyLike: instance Data.Greskell.NonEmptyLike.NonEmptyLike GHC.Base.NonEmpty
+ Data.Greskell.NonEmptyLike: singleton :: NonEmptyLike t => a -> t a
+ Data.Greskell.NonEmptyLike: toNonEmpty :: NonEmptyLike t => t a -> NonEmpty a
+ Data.Greskell.PMap: -- | Type of the value associated with the key.
+ Data.Greskell.PMap: PMapNoSuchKey :: Text -> PMapLookupException
+ Data.Greskell.PMap: PMapParseError :: Text -> String -> PMapLookupException
+ Data.Greskell.PMap: class PMapKey k where {
+ Data.Greskell.PMap: data Multi a
+ Data.Greskell.PMap: data PMap c v
+ Data.Greskell.PMap: data PMapLookupException
+ Data.Greskell.PMap: instance Data.Foldable.Foldable Data.Greskell.PMap.Multi
+ Data.Greskell.PMap: instance Data.Foldable.Foldable c => Data.Foldable.Foldable (Data.Greskell.PMap.PMap c)
+ Data.Greskell.PMap: instance Data.Greskell.AsIterator.AsIterator (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance Data.Greskell.GraphSON.FromGraphSON (c v) => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance Data.Greskell.GraphSON.FromGraphSON a => Data.Greskell.GraphSON.FromGraphSON (Data.Greskell.PMap.Multi a)
+ Data.Greskell.PMap: instance Data.Greskell.GraphSON.GraphSONTyped.GraphSONTyped (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance Data.Greskell.NonEmptyLike.NonEmptyLike Data.Greskell.PMap.Multi
+ Data.Greskell.PMap: instance Data.Greskell.NonEmptyLike.NonEmptyLike c => GHC.Base.Monoid (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance Data.Greskell.NonEmptyLike.NonEmptyLike c => GHC.Base.Semigroup (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance Data.Greskell.PMap.PMapKey Data.Text.Internal.Text
+ Data.Greskell.PMap: instance Data.Traversable.Traversable Data.Greskell.PMap.Multi
+ Data.Greskell.PMap: instance Data.Traversable.Traversable c => Data.Traversable.Traversable (Data.Greskell.PMap.PMap c)
+ Data.Greskell.PMap: instance GHC.Base.Functor Data.Greskell.PMap.Multi
+ Data.Greskell.PMap: instance GHC.Base.Functor c => GHC.Base.Functor (Data.Greskell.PMap.PMap c)
+ Data.Greskell.PMap: instance GHC.Base.Semigroup (Data.Greskell.PMap.Multi a)
+ Data.Greskell.PMap: instance GHC.Classes.Eq (c v) => GHC.Classes.Eq (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance GHC.Classes.Eq Data.Greskell.PMap.PMapLookupException
+ Data.Greskell.PMap: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Greskell.PMap.Multi a)
+ Data.Greskell.PMap: instance GHC.Classes.Ord Data.Greskell.PMap.PMapLookupException
+ Data.Greskell.PMap: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Greskell.PMap.Multi a)
+ Data.Greskell.PMap: instance GHC.Exception.Type.Exception Data.Greskell.PMap.PMapLookupException
+ Data.Greskell.PMap: instance GHC.Show.Show (c v) => GHC.Show.Show (Data.Greskell.PMap.PMap c v)
+ Data.Greskell.PMap: instance GHC.Show.Show Data.Greskell.PMap.PMapLookupException
+ Data.Greskell.PMap: instance GHC.Show.Show a => GHC.Show.Show (Data.Greskell.PMap.Multi a)
+ Data.Greskell.PMap: keyText :: PMapKey k => k -> Text
+ Data.Greskell.PMap: lookup :: (PMapKey k, NonEmptyLike c) => k -> PMap c v -> Maybe v
+ Data.Greskell.PMap: lookupAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException a
+ Data.Greskell.PMap: lookupAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ Maybe a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException (Maybe a)
+ Data.Greskell.PMap: lookupAsM :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a, MonadThrow m) => k -> PMap c GValue -> m a
+ Data.Greskell.PMap: lookupList :: (PMapKey k, NonEmptyLike c) => k -> PMap c v -> [v]
+ Data.Greskell.PMap: lookupListAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException (NonEmpty a)
+ Data.Greskell.PMap: lookupListAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ Maybe a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException [Maybe a]
+ Data.Greskell.PMap: lookupM :: (PMapKey k, NonEmptyLike c, MonadThrow m) => k -> PMap c v -> m v
+ Data.Greskell.PMap: pMapDecribeError :: PMapLookupException -> String
+ Data.Greskell.PMap: pMapDelete :: Text -> PMap c v -> PMap c v
+ Data.Greskell.PMap: pMapFromList :: NonEmptyLike c => [(Text, v)] -> PMap c v
+ Data.Greskell.PMap: pMapInsert :: NonEmptyLike c => Text -> v -> PMap c v -> PMap c v
+ Data.Greskell.PMap: pMapLookup :: NonEmptyLike c => Text -> PMap c v -> [v]
+ Data.Greskell.PMap: pMapToFail :: MonadFail m => Either PMapLookupException a -> m a
+ Data.Greskell.PMap: pMapToList :: Foldable c => PMap c v -> [(Text, v)]
+ Data.Greskell.PMap: pMapToThrow :: MonadThrow m => Either PMapLookupException a -> m a
+ Data.Greskell.PMap: type Single = First
+ Data.Greskell.PMap: type family PMapValue k :: *;
+ Data.Greskell.PMap: }
- Data.Greskell.AsLabel: lookup :: AsLabel a -> SelectedMap b -> Maybe b
+ Data.Greskell.AsLabel: lookup :: (PMapKey k, NonEmptyLike c) => k -> PMap c v -> Maybe v
- Data.Greskell.AsLabel: lookupAs :: FromGraphSON a => AsLabel a -> SelectedMap GValue -> Either AsLookupException a
+ Data.Greskell.AsLabel: lookupAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a) => k -> PMap c GValue -> Either PMapLookupException a
- Data.Greskell.AsLabel: lookupAsM :: (MonadThrow m, FromGraphSON a) => AsLabel a -> SelectedMap GValue -> m a
+ Data.Greskell.AsLabel: lookupAsM :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a, MonadThrow m) => k -> PMap c GValue -> m a
- Data.Greskell.AsLabel: lookupM :: MonadThrow m => AsLabel a -> SelectedMap b -> m b
+ Data.Greskell.AsLabel: lookupM :: (PMapKey k, NonEmptyLike c, MonadThrow m) => k -> PMap c v -> m v
- Data.Greskell.GTraversal: gV' :: [Greskell GValue] -> Walk Transform s AVertex
+ Data.Greskell.GTraversal: gV' :: [Greskell (ElementID AVertex)] -> Walk Transform s AVertex
- Data.Greskell.GTraversal: sE' :: [Greskell GValue] -> Greskell GraphTraversalSource -> GTraversal Transform () AEdge
+ Data.Greskell.GTraversal: sE' :: [Greskell (ElementID AEdge)] -> Greskell GraphTraversalSource -> GTraversal Transform () AEdge
- Data.Greskell.GTraversal: sV' :: [Greskell GValue] -> Greskell GraphTraversalSource -> GTraversal Transform () AVertex
+ Data.Greskell.GTraversal: sV' :: [Greskell (ElementID AVertex)] -> Greskell GraphTraversalSource -> GTraversal Transform () AVertex
- Data.Greskell.Graph: -- class. If you don't care, use <a>AVertexProperty</a> if type
+ Data.Greskell.Graph: -- class.
- Data.Greskell.Graph: -- | ID type of the <a>Vertex</a> this edge connects.
+ Data.Greskell.Graph: -- | Container type of the properties of the <a>Element</a>. It should be
- Data.Greskell.Graph: AEdge :: GValue -> Text -> Text -> Text -> GValue -> GValue -> PropertyMapSingle AProperty GValue -> AEdge
+ Data.Greskell.Graph: AEdge :: ElementID AEdge -> Text -> AEdge
- Data.Greskell.Graph: AVertex :: GValue -> Text -> PropertyMapList AVertexProperty GValue -> AVertex
+ Data.Greskell.Graph: AVertex :: ElementID AVertex -> Text -> AVertex
- Data.Greskell.Graph: AVertexProperty :: GValue -> Text -> v -> PropertyMapSingle AProperty GValue -> AVertexProperty v
+ Data.Greskell.Graph: AVertexProperty :: ElementID (AVertexProperty v) -> Text -> v -> AVertexProperty v
- Data.Greskell.Graph: Key :: Greskell Text -> Key a b
+ Data.Greskell.Graph: Key :: Text -> Key a b
- Data.Greskell.Graph: [aeId] :: AEdge -> GValue
+ Data.Greskell.Graph: [aeId] :: AEdge -> ElementID AEdge
- Data.Greskell.Graph: [avId] :: AVertex -> GValue
+ Data.Greskell.Graph: [avId] :: AVertex -> ElementID AVertex
- Data.Greskell.Graph: [avpId] :: AVertexProperty v -> GValue
+ Data.Greskell.Graph: [avpId] :: AVertexProperty v -> ElementID (AVertexProperty v)
- Data.Greskell.Graph: [unKey] :: Key a b -> Greskell Text
+ Data.Greskell.Graph: [unKey] :: Key a b -> Text
- Data.Greskell.Graph: class (Element e) => Edge e where {
+ Data.Greskell.Graph: class (Element e) => Edge e
- Data.Greskell.Graph: class Element e where {
+ Data.Greskell.Graph: class ElementData e => Element e where {
- Data.Greskell.Graph: type family EdgeVertexID e;
+ Data.Greskell.Graph: type family ElementPropertyContainer e :: * -> *;
Files
- ChangeLog.md +80/−0
- greskell.cabal +14/−7
- src/Data/Greskell.hs +6/−1
- src/Data/Greskell/AsLabel.hs +21/−42
- src/Data/Greskell/Extra.hs +97/−12
- src/Data/Greskell/GTraversal.hs +93/−33
- src/Data/Greskell/Graph.hs +180/−304
- src/Data/Greskell/Graph/PropertyMap.hs +242/−0
- src/Data/Greskell/NonEmptyLike.hs +44/−0
- src/Data/Greskell/PMap.hs +246/−0
- test/Data/Greskell/ExtraSpec.hs +12/−2
- test/Data/Greskell/GTraversalSpec.hs +7/−4
- test/Data/Greskell/Graph/PropertyMapSpec.hs +100/−0
- test/Data/Greskell/GraphSpec.hs +16/−205
- test/Data/Greskell/PMapSpec.hs +127/−0
- test/ServerBehaviorTest.hs +67/−3
- test/ServerTest.hs +137/−58
ChangeLog.md view
@@ -1,5 +1,85 @@ # Revision history for greskell +## 1.0.0.0 -- 2019-12-27++* Add some test cases to server-behavior-test+* Add PMap and NonEmptyLike modules.++### Data.Greskell module++* Export PMap module.++### Graph module - Element class and ElementID++* [BREAKING CHANGE] Now `ElementID` is a newtype for `GValue`. It was+ an associated type family in `Element` class. However, I think there+ was no point to make it polymorphic.+* [BREAKING CHANGE] Remove `EdgeVertexID` type family from `Edge` class.+* [BREAKING CHANGE] Add `ElementData` class, and set it as a super class of `Element`.+* [BREAKING CHANGE] Type of the ID field for `AVertex`, `AEdge` and+ `AVertexProperty` is now `ElementID`, not `GValue`.+* Add `unsafeCastElementID` function.+* Add `ElementPropertyContainer` type family to `Element` class. It's+ necessary for `gValueMap` traversal.++### Graph module - "reference" model for graph element types++Now the graph element types don't contain properties. They only have+their ID and label (and the value if it's a VertexProperty). This is+because some graph implementation of TinkerPop don't return properties+when it returns graph elements. See #6.++* [BREAKING CHANGE] Remove `avProperties` field from `AVertex`.+* [BREAKING CHANGE] Remove `aeInVLabel`, `aeOutVLabel`, `aeInV`,+ `aeOutV` and `aeProperties` fields from `AEdge`.+* [BREAKING CHANGE] Remove `avpProperties` field from `AVertexProperty`.+* [BREAKING CHANGE] Move `PropertyMap` class, `FromGraphSONWithKey`+ class, `PropertyMapSingle` type, `PropertyMapList` types and related+ functions to `Graph.PropertyMap` module. They are now all+ deprecated. Use `PMap` module instead.++### Graph module - Key for element property++* [BREAKING CHANGE] Internal of `Key` is modified from `Greskell Text`+ to `Text`, so that it can be an instance of `PMapKey` class.+* [BREAKING CHANGE] Add `KeyNoValue` data constructor for `KeyValue`+ type.+* Add `unsafeCastKey` function.+* Add `Keys` type and related functions.++### GTraversal module++* [BREAKING CHANGE] Change the signature of the following functions+ because now `ElementID` is a newtype, not a type alias.+ * `sV'`+ * `gV'`+ * `sE'`+* Add `gValueMap`, `gProject` and `gByL` functions.+* Add `LabeledByProjection` type.+* Add `unsafeCastStart` and `unsafeCastEnd` functions to `ToGTraversal` class.++### Extra module++* [BREAKING CHANGE] Remove `writeAllProperties` because `PropertyMap`+ is deprecated.+* Add `writeKeyValues` function.+* Add `writePMapProperties` function.+* Add `(<=:>)` and `(<=?>)` operators.+* Re-export property reader functions such as `lookupAs`.++### AsLabel module++* Now `SelectedMap` is a specialized type alias for `PMap`.+* [BREAKING CHANGE] `lookup`, `lookupM`, `lookupAs` and `lookupAsM`+ are now re-exports from `PMap` module, whose signature is more+ polymorphic than the original ones and have a different exception+ type.+* [BREAKING CHANGE] `AsLookupException` is removed. Use+ `PMapLookupException` instead.+* Make `AsLabel` an instance of `PMapKey`.+* Add `IsString` instance to `AsLabel`.++ ## 0.2.3.1 -- 2019-10-02 * Confirm test with `semigroups-0.19.1`.
greskell.cabal view
@@ -1,5 +1,5 @@ name: greskell-version: 0.2.3.1+version: 1.0.0.0 author: Toshio Ito <debug.ito@gmail.com> maintainer: Toshio Ito <debug.ito@gmail.com> license: BSD3@@ -26,30 +26,34 @@ -- default-extensions: other-extensions: OverloadedStrings, GeneralizedNewtypeDeriving, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeFamilies,- GADTs, DeriveTraversable+ GADTs, DeriveTraversable, DeriveGeneric exposed-modules: Data.Greskell, Data.Greskell.Gremlin, Data.Greskell.Binder, Data.Greskell.Graph,+ Data.Greskell.Graph.PropertyMap, Data.Greskell.GTraversal, Data.Greskell.AsLabel,- Data.Greskell.Extra+ Data.Greskell.Extra,+ Data.Greskell.PMap,+ Data.Greskell.NonEmptyLike -- other-modules: build-depends: base >=4.9.0.0 && <4.13,- greskell-core >=0.1.2.0 && <0.2,+ greskell-core >=0.1.3.0 && <0.2, text >=1.2.2.1 && <1.3, transformers >=0.5.2 && <0.6, aeson >=0.11.2.1 && <1.5, unordered-containers >=0.2.7.1 && <0.3, semigroups >=0.18.2 && <0.20, vector >=0.12.0.1 && <0.13,- exceptions >=0.8.3 && <0.11+ exceptions >=0.8.3 && <0.11,+ hashable >=1.2.7.0 && <1.4 test-suite spec type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test- ghc-options: -Wall -fno-warn-unused-imports "-with-rtsopts=-M512m"+ ghc-options: -Wall -fno-warn-unused-imports -fno-warn-deprecations "-with-rtsopts=-M512m" main-is: Spec.hs -- default-extensions: other-extensions: OverloadedStrings@@ -57,7 +61,9 @@ Data.Greskell.GTraversalSpec, Data.Greskell.GremlinSpec, Data.Greskell.GraphSpec,- Data.Greskell.ExtraSpec+ Data.Greskell.Graph.PropertyMapSpec,+ Data.Greskell.ExtraSpec,+ Data.Greskell.PMapSpec build-depends: base, text, aeson, unordered-containers, greskell, greskell-core, hspec >=2.2.3,@@ -120,6 +126,7 @@ flag server-behavior-test description: Tests about the behavior of Gremlin Server, rather than greskell.+ Note that this test clears the content of the Gremlin Server. default: False manual: True
src/Data/Greskell.hs view
@@ -10,6 +10,9 @@ -- packages. The following modules are excluded from re-export: -- -- - "Data.Greskell.Extra": extra utility functions.+-- - "Data.Greskell.NonEmptyLike": NonEmptyLike class, which is a+-- class of non-empty containers.+-- - "Data.Greskell.Graph.PropertyMap": deprecated PropertyMap class. module Data.Greskell ( module Data.Greskell.Greskell,@@ -20,7 +23,8 @@ module Data.Greskell.GraphSON, module Data.Greskell.GMap, module Data.Greskell.AsIterator,- module Data.Greskell.AsLabel+ module Data.Greskell.AsLabel,+ module Data.Greskell.PMap ) where import Data.Greskell.Greskell@@ -32,3 +36,4 @@ import Data.Greskell.GMap import Data.Greskell.AsIterator import Data.Greskell.AsLabel+import Data.Greskell.PMap
src/Data/Greskell/AsLabel.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, TypeFamilies, GeneralizedNewtypeDeriving, DeriveTraversable #-}+{-# LANGUAGE TypeFamilies #-} -- | -- Module: Data.Greskell.AsLabel -- Description: Label string used in .as step@@ -6,13 +6,15 @@ -- -- @since 0.2.2.0 module Data.Greskell.AsLabel- ( AsLabel(..),+ ( -- * AsLabel+ AsLabel(..), SelectedMap,+ -- * Re-exports lookup, lookupM, lookupAs, lookupAsM,- AsLookupException(..)+ PMapLookupException(..) ) where import Prelude hiding (lookup)@@ -25,14 +27,24 @@ import Data.Greskell.GraphSON (GValue, GraphSONTyped(..), FromGraphSON(..), parseEither) import Data.Greskell.Greskell (ToGreskell(..)) import qualified Data.Greskell.Greskell as Greskell+import Data.String (IsString(..)) import Data.Text (Text) import Data.Traversable (Traversable) +import Data.Greskell.PMap+ ( PMap, PMapKey(..), Single,+ lookup, lookupM, lookupAs, lookupAsM, PMapLookupException(..)+ )+ -- | 'AsLabel' @a@ represents a label string used in @.as@ step -- pointing to the data of type @a@. newtype AsLabel a = AsLabel { unAsLabel :: Text } deriving (Show,Eq,Ord) +-- | @since 1.0.0.0+instance IsString (AsLabel a) where+ fromString = AsLabel . fromString+ -- | Returns the 'Text' as a Gremlin string. instance ToGreskell (AsLabel a) where type GreskellReturn (AsLabel a) = Text@@ -42,45 +54,12 @@ instance Functor AsLabel where fmap _ (AsLabel t) = AsLabel t +-- | @since 1.0.0.0+instance PMapKey (AsLabel a) where+ type PMapValue (AsLabel a) = a+ keyText = unAsLabel+ -- | A map keyed with 'AsLabel'. Obtained from @.select@ step, for -- example.-newtype SelectedMap a = SelectedMap (HashMap Text a)- deriving (Show,Eq,Functor,Foldable,Traversable)--instance GraphSONTyped (SelectedMap a) where- gsonTypeFor _ = "g:Map"--instance FromGraphSON a => FromGraphSON (SelectedMap a) where- parseGraphSON gv = fmap SelectedMap $ parseGraphSON gv---- | An 'Exception' raised by 'lookupM' and 'lookupAsM'.-data AsLookupException = NoSuchAsLabel- -- ^ The 'SelectedMap' does not have the- -- given 'AsLabel' as the key.- | ParseError String- -- ^ Failed to parse the value into the type- -- that the 'AsLabel' indicates. The 'String'- -- is the error message.- deriving (Show,Eq,Ord)--instance Exception AsLookupException---- | Get value from 'SelectedMap'.-lookup :: AsLabel a -> SelectedMap b -> Maybe b-lookup (AsLabel l) (SelectedMap m) = HM.lookup l m---- | 'MonadThrow' version of 'lookup'. If there is no value for the--- 'AsLabel', it throws 'NoSuchAsLabel'.-lookupM :: MonadThrow m => AsLabel a -> SelectedMap b -> m b-lookupM l m = maybe (throwM NoSuchAsLabel) return $ lookup l m---- | Get value from 'SelectedMap' and parse the value into @a@.-lookupAs :: FromGraphSON a => AsLabel a -> SelectedMap GValue -> Either AsLookupException a-lookupAs l m =- case lookup l m of- Nothing -> Left NoSuchAsLabel- Just gv -> either (Left . ParseError) Right $ parseEither gv+type SelectedMap = PMap Single --- | 'MonadThrow' version of 'lookupAs'.-lookupAsM :: (MonadThrow m, FromGraphSON a) => AsLabel a -> SelectedMap GValue -> m a-lookupAsM l m = either throwM return $ lookupAs l m
src/Data/Greskell/Extra.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | -- Module: Data.Greskell.Extra -- Description: Extra utility functions implemented by Greskell@@ -7,35 +8,119 @@ -- -- @since 0.2.3.0 module Data.Greskell.Extra- ( writePropertyKeyValues,- writeAllProperties+ ( -- * Property readers+ -- $readers+ lookupAs,+ lookupAs',+ lookupListAs,+ lookupListAs',+ pMapToFail,+ -- * Property writers+ writeKeyValues,+ (<=:>),+ (<=?>),+ writePropertyKeyValues,+ writePMapProperties ) where import Data.Aeson (ToJSON)+import Data.Foldable (Foldable) import Data.Greskell.Binder (Binder, newBind) import Data.Greskell.Graph- ( PropertyMap(..), Property(..), Element+ ( Property(..), Element, KeyValue(..), (=:), Key ) import qualified Data.Greskell.Graph as Graph import Data.Greskell.GTraversal (Walk, SideEffect, gProperty)+import Data.Greskell.PMap+ ( PMap, pMapToList,+ lookupAs,+ lookupAs',+ lookupListAs,+ lookupListAs',+ pMapToFail+ ) import Data.Monoid (mconcat) import Data.Text (Text) +-- $setup+--+-- >>> :set -XOverloadedStrings+-- >>> import Data.Greskell.Binder (runBinder)+-- >>> import Data.Greskell.Greskell (toGremlin)+-- >>> import Data.Greskell.Graph (AVertex)+-- >>> import Data.List (sortBy)+-- >>> import Data.Ord (comparing)+-- >>> import qualified Data.HashMap.Strict as HashMap++-- $readers+--+-- Re-export property readers.+--+-- @since 1.0.0.0+ -- | Make a series of @.property@ steps to write the given key-value -- pairs as properties. -- -- @since 0.2.3.0+--+-- >>> let binder = (writePropertyKeyValues [("age", (21 :: Int))] :: Binder (Walk SideEffect AVertex AVertex))+-- >>> let (walk, binding) = runBinder binder+-- >>> toGremlin walk+-- "__.property(\"age\",__v0).identity()"+-- >>> sortBy (comparing fst) $ HashMap.toList binding+-- [("__v0",Number 21.0)] writePropertyKeyValues :: (ToJSON v, Element e) => [(Text, v)] -> Binder (Walk SideEffect e e)-writePropertyKeyValues pairs = fmap mconcat $ mapM toPropStep pairs+writePropertyKeyValues pairs = fmap writeKeyValues $ mapM toKeyValue pairs where- toPropStep (key, value) = fmap (gProperty $ Graph.key key) $ newBind value+ toKeyValue (key, value) = Graph.key key <=:> value --- | Make a series of @.property@ steps to write all properties in the--- given 'PropertyMap'.+-- | Make a series of @.property@ steps to write the given key-value+-- pairs as properties. Use '<=:>' and '<=?>' to make a 'KeyValue'+-- within 'Binder'. ----- @since 0.2.3.0-writeAllProperties :: (PropertyMap m, Property p, ToJSON v, Element e)- => m p v -> Binder (Walk SideEffect e e)-writeAllProperties ps = writePropertyKeyValues $ map toPair $ allProperties ps+-- >>> let keyAge = ("age" :: Key AVertex Int)+-- >>> let keyName = ("name" :: Key AVertex Text)+-- >>> let (walk, binding) = runBinder $ writeKeyValues <$> sequence [keyAge <=:> 21, keyName <=:> "Josh"]+-- >>> toGremlin walk+-- "__.property(\"age\",__v0).property(\"name\",__v1).identity()"+-- >>> sortBy (comparing fst) $ HashMap.toList binding+-- [("__v0",Number 21.0),("__v1",String "Josh")]+--+-- @since 1.0.0.0+writeKeyValues :: Element e => [KeyValue e] -> Walk SideEffect e e+writeKeyValues pairs = mconcat $ toPropStep =<< pairs where- toPair prop = (propertyKey prop, propertyValue prop)+ toPropStep (KeyValue k v) = [gProperty k v]+ toPropStep (KeyNoValue _) = []++-- | Make a series of @.property@ steps to write all properties in the+-- given 'PMap'.+--+-- @since 1.0.0.0+writePMapProperties :: (Foldable c, ToJSON v, Element e)+ => PMap c v -> Binder (Walk SideEffect e e)+writePMapProperties = writePropertyKeyValues . pMapToList++-- | Like '=:', but this one takes a real value, binds it into a+-- 'Greskell' value and returns 'KeyValue'.+--+-- @since 1.0.0.0+(<=:>) :: ToJSON b => Key a b -> b -> Binder (KeyValue a)+(<=:>) k v = (=:) k <$> newBind v++-- | Like '<=:>', but this one is for an optional property. If the+-- value is 'Just', it's equivalent to '<=:>'. If the value is+-- 'Nothing', it returns 'KeyNoValue'.+--+-- >>> let keyNName = ("nickname" :: Key AVertex (Maybe Text))+-- >>> let keyCompany = ("company" :: Key AVertex (Maybe Text))+-- >>> let (walk, binding) = runBinder $ writeKeyValues <$> sequence [keyNName <=?> Nothing, keyCompany <=?> Just "foobar.com"]+-- >>> toGremlin walk+-- "__.property(\"company\",__v0).identity()"+-- >>> sortBy (comparing fst) $ HashMap.toList binding+-- [("__v0",String "foobar.com")]+--+-- @since 1.0.0.0+(<=?>) :: ToJSON b => Key a (Maybe b) -> Maybe b -> Binder (KeyValue a)+(<=?>) k v@(Just _) = k <=:> v+(<=?>) k Nothing = return $ KeyNoValue k
src/Data/Greskell/GTraversal.hs view
@@ -102,10 +102,12 @@ gProperties, gId, gLabel,+ gValueMap, gSelect1, gSelectN, gSelectBy1, gSelectByN,+ gProject, -- ** Summarizing steps gFold, gCount,@@ -144,9 +146,11 @@ ByProjection(..), ProjectionLike(..), ByComparator(..),+ LabeledByProjection(..), gBy, gBy1,- gBy2+ gBy2,+ gByL ) where import Control.Applicative ((<$>), (<*>))@@ -155,6 +159,7 @@ import qualified Control.Category as Category import Data.Aeson (Value) import Data.Bifunctor (Bifunctor(bimap))+import Data.Foldable (foldl') import Data.List.NonEmpty (NonEmpty(..)) import Data.Monoid ((<>), mconcat, Monoid(..)) import Data.Semigroup (Semigroup, sconcat)@@ -165,12 +170,12 @@ import qualified Data.Text.Lazy as TL import Data.Greskell.Graph- ( Element(..), Vertex, Edge, Property(..),- AVertex, AEdge,+ ( Element(..), Property(..), ElementID(..), Vertex, Edge,+ AVertex, AEdge, AVertexProperty, T, Key, Cardinality,- KeyValue(..)+ KeyValue(..), Keys(..) )-import Data.Greskell.GraphSON (GValue)+import Data.Greskell.GraphSON (GValue, FromGraphSON) import Data.Greskell.Gremlin ( Comparator(..), P@@ -181,6 +186,7 @@ ) import Data.Greskell.AsIterator (AsIterator(IteratorItem)) import Data.Greskell.AsLabel (AsLabel, SelectedMap)+import Data.Greskell.PMap (PMap, Single) -- $setup --@@ -188,7 +194,7 @@ -- >>> import Data.Function ((&)) -- >>> import Data.Greskell.Greskell (gvalueInt) -- >>> import Data.Greskell.Gremlin (pBetween, pEq, pLte, oDecr, oIncr)--- >>> import Data.Greskell.Graph (tId, cList, (=:), AVertex, AVertexProperty)+-- >>> import Data.Greskell.Graph (tId, cList, (=:), AVertex, AVertexProperty, (-:)) -- >>> import Data.Greskell.GraphSON (GValueBody(..)) -- | @GraphTraversal@ class object of TinkerPop. It takes data @s@@@ -237,11 +243,27 @@ toGTraversal :: WalkType c => g c s e -> GTraversal c s e liftWalk :: (WalkType from, WalkType to, Lift from to) => g from s e -> g to s e -- ^ Lift 'WalkType' @from@ to @to@. Use this for type matching.+ + unsafeCastStart :: WalkType c => g c s1 e -> g c s2 e+ -- ^ Unsafely cast the start type @s1@ into @s2@.+ --+ -- It is recommended that @s2@ is coercible to @s1@ in terms of+ -- 'FromGraphSON'. That is, if @s2@ can parse a 'GValue', @s1@+ -- should also be able to parse that 'GValue'.+ --+ -- @since 1.0.0.0 + unsafeCastEnd :: WalkType c => g c s e1 -> g c s e2+ -- ^ Unsafely cast the end type @e1@ into @e2@. See+ -- 'unsafeCastStart'.+ --+ -- @since 1.0.0.0+ instance ToGTraversal GTraversal where toGTraversal = id liftWalk (GTraversal g) = GTraversal $ unsafeGreskellLazy $ toGremlinLazy g-+ unsafeCastStart (GTraversal g) = GTraversal $ unsafeGreskellLazy $ toGremlinLazy g+ unsafeCastEnd (GTraversal g) = GTraversal $ unsafeGreskellLazy $ toGremlinLazy g -- | A chain of one or more Gremlin steps. Like 'GTraversal', type @s@ -- is the input, type @e@ is the output, and type @c@ is a marker to@@ -288,6 +310,8 @@ instance ToGTraversal Walk where toGTraversal (Walk t) = GTraversal $ unsafeGreskellLazy ("__" <> t) liftWalk (Walk t) = Walk t+ unsafeCastStart (Walk t) = Walk t+ unsafeCastEnd (Walk t) = Walk t -- | The 'Walk' is first converted to 'GTraversal', and it's converted -- to 'Greskell'.@@ -398,9 +422,9 @@ -- | Monomorphic version of 'sV'. ----- >>> toGremlin (source "g" & sV' (map gvalueInt ([1,2,3] :: [Int])))+-- >>> toGremlin (source "g" & sV' (map (fmap ElementID . gvalueInt) ([1,2,3] :: [Int]))) -- "g.V(1,2,3)"-sV' :: [Greskell GValue]+sV' :: [Greskell (ElementID AVertex)] -- ^ vertex IDs -> Greskell GraphTraversalSource -> GTraversal Transform () AVertex sV' = sV@@ -414,11 +438,11 @@ -- | Monomorphic version of 'sE'. ----- >>> toGremlin (source "g" & sE' (map gvalueInt ([1] :: [Int])))+-- >>> toGremlin (source "g" & sE' (map (fmap ElementID . gvalueInt) ([1] :: [Int]))) -- "g.E(1)"-sE' :: [Greskell GValue]- -> Greskell GraphTraversalSource- -> GTraversal Transform () AEdge+sE' :: [Greskell (ElementID AEdge)] -- ^ edge IDs+ -> Greskell GraphTraversalSource+ -> GTraversal Transform () AEdge sE' = sE -- | @.addV()@ method on 'GraphTraversalSource'.@@ -529,12 +553,6 @@ gFilter :: (ToGTraversal g, WalkType c, WalkType p, Split c p) => g c s e -> Walk p s s gFilter walk = unsafeWalk "filter" [travToG walk] ---- TODO--- gValueMap, gProperty etc. should use Key type as an argument.--- Note that .property step is very tricky. Read the doc carefully.--- - -- | @.has@ step with one argument. -- -- >>> toGremlin (source "g" & sV' [] &. gHas1 "age")@@ -603,7 +621,7 @@ -- | @.hasId@ step. ----- >>> toGremlin (source "g" & sV' [] &. gHasId (gvalueInt $ (7 :: Int)))+-- >>> toGremlin (source "g" & sV' [] &. gHasId (fmap ElementID $ gvalueInt $ (7 :: Int))) -- "g.V().hasId(7)" gHasId :: (Element s, WalkType c) => Greskell (ElementID s) -> Walk c s s gHasId = liftWalk . gHasId'@@ -614,7 +632,7 @@ -- | @.hasId@ step with 'P' type. Supported since TinkerPop 3.2.7. ----- >>> toGremlin (source "g" & sV' [] &. gHasIdP (pLte $ gvalueInt (100 :: Int)))+-- >>> toGremlin (source "g" & sV' [] &. gHasIdP (pLte $ fmap ElementID $ gvalueInt (100 :: Int))) -- "g.V().hasId(P.lte(100))" gHasIdP :: (Element s, WalkType c) => Greskell (P (ElementID s))@@ -850,6 +868,19 @@ ByComparatorComp comp -> [toGremlin comp] ByComparatorProjComp (ByProjection p) comp -> [toGremlin p, toGremlin comp] +-- | A 'ByProjection' associated with an 'AsLabel'. You can construct+-- it by 'gByL'.+--+-- @since 1.0.0.0+data LabeledByProjection s where+ LabeledByProjection :: AsLabel a -> ByProjection s a -> LabeledByProjection s++-- | @.by@ step associated with an 'AsLabel'.+--+-- @since 1.0.0.0+gByL :: (ProjectionLike p, ToGreskell p) => AsLabel (ProjectionLikeEnd p) -> p -> LabeledByProjection (ProjectionLikeStart p)+gByL l p = LabeledByProjection l $ gBy p+ -- | @.flatMap@ step. -- -- @.flatMap@ step is a 'Transform' step even if the child walk is@@ -880,7 +911,7 @@ -- | Monomorphic version of 'gV'. -- -- @since 0.2.0.0-gV' :: [Greskell GValue] -> Walk Transform s AVertex+gV' :: [Greskell (ElementID AVertex)] -> Walk Transform s AVertex gV' = gV -- | @.as@ step.@@ -923,6 +954,22 @@ gLabel :: Element s => Walk Transform s Text gLabel = unsafeWalk "label" [] +-- | @.valueMap@ step.+--+-- >>> toGremlin (source "g" & sV' [] &. gValueMap KeysNil)+-- "g.V().valueMap()"+-- >>> toGremlin (source "g" & sV' [] &. gValueMap ("name" -: "age" -: KeysNil))+-- "g.V().valueMap(\"name\",\"age\")"+--+-- @since 1.0.0.0+gValueMap :: Element s+ => Keys s+ -> Walk Transform s (PMap (ElementPropertyContainer s) GValue)+gValueMap keys = unsafeWalk "valueMap" $ toGremlinKeys keys+ where+ toGremlinKeys KeysNil = []+ toGremlinKeys (KeysCons k rest) = toGremlin k : toGremlinKeys rest+ -- | @.select@ step with one argument. -- -- @since 0.2.2.0@@ -954,6 +1001,24 @@ gSelectByN :: AsLabel a -> AsLabel a -> [AsLabel a] -> ByProjection a b -> Walk Transform s (SelectedMap b) gSelectByN l1 l2 ls bp = modulateWith (unsafeChangeEnd $ gSelectN l1 l2 ls) [byStep bp] +-- | @.project@ step.+--+-- >>> let name_label = ("a" :: AsLabel Text)+-- >>> let name_key = ("name" :: Key AVertex Text)+-- >>> let count_label = ("b" :: AsLabel Int)+-- >>> toGremlin (source "g" & sV' [] &. gProject (gByL name_label name_key) [gByL count_label (gOut' [] >>> gCount), gByL "c" tId])+-- "g.V().project(\"a\",\"b\",\"c\").by(\"name\").by(__.out().count()).by(T.id)"+--+-- @since 1.0.0.0+gProject :: LabeledByProjection s -> [LabeledByProjection s] -> Walk Transform s (PMap Single GValue)+gProject lp_head lps = foldl' f (unsafeWalk "project" labels) (lp_head : lps)+ where+ labels = map toLabelGremlin (lp_head : lps)+ toLabelGremlin (LabeledByProjection l _) = toGremlin l+ f acc lp = acc >>> toByStep lp+ toByStep :: LabeledByProjection s -> Walk Transform a a+ toByStep (LabeledByProjection _ (ByProjection p)) = unsafeWalk "by" [toGremlin p]+ -- | @.fold@ step. gFold :: Walk Transform a [a] gFold = unsafeWalk "fold" []@@ -973,10 +1038,10 @@ -- | Monomorphic version of 'gOut'. ----- >>> toGremlin (source "g" & sV' [gvalueInt (8 :: Int)] &. gOut' ["knows"])+-- >>> toGremlin (source "g" & sV' [fmap ElementID $ gvalueInt (8 :: Int)] &. gOut' ["knows"]) -- "g.V(8).out(\"knows\")" gOut' :: (Vertex v)- => [Greskell Text]+ => [Greskell Text] -- ^ edge labels -> Walk Transform v AVertex gOut' = gOut @@ -986,7 +1051,7 @@ -> Walk Transform v e gOutE = genericTraversalWalk "outE" --- | Monomorphic version of 'gOutE'+-- | Monomorphic version of 'gOutE'. gOutE' :: (Vertex v) => [Greskell Text] -> Walk Transform v AEdge@@ -1052,12 +1117,6 @@ gSideEffect' :: (ToGTraversal g, WalkType c, Split c SideEffect) => g c s e -> Walk SideEffect s s gSideEffect' w = gSideEffect w ----- -- probably we can implement .as() step like this. GBuilder generates----- -- some 'Label', which is passed to .as() step and can be passed later----- -- to .select() step etc.----- gAs :: GBuilder (Label, Walk Filter s s)----- gAs = undefined- -- | @.addV@ step with a label. gAddV :: Vertex v => Greskell Text -> Walk SideEffect a v gAddV label = unsafeWalk "addV" [toGremlin label]@@ -1080,7 +1139,7 @@ gDropP :: Property p => Walk SideEffect (p a) (p a) gDropP = unsafeWalk "drop" [] --- | simple @.property@ step. It adds a value to the property.+-- | Simple @.property@ step. It adds a value to the property. -- -- >>> toGremlin (source "g" & sV' [] & liftWalk &. gProperty "age" (20 :: Greskell Int)) -- "g.V().property(\"age\",20)"@@ -1114,6 +1173,7 @@ arg_metaprops = expand =<< metaprops where expand (KeyValue meta_key meta_val) = [toGremlin meta_key, toGremlin meta_val]+ expand (KeyNoValue _) = [] -- | Vertex anchor for 'gAddE'. It corresponds to @.from@ or @.to@ -- step following an @.addE@ step.@@ -1155,7 +1215,7 @@ -> Walk SideEffect vs e gAddE label anch = (unsafeWalk "addE" [toGremlin label]) >>> anchorStep anch --- | Monomorphic version of 'gAddE'+-- | Monomorphic version of 'gAddE'. -- -- @since 0.2.0.0 gAddE' :: Greskell Text -> AddAnchor AVertex AVertex -> Walk SideEffect AVertex AEdge
src/Data/Greskell/Graph.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeFamilies, OverloadedStrings, FlexibleInstances, GeneralizedNewtypeDeriving, DeriveTraversable, GADTs #-}+{-# LANGUAGE TypeFamilies, OverloadedStrings, FlexibleInstances, GeneralizedNewtypeDeriving, DeriveTraversable, GADTs, DeriveGeneric #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} -- | -- Module: Data.Greskell.Graph@@ -8,10 +8,14 @@ -- This module defines types and functions about TinkerPop graph -- structure API. module Data.Greskell.Graph- ( -- * TinkerPop graph structure types+ ( -- * Element Element(..),+ ElementData(..),+ ElementID(..),+ unsafeCastElementID, Vertex,- Edge(..),+ Edge,+ -- * Property Property(..), -- * T Enum T,@@ -24,12 +28,19 @@ cList, cSet, cSingle,+ -- * Typed Key (accessor of a Property) Key(..), key,- -- ** key-value pair+ unsafeCastKey,+ -- ** Key-value pair KeyValue(..), (=:),+ -- ** Heterogeneous list of keys+ Keys(..),+ singletonKeys,+ (-:),+ -- * Concrete data types -- $concrete_types @@ -40,36 +51,26 @@ -- ** VertexProperty AVertexProperty(..), -- ** Property- AProperty(..),- -- ** PropertyMap- PropertyMap(..),- PropertyMapSingle,- PropertyMapList,- lookupOneValue,- lookupListValues,- parseOneValue,- parseListValues,- parseNonEmptyValues,- fromProperties,- -- * Internal use- FromGraphSONWithKey+ AProperty(..) ) where import Control.Applicative (empty, (<$>), (<*>), (<|>))-import Data.Aeson (Value(..), FromJSON(..))+import Data.Aeson (Value(..), FromJSON(..), ToJSON(..)) import Data.Aeson.Types (Parser) import Data.Foldable (toList, Foldable(foldr), foldlM)+import Data.Hashable (Hashable) import qualified Data.HashMap.Strict as HM import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NL import Data.Maybe (listToMaybe)-import Data.Monoid (Monoid)+import Data.Monoid (Monoid(..)) import Data.Semigroup ((<>), Semigroup) import qualified Data.Semigroup as Semigroup import Data.String (IsString(..)) import Data.Text (Text, unpack) import Data.Traversable (Traversable(traverse)) import Data.Vector (Vector)+import GHC.Generics (Generic) import Data.Greskell.GraphSON ( GraphSON(..), GraphSONTyped(..), FromGraphSON(..),@@ -81,42 +82,75 @@ ( Greskell, unsafeGreskellLazy, string, ToGreskell(..) )+import Data.Greskell.NonEmptyLike (NonEmptyLike)+import Data.Greskell.PMap (PMapKey(..), Single, Multi) -- $setup -- -- >>> import Data.Greskell.Greskell (toGremlin) +-- | ID of a graph element @e@ (vertex, edge and vertex property).+--+-- @since 1.0.0.0+newtype ElementID e =+ ElementID+ { unElementID :: GValue+ -- ^ Although it's exposed, it is recommended NOT to rely on the+ -- internal of 'ElementID'. That's because it depends on graph+ -- implementation.+ }+ deriving (Show,Eq,Generic, ToJSON, FromJSON, FromGraphSON, Hashable) +-- | Unsafely convert the element type.+instance Functor ElementID where+ fmap _ e = unsafeCastElementID e++-- | Unsafely cast the phantom type of 'ElementID'.+--+-- @since 1.0.0.0+unsafeCastElementID :: ElementID a -> ElementID b+unsafeCastElementID (ElementID e) = ElementID e++-- | Types that keep reference to TinkerPop graph Elements.+--+-- @since 1.0.0.0+class ElementData e where+ -- | ID of this Element.+ elementId :: e -> ElementID e+ -- | Label of this Element.+ elementLabel :: e -> Text+ -- | @org.apache.tinkerpop.gremlin.structure.Element@ interface in a -- TinkerPop graph.-class Element e where- type ElementID e- -- ^ ID type of the 'Element'. This depends on graph database- -- implementation and its settings.+--+-- Since greskell-1.0.0.0, 'ElementData' is a super-class of+-- 'Element'.+class ElementData e => Element e where+ -- | Property type of the 'Element'. It should be of 'Property'+ -- class. type ElementProperty e :: * -> *- -- ^ Property type of the 'Element'. It should be of 'Property'- -- class. If you don't care, use 'AVertexProperty' if type @e@ is an- -- 'Vertex' and use 'AProperty' if type @e@ is an 'Edge' or- -- VertexProperty. + -- | Container type of the properties of the 'Element'. It should be+ -- of 'NonEmptyLike' class.+ --+ -- @since 1.0.0.0+ type ElementPropertyContainer e :: * -> *+ -- | @org.apache.tinkerpop.gremlin.structure.Vertex@ interface in a -- TinkerPop graph. class (Element v) => Vertex v -- | @org.apache.tinkerpop.gremlin.structure.Edge@ interface in a -- TinkerPop graph.-class (Element e) => Edge e where- type EdgeVertexID e- -- ^ ID type of the 'Vertex' this edge connects.-+class (Element e) => Edge e -- | @org.apache.tinkerpop.gremlin.structure.Property@ interface in a -- TinkerPop graph. class Property p where+ -- | Get key of this property. propertyKey :: p v -> Text- -- ^ Get key of this property.+ -- | Get value of this property. propertyValue :: p v -> v- -- ^ Get value of this property. -- | @org.apache.tinkerpop.gremlin.structure.T@ enum. --@@ -182,28 +216,48 @@ -- | A property key accessing value @b@ in an Element @a@. In Gremlin, -- it's just a String type.-newtype Key a b = Key { unKey :: Greskell Text }+--+-- >>> toGremlin ("age" :: Key AVertex Int)+-- "\"age\""+-- >>> toGremlin (key "created_at" :: Key AEdge Text)+-- "\"created_at\""+-- >>> keyText ("name" :: Key AVertex Text)+-- "name"+--+-- Since greskell-1.0.0.0, 'Key' is newtype of 'Text'. Before that, it+-- was newtype of 'Greskell' 'Text'.+newtype Key a b = Key { unKey :: Text } deriving (Show,Eq) -- | Unsafely convert the value type @b@. instance Functor (Key a) where fmap _ (Key t) = Key t --- | Gremlin String literal as a 'Key'. instance IsString (Key a b) where fromString = Key . fromString --- | Unwrap 'Key' constructor.+-- | Return Gremlin String literal. instance ToGreskell (Key a b) where type GreskellReturn (Key a b) = Text- toGreskell = unKey+ toGreskell = string . unKey --- | Create a 'Key' from a literal string.+-- | @since 1.0.0.0+instance PMapKey (Key a b) where+ type PMapValue (Key a b) = b+ keyText (Key t) = t++-- | Create a 'Key' a text. key :: Text -> Key a b-key = Key . string+key = Key +-- | Unsafely cast the type signature of the 'Key'.+--+-- @since 1.0.0.0+unsafeCastKey :: Key a1 b1 -> Key a2 b2+unsafeCastKey = Key . unKey --- | Pair of 'Key' and its value.+-- | Pair of 'Key' and its value. Mainly used for writing properties+-- into the database. -- -- Type @a@ is the type of 'Element' that keeps the 'KeyValue' -- pair. It drops the type of the value, so that you can construct a@@ -211,7 +265,12 @@ -- -- @since 0.2.0.0 data KeyValue a where+ -- | Key and value KeyValue :: Key a b -> Greskell b -> KeyValue a+ -- | Key without value+ --+ -- @since 1.0.0.0+ KeyNoValue :: Key a b -> KeyValue a -- | Constructor operator of 'KeyValue'. --@@ -219,43 +278,75 @@ (=:) :: Key a b -> Greskell b -> KeyValue a (=:) = KeyValue --- $concrete_types--- Concrete data types based on Aeson data types.+-- | Heterogeneous list of 'Key's. It keeps the parent type @a@, but+-- discards the value type @b@. ----- Element IDs and property values are all 'GValue', because they are--- highly polymorphic. 'ElementID' and 'EdgeVertexID' are 'GValue',--- too.+-- @since 1.0.0.0+data Keys a where+ -- | Empty 'Keys'.+ KeysNil :: Keys a+ -- | Add a 'Key' to 'Keys'.+ KeysCons :: Key a b -> Keys a -> Keys a++instance Semigroup (Keys a) where+ a <> b =+ case a of+ KeysNil -> b+ KeysCons x rest -> KeysCons x (rest <> b)++instance Monoid (Keys a) where+ mempty = KeysNil+ mappend = (<>)++-- | 'Keys' with a single 'Key'. ----- As for properties, you can use 'PropertyMap' and other type-classes--- to manipulate them.+-- @since 1.0.0.0+singletonKeys :: Key a b -> Keys a+singletonKeys k = KeysCons k KeysNil++-- | Prepend a 'Key' to 'Keys'. ----- If you want to define your own graph structure types, see+-- @since 1.0.0.0+(-:) :: Key a b -> Keys a -> Keys a+(-:) = KeysCons++infixr 5 -:++-- $concrete_types+-- Graph structure data types based on Aeson.+--+-- You can use those types directly in your programs. You can also+-- define your own graph types by wrapping those with @newtype@. See -- [README.md](https://github.com/debug-ito/greskell#make-your-own-graph-structure-types)--- for detail. Basically you can use 'FromGraphSON' instances of these--- concrete data types to implement parsers for your own types.+-- for detail. ----- NOTE: In version 0.1.1.0 and before, these conrete data types were--- based on @GraphSON Value@. In version 0.2.0.0, this was changed to--- 'GValue', so that it can parse nested data structures encoded in--- GraphSON.-+-- Historical note:+--+-- - Since version 1.0.0.0, the concrete data types don't keep+-- properties, and IDs are of 'ElementID' type.+-- - In version 0.1.1.0 and before, these conrete data types were+-- based on @GraphSON Value@. In version 0.2.0.0, this was changed to+-- 'GValue', so that it can parse nested data structures encoded in+-- GraphSON. --- | General vertex type you can use for 'Vertex' class, based on--- Aeson data types.+-- | General vertex type you can use for 'Vertex' class. data AVertex = AVertex- { avId :: GValue,+ { avId :: ElementID AVertex, -- ^ ID of this vertex- avLabel :: Text,+ avLabel :: Text -- ^ Label of this vertex- avProperties :: PropertyMapList AVertexProperty GValue- -- ^ Properties of this vertex. } deriving (Show,Eq) +-- | @since 1.0.0.0+instance ElementData AVertex where+ elementId = avId+ elementLabel = avLabel+ instance Element AVertex where- type ElementID AVertex = GValue type ElementProperty AVertex = AVertexProperty+ type ElementPropertyContainer AVertex = Multi instance Vertex AVertex @@ -270,36 +361,28 @@ GObject o -> AVertex <$> (o .: "id") <*> (o .: "label")- <*> (o `optionalMonoid` "properties") _ -> empty --- | General edge type you can use for 'Edge' class, based on Aeson--- data types.+-- | General edge type you can use for 'Edge' class. data AEdge = AEdge- { aeId :: GValue,+ { aeId :: ElementID AEdge, -- ^ ID of this edge.- aeLabel :: Text,+ aeLabel :: Text -- ^ Label of this edge.- aeInVLabel :: Text,- -- ^ Label of this edge's destination vertex.- aeOutVLabel :: Text,- -- ^ Label of this edge's source vertex.- aeInV :: GValue,- -- ^ ID of this edge's destination vertex.- aeOutV :: GValue,- -- ^ ID of this edge's source vertex.- aeProperties :: PropertyMapSingle AProperty GValue- -- ^ Properties of this edge. } deriving (Show,Eq) +-- | @since 1.0.0.0+instance ElementData AEdge where+ elementId = aeId+ elementLabel = aeLabel+ instance Element AEdge where- type ElementID AEdge = GValue type ElementProperty AEdge = AProperty+ type ElementPropertyContainer AEdge = Single -instance Edge AEdge where- type EdgeVertexID AEdge = GValue+instance Edge AEdge instance GraphSONTyped AEdge where gsonTypeFor _ = "g:Edge"@@ -312,29 +395,8 @@ GObject o -> AEdge <$> (o .: "id") <*> (o .: "label")- <*> (o .: "inVLabel")- <*> (o .: "outVLabel")- <*> (o .: "inV")- <*> (o .: "outV")- <*> (o `optionalMonoid` "properties") _ -> empty -optionalMonoid :: (Monoid m, FromGraphSON m) => HM.HashMap Text GValue -> Text -> Parser m-optionalMonoid obj field_name = maybe (return mempty) parseGraphSON $ nullToNothing =<< HM.lookup field_name obj- where- nullToNothing gv = case gValueBody gv of- GNull -> Nothing- _ -> Just gv---- | __This typeclass is for internal use.__------ GraphSON parser with a property key given from outside.------ @since 0.2.0.0-class FromGraphSONWithKey a where- parseGraphSONWithKey :: Text -> GValue -> Parser a-- -- | General simple property type you can use for 'Property' class. -- -- If you are not sure about the type @v@, just use 'GValue'.@@ -358,9 +420,6 @@ GObject o -> AProperty <$> (o .: "key") <*> (o .: "value") _ -> empty -instance FromGraphSON v => FromGraphSONWithKey (AProperty v) where- parseGraphSONWithKey k v = AProperty k <$> parseGraphSON v- instance Property AProperty where propertyKey = apKey propertyValue = apValue@@ -377,20 +436,17 @@ instance Traversable AProperty where traverse f sp = fmap (\v -> sp { apValue = v } ) $ f $ apValue sp --- | General vertex property type you can use for VertexProperty,--- based on Aeson data types.+-- | General vertex property type you can use for VertexProperty. -- -- If you are not sure about the type @v@, just use 'GValue'. data AVertexProperty v = AVertexProperty- { avpId :: GValue,+ { avpId :: ElementID (AVertexProperty v), -- ^ ID of this vertex property. avpLabel :: Text, -- ^ Label and key of this vertex property.- avpValue :: v,+ avpValue :: v -- ^ Value of this vertex property.- avpProperties :: PropertyMapSingle AProperty GValue- -- ^ (meta)properties of this vertex property. } deriving (Show,Eq) @@ -401,220 +457,40 @@ instance FromGraphSON v => FromGraphSON (AVertexProperty v) where parseGraphSON gv = case gValueBody gv of- GObject o -> do- label <- o .: "label"- parseGraphSONWithKey label gv- _ -> empty--instance FromGraphSON v => FromGraphSONWithKey (AVertexProperty v) where- parseGraphSONWithKey k gv = case gValueBody gv of GObject o -> AVertexProperty <$> (o .: "id")- <*> pure k+ <*> (o .: "label") <*> (o .: "value")- <*> (o `optionalMonoid` "properties") _ -> empty instance GraphSONTyped (AVertexProperty v) where gsonTypeFor _ = "g:VertexProperty" +-- | @since 1.0.0.0+instance ElementData (AVertexProperty v) where+ elementId = avpId+ elementLabel = avpLabel+ instance Element (AVertexProperty v) where- type ElementID (AVertexProperty v) = GValue type ElementProperty (AVertexProperty v) = AProperty+ type ElementPropertyContainer (AVertexProperty v) = Single instance Property AVertexProperty where propertyKey = avpLabel propertyValue = avpValue +-- | Map the property value. instance Functor AVertexProperty where- fmap f vp = vp { avpValue = f $ avpValue vp }+ fmap f vp = vp { avpValue = f $ avpValue vp,+ avpId = unsafeCastElementID $ avpId vp+ } instance Foldable AVertexProperty where foldr f start vp = f (avpValue vp) start +-- | Traverse the property value. instance Traversable AVertexProperty where- traverse f vp = fmap (\v -> vp { avpValue = v }) $ f $ avpValue vp----- -- We could define the following constraint synonym with--- -- ConstraintKinds extension, although its semantics is not exactly--- -- correct..--- type VertexProperty p v = (Element (p v), Property p)----- | Common basic operations supported by maps of properties.-class PropertyMap m where- lookupOne :: Text -> m p v -> Maybe (p v)- -- ^ Look up a property associated with the given key.- lookupOne k m = listToMaybe $ lookupList k m- lookupList :: Text -> m p v -> [p v]- -- ^ Look up all properties associated with the given key.- putProperty :: Property p => p v -> m p v -> m p v- -- ^ Put a property into the map.- removeProperty :: Text -> m p v -> m p v- -- ^ Remove all properties associated with the given key.- allProperties :: m p v -> [p v]- -- ^ Return all properties in the map.---- | Lookup a property value from a 'PropertyMap' by key.-lookupOneValue :: (PropertyMap m, Property p) => Text -> m p v -> Maybe v-lookupOneValue k = fmap propertyValue . lookupOne k---- | Lookup a list of property values from a 'PropertyMap' by key.-lookupListValues :: (PropertyMap m, Property p) => Text -> m p v -> [v]-lookupListValues k = fmap propertyValue . lookupList k--notExistErrorMsg :: Text -> String-notExistErrorMsg k = "Property '" ++ unpack k ++ "' does not exist."---- | Lookup a property 'GValue' by the given key, and parse it.--- --- In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.--- This has changed, because property types for 'AVertex' etc have changed.-parseOneValue :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser v-parseOneValue k pm = maybe (fail err_msg) parseGraphSON $ lookupOneValue k pm- where- err_msg = notExistErrorMsg k---- | Lookup a list of property values from a 'PropertyMap' by the--- given key, and parse them.--- --- In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.--- This has changed, because property types for 'AVertex' etc have changed.-parseListValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser [v]-parseListValues k pm = mapM parseGraphSON $ lookupListValues k pm---- | Like 'parseListValues', but this function 'fail's when there is--- no property with the given key.------ In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.--- This has changed, because property types for 'AVertex' etc have changed.-parseNonEmptyValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser (NonEmpty v)-parseNonEmptyValues k pm = toNonEmpty =<< parseListValues k pm- where- toNonEmpty [] = fail $ notExistErrorMsg k- toNonEmpty (x : rest) = return (x :| rest)---- | Create a 'PropertyMap' from list of 'Property's.-fromProperties :: (PropertyMap m, Property p, Monoid (m p v))- => [p v]- -> m p v-fromProperties = foldr putProperty mempty---- | Generic implementation of 'PropertyMap'. @t@ is the type of--- cardinality, @p@ is the type of 'Property' class and @v@ is the--- type of the property value.-newtype PropertyMapGeneric t p v = PropertyMapGeneric (HM.HashMap Text (t (p v)))- deriving (Show,Eq)--instance Semigroup (t (p v)) => Semigroup (PropertyMapGeneric t p v) where- (PropertyMapGeneric a) <> (PropertyMapGeneric b) = PropertyMapGeneric $ HM.unionWith (<>) a b--instance Semigroup (t (p v)) => Monoid (PropertyMapGeneric t p v) where- mempty = PropertyMapGeneric mempty- mappend = (<>)--instance (Functor t, Functor p) => Functor (PropertyMapGeneric t p) where- fmap f (PropertyMapGeneric hm) = PropertyMapGeneric $ (fmap . fmap . fmap) f hm--instance (Foldable t, Foldable p) => Foldable (PropertyMapGeneric t p) where- foldr f start (PropertyMapGeneric hm) = foldr f2 start hm+ traverse f vp = fmap setValue $ f $ avpValue vp where- f2 t start2 = foldr f3 start2 t- f3 p start3 = foldr f start3 p--instance (Traversable t, Traversable p) => Traversable (PropertyMapGeneric t p) where- traverse f (PropertyMapGeneric hm) = fmap PropertyMapGeneric $ (traverse . traverse . traverse) f hm--putPropertyGeneric :: (Semigroup (t (p v)), Applicative t, Property p) => p v -> PropertyMapGeneric t p v -> PropertyMapGeneric t p v-putPropertyGeneric prop (PropertyMapGeneric hm) =- PropertyMapGeneric $ HM.insertWith (<>) (propertyKey prop) (pure prop) hm--removePropertyGeneric :: Text -> PropertyMapGeneric t p v -> PropertyMapGeneric t p v-removePropertyGeneric k (PropertyMapGeneric hm) = PropertyMapGeneric $ HM.delete k hm--allPropertiesGeneric :: Foldable t => PropertyMapGeneric t p v -> [p v]-allPropertiesGeneric (PropertyMapGeneric hm) = concat $ map toList $ HM.elems hm--parsePropertiesGeneric :: (Property p, PropertyMap m, Monoid (m p v), GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))- => (GValue -> Parser (Vector GValue))- -> GValue- -> Parser (m p v)-parsePropertiesGeneric normalizeCardinality gv = case gValueBody gv of- GObject obj -> foldlM folder mempty $ HM.toList obj- _ -> empty- where- folder pm (k, value) = fmap (foldr putProperty pm) $ traverse (parseProperty k) =<< normalizeCardinality value- parseProperty k value = parseTypedGValue value <|> parseGraphSONWithKey k value---- parhaps we might as well place it in GraphSON module and let it export.-parseTypedGValue :: (GraphSONTyped v, FromGraphSON v) => GValue -> Parser v-parseTypedGValue gv = do- prop <- parseGraphSON gv- let exp_type = gsonTypeFor prop- mgot_type = gValueType gv- if mgot_type /= Just exp_type- then fail ("Expected @type field of " ++ unpack exp_type ++ ", but got " ++ show mgot_type)- else return prop--expectAesonArray :: GValue -> Parser (Vector GValue)-expectAesonArray gv = case gValueBody gv of- GArray a -> return a- _ -> empty---- | A 'PropertyMap' that has a single value per key.------ 'putProperty' replaces the old property by the given property.------ '<>' returns the union of the two given property maps. If the two--- property maps share some same keys, the value from the left map--- wins.-newtype PropertyMapSingle p v = PropertyMapSingle (PropertyMapGeneric Semigroup.First p v)- deriving (Show,Eq,Semigroup,Monoid,Functor,Foldable,Traversable)--instance PropertyMap PropertyMapSingle where- lookupOne k (PropertyMapSingle (PropertyMapGeneric hm)) = fmap Semigroup.getFirst $ HM.lookup k hm- lookupList k m = maybe [] return $ lookupOne k m- putProperty p (PropertyMapSingle pg) = PropertyMapSingle $ putPropertyGeneric p pg- removeProperty t (PropertyMapSingle pg) = PropertyMapSingle $ removePropertyGeneric t pg- allProperties (PropertyMapSingle pg) = allPropertiesGeneric pg---- | In version 0.1.1.0 and before, the constraint was @FromJSON v@.--- This has changed.-instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))- => FromJSON (PropertyMapSingle p v) where- parseJSON = parseJSONViaGValue--instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))- => FromGraphSON (PropertyMapSingle p v) where- parseGraphSON = parsePropertiesGeneric (return . return)---- | A 'PropertyMap' that can keep more than one values per key.------ 'lookupOne' returns the first property associated with the given--- key.------ 'putProperty' prepends the given property to the property list.------ '<>' returns the union of the two given property maps. If the two--- property maps share some same keys, those property lists are--- concatenated.-newtype PropertyMapList p v = PropertyMapList (PropertyMapGeneric NonEmpty p v)- deriving (Show,Eq,Semigroup,Monoid,Functor,Foldable,Traversable)--instance PropertyMap PropertyMapList where- lookupList k (PropertyMapList (PropertyMapGeneric hm)) = maybe [] NL.toList $ HM.lookup k hm- putProperty p (PropertyMapList pg) = PropertyMapList $ putPropertyGeneric p pg- removeProperty t (PropertyMapList pg) = PropertyMapList $ removePropertyGeneric t pg- allProperties (PropertyMapList pg) = allPropertiesGeneric pg---- | In version 0.1.1.0 and before, the constraint was @FromJSON v@.--- This has changed.-instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))- => FromJSON (PropertyMapList p v) where- parseJSON = parseJSONViaGValue--instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))- => FromGraphSON (PropertyMapList p v) where- parseGraphSON v = parsePropertiesGeneric expectAesonArray v+ setValue v = vp { avpValue = v, avpId = unsafeCastElementID $ avpId vp }
+ src/Data/Greskell/Graph/PropertyMap.hs view
@@ -0,0 +1,242 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveTraversable, OverloadedStrings #-}+-- |+-- Module: Data.Greskell.Graph.PropertyMap+-- Description: [Deprecated] PropertyMap class and types+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- 'PropertyMap' was used in greskell prior than 1.0.0.0, but is now+-- deprecated. Use "Data.Greskell.PMap" instead.+module Data.Greskell.Graph.PropertyMap {-# DEPRECATED "Use PMap instead" #-}+ ( -- ** PropertyMap+ PropertyMap(..),+ PropertyMapSingle,+ PropertyMapList,+ lookupOneValue,+ lookupListValues,+ parseOneValue,+ parseListValues,+ parseNonEmptyValues,+ fromProperties,+ -- * Internal use+ FromGraphSONWithKey,+ -- * Re-exports+ AProperty(..),+ AVertexProperty(..)+ ) where++import Control.Applicative (empty, (<|>))+import Data.Aeson (FromJSON(..))+import Data.Aeson.Types (Parser)+import Data.Foldable (Foldable(..), foldlM)+import Data.Greskell.GraphSON+ ( FromGraphSON(..), GValue, GraphSONTyped(..), (.:),+ parseJSONViaGValue+ )+import Data.Greskell.GraphSON.GValue (gValueBody, gValueType, GValueBody(..))+import qualified Data.HashMap.Strict as HM+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NL+import Data.Maybe (listToMaybe)+import Data.Monoid (Monoid(..))+import Data.Semigroup (Semigroup((<>)))+import qualified Data.Semigroup as Semigroup+import Data.Text (Text, unpack)+import Data.Traversable (Traversable(..))+import Data.Vector (Vector)++import Data.Greskell.Graph (Property(..), AProperty(..), AVertexProperty(..))++-- | Common basic operations supported by maps of properties.+class PropertyMap m where+ lookupOne :: Text -> m p v -> Maybe (p v)+ -- ^ Look up a property associated with the given key.+ lookupOne k m = listToMaybe $ lookupList k m+ lookupList :: Text -> m p v -> [p v]+ -- ^ Look up all properties associated with the given key.+ putProperty :: Property p => p v -> m p v -> m p v+ -- ^ Put a property into the map.+ removeProperty :: Text -> m p v -> m p v+ -- ^ Remove all properties associated with the given key.+ allProperties :: m p v -> [p v]+ -- ^ Return all properties in the map.++-- | Lookup a property value from a 'PropertyMap' by key.+lookupOneValue :: (PropertyMap m, Property p) => Text -> m p v -> Maybe v+lookupOneValue k = fmap propertyValue . lookupOne k++-- | Lookup a list of property values from a 'PropertyMap' by key.+lookupListValues :: (PropertyMap m, Property p) => Text -> m p v -> [v]+lookupListValues k = fmap propertyValue . lookupList k++notExistErrorMsg :: Text -> String+notExistErrorMsg k = "Property '" ++ unpack k ++ "' does not exist."++-- | Lookup a property 'GValue' by the given key, and parse it.+-- +-- In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.+-- This has changed, because property types for 'AVertex' etc have changed.+parseOneValue :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser v+parseOneValue k pm = maybe (fail err_msg) parseGraphSON $ lookupOneValue k pm+ where+ err_msg = notExistErrorMsg k++-- | Lookup a list of property values from a 'PropertyMap' by the+-- given key, and parse them.+-- +-- In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.+-- This has changed, because property types for 'AVertex' etc have changed.+parseListValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser [v]+parseListValues k pm = mapM parseGraphSON $ lookupListValues k pm++-- | Like 'parseListValues', but this function 'fail's when there is+-- no property with the given key.+--+-- In version 0.1.1.0 and before, this function took an argument @m p (GraphSON Value)@.+-- This has changed, because property types for 'AVertex' etc have changed.+parseNonEmptyValues :: (PropertyMap m, Property p, FromGraphSON v) => Text -> m p GValue -> Parser (NonEmpty v)+parseNonEmptyValues k pm = toNonEmpty =<< parseListValues k pm+ where+ toNonEmpty [] = fail $ notExistErrorMsg k+ toNonEmpty (x : rest) = return (x :| rest)++-- | Create a 'PropertyMap' from list of 'Property's.+fromProperties :: (PropertyMap m, Property p, Monoid (m p v))+ => [p v]+ -> m p v+fromProperties = foldr putProperty mempty++-- | Generic implementation of 'PropertyMap'. @t@ is the type of+-- cardinality, @p@ is the type of 'Property' class and @v@ is the+-- type of the property value.+newtype PropertyMapGeneric t p v = PropertyMapGeneric (HM.HashMap Text (t (p v)))+ deriving (Show,Eq)++instance Semigroup (t (p v)) => Semigroup (PropertyMapGeneric t p v) where+ (PropertyMapGeneric a) <> (PropertyMapGeneric b) = PropertyMapGeneric $ HM.unionWith (<>) a b++instance Semigroup (t (p v)) => Monoid (PropertyMapGeneric t p v) where+ mempty = PropertyMapGeneric mempty+ mappend = (<>)++instance (Functor t, Functor p) => Functor (PropertyMapGeneric t p) where+ fmap f (PropertyMapGeneric hm) = PropertyMapGeneric $ (fmap . fmap . fmap) f hm++instance (Foldable t, Foldable p) => Foldable (PropertyMapGeneric t p) where+ foldr f start (PropertyMapGeneric hm) = foldr f2 start hm+ where+ f2 t start2 = foldr f3 start2 t+ f3 p start3 = foldr f start3 p++instance (Traversable t, Traversable p) => Traversable (PropertyMapGeneric t p) where+ traverse f (PropertyMapGeneric hm) = fmap PropertyMapGeneric $ (traverse . traverse . traverse) f hm++putPropertyGeneric :: (Semigroup (t (p v)), Applicative t, Property p) => p v -> PropertyMapGeneric t p v -> PropertyMapGeneric t p v+putPropertyGeneric prop (PropertyMapGeneric hm) =+ PropertyMapGeneric $ HM.insertWith (<>) (propertyKey prop) (pure prop) hm++removePropertyGeneric :: Text -> PropertyMapGeneric t p v -> PropertyMapGeneric t p v+removePropertyGeneric k (PropertyMapGeneric hm) = PropertyMapGeneric $ HM.delete k hm++allPropertiesGeneric :: Foldable t => PropertyMapGeneric t p v -> [p v]+allPropertiesGeneric (PropertyMapGeneric hm) = concat $ map toList $ HM.elems hm++parsePropertiesGeneric :: (Property p, PropertyMap m, Monoid (m p v), GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))+ => (GValue -> Parser (Vector GValue))+ -> GValue+ -> Parser (m p v)+parsePropertiesGeneric normalizeCardinality gv = case gValueBody gv of+ GObject obj -> foldlM folder mempty $ HM.toList obj+ _ -> empty+ where+ folder pm (k, value) = fmap (foldr putProperty pm) $ traverse (parseProperty k) =<< normalizeCardinality value+ parseProperty k value = parseTypedGValue value <|> parseGraphSONWithKey k value++-- parhaps we might as well place it in GraphSON module and let it export.+parseTypedGValue :: (GraphSONTyped v, FromGraphSON v) => GValue -> Parser v+parseTypedGValue gv = do+ prop <- parseGraphSON gv+ let exp_type = gsonTypeFor prop+ mgot_type = gValueType gv+ if mgot_type /= Just exp_type+ then fail ("Expected @type field of " ++ unpack exp_type ++ ", but got " ++ show mgot_type)+ else return prop++expectAesonArray :: GValue -> Parser (Vector GValue)+expectAesonArray gv = case gValueBody gv of+ GArray a -> return a+ _ -> empty++-- | A 'PropertyMap' that has a single value per key.+--+-- 'putProperty' replaces the old property by the given property.+--+-- '<>' returns the union of the two given property maps. If the two+-- property maps share some same keys, the value from the left map+-- wins.+newtype PropertyMapSingle p v = PropertyMapSingle (PropertyMapGeneric Semigroup.First p v)+ deriving (Show,Eq,Semigroup,Monoid,Functor,Foldable,Traversable)++instance PropertyMap PropertyMapSingle where+ lookupOne k (PropertyMapSingle (PropertyMapGeneric hm)) = fmap Semigroup.getFirst $ HM.lookup k hm+ lookupList k m = maybe [] return $ lookupOne k m+ putProperty p (PropertyMapSingle pg) = PropertyMapSingle $ putPropertyGeneric p pg+ removeProperty t (PropertyMapSingle pg) = PropertyMapSingle $ removePropertyGeneric t pg+ allProperties (PropertyMapSingle pg) = allPropertiesGeneric pg++-- | In version 0.1.1.0 and before, the constraint was @FromJSON v@.+-- This has changed.+instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))+ => FromJSON (PropertyMapSingle p v) where+ parseJSON = parseJSONViaGValue++instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))+ => FromGraphSON (PropertyMapSingle p v) where+ parseGraphSON = parsePropertiesGeneric (return . return)++-- | A 'PropertyMap' that can keep more than one values per key.+--+-- 'lookupOne' returns the first property associated with the given+-- key.+--+-- 'putProperty' prepends the given property to the property list.+--+-- '<>' returns the union of the two given property maps. If the two+-- property maps share some same keys, those property lists are+-- concatenated.+newtype PropertyMapList p v = PropertyMapList (PropertyMapGeneric NonEmpty p v)+ deriving (Show,Eq,Semigroup,Monoid,Functor,Foldable,Traversable)++instance PropertyMap PropertyMapList where+ lookupList k (PropertyMapList (PropertyMapGeneric hm)) = maybe [] NL.toList $ HM.lookup k hm+ putProperty p (PropertyMapList pg) = PropertyMapList $ putPropertyGeneric p pg+ removeProperty t (PropertyMapList pg) = PropertyMapList $ removePropertyGeneric t pg+ allProperties (PropertyMapList pg) = allPropertiesGeneric pg++-- | In version 0.1.1.0 and before, the constraint was @FromJSON v@.+-- This has changed.+instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))+ => FromJSON (PropertyMapList p v) where+ parseJSON = parseJSONViaGValue++instance (Property p, GraphSONTyped (p v), FromGraphSON (p v), FromGraphSONWithKey (p v))+ => FromGraphSON (PropertyMapList p v) where+ parseGraphSON v = parsePropertiesGeneric expectAesonArray v++-- | __This typeclass is for internal use.__+--+-- GraphSON parser with a property key given from outside.+--+-- @since 0.2.0.0+class FromGraphSONWithKey a where+ parseGraphSONWithKey :: Text -> GValue -> Parser a++instance FromGraphSON v => FromGraphSONWithKey (AProperty v) where+ parseGraphSONWithKey k v = AProperty k <$> parseGraphSON v++instance FromGraphSON v => FromGraphSONWithKey (AVertexProperty v) where+ parseGraphSONWithKey k gv = case gValueBody gv of+ GObject o -> AVertexProperty+ <$> (o .: "id")+ <*> pure k+ <*> (o .: "value")+ _ -> empty
+ src/Data/Greskell/NonEmptyLike.hs view
@@ -0,0 +1,44 @@+-- |+-- Module: Data.Greskell.NonEmptyLike+-- Description: Class of non-empty containers+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- @since 1.0.0.0+module Data.Greskell.NonEmptyLike+ ( NonEmptyLike(..)+ ) where++import qualified Data.Foldable as F+import Data.List.NonEmpty (NonEmpty(..))+import Data.Semigroup (Semigroup, (<>))+import qualified Data.Semigroup as S++-- | Non-empty containers. Its cardinality is one or more.+--+-- @since 1.0.0.0+class F.Foldable t => NonEmptyLike t where+ -- | Make a container with a single value.+ singleton :: a -> t a+ -- | Append two containers.+ append :: t a -> t a -> t a+ -- | Convert the container to 'NonEmpty' list.+ toNonEmpty :: t a -> NonEmpty a++-- | 'append' is '<>' from 'Semigroup'.+instance NonEmptyLike NonEmpty where+ singleton a = a :| []+ append = (<>)+ toNonEmpty = id+ +-- | 'append' is '<>' from 'Semigroup'.+instance NonEmptyLike S.First where+ singleton = S.First+ append = (<>)+ toNonEmpty (S.First a) = singleton a++-- | 'append' is '<>' from 'Semigroup'.+instance NonEmptyLike S.Last where+ singleton = S.Last+ append = (<>)+ toNonEmpty (S.Last a) = singleton a+
+ src/Data/Greskell/PMap.hs view
@@ -0,0 +1,246 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveTraversable, TypeFamilies, OverloadedStrings #-}+-- |+-- Module: Data.Greskell.PMap+-- Description: Property map, a map with Text keys and cardinality options+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- This module defines 'PMap', a map with 'Text' keys and cardinality+-- options. +--+-- @since 1.0.0.0+module Data.Greskell.PMap+ ( -- * PMap+ PMap,+ -- ** Single lookup+ lookup,+ lookupM,+ lookupAs,+ lookupAs',+ lookupAsM,+ -- ** List lookup+ lookupList,+ lookupListAs,+ lookupListAs',+ -- ** Others+ pMapInsert,+ pMapDelete,+ pMapLookup,+ pMapToList,+ pMapFromList,+ -- * Cardinality+ Single,+ Multi,+ -- * PMapKey+ PMapKey(..),+ -- * Errors+ PMapLookupException(..),+ pMapDecribeError,+ pMapToThrow,+ pMapToFail+ ) where++import Prelude hiding (lookup)++import Control.Exception (Exception)+import Control.Monad.Catch (MonadThrow(..), MonadCatch(..))+import Control.Monad.Fail (MonadFail)+import Data.Aeson.Types (Parser)+import qualified Data.Foldable as F+import Data.Functor.Identity (Identity)+import Data.Greskell.AsIterator (AsIterator(..))+import Data.Greskell.GMap (GMapEntry)+import Data.Greskell.GraphSON (GValue, GraphSONTyped(..), FromGraphSON(..), parseEither)+import qualified Data.HashMap.Strict as HM+import Data.List.NonEmpty (NonEmpty((:|)))+import Data.Maybe (listToMaybe)+import Data.Monoid (Monoid(..))+import Data.Semigroup (Semigroup((<>)))+import qualified Data.Semigroup as S+import Data.Traversable (Traversable(traverse))+import Data.Text (Text, unpack)++import Data.Greskell.NonEmptyLike (NonEmptyLike)+import qualified Data.Greskell.NonEmptyLike as NEL++-- | A property map, which has text keys and @v@ values. @c@ specifies+-- the cardinality of each item, and should be an instance of+-- 'NonEmptyLike'.+--+-- You can look up values from 'PMap' using key types of 'PMapKey'+-- class.+--+-- @since 1.0.0.0+newtype PMap c v = PMap (HM.HashMap Text (c v))+ deriving (Show,Eq,Functor,Foldable,Traversable)++instance GraphSONTyped (PMap c v) where+ gsonTypeFor _ = "g:Map"++instance FromGraphSON (c v) => FromGraphSON (PMap c v) where+ parseGraphSON gv = fmap PMap $ parseGraphSON gv++-- | Make a union of the two 'PMap's. If the two 'PMap's share some+-- keys, those values are merged by 'NEL.append' method from+-- 'NonEmptyLike'.+instance NonEmptyLike c => Semigroup (PMap c v) where+ (PMap a) <> (PMap b) = PMap (HM.unionWith NEL.append a b)++instance NonEmptyLike c => Monoid (PMap c v) where+ mempty = PMap $ HM.empty+ mappend = (<>)++instance AsIterator (PMap c v) where+ type IteratorItem (PMap c v) = GMapEntry Text (c v)++-- | Insert a key-value pair to 'PMap'. If it already has some items+-- for that key, 'NEL.append' method of the 'NonEmptyLike' type @c@+-- determines its behavior.+pMapInsert :: NonEmptyLike c => Text -> v -> PMap c v -> PMap c v+pMapInsert k v (PMap hm) = PMap $ HM.insertWith NEL.append k (NEL.singleton v) hm++-- | Delete a key and all values associated with it.+pMapDelete :: Text -> PMap c v -> PMap c v+pMapDelete k (PMap hm) = PMap $ HM.delete k hm++-- | Lookup all items for the key (low-level function). If there is no+-- item for the key, it returns an empty list.+pMapLookup :: NonEmptyLike c => Text -> PMap c v -> [v]+pMapLookup k (PMap hm) = maybe [] (F.toList . NEL.toNonEmpty) $ HM.lookup k hm++-- | List up all entries.+pMapToList :: F.Foldable c => PMap c v -> [(Text, v)]+pMapToList (PMap hm) = expandValues =<< HM.toList hm+ where+ expandValues (k, cv) = map ((,) k) $ F.toList cv++-- | Make a 'PMap' from list of entries.+pMapFromList :: NonEmptyLike c => [(Text, v)] -> PMap c v+pMapFromList = F.foldr f mempty+ where+ f (k, v) pm = pMapInsert k v pm++-- | Lookup the first value for the key from 'PMap'.+lookup :: (PMapKey k, NonEmptyLike c) => k -> PMap c v -> Maybe v+lookup k pm = listToMaybe $ lookupList k pm++-- | 'MonadThrow' version of 'lookup'. If there is no value for the+-- key, it throws 'PMapNoSuchKey'.+lookupM :: (PMapKey k, NonEmptyLike c, MonadThrow m) => k -> PMap c v -> m v+lookupM k pm = maybe (throwM $ PMapNoSuchKey $ keyText k) return $ lookup k pm++-- | Lookup the value and parse it into @a@.+lookupAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a)+ => k -> PMap c GValue -> Either PMapLookupException a+lookupAs k pm =+ case lookup k pm of+ Nothing -> Left $ PMapNoSuchKey kt+ Just gv -> either (Left . PMapParseError kt) Right $ parseEither gv+ where+ kt = keyText k++-- | Similar to 'lookupAs', but this function converts a @null@ result+-- into 'Nothing'.+--+-- A @null@ result is either (1) the key @k@ is not found in the map,+-- or (2) the key is found, but the value is @null@.+lookupAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ (Maybe a), FromGraphSON a)+ => k -> PMap c GValue -> Either PMapLookupException (Maybe a)+lookupAs' k pm = either fromError Right $ lookupAs k pm+ where+ fromError (PMapNoSuchKey _) = Right Nothing+ fromError e = Left e++-- | 'MonadThrow' version of 'lookupAs'.+lookupAsM :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a, MonadThrow m)+ => k -> PMap c GValue -> m a+lookupAsM k pm = pMapToThrow $ lookupAs k pm++-- | Lookup all items for the key. If there is no item for the key, it+-- returns an empty list.+lookupList :: (PMapKey k, NonEmptyLike c) => k -> PMap c v -> [v]+lookupList k pm = pMapLookup (keyText k) pm++-- | Look up the values and parse them into @a@.+lookupListAs :: (PMapKey k, NonEmptyLike c, PMapValue k ~ a, FromGraphSON a)+ => k -> PMap c GValue -> Either PMapLookupException (NonEmpty a)+lookupListAs k pm =+ case lookupList k pm of+ [] -> Left $ PMapNoSuchKey kt+ (x : rest) -> either (Left . PMapParseError kt) Right $ traverse parseEither (x :| rest)+ where+ kt = keyText k++-- | Similar to 'lookupListAs', but this function accepts @null@+-- results.+--+-- If the key @k@ is not found in the map, it returns an empty+-- list. If the key @k@ is found and @null@s are included in the+-- values, they are obtained as 'Nothing'.+lookupListAs' :: (PMapKey k, NonEmptyLike c, PMapValue k ~ (Maybe a), FromGraphSON a)+ => k -> PMap c GValue -> Either PMapLookupException [Maybe a]+lookupListAs' k pm = either fromError (Right . F.toList) $ lookupListAs k pm+ where+ fromError (PMapNoSuchKey _) = Right []+ fromError e = Left e++-- | The single cardinality for 'PMap'. 'pMapInsert' method replaces+-- the old value. '<>' on 'PMap' prefers the items from the left+-- 'PMap'. 'pMapFromList' prefers the first item for each key.+--+-- @since 1.0.0.0+type Single = S.First++-- | The \"one or more\" cardinality for 'PMap'. 'pMapInsert' method+-- prepends the new value at the head. '<>' on 'PMap' appends the+-- right items to the tail of the left items. 'pMapFromList' preserves+-- the order of the items for each key.+--+-- @since 1.0.0.0+newtype Multi a = Multi (NonEmpty a)+ deriving (Show,Eq,Ord,Functor,Semigroup,Foldable,Traversable,NonEmptyLike,FromGraphSON)++-- | A typed key for 'PMap'.+--+-- @since 1.0.0.0+class PMapKey k where+ -- | Type of the value associated with the key.+ type PMapValue k :: *++ -- | 'Text' representation of the key.+ keyText :: k -> Text++-- | For simple 'Text', the 'GValue' is returned without being parsed.+instance PMapKey Text where+ type PMapValue Text = GValue+ keyText = id++-- | An 'Exception' raised when looking up values from 'PMap'.+--+-- @since 1.0.0.0+data PMapLookupException =+ PMapNoSuchKey Text+ -- ^ The 'PMap' doesn't have the given key.+ | PMapParseError Text String+ -- ^ Failed to parse the value into the type that the 'PMapKey'+ -- indicates. The 'Text' is the key, and the 'String' is the error+ -- message.+ deriving (Show,Eq,Ord)++instance Exception PMapLookupException++-- | Make a human-readable description on 'PMapLookupException'.+pMapDecribeError :: PMapLookupException -> String+pMapDecribeError (PMapNoSuchKey k) = "Property '" ++ unpack k ++ "' does not exist."+pMapDecribeError (PMapParseError k pe) = "Parse error of property '" ++ unpack k ++ "': " ++ pe++-- | Convert the lookup result into a 'MonadThrow'. It throws+-- 'PMapLookupException'.+pMapToThrow :: MonadThrow m => Either PMapLookupException a -> m a+pMapToThrow (Left e) = throwM e+pMapToThrow (Right a) = return a++-- | Convert the lookup result into a 'MonadFail'. It fails with the+-- description returned by 'pMapDecribeError'.+pMapToFail :: MonadFail m => Either PMapLookupException a -> m a+pMapToFail (Left e) = fail $ pMapDecribeError e+pMapToFail (Right a) = return a
test/Data/Greskell/ExtraSpec.hs view
@@ -7,8 +7,8 @@ import Data.Aeson (Value(..)) import Data.Greskell.Binder (Binder, Binding, runBinder)-import Data.Greskell.Extra (writePropertyKeyValues)-import Data.Greskell.Graph (AVertex)+import Data.Greskell.Extra (writePropertyKeyValues, writeKeyValues)+import Data.Greskell.Graph (AVertex, (=:), Key, KeyValue) import Data.Greskell.Greskell (toGremlin) import Data.Greskell.GTraversal (Walk, WalkType) import qualified Data.HashMap.Strict as HM@@ -46,3 +46,13 @@ ("__v2", String "bar") ] )+ describe "writeKeyValues" $ do+ specify "empty" $ do+ (toGremlin $ writeKeyValues ([] :: [KeyValue AVertex])) `shouldBe` "__.identity()"+ specify "key-values" $ do+ let name :: Key AVertex Text+ name = "name"+ age :: Key AVertex Int+ age = "age"+ input = writeKeyValues [name =: "toshio", age =: 30]+ (toGremlin input) `shouldBe` "__.property(\"name\",\"toshio\").property(\"age\",30).identity()"
test/Data/Greskell/GTraversalSpec.hs view
@@ -14,7 +14,7 @@ pEq, pNeq, pInside, pGte ) import Data.Greskell.Graph- ( Element,+ ( Element, ElementID(..), AVertex, Key, key, tLabel, tId )@@ -48,7 +48,7 @@ specify "g.V()" $ do (toGremlin $ sV' [] $ source "g") `shouldBe` ("g.V()") specify "g.V(1,2,3)" $ do- let ids = map gvalueInt $ ([1,2,3] :: [Int])+ let ids = map (fmap ElementID . gvalueInt) $ ([1,2,3] :: [Int]) (toGremlin $ sV' ids $ source "g") `shouldBe` ("g.V(1,2,3)") spec_order_by :: Spec@@ -89,7 +89,8 @@ let gt = source "g" & sV' [] &. gHas2P ("x" :: Key e Int) (pEq 100) &. gOut' [] &. gRange 0 100 toGremlin gt `shouldBe` "g.V().has(\"x\",P.eq(100)).out().range(0,100)" specify "(&) and (&.) and (>>>)" $ do- let gt = source "g" & sV' [gvalueInt (200 :: Int)] &. (gOut' [] >>> gOut' ["friends_to"] >>> gValues ["name"])+ let vid = fmap ElementID $ gvalueInt (200 :: Int)+ gt = source "g" & sV' [vid] &. (gOut' [] >>> gOut' ["friends_to"] >>> gValues ["name"]) toGremlin gt `shouldBe` "g.V(200).out().out(\"friends_to\").values(\"name\")" specify "($) and ($.)" $ do let gt = gRange 20 30 $. gNot (gOut' ["friends_to"]) $. sV' [] $ source "g"@@ -117,7 +118,9 @@ `shouldBe` "g.E().hasLabel(P.neq(\"friends_to\"))" describe "gHasIdP" $ do specify "P" $ do- toGremlin (source "g" & sV' [] &. gHasIdP (pInside (gvalueInt (10 :: Int)) (gvalueInt (20 :: Int))))+ let toID :: Int -> Greskell (ElementID AVertex)+ toID = fmap ElementID . gvalueInt+ toGremlin (source "g" & sV' [] &. gHasIdP (pInside (toID 10) (toID 20))) `shouldBe` "g.V().hasId(P.inside(10,20))" describe "gHasKeyP, gProperties" $ do specify "P" $ do
+ test/Data/Greskell/Graph/PropertyMapSpec.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE OverloadedStrings #-}+module Data.Greskell.Graph.PropertyMapSpec (main,spec) where++import Data.Monoid (mempty, (<>))+import Test.Hspec++import Data.Greskell.Graph.PropertyMap+ ( PropertyMapSingle, PropertyMapList, AProperty(..),+ PropertyMap(..)+ )++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "PropertyMapSingle" $ do+ let pm :: PropertyMapSingle AProperty Int+ pm = putProperty (AProperty "buzz" 300)+ $ putProperty (AProperty "bar" 200)+ $ putProperty (AProperty "foo" 100) mempty+ specify "allProperties" $ do+ allProperties pm `shouldMatchList`+ [ AProperty "buzz" 300,+ AProperty "bar" 200,+ AProperty "foo" 100+ ]+ specify "lookupOne existing" $ do+ lookupOne "foo" pm `shouldBe` (Just $ AProperty "foo" 100)+ specify "lookupOne non-existing" $ do+ lookupOne "HOGE" pm `shouldBe` Nothing+ specify "lookupList existing" $ do+ lookupList "bar" pm `shouldBe` [AProperty "bar" 200]+ specify "lookupList non-existing" $ do+ lookupList "HOGE" pm `shouldBe` []+ specify "putProperty overrides" $ do+ let pm2 = putProperty (AProperty "foo" 500) pm+ lookupOne "foo" pm2 `shouldBe` Just (AProperty "foo" 500)+ lookupList "foo" pm2 `shouldBe` [AProperty "foo" 500]+ specify "removeProperty" $ do+ let pm2 = removeProperty "HOGE" $ removeProperty "bar" pm+ lookupList "bar" pm2 `shouldBe` []+ specify "mappend prefers the left" $ do+ let pm2 :: PropertyMapSingle AProperty Int+ pm2 = putProperty (AProperty "hoge" 600)+ $ putProperty (AProperty "bar" 500) mempty+ pm3 = pm <> pm2+ allProperties pm3 `shouldMatchList`+ [ AProperty "buzz" 300,+ AProperty "bar" 200,+ AProperty "foo" 100,+ AProperty "hoge" 600+ ]+ describe "PropertyMapList" $ do+ let pm :: PropertyMapList AProperty Int+ pm = putProperty (AProperty "foo" 100)+ $ putProperty (AProperty "foo" 200)+ $ putProperty (AProperty "bar" 300)+ $ putProperty (AProperty "foo" 400) mempty+ specify "allProperties" $ do+ allProperties pm `shouldMatchList`+ [ AProperty "foo" 100,+ AProperty "foo" 200,+ AProperty "bar" 300,+ AProperty "foo" 400+ ]+ specify "lookupOne existing" $ do+ lookupOne "foo" pm `shouldBe` Just (AProperty "foo" 100)+ specify "lookupOne non-existing" $ do+ lookupOne "HOGE" pm `shouldBe` Nothing+ specify "lookupList existing" $ do+ lookupList "foo" pm `shouldBe` map (AProperty "foo") [100,200,400]+ specify "lookupList non-existing" $ do+ lookupList "HOGE" pm `shouldBe` []+ specify "putProperty appends" $ do+ let pm2 = putProperty (AProperty "bar" 500) pm+ lookupOne "bar" pm2 `shouldBe` Just (AProperty "bar" 500)+ lookupList "bar" pm2 `shouldBe` map (AProperty "bar") [500,300]+ specify "removeProperty" $ do+ let pm2 = removeProperty "foo" $ removeProperty "HOGE" pm+ lookupOne "foo" pm2 `shouldBe` Nothing+ lookupList "foo" pm2 `shouldBe` []+ specify "mappend appends" $ do+ let pm2 :: PropertyMapList AProperty Int+ pm2 = putProperty (AProperty "bar" 500)+ $ putProperty (AProperty "buzz" 600)+ $ putProperty (AProperty "foo" 700) mempty+ pm3 = pm <> pm2+ lookupList "foo" pm3 `shouldBe` map (AProperty "foo") [100,200,400,700]+ lookupList "bar" pm3 `shouldBe` map (AProperty "bar") [300,500]+ allProperties pm3 `shouldMatchList`+ [ AProperty "foo" 100,+ AProperty "foo" 200,+ AProperty "foo" 400,+ AProperty "foo" 700,+ AProperty "bar" 300,+ AProperty "bar" 500,+ AProperty "buzz" 600+ ]+
test/Data/Greskell/GraphSpec.hs view
@@ -9,9 +9,10 @@ import Test.Hspec import Data.Greskell.Graph- ( AProperty(..), PropertyMapSingle, PropertyMapList,- PropertyMap(..),- AEdge(..), AVertexProperty(..), AVertex(..)+ ( AProperty(..),+ -- PropertyMapSingle, PropertyMapList,+ AEdge(..), AVertexProperty(..), AVertex(..),+ ElementID(..) ) import Data.Greskell.GraphSON ( nonTypedGraphSON, typedGraphSON, typedGraphSON',@@ -23,99 +24,11 @@ spec :: Spec spec = do- spec_PropertyMap spec_AEdge spec_AProperty spec_AVertexProperty spec_AVertex -spec_PropertyMap :: Spec-spec_PropertyMap = do- describe "PropertyMapSingle" $ do- let pm :: PropertyMapSingle AProperty Int- pm = putProperty (AProperty "buzz" 300)- $ putProperty (AProperty "bar" 200)- $ putProperty (AProperty "foo" 100) mempty- specify "allProperties" $ do- allProperties pm `shouldMatchList`- [ AProperty "buzz" 300,- AProperty "bar" 200,- AProperty "foo" 100- ]- specify "lookupOne existing" $ do- lookupOne "foo" pm `shouldBe` (Just $ AProperty "foo" 100)- specify "lookupOne non-existing" $ do- lookupOne "HOGE" pm `shouldBe` Nothing- specify "lookupList existing" $ do- lookupList "bar" pm `shouldBe` [AProperty "bar" 200]- specify "lookupList non-existing" $ do- lookupList "HOGE" pm `shouldBe` []- specify "putProperty overrides" $ do- let pm2 = putProperty (AProperty "foo" 500) pm- lookupOne "foo" pm2 `shouldBe` Just (AProperty "foo" 500)- lookupList "foo" pm2 `shouldBe` [AProperty "foo" 500]- specify "removeProperty" $ do- let pm2 = removeProperty "HOGE" $ removeProperty "bar" pm- lookupList "bar" pm2 `shouldBe` []- specify "mappend prefers the left" $ do- let pm2 :: PropertyMapSingle AProperty Int- pm2 = putProperty (AProperty "hoge" 600)- $ putProperty (AProperty "bar" 500) mempty- pm3 = pm <> pm2- allProperties pm3 `shouldMatchList`- [ AProperty "buzz" 300,- AProperty "bar" 200,- AProperty "foo" 100,- AProperty "hoge" 600- ]- describe "PropertyMapList" $ do- let pm :: PropertyMapList AProperty Int- pm = putProperty (AProperty "foo" 100)- $ putProperty (AProperty "foo" 200)- $ putProperty (AProperty "bar" 300)- $ putProperty (AProperty "foo" 400) mempty- specify "allProperties" $ do- allProperties pm `shouldMatchList`- [ AProperty "foo" 100,- AProperty "foo" 200,- AProperty "bar" 300,- AProperty "foo" 400- ]- specify "lookupOne existing" $ do- lookupOne "foo" pm `shouldBe` Just (AProperty "foo" 100)- specify "lookupOne non-existing" $ do- lookupOne "HOGE" pm `shouldBe` Nothing- specify "lookupList existing" $ do- lookupList "foo" pm `shouldBe` map (AProperty "foo") [100,200,400]- specify "lookupList non-existing" $ do- lookupList "HOGE" pm `shouldBe` []- specify "putProperty appends" $ do- let pm2 = putProperty (AProperty "bar" 500) pm- lookupOne "bar" pm2 `shouldBe` Just (AProperty "bar" 500)- lookupList "bar" pm2 `shouldBe` map (AProperty "bar") [500,300]- specify "removeProperty" $ do- let pm2 = removeProperty "foo" $ removeProperty "HOGE" pm- lookupOne "foo" pm2 `shouldBe` Nothing- lookupList "foo" pm2 `shouldBe` []- specify "mappend appends" $ do- let pm2 :: PropertyMapList AProperty Int- pm2 = putProperty (AProperty "bar" 500)- $ putProperty (AProperty "buzz" 600)- $ putProperty (AProperty "foo" 700) mempty- pm3 = pm <> pm2- lookupList "foo" pm3 `shouldBe` map (AProperty "foo") [100,200,400,700]- lookupList "bar" pm3 `shouldBe` map (AProperty "bar") [300,500]- allProperties pm3 `shouldMatchList`- [ AProperty "foo" 100,- AProperty "foo" 200,- AProperty "foo" 400,- AProperty "foo" 700,- AProperty "bar" 300,- AProperty "bar" 500,- AProperty "buzz" 600- ]-- loadGraphSON :: FromJSON a => FilePath -> IO (Either String a) loadGraphSON filename = fmap Aeson.eitherDecode $ BSL.readFile ("test/graphson/" ++ filename) @@ -124,28 +37,14 @@ it "should parse GraphSON v1" $ do let expected = nonTypedGraphSON $ AEdge- { aeId = nonTypedGValue $ GNumber 13,- aeLabel = "develops",- aeInVLabel = "software",- aeOutVLabel = "person",- aeInV = nonTypedGValue $ GNumber 10,- aeOutV = nonTypedGValue $ GNumber 1,- aeProperties = putProperty- (AProperty "since" $ nonTypedGValue $ GNumber 2009)- $ mempty+ { aeId = ElementID $ nonTypedGValue $ GNumber 13,+ aeLabel = "develops" } loadGraphSON "edge_v1.json" `shouldReturn` Right expected let expected_v23 = typedGraphSON $ AEdge- { aeId = typedGValue' "g:Int32" $ GNumber 13,- aeLabel = "develops",- aeInVLabel = "software",- aeOutVLabel = "person",- aeInV = typedGValue' "g:Int32" $ GNumber 10,- aeOutV = typedGValue' "g:Int32" $ GNumber 1,- aeProperties = putProperty- (AProperty "since" $ typedGValue' "g:Int32" $ GNumber 2009)- $ mempty+ { aeId = ElementID $ typedGValue' "g:Int32" $ GNumber 13,+ aeLabel = "develops" } it "should parse GraphSON v2" $ do loadGraphSON "edge_v2.json" `shouldReturn` Right expected_v23@@ -176,18 +75,16 @@ it "should parse GraphSON v1" $ do let ex = nonTypedGraphSON $ AVertexProperty- { avpId = nonTypedGValue $ GNumber 0,+ { avpId = ElementID $ nonTypedGValue $ GNumber 0, avpLabel = "name",- avpValue = nonTypedGValue $ GString "marko",- avpProperties = mempty+ avpValue = nonTypedGValue $ GString "marko" } loadGraphSON "vertex_property_v1.json" `shouldReturn` Right ex let ex23 = typedGraphSON $ AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 0,+ { avpId = ElementID $ typedGValue' "g:Int64" $ GNumber 0, avpLabel = "name",- avpValue = nonTypedGValue $ GString "marko",- avpProperties = mempty+ avpValue = nonTypedGValue $ GString "marko" } it "should parse GraphSON v2" $ do loadGraphSON "vertex_property_v2.json" `shouldReturn` Right ex23@@ -199,100 +96,14 @@ it "should parse GraphSON v1" $ do let ex = nonTypedGraphSON $ AVertex- { avId = nonTypedGValue $ GNumber 1,- avLabel = "person",- avProperties = foldr putProperty mempty- [ AVertexProperty- { avpId = nonTypedGValue $ GNumber 0,- avpLabel = "name",- avpValue = nonTypedGValue $ GString "marko",- avpProperties = mempty- },- AVertexProperty- { avpId = nonTypedGValue $ GNumber 6,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "san diego",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ nonTypedGValue $ GNumber 1997,- AProperty "endTime" $ nonTypedGValue $ GNumber 2001- ]- },- AVertexProperty- { avpId = nonTypedGValue $ GNumber 7,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "santa cruz",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ nonTypedGValue $ GNumber 2001,- AProperty "endTime" $ nonTypedGValue $ GNumber 2004- ]- },- AVertexProperty- { avpId = nonTypedGValue $ GNumber 8,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "brussels",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ nonTypedGValue $ GNumber 2004,- AProperty "endTime" $ nonTypedGValue $ GNumber 2005- ]- },- AVertexProperty- { avpId = nonTypedGValue $ GNumber 9,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "santa fe",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ nonTypedGValue $ GNumber 2005- ]- }- ]+ { avId = ElementID $ nonTypedGValue $ GNumber 1,+ avLabel = "person" } loadGraphSON "vertex_v1.json" `shouldReturn` Right ex let ex23 = typedGraphSON $ AVertex- { avId = typedGValue' "g:Int32" $ GNumber 1,- avLabel = "person",- avProperties = foldr putProperty mempty- [ AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 0,- avpLabel = "name",- avpValue = nonTypedGValue $ GString "marko",- avpProperties = mempty- },- AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 6,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "san diego",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ typedGValue' "g:Int32" $ GNumber 1997,- AProperty "endTime" $ typedGValue' "g:Int32" $ GNumber 2001- ]- },- AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 7,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "santa cruz",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ typedGValue' "g:Int32" $ GNumber 2001,- AProperty "endTime" $ typedGValue' "g:Int32" $ GNumber 2004- ]- },- AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 8,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "brussels",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ typedGValue' "g:Int32" $ GNumber 2004,- AProperty "endTime" $ typedGValue' "g:Int32" $ GNumber 2005- ]- },- AVertexProperty- { avpId = typedGValue' "g:Int64" $ GNumber 9,- avpLabel = "location",- avpValue = nonTypedGValue $ GString "santa fe",- avpProperties = foldr putProperty mempty- [ AProperty "startTime" $ typedGValue' "g:Int32" $ GNumber 2005- ]- }- ]+ { avId = ElementID $ typedGValue' "g:Int32" $ GNumber 1,+ avLabel = "person" } it "should parse GraphSON v2" $ do loadGraphSON "vertex_v2.json" `shouldReturn` Right ex23
+ test/Data/Greskell/PMapSpec.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE OverloadedStrings #-}+module Data.Greskell.PMapSpec (main,spec) where++import Prelude hiding (lookup)++import Data.Text (Text)+import Test.Hspec++import Data.Greskell.PMap+ ( PMap, Single, Multi,+ pMapFromList, pMapToList, pMapInsert, pMapDelete,+ lookup, lookupList+ )++main :: IO ()+main = hspec spec++-- the tests are based on PropertyMapSpec.++spec :: Spec+spec = do+ describe "PMap Single" $ do+ let pm :: PMap Single Int+ pm = pMapFromList+ [ ("buzz", 300),+ ("bar", 200),+ ("foo", 100)+ ]+ specify "pMapToList" $ do+ pMapToList pm `shouldMatchList`+ [ ("buzz", 300),+ ("bar", 200),+ ("foo", 100)+ ]+ specify "lookup existing" $ do+ lookup ("foo" :: Text) pm `shouldBe` Just 100+ specify "lookup non-existing" $ do+ lookup ("HOGE" :: Text) pm `shouldBe` Nothing+ specify "lookupList existing" $ do+ lookupList ("foo" :: Text) pm `shouldBe` [100]+ specify "lookupList non-existing" $ do+ lookupList ("HOGE" :: Text) pm `shouldBe` []+ specify "pMapInsert overrides" $ do+ let pm2 = pMapInsert "foo" 500 pm+ pMapToList pm2 `shouldMatchList`+ [ ("buzz", 300),+ ("bar", 200),+ ("foo", 500)+ ]+ specify "pMapFromList prefers the first value" $ do+ let pm2 :: PMap Single Int+ pm2 = pMapFromList+ [("foo", 100), ("foo", 200), ("foo", 300), ("bar", 400)]+ pMapToList pm2 `shouldMatchList` [("foo", 100), ("bar", 400)]+ specify "pMapDelete" $ do+ let pm2 = pMapDelete "HOGE" $ pMapDelete "bar" $ pm+ pMapToList pm2 `shouldMatchList`+ [ ("buzz", 300),+ ("foo", 100)+ ]+ specify "<> prefers the left" $ do+ let pm2 = pMapFromList [("hoge", 600), ("bar", 500)]+ pm3 = pm <> pm2+ pMapToList pm3 `shouldMatchList`+ [ ("buzz", 300),+ ("bar", 200),+ ("foo", 100),+ ("hoge", 600)+ ]+ describe "PMap Multi" $ do+ let pm :: PMap Multi Int+ pm = pMapFromList+ [ ("foo", 100),+ ("foo", 200),+ ("bar", 300),+ ("foo", 400)+ ]+ specify "pMapToList" $ do+ pMapToList pm `shouldMatchList`+ [ ("foo", 100),+ ("foo", 200),+ ("bar", 300),+ ("foo", 400)+ ]+ specify "lookup existing" $ do+ lookup ("foo" :: Text) pm `shouldBe` Just 100+ specify "lookup non-existing" $ do+ lookup ("HOGE" :: Text) pm `shouldBe` Nothing+ specify "lookupList existing" $ do+ lookupList ("foo" :: Text) pm `shouldBe` [100,200,400]+ specify "lookupList non-existing" $ do+ lookupList ("HOGE" :: Text) pm `shouldBe` []+ specify "pMapInsert prepends" $ do+ let pm2 = pMapInsert "bar" 500 pm+ pMapToList pm2 `shouldMatchList`+ [ ("bar", 500),+ ("foo", 100),+ ("foo", 200),+ ("bar", 300),+ ("foo", 400)+ ]+ lookup ("bar" :: Text) pm2 `shouldBe` Just 500+ lookupList ("bar" :: Text) pm2 `shouldBe` [500, 300]+ specify "pMapDelete" $ do+ let pm2 = pMapDelete "HOGE" $ pMapDelete "foo" pm+ pMapToList pm2 `shouldMatchList` [("bar", 300)]+ let pm3 = pMapDelete "bar" pm2+ pMapToList pm3 `shouldMatchList` []+ specify "<> appends" $ do+ let pm2 = pMapFromList+ [ ("bar", 500),+ ("buzz", 600),+ ("foo", 700)+ ]+ pm3 = pm <> pm2+ lookupList ("foo" :: Text) pm3 `shouldBe` [100, 200, 400, 700]+ lookupList ("bar" :: Text) pm3 `shouldBe` [300, 500]+ pMapToList pm3 `shouldMatchList`+ [ ("foo", 100),+ ("foo", 200),+ ("bar", 300),+ ("foo", 400),+ ("bar", 500),+ ("buzz", 600),+ ("foo", 700)+ ]+
test/ServerBehaviorTest.hs view
@@ -3,16 +3,23 @@ import qualified Data.Vector as V import qualified Network.Greskell.WebSocket.Client as WS+import System.IO (hPutStrLn, stderr) import Test.Hspec +import Control.Category ((<<<))+import Control.Monad (void)+import Data.Text (Text) import Data.Greskell.Binder (newBind, runBinder) import Data.Greskell.Graph- ( AVertex, Key,+ ( AVertex(..), Key, AEdge, Property(propertyKey, propertyValue),+ AVertexProperty(..) )+import Data.Greskell.GraphSON (parseEither) import Data.Greskell.GTraversal ( Walk, GTraversal, SideEffect,- source, sV', sAddV', gProperty, gId, gValues, gHasId, gHasLabel, gHas2,- ($.), liftWalk+ source, sV', sE', sAddV', gProperty, gId, gValues, gHasId, gHasLabel, gHas2,+ ($.), liftWalk,+ gAddE', gTo, gV', gProperties ) import ServerTest.Common (withEnv, withClient)@@ -23,6 +30,8 @@ spec :: Spec spec = withEnv $ do spec_values_type+ spec_generic_element_ID+ spec_vertex_with_props clearGraph :: WS.Client -> IO () clearGraph client = WS.drainResults =<< WS.submitRaw client "g.V().drop()" Nothing@@ -52,3 +61,58 @@ got_ids <- putProp got <- getProp (got_ids V.! 0) V.toList got `shouldBe` [100]++spec_generic_element_ID :: SpecWith (String, Int)+spec_generic_element_ID = do+ specify "get Vertex ID as GValue, query Vertex by GValue" $ withClient $ \client -> do+ let prop_key :: Key AVertex Int+ prop_key = "sample"+ prop_val = 125+ make_v = liftWalk gId $. gProperty prop_key prop_val $. (sAddV' "test" $ source "g")+ clearGraph client+ got_ids <- fmap V.toList $ WS.slurpResults =<< WS.submit client make_v Nothing+ hPutStrLn stderr ("Got Vertex IDs: " <> show got_ids)+ length got_ids `shouldBe` 1+ let (q, qbind) = runBinder $ do+ vid <- newBind (got_ids !! 0)+ return $ gValues [prop_key] $. (sV' [vid] $ source "g")+ got_vals <- fmap V.toList $ WS.slurpResults =<< WS.submit client q (Just qbind)+ got_vals `shouldBe` [125]+ specify "get Edge ID as GValue, query Edge by GValue" $ withClient $ \client -> do+ let vname_key :: Key AVertex Text+ vname_key = "name"+ ename_key :: Key AEdge Text+ ename_key = "name"+ makeV n = (liftWalk $ gProperty vname_key n) $. (sAddV' "test_v" $ source "g")+ makeE fn tn = liftWalk gId+ $. gProperty ename_key "e_test"+ $. gAddE' "test_e" (gTo $ gHas2 vname_key tn <<< gV' [])+ $. gHas2 vname_key fn+ $. (liftWalk $ sV' [] $ source "g")+ clearGraph client+ void $ WS.slurpResults =<< WS.submit client (makeV "v_from") Nothing+ void $ WS.slurpResults =<< WS.submit client (makeV "v_to") Nothing+ got_ids <- fmap V.toList $ WS.slurpResults =<< WS.submit client (makeE "v_from" "v_to") Nothing+ hPutStrLn stderr ("Got Edge IDs: " <> show got_ids)+ length got_ids `shouldBe` 1+ let (q, qbind) = runBinder $ do+ eid <- newBind (got_ids !! 0)+ return $ gValues [ename_key] $. (sE' [eid] $ source "g")+ got_vals <- fmap V.toList $ WS.slurpResults =<< WS.submit client q (Just qbind)+ got_vals `shouldBe` ["e_test"]+ +spec_vertex_with_props :: SpecWith (String, Int)+spec_vertex_with_props = do+ let prop_key :: Key AVertex Int+ prop_key = "sample"+ makeV = gProperty prop_key 1132 $. (sAddV' "test" $ source "g")+ getProps vid = gProperties [prop_key] $. (sV' [vid] $ source "g")+ specify "get Vertex element as AVertex, VertexProperty element as AVertexProperty" $ withClient $ \client -> do+ clearGraph client+ got_vs <- fmap V.toList $ WS.slurpResults =<< WS.submit client makeV Nothing+ (fmap avLabel $ got_vs) `shouldBe` ["test"]+ let (query, bindings) = runBinder $ do+ vid <- newBind $ avId (got_vs !! 0)+ return $ getProps vid+ got_vps <- fmap V.toList $ WS.slurpResults =<< WS.submit client query (Just bindings)+ map (\vp -> (avpLabel vp, avpValue vp)) got_vps `shouldBe` [("sample", 1132)]
test/ServerTest.hs view
@@ -1,12 +1,13 @@ {-# LANGUAGE OverloadedStrings, TypeFamilies #-} module Main (main,spec) where -import Control.Category ((<<<))+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 (sortBy)+import Data.List (sort) import Data.Monoid (mempty, (<>)) import Data.Scientific (Scientific) import Data.Text (unpack, Text)@@ -14,7 +15,7 @@ import qualified Network.Greskell.WebSocket.Client as WS import Test.Hspec -import Data.Greskell.AsLabel (AsLabel(..), lookupAsM)+import Data.Greskell.AsLabel (AsLabel(..)) import qualified Data.Greskell.AsLabel as As import Data.Greskell.AsIterator ( AsIterator(IteratorItem)@@ -32,9 +33,9 @@ ) import Data.Greskell.Graph ( AVertex(..), AEdge(..), AProperty(..), AVertexProperty(..),- PropertyMapSingle, Key,+ Key, Keys(..), (-:), singletonKeys, T, tId, tLabel, tKey, tValue, cList, (=:),- fromProperties, allProperties+ ElementID(..) ) import Data.Greskell.GraphSON ( FromGraphSON, nonTypedGValue, GValue,@@ -43,12 +44,15 @@ import Data.Greskell.GTraversal ( Walk, GTraversal, SideEffect, source, sV', sE', gV', sAddV', gAddE', gTo,- ($.), gOrder, gBy1, gBy,+ ($.), gOrder, gBy1, gBy, gByL, Transform, unsafeWalk, unsafeGTraversal,- gProperties, gProperty, gPropertyV, liftWalk,+ gProperties, gProperty, gPropertyV, liftWalk, gValues, gAs, gSelect1, gSelectN, gSelectBy1, gSelectByN,- gFilter, gOut'+ gFilter, gOut', gOutV, gOutV', gInV, gInV', gId, gLabel, gProject,+ gValueMap,+ gProject, gByL )+import Data.Greskell.PMap (lookupAsM, lookupListAs, pMapToThrow) import ServerTest.Common (withEnv, withClient) @@ -65,7 +69,7 @@ spec_graph spec_as spec_selectBy-+ spec_project spec_basics :: SpecWith (String,Int) spec_basics = do@@ -174,7 +178,7 @@ spec_T :: SpecWith (String,Int) spec_T = describe "T enum" $ do- specFor' "tId" (gMapT tId) parseEither [(Right 10 :: Either String Int)]+ 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)]@@ -238,55 +242,87 @@ got <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing (map (fmap parseEither) $ V.toList got) `shouldMatchList` expected specify "AEdge" $ withClient $ \client -> do- let trav = sE' [] $ source "g"- expE :: Int -> Int -> Text -> (Text,Text,Text,Either String Int, Either String Int, PropertyMapSingle AProperty (Either String Text))- expE outv inv cond = ("depends_on", "package", "package", Right outv, Right inv, props)- where- props = fromProperties [AProperty "condition" $ Right cond]- getE e = ( aeLabel e, aeInVLabel e, aeOutVLabel e,- parseEither $ aeOutV e, parseEither $ aeInV e,- fmap parseEither $ aeProperties e- )- expected = [ expE 1 2 ">=0.11.2.1",- expE 1 3 ">=1.2.2.1",- expE 2 3 ">=1.2.3"+ 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 <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing- (map getE $ V.toList got) `shouldMatchList` expected- let getVP vp = (avpLabel vp, parseEither $ avpValue vp, fmap parseEither $ avpProperties vp)+ got <- traverse parseResult =<< WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing+ V.toList got `shouldMatchList` expected specify "AVertexProperty" $ withClient $ \client -> do- let trav = gProperties [] $. sV' [] $ source "g"- expName :: Text -> (Text,Either String Text, PropertyMapSingle AProperty (Either String Text))- expName val = ("name", Right val, mempty)- expVer :: Text -> Text -> (Text,Either String Text, PropertyMapSingle AProperty (Either String Text))- expVer val date = ("version", Right val, fromProperties [AProperty "date" $ Right date])- expected = [ expName "greskell",- expName "aeson",- expName "text",- expVer "0.1.1.0" "2018-04-08",- expVer "1.3.1.1" "2018-05-10",- expVer "1.2.2.0" "2017-09-20",- expVer "1.2.3.0" "2017-12-27",- expVer "1.2.2.0" "2017-12-23"+ 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 <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing- (map getVP $ V.toList got) `shouldMatchList` expected+ got <- traverse parseResult =<< WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing+ V.toList got `shouldMatchList` expected specify "AVertex" $ withClient $ \client -> do- let trav = sV' [] $ source "g"- getV v = ( parseEither $ avId v,- avLabel v,- sort' $ map getVP $ allProperties $ avProperties v- )- sort' = sortBy $ \(k1, v1, _) (k2, v2, _) -> compare (show k1,show v1) (show k2,show v2)- expV :: Int -> Text -> [(Text, Text)] -> (Either String Int,Text,[(Text,Either String Text,PropertyMapSingle AProperty (Either String Text))])- expV vid name ver_dates = (Right vid, "package", ("name", Right name, mempty) : map toVP ver_dates)- toVP (ver, date) = ("version", Right ver, fromProperties [AProperty "date" $ Right date])- expected = [ expV 1 "greskell" [("0.1.1.0", "2018-04-08")],- expV 2 "aeson" [("1.2.2.0", "2017-09-20"), ("1.3.1.1", "2018-05-10")],- expV 3 "text" [("1.2.2.0", "2017-12-23"), ("1.2.3.0", "2017-12-27")]+ 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 <- WS.slurpResults =<< WS.submit client (withPrelude' trav) Nothing- (map getV $ V.toList got) `shouldMatchList` expected+ 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@@ -312,8 +348,8 @@ ) finalize :: GTraversal c s e -> Text finalize gt = toGremlin $ iterateTraversal gt- num :: Integer -> Greskell GValue- num = gvalueInt+ 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@@ -326,13 +362,14 @@ [ 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 <> " }"] spec_as :: SpecWith (String,Int) spec_as = do let start :: GTraversal Transform () Int start = unsafeGTraversal "__(1,2,3)"- mult :: Greskell Int -> Walk Transform Int Int- mult factor = unsafeWalk "map" ["{ it.get() * " <> toGremlin factor <> " }"]+ mult = multiplyWalk specify "gAs and gSelect1" $ withClient $ \client -> do let label :: AsLabel Int label = AsLabel "a"@@ -375,3 +412,45 @@ 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]