{-| Module : Data.RDF.Graph Description : Representation and Incremental Processing of RDF Data Copyright : Travis Whitaker 2016 License : MIT Maintainer : pi.boy.travis@gmail.com Stability : Provisional Portability : Portable This module provides conversion between RDF triples and @fgl@ graphs. Naturally these functions will force the entire graph into memory. -} {-# LANGUAGE DeriveGeneric , DeriveAnyClass #-} module Data.RDF.Graph ( -- FGL Supporting Types GNode(..) , GEdge -- * Conversion to FGL Graphs , rdfGraph , triplesGraph -- * Conversion from FGL Graphs , graphRDF , graphTriples ) where import Control.DeepSeq import qualified Data.Graph.Inductive.Graph as G import qualified Data.Graph.Inductive.NodeMap as G import Data.Maybe import Data.RDF.Types import GHC.Generics -- | An RDF 'Subject' or 'Object' as a 'G.Graph' node. This common -- representation is necessary because the 'Object' of one 'Triple' might be -- the 'Subject' of another. data GNode = IRIGNode !IRI | BlankGNode !BlankNode | LiteralGNode !Literal deriving ( Eq , Ord , Read , Show , Generic , NFData ) -- | A 'G.Graph' edge is an RDF 'Predicate'. type GEdge = Predicate -- | Convert a 'Subject' to a 'GNode'. subjectNode :: Subject -> GNode subjectNode (IRISubject i) = IRIGNode i subjectNode (BlankSubject b) = BlankGNode b -- | Convert an 'Object' to a 'GNode'. objectNode :: Object -> GNode objectNode (IRIObject i) = IRIGNode i objectNode (BlankObject b) = BlankGNode b objectNode (LiteralObject l) = LiteralGNode l -- | Convert a 'GNode' to a 'Subject'. This will fail if the 'GNode' contains a -- 'Literal'. nodeSubject :: GNode -> Either String Subject nodeSubject (IRIGNode i) = Right (IRISubject i) nodeSubject (BlankGNode b) = Right (BlankSubject b) nodeSubject _ = Left "nodeSubject: subject must IRI or blank node." -- | Convert a 'GNode' to an 'Object'. nodeObject :: GNode -> Object nodeObject (IRIGNode i) = IRIObject i nodeObject (BlankGNode b) = BlankObject b nodeObject (LiteralGNode l) = LiteralObject l -- | Convert an 'RDFGraph' into a 'G.DynGraph' and 'G.NodeMap'. The 'graphLabel' -- is discarded. rdfGraph :: G.DynGraph g => RDFGraph -> (g GNode GEdge, G.NodeMap GNode) rdfGraph (RDFGraph _ ts) = triplesGraph ts -- | Convert a list of 'Triple's into a 'G.DynGraph' and a 'G.NodeMap'. triplesGraph :: G.DynGraph g => [Triple] -> (g GNode GEdge, G.NodeMap GNode) triplesGraph triples = G.mkMapGraph nodes edges where (nodes, edges) = go ([],[]) triples go (ns, es) [] = (ns, es) go (ns, es) (Triple s p o:ts) = let s' = subjectNode s o' = objectNode o in go (s':o':ns, (s', o', p):es) ts -- | Convert a 'G.Graph' into an 'RDFGraph'. This will fail if the graph -- contains any 'LiteralGNode's with an outward degree greater than zero, -- since such a graph is illegal in RDF. graphRDF :: G.Graph g => Maybe IRI -> g GNode GEdge -> Either String RDFGraph graphRDF l = (RDFGraph l <$>) . graphTriples -- | Convert a 'G.Graph' into a list of 'Triple's. This will fail if the graph -- contains any 'LiteralGNode's with an outward degree greater than zero, -- since such a graph is illegal in RDF. graphTriples :: G.Graph g => g GNode GEdge -> Either String [Triple] graphTriples g = go (G.labEdges g) -- The use of fromJust is safe here, since labEdges will never return -- an edge to a node not present in the graph. where go [] = Right [] go ((si, oi, p):ts) = let s = nodeSubject (fromJust (G.lab g si)) o = nodeObject (fromJust (G.lab g oi)) in ((\s' -> (Triple s' p o:)) <$> s) <*> go ts