hsparql 0.3.4 → 0.3.5
raw patch · 3 files changed
+141/−57 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Database.HSparql.QueryGenerator: instance Database.HSparql.QueryGenerator.QueryShow Database.HSparql.QueryGenerator.QueryForm
+ Database.HSparql.QueryGenerator: askTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()
+ Database.HSparql.QueryGenerator: bind_ :: Expr -> Variable -> Query ()
+ Database.HSparql.QueryGenerator: constructTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()
+ Database.HSparql.QueryGenerator: data Prefix
+ Database.HSparql.QueryGenerator: describeIRI_ :: IRIRef -> Query ()
+ Database.HSparql.QueryGenerator: distinct_ :: Query ()
+ Database.HSparql.QueryGenerator: filterExpr_ :: (TermLike a) => a -> Query ()
+ Database.HSparql.QueryGenerator: groupBy_ :: (TermLike a) => a -> Query ()
+ Database.HSparql.QueryGenerator: instance GHC.Classes.Eq Database.HSparql.QueryGenerator.Prefix
+ Database.HSparql.QueryGenerator: limit_ :: Int -> Query ()
+ Database.HSparql.QueryGenerator: optional_ :: Query a -> Query ()
+ Database.HSparql.QueryGenerator: reduced_ :: Query ()
+ Database.HSparql.QueryGenerator: subQuery :: Query SelectQuery -> Query Pattern
+ Database.HSparql.QueryGenerator: subQuery_ :: Query SelectQuery -> Query ()
+ Database.HSparql.QueryGenerator: triple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()
+ Database.HSparql.QueryGenerator: union_ :: Query a -> Query b -> Query ()
+ Database.HSparql.QueryGenerator: updateTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()
Files
- Database/HSparql/QueryGenerator.hs +139/−55
- hsparql.cabal +1/−1
- tests/AllTests.hs +1/−1
Database/HSparql/QueryGenerator.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ExistentialQuantification, OverloadedLists #-} -- |The query generator DSL for SPARQL, used when connecting to remote -- endpoints.@@ -12,29 +12,30 @@ -- * Query Actions , prefix , var- , Database.HSparql.QueryGenerator.triple+ , Database.HSparql.QueryGenerator.triple, triple_ , mkPredicateObject- , constructTriple- , askTriple- , updateTriple- , describeIRI- , optional- , union- , filterExpr- , bind+ , constructTriple, constructTriple_+ , askTriple, askTriple_+ , updateTriple, updateTriple_+ , describeIRI, describeIRI_+ , optional, optional_+ , union, union_+ , filterExpr, filterExpr_+ , bind, bind_+ , subQuery, subQuery_ , select , selectVars , as -- ** Duplicate handling- , distinct- , reduced+ , distinct, distinct_+ , reduced, reduced_ -- ** Limit handling- , limit+ , limit, limit_ -- ** Groups handling- , groupBy+ , groupBy, groupBy_ -- ** Order handling , orderNext@@ -81,6 +82,7 @@ -- * Types , Query+ , Prefix , Variable , VarOrNode(..) , BlankNodePattern@@ -100,8 +102,11 @@ ) where -import Control.Monad.State-import Data.List (intercalate)+import Control.Monad.State+import Data.List (intercalate, intersperse)+import qualified Data.List as L+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NE import qualified Data.Text as T import qualified Data.RDF as RDF @@ -110,22 +115,28 @@ -- |The 'State' monad applied to 'QueryData'. type Query a = State QueryData a +-- |Execute a 'Query' action, starting with initial 'QueryData', then process+-- the resulting 'QueryData'.+execQuery :: QueryData -> Query a -> (QueryData -> b) -> b+execQuery qd q f = f $ execState q qd+ -- |Execute a 'Query' action, starting with the empty 'queryData', then process -- the resulting 'QueryData'.-execQuery :: Query a -> (QueryData -> b) -> b-execQuery q f = f $ execState q queryData+execQuery0 :: Query a -> (QueryData -> b) -> b+execQuery0 = execQuery queryData -- |Execute a 'Select Query' action, returning the 'String' representation of the query. createSelectQuery :: Query SelectQuery -> String-createSelectQuery q = execQuery specifyVars qshow- where specifyVars :: Query ()- specifyVars = do- query <- q- modify $ \s -> s { vars = queryExpr query, queryType = SelectType }+createSelectQuery q = execQuery0 (specifyVars q) qshow +specifyVars :: Query SelectQuery -> Query ()+specifyVars q = do+ query <- q+ modify $ \s -> s { vars = queryExpr query, queryType = SelectType }+ -- |Execute a 'Construct Query' action, returning the 'String' representation of the query. createConstructQuery :: Query ConstructQuery -> String-createConstructQuery q = execQuery specifyType qshow+createConstructQuery q = execQuery0 specifyType qshow where specifyType :: Query () specifyType = do query <- q@@ -133,7 +144,7 @@ -- |Execute a 'Ask Query' action, returning the 'String' representation of the query. createAskQuery :: Query AskQuery -> String-createAskQuery q = execQuery specifyType qshow+createAskQuery q = execQuery0 specifyType qshow where specifyType :: Query () specifyType = do query <- q@@ -141,7 +152,7 @@ -- |Execute a 'Update Query' action, returning the 'String' representation of the query. createUpdateQuery :: Query UpdateQuery -> String-createUpdateQuery q = execQuery specifyType qshow+createUpdateQuery q = execQuery0 specifyType qshow where specifyType :: Query () specifyType = do query <- q@@ -149,7 +160,7 @@ -- |Execute a 'Describe Query' action, returning the 'String' representation of the query. createDescribeQuery :: Query DescribeQuery -> String-createDescribeQuery q = execQuery specifyType qshow+createDescribeQuery q = execQuery0 specifyType qshow where specifyType :: Query () specifyType = do query <- q@@ -167,9 +178,11 @@ -- |Create and return a variable to the query, usable in later expressions. var :: Query Variable-var = do n <- gets varsIdx+var = do qis <- gets (NE.init . subQueryIdx)+ n <- gets varsIdx+ let sqis = NE.fromList (qis ++ [n]) modify $ \s -> s { varsIdx = n + 1 }- return $ Variable n+ return $ Variable sqis -- |Restrict the query to only results for which values match constants in this -- triple, or for which the variables can be bound.@@ -179,29 +192,44 @@ modify $ \s -> s { pattern = appendPattern t (pattern s) } return t +triple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()+triple_ a b c = void $ triple a b c+ constructTriple :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query Pattern constructTriple a b c = do let t = QTriple (varOrTerm a) (varOrTerm b) (varOrTerm c) modify $ \s -> s { constructTriples = appendTriple t (constructTriples s) } return t +constructTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()+constructTriple_ a b c = void $ constructTriple a b c+ askTriple :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query Pattern askTriple a b c = do let t = QTriple (varOrTerm a) (varOrTerm b) (varOrTerm c) modify $ \s -> s { askTriples = appendTriple t (askTriples s) } return t +askTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()+askTriple_ a b c = void $ askTriple a b c+ updateTriple :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query Pattern updateTriple a b c = do let t = QTriple (varOrTerm a) (varOrTerm b) (varOrTerm c) -- TODO: should only allow terms modify $ \s -> s { updateTriples = appendTriple t (updateTriples s) } return t +updateTriple_ :: (SubjectTermLike a, PredicateTermLike b, ObjectTermLike c) => a -> b -> c -> Query ()+updateTriple_ a b c = void $ updateTriple a b c+ describeIRI :: IRIRef -> Query IRIRef describeIRI newIri = do modify $ \s -> s { describeURI = Just newIri } return newIri +describeIRI_ :: IRIRef -> Query ()+describeIRI_ = void . describeIRI+ selectVars :: [Variable] -> Query SelectQuery selectVars vs = return SelectQuery { queryExpr = fmap SelectVar vs } @@ -215,20 +243,26 @@ optional q = do -- Determine the patterns by executing the action on a blank QueryData, and -- then pulling the patterns out from there.- let option = execQuery q $ OptionalGraphPattern . pattern+ let option = execQuery0 q $ OptionalGraphPattern . pattern modify $ \s -> s { pattern = appendPattern option (pattern s) } return option +optional_ :: Query a -> Query ()+optional_ = void . optional+ -- |Add a union structure to the query pattern. As with 'optional' blocks, -- variables must be defined prior to the opening of any block. union :: Query a -> Query b -> Query Pattern union q1 q2 = do- let p1 = execQuery q1 pattern- p2 = execQuery q2 pattern+ let p1 = execQuery0 q1 pattern+ p2 = execQuery0 q2 pattern union' = UnionGraphPattern p1 p2 modify $ \s -> s { pattern = appendPattern union' (pattern s) } return union' +union_ :: Query a -> Query b -> Query ()+union_ a b = void $ union a b+ -- |Restrict results to only those for which the given expression is true. filterExpr :: (TermLike a) => a -> Query Pattern filterExpr e = do@@ -236,12 +270,44 @@ modify $ \s -> s { pattern = appendPattern f (pattern s) } return f +filterExpr_ :: (TermLike a) => a -> Query ()+filterExpr_ = void . filterExpr++-- |Bind the result of an expression to a variable. bind :: Expr -> Variable -> Query Pattern bind e v = do let b = Bind (expr e) v modify $ \s -> s { pattern = appendPattern b (pattern s) } return b +bind_ :: Expr -> Variable -> Query ()+bind_ a b = void $ bind a b++-- |Perform a subquery.+subQuery :: Query SelectQuery -> Query Pattern+subQuery q = do+ -- Manage subquery indexes+ qis <- gets subQueryIdx+ let sqis = qis |> 0+ qis' = NE.fromList $ NE.init qis ++ [NE.last qis + 1]+ -- Execute the subquery action+ let subQueryData0 = queryData { subQueryIdx = sqis }+ subQueryData = execQuery subQueryData0 (specifyVars q) id+ -- Merge prefixes+ prefixesParentQuery <- gets prefixes+ let prefixesSubQuery = prefixes subQueryData+ newPrefixes = prefixesParentQuery `L.union` prefixesSubQuery+ -- Create the subquery pattern and remove prefixes from the subquery+ let sq = SubQuery $ subQueryData { prefixes = [] }+ -- Append the subquery pattern, update the subquery index and the prefixes.+ modify $ \s -> s { pattern = appendPattern sq (pattern s)+ , subQueryIdx = qis'+ , prefixes = newPrefixes }+ return sq++subQuery_ :: Query SelectQuery -> Query ()+subQuery_ = void . subQuery+ -- Random auxiliary -- |Form a 'Node', with the 'Prefix' and reference name.@@ -256,17 +322,26 @@ distinct = do modify $ \s -> s { duplicates = Distinct } gets duplicates +distinct_ :: Query ()+distinct_ = void distinct+ -- |Set duplicate handling to 'Reduced'. By default, there are no reductions. reduced :: Query Duplicates reduced = do modify $ \s -> s { duplicates = Reduced } gets duplicates +reduced_ :: Query ()+reduced_ = void reduced+ -- |Set limit handling to the given value. By default, there are no limits. -- Note: negative numbers cause no query results to be returned. limit :: Int -> Query Limit limit n = do modify $ \s -> s { limits = Limit n } gets limits +limit_ :: Int -> Query ()+limit_ = void . limit+ -- Grouping -- |Divide the solution into one or more groups.@@ -275,6 +350,9 @@ modify $ \s -> s { groups = groups s ++ [GroupBy . expr $ e] } gets groups +groupBy_ :: (TermLike a) => a -> Query ()+groupBy_ = void . groupBy+ -- Order handling -- |Alias of 'orderNextAsc'.@@ -518,6 +596,7 @@ , varsIdx = 0 , vars = [] , queryType = TypeNotSet+ , subQueryIdx = [0] , pattern = GroupGraphPattern [] , constructTriples = [] , askTriples = []@@ -543,9 +622,9 @@ deriving (Show) data Prefix = Prefix T.Text RDF.Node- deriving (Show)+ deriving (Show, Eq) -data Variable = Variable Int+data Variable = Variable (NonEmpty Int) deriving (Show) data DynamicPredicate = forall a. (PredicateTermLike a, QueryShow a, Show a) => DynamicPredicate a@@ -653,6 +732,7 @@ | Bind Expr Variable | OptionalGraphPattern GroupGraphPattern | UnionGraphPattern GroupGraphPattern GroupGraphPattern+ | SubQuery QueryData data GroupGraphPattern = GroupGraphPattern [Pattern] @@ -674,6 +754,7 @@ , varsIdx :: Int , vars :: [SelectExpr] , queryType :: QueryType+ , subQueryIdx :: NonEmpty Int , pattern :: GroupGraphPattern , constructTriples :: [Pattern] -- QTriple , askTriples :: [Pattern]@@ -688,8 +769,6 @@ data QueryType = SelectType | ConstructType | AskType | UpdateType | DescribeType | TypeNotSet -data QueryForm = SelectForm QueryData | ConstructForm QueryData | AskForm QueryData | UpdateForm QueryData | DescribeForm QueryData- data ConstructQuery = ConstructQuery { queryConstructs :: [Pattern] } @@ -740,7 +819,8 @@ qshow = unwords . fmap qshow instance QueryShow Variable where- qshow (Variable v) = "?x" ++ show v+ qshow (Variable vs) = "?x" ++ indexes+ where indexes = mconcat . intersperse "_" . fmap show . NE.toList $ vs instance QueryShow [Variable] where qshow = unwords . fmap qshow@@ -832,6 +912,7 @@ qshow (Bind e v) = "BIND(" ++ qshow e ++ " AS " ++ qshow v ++ ")" qshow (OptionalGraphPattern p) = "OPTIONAL " ++ qshow p qshow (UnionGraphPattern p1 p2) = qshow p1 ++ " UNION " ++ qshow p2+ qshow (SubQuery qd) = intercalate " " ["{", qshow qd, "}"] instance QueryShow [Pattern] where qshow = unwords . fmap qshow@@ -854,17 +935,6 @@ qshow [] = "" qshow os = unwords $ "ORDER BY" : fmap qshow os -instance QueryShow QueryForm where- qshow (SelectForm qd) = unwords- [ "SELECT"- , qshow (duplicates qd)- , qshow (vars qd)- ]- qshow (ConstructForm qd) = "CONSTRUCT { " ++ qshow (constructTriples qd) ++ " }"- qshow (AskForm qd) = "ASK { " ++ qshow (askTriples qd) ++ " }"- qshow (UpdateForm qd) = "INSERT DATA { " ++ qshow (updateTriples qd) ++ " }"- qshow (DescribeForm qd) = "DESCRIBE " ++ qshow (describeURI qd)- instance QueryShow QueryData where qshow qd = query where prefixDecl = qshow (prefixes qd)@@ -878,27 +948,36 @@ query = case queryType qd of SelectType -> unwords' [ prefixDecl- , qshow (SelectForm qd)+ , "SELECT"+ , qshow (duplicates qd)+ , qshow (vars qd) , whereClause , solutionModifier ] ConstructType -> unwords [ prefixDecl- , qshow (ConstructForm qd)+ , "CONSTRUCT {"+ , qshow (constructTriples qd)+ , "}" , whereClause ] DescribeType -> unwords [ prefixDecl- , qshow (DescribeForm qd)+ , "DESCRIBE"+ , qshow (describeURI qd) , whereClause ] AskType -> unwords [ prefixDecl- , qshow (AskForm qd)+ , "ASK {"+ , qshow (askTriples qd)+ , "}" ] UpdateType -> unwords [ prefixDecl- , qshow (UpdateForm qd)+ , "INSERT DATA {"+ , qshow (updateTriples qd)+ , "}" ] -- FIXME TypeNotSet ->@@ -919,9 +998,14 @@ -- | Alternative version of 'unwords' that avoid adding spaces on empty strings. {-# NOINLINE [1] unwords' #-} unwords' :: [String] -> String-unwords' [] = ""-unwords' (w:ws) = w ++ go ws+unwords' [] = ""+unwords' ("":ws) = unwords' ws+unwords' (w:ws) = w ++ go ws where go [] = "" go ("":vs) = go vs go (v:vs) = ' ' : (v ++ go vs)++-- | Append a element to a 'NonEmpty' list.+(|>) :: NonEmpty a -> a -> NonEmpty a+xs |> x = NE.fromList $ NE.toList xs ++ [x]
hsparql.cabal view
@@ -1,6 +1,6 @@ Name: hsparql Homepage: https://github.com/robstewart57/hsparql-Version: 0.3.4+Version: 0.3.5 Synopsis: A SPARQL query generator and DSL, and a client to query a SPARQL server. Category: Database Description:
tests/AllTests.hs view
@@ -21,7 +21,7 @@ app req respond = respond $ response req where response req_ = case rawQueryString req_ of- "?query=PREFIX%20foaf%3A%20%3Chttp%3A%2F%2Fxmlns.com%2Ffoaf%2F0.1%2F%3E%20PREFIX%20dbpedia%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fproperty%2F%3E%20PREFIX%20dbprop%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fresource%2F%3E%20SELECT%20%20%3Fx1%20WHERE%20%7B%3Fx0%20dbpedia%3Agenre%20dbprop%3AWeb_browser%20.%20%3Fx0%20foaf%3Aname%20%3Fx1%20.%7D"+ "?query=PREFIX%20foaf%3A%20%3Chttp%3A%2F%2Fxmlns.com%2Ffoaf%2F0.1%2F%3E%20PREFIX%20dbpedia%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fproperty%2F%3E%20PREFIX%20dbprop%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fresource%2F%3E%20SELECT%20%3Fx1%20WHERE%20%7B%3Fx0%20dbpedia%3Agenre%20dbprop%3AWeb_browser%20.%20%3Fx0%20foaf%3Aname%20%3Fx1%20.%7D" -> selectResponse "?query=PREFIX%20dbprop%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fproperty%2F%3E%20PREFIX%20dbpedia%3A%20%3Chttp%3A%2F%2Fdbpedia.org%2Fresource%2F%3E%20ASK%20%7B%20%3Fx0%20dbprop%3Agenre%20dbpedia%3AWeb_browser%20.%20%7D" -> askResponse