packages feed

sphinx 0.0 → 0.1

raw patch · 4 files changed

+271/−189 lines, 4 files

Files

Text/Search/Sphinx.hs view
@@ -7,227 +7,87 @@ import System import Control.Exception import Data.Binary.Get-import Data.Binary.Put-import Data.ByteString.Lazy hiding (pack, length, map)+import Data.Binary.Put (runPut, Put)+import Data.ByteString.Lazy hiding (pack, length, map, groupBy, head) import Data.ByteString.Lazy.Char8 (pack) import qualified Data.ByteString.Lazy as BS import Data.Char (ord, chr)-import Control.Monad import Data.Int (Int64) import Prelude hiding (readList)+import Text.Search.Sphinx.Get+import Text.Search.Sphinx.Put+import Text.Search.Sphinx.Configuration+import Text.Search.Sphinx.Types (SearchResult)+import qualified Text.Search.Sphinx.Types as T --- | The configuration for a query-data Configuration = Configuration {-    -- | The hostname of the Sphinx daemon-    host :: String-    -- | The portnumber of the Sphinx daemon-  , port :: PortID-    -- | How many records to seek from result-set start (default is 0)-  , offset :: Int-    -- | How many records to return from result-set starting at offset (default is 20)-  , limit :: Int-    -- | Query matching mode-  , mode :: Int-    -- | Ranking mode-  , ranker :: Int-    -- | Match sorting mode-  , sort :: Int-    -- | Attribute to sort by-  , sortBy :: String-    -- | Minimum ID to match, 0 means no limit-  , minId :: Int-    -- | Maximum ID to match, 0 means no limit-  , maxId :: Int-    -- | Group-by sorting clause (to sort groups in result set with)-  , groupSort :: String-} -data AttributeType = AttrNone			-                   | AttrInteger		-                   | AttrTimestamp		-                   | AttrOrdinal		-                   | AttrBool			-                   | AttrFloat			-                   | AttrMulti--attributeType :: Int -> AttributeType-attributeType 0          = AttrNone-attributeType 1          = AttrInteger-attributeType 2          = AttrTimestamp-attributeType 3          = AttrOrdinal-attributeType 4          = AttrBool-attributeType 5          = AttrFloat-attributeType 0X40000000 = AttrMulti- type Connection = (Handle, Configuration)   connect :: Configuration -> IO Connection-connect cfg = do connection <- connectTo (host cfg) (port cfg)-                 bs <- hGet connection 4-                 let version = runGet getWord32be bs-                     myVersion = runPut (putWord32be 1)+connect cfg = do connection <- connectTo (host cfg) (PortNumber $ fromIntegral $ port cfg)+                 bs         <- hGet connection 4+                 let version   = runGet getWord32be bs+                     myVersion = runPut (num 1)                  hPut connection myVersion                  return (connection, cfg)-                 --sClose connection -num = putWord32be . toEnum-num64 = putWord64be . toEnum -numList ls = do num (length ls)-                mapM_ num ls--nums cfg = mapM_ (\x -> num $ x cfg)-num64s cfg = mapM_ (\x -> num64 $ x cfg)--str :: String -> Put-str s = do let bs = pack s-           num (Prelude.length s) -- todo: dangerous-           putLazyByteString bs--addQuery :: Connection -> String -> String -> String -> Put-addQuery c q i cm = addQuery' c q i cm--addQuery' (handle, cfg) query index comment = do-    nums cfg [offset, limit, mode, ranker, sort]-    -- pack sortbyLen and sortBy+addQuery :: Configuration -> String -> String -> String -> Put+addQuery cfg query index comment = do+    nums cfg [ offset+             , limit+             , T.matchMode . mode+             , T.rank      . ranker+             , T.sort      . sort]     str (sortBy cfg)-    -- pack the queryLen and query encoded in utf-8     str query-    -- weights-    numList [100, 1]-    -- pack len index and index+    numList (weights cfg)     str index-    num 1 -- tODO: id64 range marker+    num 1     num64s cfg [minId, maxId]-    -- pack len filters + filters-    num 0 -- todo-    -- pack groupfunc-    num 0 -- todo-    -- pack len groupby + groupby-    str "" --todo-    -- -- req.append ( pack ( '>2L', self._maxmatches) ) )-    nums cfg [const 1000] --todo--    -- -- req.append ( len + self._groupsort )-    str (groupSort cfg)--    -- -- req.append ( pack ( '>LLL', self._cutoff, self._retrycount, self._retrydelay)) -    nums cfg [const 0, const 0, const 0]-    -- -- req.append ( self._groupdistinct)-    str "" --todo-    -- -- anchor point-    num 0 --todo-    -- -- per index weights-    num 0 --todo-    -- pack [max_query_time]-    num 0 --todo-    -- -- per-field weights-    num 0 --todo-    -- pack commentLen + comment+    num 0 -- todo: pack len filters + filters+    enum (groupByFunc   cfg)+    str  (groupBy       cfg)+    num  (maxMatches    cfg)+    str  (groupSort     cfg)+    num  (cutoff        cfg)+    num  (retryCount    cfg)+    num  (retryDelay    cfg)+    str  (groupDistinct cfg)+    num 0 -- anchor point: todo+    stringIntList (indexWeights cfg)+    num (maxQueryTime cfg)+    stringIntList (fieldWeights cfg)     str comment --- | A basic, default configuration.-defaultConfig = Configuration {-                  port = PortNumber 3312-                , host = "127.0.0.1"-                , offset = 0-                , limit = 20-                , mode = sph_match_all-                , ranker = sph_rank_proximity_bm25-                , sort = sort_relevance-                , sortBy = ""-                , minId = 0-                , maxId = 0-                , groupSort = "@group desc"-              }--sph_match_all = 0-sph_rank_proximity_bm25 = 0-sort_relevance = 0--cmdSearch = putWord16be 0-verCmdSearch = putWord16be 0x113--query :: Configuration -> String -> IO [Text.Search.Sphinx.SearchResult]-query config s = do+-- | The 'query' function queries the Sphinx daemon.+query :: Configuration -- ^ The configuration+      -> String        -- ^ The indexes, "*" means every index+      -> String        -- ^ The query string+      -> IO SearchResult+query config indexes s = do     conn <- connect config-    let q = addQuery conn s "*" ""-    runQueries (fst conn) q 1+    let q = addQuery config s indexes ""+    results <- runQueries (fst conn) q 1+    return $ head results -- We only do one query, so we always have one SearchResult  runQueries :: Handle -> Put -> Int -> IO [SearchResult] runQueries conn q numQueries = do     let req = runPut (makeRunQuery q numQueries)     hPut conn req     hFlush conn-    getResponse conn 1+    getResponse conn numQueries  makeRunQuery query numQueries =  do-  cmdSearch-  verCmdSearch+  cmd T.ScSearch+  verCmd T.VcSearch   num $ fromEnum $ BS.length (runPut query) + 4   num numQueries   query ---getResponse :: Handle -> Int -> IO [SearchResult] getResponse conn numResults = do-  bs <- hGet conn 8-  let x@(status, version, len) = runGet f bs+  header <- hGet conn 8+  let x@(status, version, len) = readHeader header   response <- hGet conn (fromIntegral len)   return $ runGet (numResults `times` getResult) response- where-  f = do status <- getWord16be-         version <- getWord16be-         length <- getWord32be-         return (status, version, length)---- todo applicative-getNum :: Get Int-getNum = getWord32be >>= return . fromEnum--getNum64 :: Get Int64-getNum64 = getWord64be >>= return . fromIntegral--readList f = do num <- getNum-                num `times` f--type SearchResult = ([(Int64, Int, [Int])], Int, Int, Int, [(ByteString, Int, Int)])-getResult :: Get SearchResult-getResult = do-    status     <- getNum-    -- todo: we suppose the status is OK-    fields     <- readList readField-    attrs      <- readList readAttr-    matchCount <- getNum-    id64       <- getNum-    matches    <- matchCount `times` readMatch (id64 > 0) (map snd attrs)-    [total, totalFound, time, numWords] <- 4 `times` getNum-    wrds       <- numWords `times` readWord-    return $ (matches, total, totalFound, time, wrds)--times = replicateM--readWord = do s <- readStr-              [doc, hits] <- 2 `times` getNum-              return (s, doc, hits)--readField = readStr--readMatch isId64 attrs = do-    doc <- if isId64 then getNum64 else (getNum >>= return . fromIntegral)-    weight <- getNum-    matchAttrs <- mapM readMatchAttr attrs-    return (doc, weight, matchAttrs)--readMatchAttr AttrFloat = error "readMatchAttr for AttrFloat not implemented yet."-readMatchAttr AttrMulti = error "readMatchAttr for AttrFloat not implemented yet."-readMatchAttr _         = getNum---readStr = do len <- getNum-             getLazyByteString (fromIntegral len)--readAttr = do-    s <- readStr-    t <- getNum-    return (s, attributeType t)
+ Text/Search/Sphinx/Configuration.hs view
@@ -0,0 +1,76 @@+module Text.Search.Sphinx.Configuration where++import qualified Text.Search.Sphinx.Types as T++-- | The configuration for a query+data Configuration = Configuration {+    -- | The hostname of the Sphinx daemon+    host :: String+    -- | The portnumber of the Sphinx daemon+  , port :: Int+    -- | Per-field weights+  , weights :: [Int]+    -- | How many records to seek from result-set start (default is 0)+  , offset :: Int+    -- | How many records to return from result-set starting at offset (default is 20)+  , limit :: Int+    -- | Query matching mode+  , mode :: T.MatchMode+    -- | Ranking mode+  , ranker :: T.Rank+    -- | Match sorting mode+  , sort :: T.Sort+    -- | Attribute to sort by+  , sortBy :: String+    -- | Minimum ID to match, 0 means no limit+  , minId :: Int+    -- | Maximum ID to match, 0 means no limit+  , maxId :: Int+    -- | Group-by sorting clause (to sort groups in result set with)+  , groupBy :: String+    -- | Group-by count-distinct attribute+  , groupSort :: String+    -- | Group-by function (to pre-process group-by attribute value with)+  , groupByFunc :: T.GroupByFunction+    -- | Group-by attribute name +  , groupDistinct :: String+    -- | Maximum number of matches to retrieve+  , maxMatches :: Int+    -- | Cutoff to stop searching at+  , cutoff :: Int+    -- | Distributed retries count+  , retryCount :: Int+    -- | Distributed retries delay+  , retryDelay :: Int+    -- | Per-index weights+  , indexWeights :: [(String, Int)]+    -- | Maximum query time in milliseconds, 0 means no limit+  , maxQueryTime :: Int+    -- | Per-field-name weights+  , fieldWeights :: [(String, Int)]+}+-- | A basic, default configuration.+defaultConfig = Configuration {+                  port          = 3312+                , host          = "127.0.0.1"+                , weights       = []+                , offset        = 0+                , limit         = 20+                , mode          = T.All+                , ranker        = T.ProximityBm25+                , sort          = T.Relevance+                , sortBy        = ""+                , minId         = 0+                , maxId         = 0+                , groupSort     = "@group desc"+                , groupBy       = ""+                , groupByFunc   = T.Day+                , groupDistinct = ""+                , maxMatches    = 1000+                , cutoff        = 0+                , retryCount    = 0+                , retryDelay    = 0+                , indexWeights  = []+                , maxQueryTime  = 0+                , fieldWeights  = []+              }
+ Text/Search/Sphinx/Types.hs view
@@ -0,0 +1,146 @@+module Text.Search.Sphinx.Types where++import Data.ByteString.Lazy (ByteString)+import Data.Int (Int64)++-- | Search commands+data SearchdCommand = ScSearch+                    | ScExcerpt+                    | ScUpdate+                    | ScKeywords+                    deriving (Show, Enum)++searchdCommand :: SearchdCommand -> Int+searchdCommand = fromEnum++-- | Current client-side command implementation versions+data VerCommand = VcSearch+                | VcExcerpt+                | VcUpdate+                | VcKeywords+                deriving (Show)++verCommand VcSearch   = 0x113+verCommand VcExcerpt  = 0x100+verCommand VcUpdate   = 0x101+verCommand VcKeywords = 0x100++-- | Searchd status codes+data Searchd = Ok+             | Error+             | Retry+             | Warning+             deriving (Show, Enum)++searchd :: Searchd -> Int+searchd = fromEnum++-- | Match modes+data MatchMode = All+               | Any+               | Phrase+               | Boolean+               | Extended+               | Fullscan+               | Extended2  -- extended engine V2 (TEMPORARY, WILL BE REMOVED)+               deriving (Show, Enum)++matchMode :: MatchMode -> Int+matchMode = fromEnum++-- | Ranking modes (ext2 only)+data Rank = ProximityBm25  -- default mode, phrase proximity major factor and BM25 minor one+          | Bm25           -- statistical mode, BM25 ranking only (faster but worse quality)+          | None           -- no ranking, all matches get a weight of 1+          | WordCount      -- simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts+          deriving (Show, Enum)++rank :: Rank -> Int+rank = fromEnum++-- | Sort modes+data Sort = Relevance+          | AttrDesc+          | AttrAsc+          | TimeSegments+          | SortExtended -- constructor already existed+          | Expr+          deriving (Show, Enum)++sort :: Sort -> Int+sort = fromEnum++-- | Filter types+data Filter = Values+            | Range+            | FloatRange+            deriving (Show, Enum)++filter :: Filter -> Int+filter = fromEnum++-- | Attribute types+data AttrT = AttrTInteger+           | AttrTTimestamp+           | AttrTOrdinal+           | AttrTBool+           | AttrTFloat+           | AttrTMulti+           deriving (Show)++instance Enum AttrT where+    toEnum = toAttrT+    fromEnum = attrT++toAttrT 1          = AttrTInteger+toAttrT 2          = AttrTTimestamp+toAttrT 3          = AttrTOrdinal+toAttrT 4          = AttrTBool+toAttrT 5          = AttrTFloat+toAttrT 0x40000000 = AttrTMulti     ++attrT AttrTInteger   = 1+attrT AttrTTimestamp = 2+attrT AttrTOrdinal   = 3+attrT AttrTBool      = 4+attrT AttrTFloat     = 5+attrT AttrTMulti     = 0x40000000++-- | Grouping functions+data GroupByFunction = Day+                     | Week+                     | Month+                     | Year+                     | Attr+                     | AttrPair+                     deriving (Show, Enum)++groupByFunction :: GroupByFunction -> Int+groupByFunction = fromEnum++-- | The result of a query+data SearchResult = SearchResult {+      -- | The matches+      matches :: [Match]+      -- | Total amount of matches retrieved on server by this query.+    , total   :: Int+      -- | Total amount of matching documents in index.+    , totalFound :: Int+      -- | List of processed words with the number of docs and the number of hits.+    , words :: [(ByteString, Int, Int)]+}+ deriving Show++data Match = Match {+             -- Document ID+               documentId :: Int64+             -- Document weight+             , documentWeight :: Int+             -- Attribute values+             , attributeValues :: [Attr]+             }+ deriving Show++data Attr = AttrMulti [Int]+          | AttrNum  Int+ deriving Show
sphinx.cabal view
@@ -1,5 +1,5 @@ Name:            sphinx-Version:         0.0+Version:         0.1 Synopsis:        Haskell bindings to the Sphinx full-text searching deamon. Description:     Haskell bindings to the Sphinx full-text searching deamon. This                  module is heavily inspired by the php and python client.@@ -9,6 +9,6 @@ Copyright:       (c) 2008 Tupil Author:          Tupil Maintainer:      Chris Eidhof <ce+sphinx@tupil.com>-Exposed-Modules: Text.Search.Sphinx+Exposed-Modules: Text.Search.Sphinx, Text.Search.Sphinx.Types, Text.Search.Sphinx.Configuration Build-Type:      Simple Build-Depends:   base, binary, bytestring, network, haskell98