hasbolt-extras 0.0.0.20 → 0.0.0.21
raw patch · 6 files changed
+131/−11 lines, 6 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Database.Bolt.Extras.DSL: (#) :: a -> (a -> b) -> b
+ Database.Bolt.Extras.DSL: (-:) :: NodeSelector -> PathPart -> PathSelector
+ Database.Bolt.Extras.DSL: (.#) :: SelectorLike a => a -> [(Text, Value)] -> a
+ Database.Bolt.Extras.DSL: (.:) :: SelectorLike a => a -> Text -> a
+ Database.Bolt.Extras.DSL: (<-:) :: NodeSelector -> PathPart -> PathSelector
+ Database.Bolt.Extras.DSL: defN :: NodeSelector
+ Database.Bolt.Extras.DSL: defR :: RelSelector
+ Database.Bolt.Extras.DSL: infixl 9 .#
+ Database.Bolt.Extras.DSL: type CypherDSL a = Free Expr ()
- Database.Bolt.Extras.DSL: infixl 1 :<-!:
+ Database.Bolt.Extras.DSL: infixl 1 <-:
Files
- CHANGELOG.md +4/−0
- hasbolt-extras.cabal +1/−1
- src/Database/Bolt/Extras/DSL.hs +47/−3
- src/Database/Bolt/Extras/DSL/Internal/Instances.hs +20/−6
- src/Database/Bolt/Extras/DSL/Internal/Language.hs +6/−1
- src/Database/Bolt/Extras/DSL/Internal/Types.hs +53/−0
CHANGELOG.md view
@@ -6,6 +6,10 @@ ## [Unreleased] +## [0.0.0.21] - 2019-09-09+### Added+- `OverloadedLabels` instances and operators for easy selector writing.+ ## [0.0.0.20] - 2019-09-06 ### Added - `WITH` statement in DSL.
hasbolt-extras.cabal view
@@ -1,5 +1,5 @@ name: hasbolt-extras-version: 0.0.0.20+version: 0.0.0.21 synopsis: Extras for hasbolt library description: Extras for hasbolt library homepage: https://github.com/biocad/hasbolt-extras#readme
src/Database/Bolt/Extras/DSL.hs view
@@ -1,8 +1,52 @@ module Database.Bolt.Extras.DSL (- module Database.Bolt.Extras.DSL.Internal.Types- , module Database.Bolt.Extras.DSL.Internal.Language- , module Database.Bolt.Extras.DSL.Internal.Executer+ -- * Selectors for nodes, relations and paths+ --+ -- | These data types let you specify Cypher queries.+ --+ -- With @OverloadedLabels@ and operators you can write selectors in very concise+ -- Cypher-like form:+ --+ -- > (#n .: "Name" .# ["name" =: "C42"]) -: (defR .: "NAME_OF") :!->: (#m .: "Molecule")+ -- > (n:Name{name:"C42"})-[:NAME_OF]->(m:Molecule)+ --+ NodeSelector(..),+ RelSelector(..),+ SelectorLike(..),+ (.:), (.#),+ toNodeSelector, toRelSelector,+ PathSelector(..),+ PathPart(..),+ (-:), (<-:),+ Selector(..),+ Selectors,++ -- ** Default selectors+ defaultNode, defN, defaultRel, defR,++ -- * Cypher conditions+ Cond(..),+ Conds(..),++ -- * DSL for Cypher+ --+ -- | The free-monadic DSL lets you write Cypher queries in Haskell like this:+ --+ -- > formQuery $ do+ -- > matchF [+ -- > PS $ (#n .: "Name" .# ["name" =: "C42"]) -: (defR .: "NAME_OF") :!->: (#m .: "Molecule")+ -- > ]+ -- > returnF ["n", "m"]+ --++ -- ** DSL operations+ module Database.Bolt.Extras.DSL.Internal.Language,++ -- ** Rendering Cypher queries+ formQuery,++ -- ** Implementation details+ Expr(..) ) where import Database.Bolt.Extras.DSL.Internal.Executer
src/Database/Bolt/Extras/DSL/Internal/Instances.hs view
@@ -1,20 +1,34 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Database.Bolt.Extras.DSL.Internal.Instances () where import Control.Monad.Writer (execWriter, tell)+import Data.Function ((&)) import Data.Monoid ((<>))+import Data.Proxy (Proxy (..)) import Data.Text (intercalate, pack) import Database.Bolt.Extras (ToCypher (..), fromInt)-import Database.Bolt.Extras.DSL.Internal.Types+import GHC.OverloadedLabels (IsLabel (..))+import GHC.TypeLits (KnownSymbol,+ symbolVal) import NeatInterpolation (text) import Text.Printf (printf)++import Database.Bolt.Extras.DSL.Internal.Types++instance KnownSymbol x => IsLabel x NodeSelector where+ fromLabel = defaultNode & withIdentifier (pack $ symbolVal @x Proxy)++instance KnownSymbol x => IsLabel x RelSelector where+ fromLabel = defaultRel & withIdentifier (pack $ symbolVal @x Proxy) instance SelectorLike NodeSelector where withIdentifier idx node = node { nodeIdentifier = Just idx }
src/Database/Bolt/Extras/DSL/Internal/Language.hs view
@@ -1,6 +1,7 @@ module Database.Bolt.Extras.DSL.Internal.Language (- createF+ CypherDSL+ , createF , matchF , optionalMatchF , mergeF@@ -18,6 +19,10 @@ import Data.Text (Text) import Database.Bolt.Extras.DSL.Internal.Types (Conds (..), Expr (..), Selectors)++-- | A synonym for 'Free' DSL.+--+type CypherDSL a = Free Expr () -- | Prepare 'CREATE' query --
src/Database/Bolt/Extras/DSL/Internal/Types.hs view
@@ -16,13 +16,20 @@ , Conds (..) , Expr (..) , SelectorLike (..)+ , (.:)+ , (.#) , (#)+ , (-:)+ , (<-:) , defaultNode+ , defN , defaultRel+ , defR , toNodeSelector , toRelSelector ) where +import Data.Foldable (foldl') import Data.Map.Strict (toList) import Data.Text (Text) import Database.Bolt (Node (..), URelationship (..),@@ -38,6 +45,12 @@ -- | Selector for 'Node's. --+-- This datatype has @OverloadedLabels@ instance to simplify specifying nodes. Labels produce+-- empty nodes.+--+-- > #foo :: NodeSelector+-- > -- foo = NodeSelector (Just "foo") [] []+-- data NodeSelector = NodeSelector { nodeIdentifier :: Maybe Text , nodeLabels :: [Text] , nodeProperties :: [(Text, Value)]@@ -46,13 +59,31 @@ -- | Selector for 'URelationship's. --+-- This datatype has @OverloadedLabels@ instance as well, similar to 'NodeSelector'. data RelSelector = RelSelector { relIdentifier :: Maybe Text , relLabel :: Text , relProperties :: [(Text, Value)] } deriving (Show, Eq) +-- | Operator version of 'withLabel'. To be used with @OverloadedLabels@ instances.+--+-- > #foo .: "Foo" :: NodeSelector+--+infixl 9 .:+(.:) :: SelectorLike a => a -> Text -> a+(.:) = flip withLabel +-- | Operator version of 'withProp'. To be used with @OverloadedLabels@ instances.+--+-- See also 'Database.Bolt.=:' from @Database.Bolt@ package.+--+-- > #foo .# ["bar" =: 42, "baz" =: "baz"] :: NodeSelector+--+infixl 9 .#+(.#) :: SelectorLike a => a -> [(Text, Value)] -> a+(.#) = foldl' (flip withProp)+ (#) :: a -> (a -> b) -> b (#) = flip ($) @@ -71,6 +102,18 @@ | P NodeSelector -- ^ starting node of Path deriving (Show, Eq) +-- | Combined version of ':-!:' and 'P' for specifying the first node of path.+--+infixl 1 -:+(-:) :: NodeSelector -> PathPart -> PathSelector+ns -: pp = P ns :-!: pp++-- | Combined version of ':<-!:' and 'P' for specifying the first node of path.+--+infixl 1 <-:+(<-:) :: NodeSelector -> PathPart -> PathSelector+ns <-: pp = P ns :<-!: pp+ data Selector = PS PathSelector -- ^ path selector | TS Text -- ^ free text selector deriving (Show, Eq)@@ -109,11 +152,21 @@ | Text Text next -- ^ free text query deriving (Show, Eq, Functor) +-- | Empty 'NodeSelector'. defaultNode :: NodeSelector defaultNode = NodeSelector Nothing [] [] +-- | Shorter synonym for 'defaultRel'.+defN :: NodeSelector+defN = defaultNode++-- | Empty 'RelSelector'. defaultRel :: RelSelector defaultRel = RelSelector Nothing "" []++-- | Shorter synonym for 'defaultRel'.+defR :: RelSelector+defR = defaultRel toNodeSelector :: Node -> NodeSelector toNodeSelector Node{..} = defaultNode { nodeLabels = labels