{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module Database.Redis.ManualCommands.Topk where

import Data.ByteString (ByteString)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE

import Database.Redis.Core
import Database.Redis.Protocol
import Database.Redis.Types

data TopkInfo = TopkInfo
    { TopkInfo -> Integer
topkInfoK :: Integer
      -- ^ Number of items kept in the Top-K list.
    , TopkInfo -> Integer
topkInfoWidth :: Integer
      -- ^ Number of counters in each array.
    , TopkInfo -> Integer
topkInfoDepth :: Integer
      -- ^ Number of counter arrays.
    , TopkInfo -> Double
topkInfoDecay :: Double
      -- ^ Decay factor of the sketch.
    } deriving (Int -> TopkInfo -> ShowS
[TopkInfo] -> ShowS
TopkInfo -> String
(Int -> TopkInfo -> ShowS)
-> (TopkInfo -> String) -> ([TopkInfo] -> ShowS) -> Show TopkInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TopkInfo -> ShowS
showsPrec :: Int -> TopkInfo -> ShowS
$cshow :: TopkInfo -> String
show :: TopkInfo -> String
$cshowList :: [TopkInfo] -> ShowS
showList :: [TopkInfo] -> ShowS
Show, TopkInfo -> TopkInfo -> Bool
(TopkInfo -> TopkInfo -> Bool)
-> (TopkInfo -> TopkInfo -> Bool) -> Eq TopkInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TopkInfo -> TopkInfo -> Bool
== :: TopkInfo -> TopkInfo -> Bool
$c/= :: TopkInfo -> TopkInfo -> Bool
/= :: TopkInfo -> TopkInfo -> Bool
Eq)

instance RedisResult TopkInfo where
    decode :: Reply -> Either Reply TopkInfo
decode Reply
r = do
        fields <- Reply -> Either Reply [(ByteString, Reply)]
forall a. RedisResult a => Reply -> Either Reply a
decode Reply
r :: Either Reply [(ByteString, Reply)]
        topkInfoK <- decodeField "k" fields
        topkInfoWidth <- decodeField "width" fields
        topkInfoDepth <- decodeField "depth" fields
        topkInfoDecay <- decodeField "decay" fields
        pure TopkInfo{..}
      where
        decodeField :: a -> [(a, Reply)] -> Either Reply b
decodeField a
key [(a, Reply)]
fields = Either Reply b
-> (Reply -> Either Reply b) -> Maybe Reply -> Either Reply b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Reply -> Either Reply b
forall a b. a -> Either a b
Left Reply
r) Reply -> Either Reply b
forall a. RedisResult a => Reply -> Either Reply a
decode (a -> [(a, Reply)] -> Maybe Reply
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup a
key [(a, Reply)]
fields)

