packages feed

sphinx 0.2.1 → 0.3.3

raw patch · 7 files changed

+428/−153 lines, 7 filesdep +data-binary-ieee754dep ~basenew-uploader

Dependencies added: data-binary-ieee754

Dependency ranges changed: base

Files

Text/Search/Sphinx.hs view
@@ -1,55 +1,205 @@--- | This is the Haskell version of the Sphinx searchd client.+-- The following functions are not yet implemented:+-- setFilterFloatRange, setGeoAnchor+-- resetFilters, resetGroupBy+-- updateAttributes,+-- buildKeyWords, escapeString, status, open, close+module Text.Search.Sphinx ( module Text.Search.Sphinx+  , Configuration(..), defaultConfig+  ) where -module Text.Search.Sphinx ( Configuration (..)-                          , query-                          , defaultConfig-                          ) where+import qualified Text.Search.Sphinx.Types as T (+  VerCommand(VcSearch, VcExcerpt),+  SearchdCommand(ScSearch, ScExcerpt),+  Filter, Filter(..),+  fromEnumFilter, Filter(..),+  QueryStatus(..), toStatus, Status(..),+  SearchResult, Result(..), QueryResult(..)) -import Network-import IO hiding (bracket)-import System-import Control.Exception-import Data.Binary.Get-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 Text.Search.Sphinx.Configuration (Configuration(..), defaultConfig)+import qualified Text.Search.Sphinx.ExcerptConfiguration as ExConf (ExcerptConfiguration(..))+import Text.Search.Sphinx.Get (times, getResult, readHeader, getStr)+import Text.Search.Sphinx.Put (num, num64, enum, list, numC, strC,+                              numC64, stringIntList, str, cmd, verCmd)++import Data.Binary.Put (Put, runPut)+import Data.Binary.Get (runGet, getWord32be)+import qualified Data.ByteString.Lazy as BS (ByteString, length, hGet, hPut, tail, append) 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 +import Network (connectTo, PortID(PortNumber))+import IO (Handle, hFlush)+import Data.Bits ((.|.)) -type Connection = (Handle, Configuration)+import Prelude hiding (filter, tail) +{- the funnest way to debug this is to run the same query with an existing working client and look at the difference+ - sudo tcpflow -i lo dst port 9306 +-}+import Debug.Trace; debug a = trace (show a) a -connect :: Configuration -> IO Connection-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)+{- weren't working properly, should try out on latest version now+setFilter :: Configuration -> String -> [Int64] -> Bool -> Configuration+setFilter cfg attr values exclude =+  let f = (T.FilterValues attr values)+  in  addFilter cfg (if exclude then T.ExclusionFilter f else f) +setFilterRange :: Configuration -> String -> Int64 -> Int64 -> Bool -> Configuration+setFilterRange cfg attr min max exclude =+  let f = (T.FilterRange attr min max)+  in  addFilter cfg (if exclude then T.ExclusionFilter f else f) +--setFilterFloatRange :: Configuration -> String -> Float -> Float -> Bool -> Configuration+--setFilterFloatRange cfg attr min max exclude =+  --let f = (T.FilterFloatRange attr min max)+  --in  addFilter cfg (if exclude then T.ExclusionFilter f else f)++-- | alternative interface to setFilter* using Filter constructors+addFilter :: Configuration -> T.Filter -> Configuration+addFilter cfg filter = cfg { filters = filter : (filters cfg) }+  -}++-- | The 'query' function runs a single query against the Sphinx daemon.+query :: Configuration -- ^ The configuration+      -> String        -- ^ The indexes, "*" means every index+      -> String        -- ^ The query string+      -> IO (T.Result T.SearchResult) -- ^ just one search result back+query config indexes s = do+    conn <- connect (host config) (port config)+    let q = addQuery config s indexes ""+    results <- runQueries conn [q] 1+    return $ case results of+      T.Ok rs -> case head rs of -- there is just 1 result+                        T.QueryOk result        -> T.Ok result+                        T.QueryWarning w result -> T.Warning w result+                        T.QueryError code e     -> T.Error code e+      T.Error   code error      -> T.Error code error+      T.Retry   retry           -> T.Retry retry+      T.Warning warning (result:results) -> case result of+                        T.QueryOk result        -> T.Warning warning result+                        T.QueryWarning w result -> T.Warning (BS.append warning w) result+                        T.QueryError code e     -> T.Error code e+         +connect :: String -> Int -> IO Handle+connect host port = do+  connection <- connectTo host (PortNumber $ fromIntegral $ port)+  bs         <- BS.hGet connection 4+  let version   = runGet getWord32be bs+      myVersion = runPut (num 1)+  BS.hPut connection myVersion+  return connection++-- | TODO: add configuration options+buildExcerpts :: ExConf.ExcerptConfiguration -- ^ Contains host and port for connection and optional configuration for buildExcerpts+              -> [String]               -- ^ list of document contents to be highlighted+              -> String                 -- ^ The indexes, "*" means every index+              -> String                 -- ^ The query string to use for excerpts+              -> IO (T.Result [BS.ByteString]) -- ^ the documents with excerpts highlighted+buildExcerpts config docs indexes words = do+  conn <- connect (ExConf.host config) (ExConf.port config)+  let req = runPut $ makeBuildExcerpt addExcerpt+  BS.hPut conn req+  hFlush conn+  (status, response) <- getResponse conn+  case status of+    T.OK      -> return $ T.Ok (getResults response)+    T.WARNING -> return $ T.Warning (runGet getStr response) (getResults response)+    T.RETRY   -> return $ T.Retry (errorMessage response)+    T.ERROR n -> return $ T.Error n (errorMessage response)+  where+    getResults response = runGet ((length docs) `times` getStr) response+    errorMessage response = BS.tail (BS.tail (BS.tail (BS.tail response)))++    makeBuildExcerpt putExcerpt = do+      cmd    T.ScExcerpt+      verCmd T.VcExcerpt  +      num $ fromEnum $ BS.length (runPut putExcerpt)+      putExcerpt++    addExcerpt :: Put+    addExcerpt = do+      num 0 -- mode+      num $ excerptFlags config+      str indexes+      str words+      strC config [ExConf.beforeMatch, ExConf.afterMatch, ExConf.chunkSeparator]+      numC config [ExConf.limit, ExConf.around, ExConf.limitPassages, ExConf.limitWords, ExConf.startPassageId]+      str $ ExConf.htmlStripMode config+      list str docs++    modeFlag :: ExConf.ExcerptConfiguration -> (ExConf.ExcerptConfiguration -> Bool) -> Int -> Int+    modeFlag cfg setting value = if setting cfg then value else 0++    excerptFlags :: ExConf.ExcerptConfiguration -> Int+    excerptFlags cfg = foldl (.|.) 1 (map (\(s,v) -> modeFlag cfg s v) [+        (ExConf.exactPhrase,      2 )+      , (ExConf.singlePassage,    4 )+      , (ExConf.useBoundaries,    8 )+      , (ExConf.weightOrder,     16 )+      , (ExConf.queryMode,       32 )+      , (ExConf.forceAllWords,   64 )+      , (ExConf.loadFiles,      128 )+      , (ExConf.allowEmpty,     256 )+      ])+++-- | right now this is just a lower leve method used by the query method+-- | eventually it will be its own entry point that can run multiple queries by using addQuery+-- | right now it just runs the first query and ignores the rest+runQueries :: Handle -> [Put] -> Int -> IO (T.Result [T.QueryResult])+runQueries conn qs numQueries = do+    let nQueries = 1+    let req = runPut $ makeRunQuery (head qs) nQueries+    BS.hPut conn req+    hFlush conn+    getSearchResult conn nQueries+  where +    makeRunQuery query n = do+      cmd T.ScSearch+      verCmd T.VcSearch+      num $ fromEnum $ BS.length (runPut query) + 4+      num n+      query++getSearchResult :: Handle -> Int -> IO (T.Result [T.QueryResult])+getSearchResult conn numResults = do+  (status, response) <- getResponse conn+  case status of+    T.OK      -> return $ T.Ok (getResults response)+    T.WARNING -> return $ T.Warning (runGet getStr response) (getResults response)+    T.RETRY   -> return $ T.Retry (errorMessage response)+    T.ERROR n -> return $ T.Error n (errorMessage response)+  where+    getResults response = runGet (numResults `times` getResult) response+    errorMessage response = BS.tail (BS.tail (BS.tail (BS.tail response)))++getResponse :: Handle -> IO (T.Status, BS.ByteString)+getResponse conn = do+  header <- BS.hGet conn 8+  let (status, version, len) = readHeader header+  if len == 0+    then error "received zero-sized searchd response (bad query?)"+    else return ()+  response <- BS.hGet conn (fromIntegral len)+  return (status, response)++-- | right now this is just a lower level method used by the query method+-- | when runQueries works properly this is used to run batched queries addQuery :: Configuration -> String -> String -> String -> Put-addQuery cfg query index comment = do-    nums cfg [ offset+addQuery cfg query indexes comment = do+    numC cfg [ offset              , limit-             , T.matchMode . mode-             , T.rank      . ranker-             , T.sort      . sort]+             , fromEnum . mode+             , fromEnum . ranker+             , fromEnum . sort]     str (sortBy cfg)     str query-    numList (weights cfg)-    str index-    num 1-    num64s cfg [minId, maxId]-    num 0 -- todo: pack len filters + filters+    list num (weights cfg)+    str indexes+    num 1                     -- id64 range marker+    numC64 cfg [minId, maxId] -- id64 range++    list putFilter (filters cfg)+     enum (groupByFunc   cfg)     str  (groupBy       cfg)     num  (maxMatches    cfg)@@ -58,39 +208,24 @@     num  (retryCount    cfg)     num  (retryDelay    cfg)     str  (groupDistinct cfg)-    num 0 -- anchor point: todo+    num 0 -- anchor point for setGeoAnchor     stringIntList (indexWeights cfg)     num (maxQueryTime cfg)     stringIntList (fieldWeights cfg)     str comment---- | 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 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 numQueries+    num 0 -- attribute overrides (none)+    str (selectClause cfg) -- select-list+    where+      {- Not working properly -}+      putFilter :: T.Filter -> Put+      putFilter (T.ExclusionFilter filter) = putFilter_ filter True+      putFilter filter                     = putFilter_ filter False -makeRunQuery query numQueries =  do-  cmd T.ScSearch-  verCmd T.VcSearch-  num $ fromEnum $ BS.length (runPut query) + 4-  num numQueries-  query+      putFilter_ f@(T.FilterValues attr values)  ex = putFilter__ f attr (list num64) [values] ex+      putFilter_ f@(T.FilterRange  attr min max) ex = putFilter__ f attr num64 [min, max] ex -getResponse conn numResults = do-  header <- hGet conn 8-  let x@(status, version, len) = readHeader header-  response <- hGet conn (fromIntegral len)-  return $ runGet (numResults `times` getResult) response+      putFilter__ filter attr puter values exclude = do+        str (debug attr)+        num $ (debug $ T.fromEnumFilter filter)+        mapM_ puter (debug values)+        num $ (debug $ fromEnum exclude)
Text/Search/Sphinx/Configuration.hs view
@@ -26,6 +26,8 @@   , minId :: Int     -- | Maximum ID to match, 0 means no limit   , maxId :: Int+    -- | attribute filters+  , filters :: [T.Filter]     -- | Group-by sorting clause (to sort groups in result set with)   , groupBy :: String     -- | Group-by count-distinct attribute@@ -48,7 +50,10 @@   , maxQueryTime :: Int     -- | Per-field-name weights   , fieldWeights :: [(String, Int)]+    -- | attributes to select, defaults to '*'+  , selectClause :: String -- setSelect in regular API }+ deriving (Show)  -- | A basic, default configuration. defaultConfig = Configuration {@@ -63,6 +68,7 @@                 , sortBy        = ""                 , minId         = 0                 , maxId         = 0+                , filters       = []                 , groupSort     = "@group desc"                 , groupBy       = ""                 , groupByFunc   = T.Day@@ -74,4 +80,5 @@                 , indexWeights  = []                 , maxQueryTime  = 0                 , fieldWeights  = []+                , selectClause  = "*"               }
+ Text/Search/Sphinx/ExcerptConfiguration.hs view
@@ -0,0 +1,61 @@+module Text.Search.Sphinx.ExcerptConfiguration where++-- import qualified Text.Search.Sphinx.Types as T++data ExcerptConfiguration = ExcerptConfiguration {+    -- | The hostname of the Sphinx daemon+    host :: String+    -- | The portnumber of the Sphinx daemon+  , port :: Int+  , beforeMatch :: String+  , afterMatch :: String+  , chunkSeparator :: String+  , limit  :: Int+  , around :: Int+  , exactPhrase :: Bool+  , singlePassage :: Bool+  , useBoundaries :: Bool+  , weightOrder :: Bool+  -- | warning! broken on 1.10-beta (keep to default of false)+  , queryMode :: Bool+  , forceAllWords :: Bool+  , limitPassages :: Int+  , limitWords :: Int+  , startPassageId :: Int+  , loadFiles :: Bool+  , htmlStripMode :: String+  , allowEmpty :: Bool+}+ deriving (Show)++-- this is true to the API+defaultConfig = ExcerptConfiguration {+    port          = 3312+  , host          = "127.0.0.1"+  , beforeMatch = "<b>"+  , afterMatch = "</b>"+  , chunkSeparator = "..."+  , limit  = 256+  , around = 5+  , exactPhrase = False+  , singlePassage = False+  , weightOrder = False+  , queryMode = False+  , forceAllWords = False+  , limitPassages = 0+  , limitWords = 0+  , useBoundaries = False+  , startPassageId = 1+  , loadFiles = False+  , htmlStripMode = "index" -- ^ "none", "strip", "index", and "retain". +  , allowEmpty = False+}++-- this seems better to me+altConfig = defaultConfig {+    beforeMatch = "<span class='match'>"+  , afterMatch = "</span>"+  , chunkSeparator = " &#8230; "+  -- , queryMode = True Buggy!+  , forceAllWords = True+}
Text/Search/Sphinx/Get.hs view
@@ -1,62 +1,77 @@ module Text.Search.Sphinx.Get where  import Data.Binary.Get+import Data.Binary.IEEE754+ import Data.Int (Int64) import Prelude hiding (readList) import Data.ByteString.Lazy hiding (pack, length, map, groupBy) import Control.Monad-import Text.Search.Sphinx.Types+import qualified Text.Search.Sphinx.Types as T+import Data.Maybe (isJust, fromJust)  -- Utility functions getNum :: Get Int getNum = getWord32be >>= return . fromEnum +getFloat :: Get Float+getFloat = getFloat32be+ getNum64 :: Get Int64 getNum64 = getWord64be >>= return . fromIntegral -getNums = readList getNum readList f = do num <- getNum                 num `times` f times = replicateM-readField = readStr-readStr = do len <- getNum-             getLazyByteString (fromIntegral len) +getStr = do len <- getNum+            getLazyByteString (fromIntegral len) -getResult :: Get SearchResult+getResult :: Get (T.QueryResult) 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 (SearchResult matches total totalFound wrds)+  statusNum <- getNum+  case T.toQueryStatus statusNum of+    T.QueryERROR n -> do e <- getStr+                         return $ T.QueryError statusNum e+    T.QueryOK      -> getResultOk >>= return . T.QueryOk+    T.QueryWARNING -> do w <- getStr+                         getResultOk >>= return . (T.QueryWarning w)+  where+    getResultOk = do+      fields     <- readList getStr+      attrs      <- readList readAttrPair+      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 $ T.SearchResult matches total totalFound wrds (map fst attrs)  -readWord = do s <- readStr+readWord = do s <- getStr               [doc, hits] <- 2 `times` getNum               return (s, doc, hits)  readMatch isId64 attrs = do     doc <- if isId64 then getNum64 else (getNum >>= return . fromIntegral)     weight <- getNum-    matchAttrs <- mapM readMatchAttr attrs-    return $ Match doc weight matchAttrs+    matchAttrs <- mapM readAttr attrs+    return $ T.Match doc weight matchAttrs+  where+    readAttr (T.AttrTMulti attr)  = (readList (readAttr attr)) >>= return . T.AttrMulti+    readAttr T.AttrTBigInt    = getNum64 >>= return . T.AttrBigInt+    readAttr T.AttrTString    = getStr  >>= return . T.AttrString+    readAttr T.AttrTUInt      = getNum >>= return . T.AttrUInt+    readAttr T.AttrTFloat     = getFloat >>= return . T.AttrFloat+    readAttr _                = getNum  >>= return . T.AttrUInt -readMatchAttr AttrTFloat  = error "readMatchAttr for AttrFloat not implemented yet."-readMatchAttr AttrTMulti  = getNums >>= return . AttrMulti-readMatchAttr _           = getNum  >>= return . AttrNum -readAttr = do-    s <- readStr+readAttrPair = do+    s <- getStr     t <- getNum     return (s, toEnum t)  readHeader = runGet $ do status  <- getWord16be                          version <- getWord16be                          length  <- getWord32be-                         return (status, version, length)+                         return (T.toStatus $ fromIntegral status, version, length)
Text/Search/Sphinx/Put.hs view
@@ -1,22 +1,27 @@ module Text.Search.Sphinx.Put where +import Data.Int (Int64)+import Data.Binary (Word64) import Data.Binary.Put import Data.ByteString.Lazy hiding (pack, length, map, groupBy) import Data.ByteString.Lazy.Char8 (pack) import qualified Data.ByteString.Lazy as BS import qualified Text.Search.Sphinx.Types as T -num = putWord32be . toEnum-num64 = putWord64be . toEnum+num     = putWord32be . fromIntegral+num64 i = putWord64be $ fromIntegral i+ enum :: Enum a => a -> Put enum = num . fromEnum-numList ls = do num (length ls)-                mapM_ num ls-nums cfg   = mapM_ (\x -> num $ x cfg)-num64s cfg = mapM_ (\x -> num64 $ x cfg) +list f ls = num (length ls) >> mapM_ f ls++numC   cfg = mapM_ (\x -> num   $ x cfg)+numC64 cfg = mapM_ (\x -> num64 $ x cfg)+strC   cfg = mapM_ (\x -> str   $ x cfg)+ stringIntList :: [(String, Int)] -> Put-stringIntList xs = num (length xs) >> mapM_ strInt xs+stringIntList xs = list strInt xs  where strInt (s,i) = str s >> num i  str :: String -> Put
Text/Search/Sphinx/Types.hs view
@@ -1,7 +1,10 @@-module Text.Search.Sphinx.Types where+module Text.Search.Sphinx.Types (+    module Text.Search.Sphinx.Types+  , ByteString ) where  import Data.ByteString.Lazy (ByteString) import Data.Int (Int64)+import Data.Maybe (Maybe, isJust)  -- | Search commands data SearchdCommand = ScSearch@@ -20,21 +23,35 @@                 | VcKeywords                 deriving (Show) -verCommand VcSearch   = 0x113-verCommand VcExcerpt  = 0x100+-- | Important! only 1.1 compatible+verCommand VcSearch   = 0x117+verCommand VcExcerpt  = 0x102 verCommand VcUpdate   = 0x101 verCommand VcKeywords = 0x100  -- | Searchd status codes-data Searchd = Ok-             | Error-             | Retry-             | Warning-             deriving (Show, Enum)+data Status = OK+            | RETRY+            | WARNING+            | ERROR Int+            deriving (Show) -searchd :: Searchd -> Int-searchd = fromEnum+-- | status from an individual query+data QueryStatus = QueryOK+                 | QueryWARNING+                 | QueryERROR Int+                 deriving (Show) +toQueryStatus 0 = QueryOK+toQueryStatus 3 = QueryWARNING+toQueryStatus 2 = error "Didn't think retry was possible"+toQueryStatus n = QueryERROR n++toStatus 0 = OK+toStatus 2 = RETRY+toStatus 3 = WARNING+toStatus n = ERROR n+ -- | Match modes data MatchMode = All                | Any@@ -45,19 +62,18 @@                | 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+          | Proximity      -- internally used to emulate SPH_MATCH_ALL queries+          | MatchAny       -- internaly used to emulate SPHINX_MATCH_ANY searching mode+          | Fieldmask      -- ?+          | Sph04          -- like ProximityBm25, but more weight given to matches at beginning or end of field+          | Total           deriving (Show, Enum) -rank :: Rank -> Int-rank = fromEnum- -- | Sort modes data Sort = Relevance           | AttrDesc@@ -67,45 +83,58 @@           | Expr           deriving (Show, Enum) -sort :: Sort -> Int-sort = fromEnum- -- | Filter types-data Filter = Values-            | Range-            | FloatRange-            deriving (Show, Enum)+data Filter = ExclusionFilter Filter+            | FilterValues String [Int64]+            | FilterRange  String Int64 Int64+            -- TODO | FilterFloatRange attr Float Float+            deriving (Show) -filter :: Filter -> Int-filter = fromEnum+-- | shortcut for creating an exclusion filter+exclude filter = ExclusionFilter filter +fromEnumFilter (FilterValues _ _)  = 0+fromEnumFilter (FilterRange _ _ _) = 1+-- fromEnumFilter (FilterFloatRange _ _ _) = 2+ -- | Attribute types-data AttrT = AttrTInteger-           | AttrTTimestamp-           | AttrTOrdinal-           | AttrTBool-           | AttrTFloat-           | AttrTMulti+data AttrT = AttrTUInt          -- unsigned 32-bit integer+           | AttrTTimestamp     -- timestamp+           | AttrTStr2Ordinal   -- ordinal string number (integer at search time, specially handled at indexing time)+           | AttrTBool          -- boolean bit field+           | AttrTFloat         -- floating point number (IEEE 32-bit)+           | AttrTBigInt        -- signed 64-bit integer+           | AttrTString        -- string (binary; in-memory)+           | AttrTWordCount     -- string word count (integer at search time,tokenized and counted at indexing time)+           | AttrTMulti AttrT   -- multiple values (0 or more)             deriving (Show)  instance Enum AttrT where     toEnum = toAttrT     fromEnum = attrT -toAttrT 1          = AttrTInteger+toAttrT 1          = AttrTUInt toAttrT 2          = AttrTTimestamp-toAttrT 3          = AttrTOrdinal+toAttrT 3          = AttrTStr2Ordinal toAttrT 4          = AttrTBool toAttrT 5          = AttrTFloat-toAttrT 0x40000000 = AttrTMulti     +toAttrT 6          = AttrTBigInt+toAttrT 7          = AttrTString+toAttrT 8          = AttrTWordCount+toAttrT 0x40000001 = AttrTMulti AttrTUInt -attrT AttrTInteger   = 1-attrT AttrTTimestamp = 2-attrT AttrTOrdinal   = 3-attrT AttrTBool      = 4-attrT AttrTFloat     = 5-attrT AttrTMulti     = 0x40000000+attrMultiMask = 0x40000000 +attrT AttrTUInt        = 1+attrT AttrTTimestamp   = 2+attrT AttrTStr2Ordinal = 3+attrT AttrTBool        = 4+attrT AttrTFloat       = 5+attrT AttrTBigInt      = 6+attrT AttrTString      = 7+attrT AttrTWordCount   = 8+attrT (AttrTMulti AttrTUInt) = 0x40000001+ -- | Grouping functions data GroupByFunction = Day                      | Week@@ -115,9 +144,6 @@                      | AttrPair                      deriving (Show, Enum) -groupByFunction :: GroupByFunction -> Int-groupByFunction = fromEnum- -- | The result of a query data SearchResult = SearchResult {       -- | The matches@@ -126,11 +152,27 @@     , 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.+      -- | processed words with the number of docs and the number of hits.     , words :: [(ByteString, Int, Int)]+      -- | List of attribute names returned in the result.+      -- | The Match will contain just the attribute values in the same order.+    , attributeNames :: [ByteString] }  deriving Show +-- | a single query result, runQueries returns a list of these+data QueryResult = QueryOk SearchResult+                 | QueryWarning ByteString SearchResult+                 | QueryError Int ByteString+                 deriving (Show)++-- | a result returned from searchd+data Result a = Ok a+              | Warning ByteString a+              | Error Int ByteString+              | Retry ByteString+              deriving (Show)+ data Match = Match {              -- Document ID                documentId :: Int64@@ -141,6 +183,9 @@              }  deriving Show -data Attr = AttrMulti [Int]-          | AttrNum  Int- deriving Show+data Attr = AttrMulti [Attr]+          | AttrUInt  Int+          | AttrBigInt Int64+          | AttrString ByteString+          | AttrFloat Float+          deriving (Show)
sphinx.cabal view
@@ -1,5 +1,5 @@ Name:            sphinx-Version:         0.2.1+Version:         0.3.3 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.@@ -8,9 +8,16 @@ License-file:    LICENSE Copyright:       (c) 2008 Tupil Author:          Tupil-Maintainer:      Chris Eidhof <ce+sphinx@tupil.com>-Exposed-Modules: Text.Search.Sphinx, Text.Search.Sphinx.Types,-                 Text.Search.Sphinx.Configuration, Text.Search.Sphinx.Indexable+Maintainer:      Greg Weber <greg@gregweber.info>++Exposed-Modules: Text.Search.Sphinx,+                 Text.Search.Sphinx.Types,+                 Text.Search.Sphinx.Configuration, Text.Search.Sphinx.ExcerptConfiguration, +                 Text.Search.Sphinx.Indexable+ Other-Modules:   Text.Search.Sphinx.Get, Text.Search.Sphinx.Put+ Build-Type:      Simple-Build-Depends:   base, binary, bytestring, network, haskell98, xml+Build-Depends:   base >= 4 && < 5,+                 binary, data-binary-ieee754,+                 bytestring, network, haskell98, xml