{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
-- ----------------------------------------------------------------------------
{- |
The document table interface.
-}
-- ----------------------------------------------------------------------------
module Hunt.DocTable
where
import Prelude hiding (filter, lookup, map, null)
import Control.Applicative (Applicative, (<$>))
import Control.Monad
import Data.Aeson
import Data.Maybe (catMaybes, fromJust)
import Data.Set (Set)
import qualified Data.Set as S
import Hunt.Common.BasicTypes
import Hunt.Common.DocId
import Hunt.Common.DocIdMap (DocIdMap (..))
import qualified Hunt.Common.DocIdMap as DM
import Hunt.Common.DocIdSet (DocIdSet)
import qualified Hunt.Common.DocIdSet as DS
import Hunt.Common.Document (Document,
DocumentWrapper (wrap, unwrap))
-- ------------------------------------------------------------
-- | The document table type class which needs to be implemented to be used by the 'Interpreter'.
-- The type parameter @i@ is the implementation.
-- The implementation must have a value type parameter.
class (DocumentWrapper (DValue i)) => DocTable i where
-- | The value type of the document table.
type DValue i :: *
-- | Test whether the document table is empty.
null :: (Monad m) => i -> m Bool
-- | Returns the number of unique documents in the table.
size :: (Monad m) => i -> m Int
-- | Lookup a document by its ID.
lookup :: (Monad m) => DocId -> i -> m (Maybe (DValue i))
-- | Lookup the 'DocId' of a document by an 'URI'.
lookupByURI :: (Monad m) => URI -> i -> m (Maybe DocId)
-- | Union of two disjoint document tables. It is assumed, that the
-- DocIds and the document 'URI's of both indexes are disjoint.
union :: (Monad m) => i -> i -> m i
-- | Test whether the 'DocId's of both tables are disjoint.
disjoint :: (Monad m) => i -> i -> m Bool
-- | Insert a document into the table. Returns a tuple of the 'DocId' for that document and the
-- new table. If a document with the same 'URI' is already present, its id will be returned
-- and the table is returned unchanged.
insert :: (Monad m) => DValue i -> i -> m (DocId, i)
-- | Update a document with a certain 'DocId'.
update :: (Monad m) => DocId -> DValue i -> i -> m i
-- | Update a document by 'DocId' with the result of the provided function.
adjust :: (Monad m) => (DValue i -> m (DValue i)) -> DocId -> i -> m i
adjust f did d =
maybe (return d) (upd d did <=< f) =<< lookup did d
--maybe d (update d did . f) $ lookup d did
where upd i docid v = update docid v i
-- | Update a document by 'URI' with the result of the provided function.
adjustByURI :: (Monad m) => (DValue i -> m (DValue i)) -> URI -> i -> m i
adjustByURI f uri d
= maybe (return d) (flip (adjust f) d) =<< lookupByURI uri d
-- | Removes the document with the specified 'DocId' from the table.
delete :: (Monad m) => DocId -> i -> m i
-- | Removes the document with the specified 'URI' from the table.
deleteByURI :: (Monad m) => URI -> i -> m i
deleteByURI u ds
= maybe (return ds) (flip delete ds) =<< lookupByURI u ds
-- | Deletes a set of documents by 'DocId' from the table.
difference :: (Monad m) => DocIdSet -> i -> m i
-- | Deletes a set of documents by 'URI' from the table.
differenceByURI :: (Monad m) => Set URI -> i -> m i
differenceByURI uris d = do -- XXX: eliminate S.toList?
ids <- liftM (DS.fromList . catMaybes) . mapM (flip lookupByURI d) . S.toList $ uris
difference ids d
-- | Map a function over all values of the document table.
map :: (Monad m) => (DValue i -> DValue i) -> i -> m i
-- | Filters all documents that satisfy the predicate.
filter :: (Monad m) => (DValue i -> Bool) -> i -> m i
-- | Convert document table to a 'DocIdMap'.
toMap :: (Monad m) => i -> m (DocIdMap (DValue i))
-- | Empty 'DocTable'.
empty :: i
restrict :: (Functor m, Monad m, Applicative m, DocTable i) => DocIdSet -> i -> m i
restrict is dt
= foldM ins empty $ DS.toList is
where
ins m i = do v <- fromJust <$> lookup i dt
update i v m
-- ------------------------------------------------------------
-- | JSON dump of the document table.
toJSON'DocTable :: (Functor m, Monad m, Applicative m, DocTable i) => i -> m Value
toJSON'DocTable dt
= do didm <- DM.map unwrap <$> toMap dt
return $ toJSON didm
-- | JSON import of the document table.
fromJSON'DocTable :: (Functor m, Monad m, Applicative m, DocTable i) => Value -> m i
fromJSON'DocTable v
= foldM ins empty $ dm'
where
ins res (did, doc) = update did doc res
dm :: DocIdMap Document
dm = case fromJSON v of
Error _ -> DM.empty
Success m -> m
dm'= DM.toList . DM.map wrap $ dm
-- ------------------------------------------------------------