-- |Adds one or more items to a Top-K sketch (<https://redis.io/commands/topk.add>).
--
-- Returns the items dropped from the sketch after each insertion, or 'Nothing' when no item was expelled.
--
-- /O(n \cdot d)/, where /n/ is the number of items and /d/ is the depth of the sketch.
--
-- Since RedisBloom 2.0.0
topkAdd
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> NonEmpty ByteString -- ^ Items to add.
    -> m (f [Maybe ByteString])
topkAdd :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> NonEmpty ByteString -> m (f [Maybe ByteString])
topkAdd ByteString
key NonEmpty ByteString
items = [ByteString] -> m (f [Maybe ByteString])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest ([ByteString] -> m (f [Maybe ByteString]))
-> [ByteString] -> m (f [Maybe ByteString])
forall a b. (a -> b) -> a -> b
$ [ByteString
"TOPK.ADD", ByteString
key] [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ NonEmpty ByteString -> [ByteString]
forall a. NonEmpty a -> [a]
NE.toList NonEmpty ByteString
items

-- |Returns the count for one or more items in a Top-K sketch (<https://redis.io/commands/topk.count>).
--
-- Returns @0@ for items that are not tracked by the sketch.
--
-- /O(n \cdot d)/, where /n/ is the number of items and /d/ is the depth of the sketch.
--
-- Since RedisBloom 2.0.0
topkCount
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> NonEmpty ByteString -- ^ Items to count.
    -> m (f [Integer])
topkCount :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> NonEmpty ByteString -> m (f [Integer])
topkCount ByteString
key NonEmpty ByteString
items = [ByteString] -> m (f [Integer])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest ([ByteString] -> m (f [Integer]))
-> [ByteString] -> m (f [Integer])
forall a b. (a -> b) -> a -> b
$ [ByteString
"TOPK.COUNT", ByteString
key] [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ NonEmpty ByteString -> [ByteString]
forall a. NonEmpty a -> [a]
NE.toList NonEmpty ByteString
items

-- |Increments the count of one or more items by a configured amount (<https://redis.io/commands/topk.incrby>).
--
-- Returns the items dropped from the sketch after each increment, or 'Nothing' when no item was expelled.
--
-- /O(n \cdot d)/, where /n/ is the number of item-increment pairs and /d/ is the depth of the sketch.
--
-- Since RedisBloom 2.0.0
topkIncrby
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> NonEmpty (ByteString, Integer) -- ^ Item and increment pairs.
    -> m (f [Maybe ByteString])
topkIncrby :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString
-> NonEmpty (ByteString, Integer) -> m (f [Maybe ByteString])
topkIncrby ByteString
key NonEmpty (ByteString, Integer)
itemIncrements =
    [ByteString] -> m (f [Maybe ByteString])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest ([ByteString] -> m (f [Maybe ByteString]))
-> [ByteString] -> m (f [Maybe ByteString])
forall a b. (a -> b) -> a -> b
$ [ByteString
"TOPK.INCRBY", ByteString
key] [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ ((ByteString, Integer) -> [ByteString])
-> [(ByteString, Integer)] -> [ByteString]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ByteString, Integer) -> [ByteString]
forall {a}. RedisArg a => (ByteString, a) -> [ByteString]
encodePair (NonEmpty (ByteString, Integer) -> [(ByteString, Integer)]
forall a. NonEmpty a -> [a]
NE.toList NonEmpty (ByteString, Integer)
itemIncrements)
  where
    encodePair :: (ByteString, a) -> [ByteString]
encodePair (ByteString
item, a
increment) = [ByteString
item, a -> ByteString
forall a. RedisArg a => a -> ByteString
encode a
increment]

-- |Returns information about a Top-K sketch (<https://redis.io/commands/topk.info>).
--
-- $O(1)$
--
-- Since RedisBloom 2.0.0
topkInfo
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> m (f TopkInfo)
topkInfo :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> m (f TopkInfo)
topkInfo ByteString
key = [ByteString] -> m (f TopkInfo)
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest [ByteString
"TOPK.INFO", ByteString
key]

-- |Returns the items in a Top-K sketch (<https://redis.io/commands/topk.list>).
--
-- /O(k)/, where /k/ is the configured top-k size.
--
-- Since RedisBloom 2.0.0
topkList
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> m (f [ByteString])
topkList :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> m (f [ByteString])
topkList ByteString
key = [ByteString] -> m (f [ByteString])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest [ByteString
"TOPK.LIST", ByteString
key]

-- |Returns the items in a Top-K sketch along with their approximated counts (<https://redis.io/commands/topk.list>).
--
-- /O(k)/, where /k/ is the configured top-k size.
--
-- Since RedisBloom 2.0.0
topkListWithCount
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> m (f [(ByteString, Integer)])
topkListWithCount :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> m (f [(ByteString, Integer)])
topkListWithCount ByteString
key = [ByteString] -> m (f [(ByteString, Integer)])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest [ByteString
"TOPK.LIST", ByteString
key, ByteString
"WITHCOUNT"]

-- |Creates an empty Top-K sketch (<https://redis.io/commands/topk.reserve>).
--
-- The sketch will fail to be created if the key already exists.
--
-- /O(1)/
--
-- Since RedisBloom 2.0.0
topkReserve
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch to create.
    -> Integer -- ^ Number of items to keep in the sketch.
    -> Integer -- ^ Number of counters in each array.
    -> Integer -- ^ Number of counter arrays.
    -> Double -- ^ Decay factor.
    -> m (f Status)
topkReserve :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString
-> Integer -> Integer -> Integer -> Double -> m (f Status)
topkReserve ByteString
key Integer
topk Integer
width Integer
depth Double
decay =
    [ByteString] -> m (f Status)
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest [ByteString
"TOPK.RESERVE", ByteString
key, Integer -> ByteString
forall a. RedisArg a => a -> ByteString
encode Integer
topk, Integer -> ByteString
forall a. RedisArg a => a -> ByteString
encode Integer
width, Integer -> ByteString
forall a. RedisArg a => a -> ByteString
encode Integer
depth, Double -> ByteString
forall a. RedisArg a => a -> ByteString
encode Double
decay]

-- |Checks whether one or more items are present in a Top-K sketch (<https://redis.io/commands/topk.query>).
--
-- A 'False' value means the item is not currently one of the tracked heavy hitters.
--
-- /O(n \cdot d)/, where /n/ is the number of items and /d/ is the depth of the sketch.
--
-- Since RedisBloom 2.0.0
topkQuery
    :: (RedisCtx m f)
    => ByteString -- ^ Key of the Top-K sketch.
    -> NonEmpty ByteString -- ^ Items to check.
    -> m (f [Bool])
topkQuery :: forall (m :: * -> *) (f :: * -> *).
RedisCtx m f =>
ByteString -> NonEmpty ByteString -> m (f [Bool])
topkQuery ByteString
key NonEmpty ByteString
items = [ByteString] -> m (f [Bool])
forall (m :: * -> *) (f :: * -> *) a.
(RedisCtx m f, RedisResult a) =>
[ByteString] -> m (f a)
sendRequest ([ByteString] -> m (f [Bool])) -> [ByteString] -> m (f [Bool])
forall a b. (a -> b) -> a -> b
$ [ByteString
"TOPK.QUERY", ByteString
key] [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ NonEmpty ByteString -> [ByteString]
forall a. NonEmpty a -> [a]
NE.toList NonEmpty ByteString
items