packages feed

rdf4h-5.2.0: src/Data/RDF/Graph/TList.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}

-- | "TriplesGraph" contains a list-backed graph implementation suitable
--  for smallish graphs or for temporary graphs that will not be queried.
--  It maintains the triples in the order that they are given in, and is
--  especially useful for holding N-Triples, where it is often desirable
--  to preserve the order of the triples when they were originally parsed.
--  Duplicate triples are not filtered. If you might have duplicate triples,
--  use @MGraph@ instead, which is also more efficient. However, the query
--  functions of this graph (select, query) remove duplicates from their
--  result triples (but triplesOf does not) since it is usually cheap
--  to do so.
module Data.RDF.Graph.TList (TList) where

import Prelude
#if MIN_VERSION_base(4,9,0)
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup
#else
#endif
#else
#endif
import Control.DeepSeq (NFData)
import Data.Binary
import Data.List (nub)
import Data.RDF.Namespace
import Data.RDF.Query
import Data.RDF.Types (BaseUrl, NodeSelector, Object, Predicate, RDF, Rdf (..), Subject, Triple (..), Triples)
import GHC.Generics

-- | A simple implementation of the 'RDF' type class that represents
--  the graph internally as a list of triples.
--
--  Note that this type of RDF is fine for interactive
--  experimentation and querying of smallish (<10,000 triples) graphs,
--  but there are better options for larger graphs or graphs that you
--  will do many queries against (e.g., @MGraph@ is faster for queries).
--
--  The time complexity of the functions (where n == num_triples) are:
--
--   * 'empty'    : O(1)
--
--   * 'mkRdf'  : O(n)
--
--   * 'triplesOf': O(1)
--
--   * 'select'   : O(n)
--
--   * 'query'    : O(n)
data TList deriving (Generic)

instance Binary TList

instance NFData TList

newtype instance RDF TList = TListC (Triples, Maybe BaseUrl, PrefixMappings)
  deriving (Generic, NFData)

instance Rdf TList where
  baseUrl = baseUrl'
  prefixMappings = prefixMappings'
  addPrefixMappings = addPrefixMappings'
  empty = empty'
  mkRdf = mkRdf'
  addTriple = addTriple'
  removeTriple = removeTriple'
  triplesOf = triplesOf'
  uniqTriplesOf = uniqTriplesOf'
  select = select'
  query = query'
  showGraph = showGraph'

showGraph' :: RDF TList -> String
showGraph' gr = concatMap (\t -> show t <> "\n") (expandTriples gr)

prefixMappings' :: RDF TList -> PrefixMappings
prefixMappings' (TListC (_, _, pms)) = pms

addPrefixMappings' :: RDF TList -> PrefixMappings -> Bool -> RDF TList
addPrefixMappings' (TListC (ts, baseURL, pms)) pms' replace =
  let merge = if replace then flip (<>) else (<>)
   in TListC (ts, baseURL, merge pms pms')

baseUrl' :: RDF TList -> Maybe BaseUrl
baseUrl' (TListC (_, baseURL, _)) = baseURL

empty' :: RDF TList
empty' = TListC (mempty, Nothing, PrefixMappings mempty)

-- We no longer remove duplicates here, as it is very time consuming and is often not
-- necessary (raptor does not seem to remove dupes either). Instead, we remove dupes
-- from the results of the select' and query' functions, since it is cheap to do
-- there in most cases, but not when triplesOf' is called.
mkRdf' :: Triples -> Maybe BaseUrl -> PrefixMappings -> RDF TList
mkRdf' ts baseURL pms = TListC (ts, baseURL, pms)

addTriple' :: RDF TList -> Triple -> RDF TList
addTriple' (TListC (ts, bURL, preMapping)) t = TListC (t : ts, bURL, preMapping)

removeTriple' :: RDF TList -> Triple -> RDF TList
removeTriple' (TListC (ts, bURL, preMapping)) t = TListC (newTs, bURL, preMapping)
  where
    newTs = filter (/= t) ts

triplesOf' :: RDF TList -> Triples
triplesOf' ((TListC (ts, _, _))) = ts

uniqTriplesOf' :: RDF TList -> Triples
uniqTriplesOf' = nub . expandTriples

select' :: RDF TList -> NodeSelector -> NodeSelector -> NodeSelector -> Triples
select' g s p o = nub $ filter (matchSelect s p o) $ triplesOf g

query' :: RDF TList -> Maybe Subject -> Maybe Predicate -> Maybe Object -> Triples
query' g s p o = nub $ filter (matchPattern s p o) $ triplesOf g

matchSelect :: NodeSelector -> NodeSelector -> NodeSelector -> Triple -> Bool
matchSelect s p o (Triple s' p' o') = match s s' && match p p' && match o o'
  where
    match fn n = maybe True ($ n) fn

matchPattern :: Maybe Subject -> Maybe Predicate -> Maybe Object -> Triple -> Bool
matchPattern s p o (Triple s' p' o') = match s s' && match p p' && match o o'
  where
    match n1 n2 = maybe True (== n2) n1