module Database.RethinkDB.MapReduce where
import Control.Monad.State
import Control.Monad.Writer
import qualified Data.Text as T
import Data.Maybe
import Database.RethinkDB.Protobuf.Ql2.Term.TermType
import qualified Database.RethinkDB.Protobuf.Ql2.Datum as Datum
import Database.RethinkDB.ReQL
import Database.RethinkDB.Objects
termToMapReduce ::
(ReQL -> ReQL) -> State QuerySettings (ReQL -> ReQL, ReQL -> ReQL -> ReQL, Maybe (ReQL -> ReQL))
termToMapReduce f = do
v <- newVarId
body <- baseReQL $ f (op VAR [v] ())
return . toReduce $ toMapReduce v body
toReduce :: MapReduce -> (ReQL -> ReQL, ReQL -> ReQL -> ReQL, Maybe (ReQL -> ReQL))
toReduce (None t) = (\_ -> expr (), \_ _ -> expr (), Just $ const t)
toReduce (Map m) = ((\x -> expr [x]) . m, unionReduce, Nothing)
toReduce (MapReduce m r f) = (m, r, f)
unionReduce :: ReQL -> ReQL -> ReQL
unionReduce a b = op UNION (a, b) ()
sameVar :: Int -> BaseArray -> Bool
sameVar x [BaseReQL DATUM (Just (Datum.Datum{ Datum.r_num = Just y })) _ _] =
fromIntegral x == y
sameVar _ _ = False
notNone :: MapReduce -> Bool
notNone None{} = False
notNone _ = True
wrap :: BaseReQL -> ReQL
wrap = ReQL . return
toFun1 :: ReQL -> (ReQL -> ReQL)
toFun1 f a = op FUNCALL (f, a) ()
toFun2 :: ReQL -> (ReQL -> ReQL -> ReQL)
toFun2 f a b = op FUNCALL (f, a, b) ()
toMapReduce :: Int -> BaseReQL -> MapReduce
toMapReduce _ t@(BaseReQL DATUM _ _ _) = None $ wrap t
toMapReduce v (BaseReQL VAR _ w _) | sameVar v w = Map id
toMapReduce v t@(BaseReQL type' _ args optargs) = let
args' = map (toMapReduce v) args
optargs' = map (\(BaseAttribute k vv) -> (k, toMapReduce v vv)) optargs
count = length $ filter notNone $ args' ++ map snd optargs'
rebuild = (if count == 1 then rebuild0 else rebuildx) type' args' optargs'
in if count == 0 then None $ wrap t
else if not $ count == 1
then rebuild else
case (type', args', optargs') of
(MAP, [Map m, None f], []) -> Map (toFun1 f . m)
(REDUCE, [Map m, None f], _) | Just mbase <- optargsToBase optargs ->
MapReduce m (toFun2 f) (fmap (toFun2 f) mbase)
(COUNT, [Map _], []) ->
MapReduce (const (num 1)) (\a b -> op ADD (a, b) ()) Nothing
(tt, (Map m : _), _) | tt `elem` mappableTypes ->
(Map ((\x -> op tt (expr x : map expr (tail args)) (noRecurse : map baseAttrToAttr optargs)) . m))
_ -> rebuild
optargsToBase :: [BaseAttribute] -> Maybe (Maybe ReQL)
optargsToBase [] = Just Nothing
optargsToBase [BaseAttribute "base" b] = Just (Just $ ReQL $ return b)
optargsToBase _ = Nothing
baseAttrToAttr :: BaseAttribute -> Attribute
baseAttrToAttr (BaseAttribute k v) = k := v
noRecurse :: Attribute
noRecurse = "_NO_RECURSE_" := True
mappableTypes :: [TermType]
mappableTypes = [GET_FIELD, PLUCK, WITHOUT, MERGE, HAS_FIELDS]
data MapReduce =
None ReQL |
Map (ReQL -> ReQL) |
MapReduce (ReQL -> ReQL) (ReQL -> ReQL -> ReQL) (Maybe (ReQL -> ReQL))
rebuild0 :: TermType -> [MapReduce] -> [(T.Text, MapReduce)] -> MapReduce
rebuild0 ttype args optargs = MapReduce maps reduce finals where
(finally2, [mr]) = extract Nothing ttype args optargs
(maps, reduce, finally1) = toReduce mr
finals = Just $ maybe finally2 (finally2 .) finally1
rebuildx :: TermType -> [MapReduce] -> [(Key, MapReduce)] -> MapReduce
rebuildx ttype args optargs = MapReduce maps reduces finallys where
(finally, mrs) = extract (Just 0) ttype args optargs
index = zip ([0..] :: [Int])
triplets = map toReduce mrs
maps x = expr $ map (($ x) . fst3) triplets
reduces a b = expr $ map (uncurry $ mkReduce a b) . index $ map snd3 triplets
finallys = let fs = map thrd3 triplets in
if all isNothing fs
then Just finally
else Just $ \x -> finally $ expr $ map (uncurry $ mkFinally x) . index $
map (fromMaybe id) fs
mkReduce a b i f = f (op NTH (a, i) ()) (op NTH (b, i) ())
mkFinally x i f = f (op NTH (x, i) ())
fst3 :: (a,b,c) -> a
fst3 (a,_,_) = a
snd3 :: (a,b,c) -> b
snd3 (_,b,_) = b
thrd3 :: (a,b,c) -> c
thrd3 (_,_,c) = c
extract ::
Maybe Int -> TermType -> [MapReduce] -> [(Key, MapReduce)]
-> (ReQL -> ReQL, [MapReduce])
extract st tt args optargs = fst $ flip runState st $ runWriterT $ do
args' <- sequence $ map extractOne args
optargvs' <- sequence $ map extractOne (map snd optargs)
let optargks = map fst optargs
return $ \v -> op tt (map ($ v) args') (zipWith (:=) optargks $ map ($ v) optargvs')
extractOne :: MapReduce -> WriterT [MapReduce] (State (Maybe Int)) (ReQL -> ReQL)
extractOne (None term) = return $ const term
extractOne mr = do
tell [mr]
st <- get
case st of
Nothing -> return id
Just n -> do
put $ Just $ n + 1
return $ \v -> op NTH (v, n) ()