{-# LANGUAGE BangPatterns #-} -- | -- Module : Database.LevelDB.Streaming -- Copyright : (c) 2014 Kim Altintop -- License : BSD3 -- Maintainer : kim.altintop@gmail.com -- Stability : experimental -- Portability : non-portable -- -- High-level, "Data.List"-like, streaming interface to -- "Database.LevelDB.Iterator". -- -- This module contains types and functions to construct 'Stream's from -- 'Database.LevelDB.Iterator.Iterator's, and re-exports the functions operating -- on 'Stream's from "Data.Stream.Monadic". -- -- __Note__ that most of the functions from the latter module are -- (intentionally) conflicting with the "Prelude", it is thus recommended to -- import this module qualified: -- -- > import Database.LevelDB -- or Database.LevelDB.Base -- > import qualified Database.LevelDB.Streaming as S module Database.LevelDB.Streaming ( KeyRange (..) , Direction (..) , Key , Value , Entry -- * Constructing streams -- $caveats , keySlice , entrySlice -- * Re-exports , module Data.Stream.Monadic ) where import Control.Applicative import Control.Monad.IO.Class import Data.ByteString (ByteString) import Data.Stream.Monadic import Database.LevelDB.Base import Prelude hiding (drop, filter, foldl, map, mapM, mapM_, take) data KeyRange = KeyRange { start :: !ByteString , end :: ByteString -> Ordering } | AllKeys data Direction = Asc | Desc deriving Show type Key = ByteString type Value = ByteString type Entry = (Key, Value) -- $caveats -- Caveats: -- -- * remember that 'Iterator's are /not/ threadsafe -- * consider that traversing a 'Stream' mutates the underlying 'Iterator', so -- the above applies to the 'Stream' as well -- * because of the destructive update semantics of 'Iterator's, the following -- example will (perhaps obviously) /not/ work as expected: -- -- > withIter db def $ \ i -> -- > toList $ zip (keySlice i AllKeys Asc) (keySlice i AllKeys Desc) -- -- However, the following /will/ work: -- -- > withIter db def $ \ i -> -- > foldl (+) 0 . map (*2) . map ByteString.length $ keySlice i AllKeys Asc -- -- Here, fusion ensures the key slice is traversed only once, while the next -- example will incur rewinding the 'Iterator' and traversing it a second time: -- -- > withIter db def $ \ i -> -- > let slice = keySlice i AllKeys Asc -- > count f = foldl' (\ c k -> c + f k) 0 -- > in liftM2 (+) (count ByteString.length slice) (count (const 1) slice) -- -- To summarise: it is recommended to always create 'Stream's with their own -- exclusive 'Iterator', and to not share them across threads. -- | Create a 'Stream' which yields only the keys of the given 'KeyRange' (in -- the given 'Direction'). keySlice :: (Applicative m, MonadIO m) => Iterator -> KeyRange -> Direction -> Stream m Key keySlice i (KeyRange s e) d = Stream next (iterSeek i s >> pure i) where next it = do key <- iterKey it case key of Nothing -> pure Done Just k -> case d of Asc | e k < GT -> Yield k <$> (iterNext it >> pure it) | otherwise -> pure Done Desc | e k > LT -> Yield k <$> (iterPrev it >> pure it) | otherwise -> pure Done keySlice i AllKeys Asc = Stream next (iterFirst i >> pure i) where next it = iterKey it >>= maybe (pure Done) (\ k -> Yield k <$> (iterNext it >> pure it)) keySlice i AllKeys Desc = Stream next (iterLast i >> pure i) where next it = iterKey it >>= maybe (pure Done) (\ k -> Yield k <$> (iterPrev it >> pure it)) -- | Create a 'Stream' which yields key/value pairs of the given 'KeyRange' (in -- the given 'Direction'). entrySlice :: (Applicative m, MonadIO m) => Iterator -> KeyRange -> Direction -> Stream m Entry entrySlice i (KeyRange s e) d = Stream next (iterSeek i s >> pure i) where next it = do entry <- iterEntry it case entry of Nothing -> pure Done Just x@(!k,_) -> case d of Asc | e k < GT -> Yield x <$> (iterNext it >> pure it) | otherwise -> pure Done Desc | e k > LT -> Yield x <$> (iterPrev it >> pure it) | otherwise -> pure Done entrySlice i AllKeys Asc = Stream next (iterFirst i >> pure i) where next it = iterEntry it >>= maybe (pure Done) (\ x -> Yield x <$> (iterNext it >> pure it)) entrySlice i AllKeys Desc = Stream next (iterLast i >> pure i) where next it = iterEntry it >>= maybe (pure Done) (\ x -> Yield x <$> (iterPrev it >> pure it))