- Create a new project called hedis-trie with a simple stack template:
stack new hedis-trie simple
- Add a dependency on the hedis library in the build-depends sub-section of the executable section:
executable hedis-trie hs-source-dirs: src main-is: Main.hs default-language: Haskell2010 build-depends: base >= 4.7 && < 5 , hedis , bytestring
-
Open src/Main.hs. We will be adding our source here.
- Add the initial module declaration and import the required headers. Enable OverloadedStrings as we will be dealing with ByteString in this recipe:
{-# LANGUAGE OverloadedStrings #-} module Main where import Prelude as P import Database.Redis import Data.ByteString.Char8 as B import Data.Char import Data.Monoid import Control.Monad import Control.Monad.IO.Class
- Write a function to take a sentence, break it into words, and return prefixes of all the words:
prefixes :: B.ByteString -> [[B.ByteString]] prefixes = P.map (P.tail . B.inits) . B.words
- Write a function that take two input values, a list of prefixes for each word (a list of a list), and the key of the Redis hash set where we are storing data. . The function returns the number of keys updated. Since we are working with a list of lists, we use two foldM. Each prefix contributes a sorted set, and we add the input key to each of these sorted set with default score of 0.0:
addKeys :: (RedisCtx m f, Applicative f) => [[B.ByteString]] ->
B.ByteString -> m (f Integer) addKeys prefixes hashkey = let addtrie i p = do rs <- zadd p [(0.0, hashkey)] pure $ (+) <$> i <*> rs addtries ps = foldM addtrie (pure 0) ps addtriesS s ps = do rs <- addtries ps pure $ (+) <$> s <*> rs in foldM addtriesS (pure 0) prefixes
- Create a hash for the stock symbol and its name:
addSymbol :: (RedisCtx m f, Applicative f) => B.ByteString ->
B.ByteString -> m (f Bool) addSymbol symbol name = do hset symbol "NAME" name
- Prepare some data to be added to Redis. We will add all symbols from the Singapore exchange. The symbols and their names are embedded in the code:
stockData :: [(B.ByteString, B.ByteString)] stockData = [ ("MT1", "Dragon Group International Ltd") , ("BKV", "Dukang Distillers Holdings Ltd") ,("CZ4", "Dutech Holdings Ltd") ,("5SO", "Duty Free International Ltd") ,("NO4", "Dyna-Mac Holdings Ltd") ,("D6U", "Dynamic Colours Ltd") ,("BDG", "Eastern Holdings Ltd") ,("BWCU", "EC World Real Estate Investment Trust") ,("5CT", "EcoWise Holdings Ltd") ,("5HG", "Edition Ltd") ,("42Z", "Eindec Corporation Ltd") ,("E16", "Elec & Eltek International Co Ltd") ,("BIX", "Ellipsiz Ltd")]
- Take the preceding data and add it to the Redis server. The symbol and its name is added to the hash set, whereas all the prefixes for the name (by separating into words) are added to sorted sets. Each prefix will create a new sorted set:
addData :: (RedisCtx m f, Applicative f) => [(B.ByteString,
B.ByteString)] -> m () addData stocks = do forM_ stocks $ \(stock, name) -> do addSymbol stock name -- convert name into lower case so that we can do a
generic
search let nameL = B.map toLower name namePs = prefixes nameL addKeys namePs stock
- Search the stock and return the list of stocks:
searchStocks :: B.ByteString -> Redis [B.ByteString] searchStocks search = do stocks <- zrange search 0 (-1) case stocks of Right ss -> forM ss $ \s -> do n <- hget s "NAME" case n of Right (Just name) -> return $ s <> ": " <> name _ -> return $ s <> ": name not found
***error***"
main :: IO () main = do conn <- checkedConnect defaultConnectInfo runRedis conn $ do liftIO $ B.putStrLn "Adding stocks to the redis trie index" addData stockData liftIO $ B.putStrLn "Seaching for strings" found1 <- searchStocks "holdi" liftIO $ do B.putStrLn "Results for \"holdi\"" forM_ found1 B.putStrLn found2 <- searchStocks "dyna" liftIO $ do B.putStrLn "Results for \"dyna\"" forM_ found2 B.putStrLn
- Build and execute the project:
stack build stack exec -- hedis-trie
- You should see the following output:
