{-# 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 -- ------------------------------------------------------------