git.iko.soy/typeable/bloodhound
on master help sign in
refactor of dispatch, began query expansion
c4becf9Chris Allen
Database/Bloodhound/Client.hs+44−46
@@ -25,11 +25,17 @@ import Data.Text (Text)
25import Network.HTTP.Conduit
25import Network.HTTP.Conduit
26import qualified Network.HTTP.Types.Method as NHTM
26import qualified Network.HTTP.Types.Method as NHTM
27import qualified Network.HTTP.Types.Status as NHTS
27import qualified Network.HTTP.Types.Status as NHTS
28+import Prelude hiding (head)
28
29
29import Database.Bloodhound.Types
30import Database.Bloodhound.Types
30import Database.Bloodhound.Types.Class
31import Database.Bloodhound.Types.Class
31import Database.Bloodhound.Types.Instances
32import Database.Bloodhound.Types.Instances
32
33
34+-- find way to avoid destructuring Servers and Indexes?
35+-- make get, post, put, delete helpers.
36+-- make dispatch take URL last for better variance and
37+-- utilization of partial application
38+
33mkShardCount :: Int -> Maybe ShardCount
39mkShardCount :: Int -> Maybe ShardCount
34mkShardCount n
40mkShardCount n
35 | n < 1 = Nothing
41 | n < 1 = Nothing
@@ -47,9 +53,9 @@ responseIsError resp = NHTS.statusCode (responseStatus resp) > 299
47
53
48emptyBody = L.pack ""
54emptyBody = L.pack ""
49
55
50−dispatch :: String -> Method -> Maybe L.ByteString
56+dispatch :: Method -> String -> Maybe L.ByteString
51 -> IO Reply
57 -> IO Reply
52−dispatch url method body = do
58+dispatch method url body = do
53 initReq <- parseUrl url
59 initReq <- parseUrl url
54 let reqBody = RequestBodyLBS $ fromMaybe emptyBody body
60 let reqBody = RequestBodyLBS $ fromMaybe emptyBody body
55 let req = initReq { method = method
61 let req = initReq { method = method
@@ -60,6 +66,16 @@ dispatch url method body = do
60joinPath :: [String] -> String
66joinPath :: [String] -> String
61joinPath = intercalate "/"
67joinPath = intercalate "/"
62
68
69+delete = flip (dispatch NHTM.methodDelete) $ Nothing
70+get = flip (dispatch NHTM.methodGet) $ Nothing
71+head = flip (dispatch NHTM.methodHead) $ Nothing
72+put = dispatch NHTM.methodPost
73+post = dispatch NHTM.methodPost
74+
75+-- indexDocument s ix name doc = put (root </> s </> ix </> name </> doc) (Just encode doc)
76+-- http://hackage.haskell.org/package/http-client-lens-0.1.0/docs/Network-HTTP-Client-Lens.html
77+-- https://github.com/supki/libjenkins/blob/master/src/Jenkins/Rest/Internal.hs
78+
63getStatus :: Server -> IO (Maybe (Status Version))
79getStatus :: Server -> IO (Maybe (Status Version))
64getStatus (Server server) = do
80getStatus (Server server) = do
65 request <- parseUrl $ joinPath [server]
81 request <- parseUrl $ joinPath [server]
@@ -68,36 +84,31 @@ getStatus (Server server) = do
68
84
69createIndex :: Server -> IndexSettings -> IndexName -> IO Reply
85createIndex :: Server -> IndexSettings -> IndexName -> IO Reply
70createIndex (Server server) indexSettings (IndexName indexName) =
86createIndex (Server server) indexSettings (IndexName indexName) =
71− dispatch url method body where
87+ put url body
72− url = joinPath [server, indexName]
88+ where url = joinPath [server, indexName]
73− method = NHTM.methodPut
74− body = Just $ encode indexSettings
89+ body = Just $ encode indexSettings
75
90
76deleteIndex :: Server -> IndexName -> IO Reply
91deleteIndex :: Server -> IndexName -> IO Reply
77deleteIndex (Server server) (IndexName indexName) =
92deleteIndex (Server server) (IndexName indexName) =
78− dispatch url method body where
79− url = joinPath [server, indexName]
93+ delete $ joinPath [server, indexName]
80− method = NHTM.methodDelete
81− body = Nothing
82
94
83respIsTwoHunna :: Reply -> Bool
95respIsTwoHunna :: Reply -> Bool
84respIsTwoHunna resp = NHTS.statusCode (responseStatus resp) == 200
96respIsTwoHunna resp = NHTS.statusCode (responseStatus resp) == 200
85
97
86existentialQuery url = do
98existentialQuery url = do
87− reply <- dispatch url NHTM.methodHead Nothing
99+ reply <- head url
88 return (reply, respIsTwoHunna reply)
100 return (reply, respIsTwoHunna reply)
89
101
90indexExists :: Server -> IndexName -> IO Bool
102indexExists :: Server -> IndexName -> IO Bool
91indexExists (Server server) (IndexName indexName) = do
103indexExists (Server server) (IndexName indexName) = do
92 (reply, exists) <- existentialQuery url
104 (reply, exists) <- existentialQuery url
93− return exists where
105+ return exists
94− url = joinPath [server, indexName]
106+ where url = joinPath [server, indexName]
95
107
96refreshIndex :: Server -> IndexName -> IO Reply
108refreshIndex :: Server -> IndexName -> IO Reply
97−refreshIndex (Server server) (IndexName indexName) = dispatch url method body where
109+refreshIndex (Server server) (IndexName indexName) =
110+ post url Nothing
98− url = joinPath [server, indexName, "_refresh"]
111+ where url = joinPath [server, indexName, "_refresh"]
99− method = NHTM.methodPost
100− body = Nothing
101
112
102stringifyOCIndex oci = case oci of
113stringifyOCIndex oci = case oci of
103 OpenIndex -> "_open"
114 OpenIndex -> "_open"
@@ -105,11 +116,9 @@ stringifyOCIndex oci = case oci of
105
116
106openOrCloseIndexes :: OpenCloseIndex -> Server -> IndexName -> IO Reply
117openOrCloseIndexes :: OpenCloseIndex -> Server -> IndexName -> IO Reply
107openOrCloseIndexes oci (Server server) (IndexName indexName) =
118openOrCloseIndexes oci (Server server) (IndexName indexName) =
108− dispatch url method body where
119+ post url Nothing
109− ociString = stringifyOCIndex oci
120+ where ociString = stringifyOCIndex oci
110− url = joinPath [server, indexName, ociString]
121+ url = joinPath [server, indexName, ociString]
111− method = NHTM.methodPost
112− body = Nothing
113
122
114openIndex :: Server -> IndexName -> IO Reply
123openIndex :: Server -> IndexName -> IO Reply
115openIndex = openOrCloseIndexes OpenIndex
124openIndex = openOrCloseIndexes OpenIndex
@@ -120,40 +129,31 @@ closeIndex = openOrCloseIndexes CloseIndex
120createMapping :: ToJSON a => Server -> IndexName
129createMapping :: ToJSON a => Server -> IndexName
121 -> MappingName -> a -> IO Reply
130 -> MappingName -> a -> IO Reply
122createMapping (Server server) (IndexName indexName) (MappingName mappingName) mapping =
131createMapping (Server server) (IndexName indexName) (MappingName mappingName) mapping =
123− dispatch url method body where
132+ put url body
124− url = joinPath [server, indexName, mappingName, "_mapping"]
133+ where url = joinPath [server, indexName, mappingName, "_mapping"]
125− method = NHTM.methodPut
126− body = Just $ encode mapping
134+ body = Just $ encode mapping
127
135
128deleteMapping :: Server -> IndexName -> MappingName -> IO Reply
136deleteMapping :: Server -> IndexName -> MappingName -> IO Reply
129deleteMapping (Server server) (IndexName indexName) (MappingName mappingName) =
137deleteMapping (Server server) (IndexName indexName) (MappingName mappingName) =
130− dispatch url method body where
131− url = joinPath [server, indexName, mappingName, "_mapping"]
138+ delete $ joinPath [server, indexName, mappingName, "_mapping"]
132− method = NHTM.methodDelete
133− body = Nothing
134
139
135indexDocument :: ToJSON doc => Server -> IndexName -> MappingName
140indexDocument :: ToJSON doc => Server -> IndexName -> MappingName
136 -> doc -> DocId -> IO Reply
141 -> doc -> DocId -> IO Reply
137indexDocument (Server server) (IndexName indexName)
142indexDocument (Server server) (IndexName indexName)
138 (MappingName mappingName) document (DocId docId) =
143 (MappingName mappingName) document (DocId docId) =
139− dispatch url method body where
144+ put url body
140− url = joinPath [server, indexName, mappingName, docId]
145+ where url = joinPath [server, indexName, mappingName, docId]
141− method = NHTM.methodPut
142− body = Just (encode document)
146+ body = Just (encode document)
143
147
144deleteDocument :: Server -> IndexName -> MappingName
148deleteDocument :: Server -> IndexName -> MappingName
145 -> DocId -> IO Reply
149 -> DocId -> IO Reply
146deleteDocument (Server server) (IndexName indexName)
150deleteDocument (Server server) (IndexName indexName)
147 (MappingName mappingName) (DocId docId) =
151 (MappingName mappingName) (DocId docId) =
148− dispatch url method body where
149− url = joinPath [server, indexName, mappingName, docId]
152+ delete $ joinPath [server, indexName, mappingName, docId]
150− method = NHTM.methodDelete
151− body = Nothing
152
153
153bulk :: Server -> [BulkOperation] -> IO Reply
154bulk :: Server -> [BulkOperation] -> IO Reply
154−bulk (Server server) bulkOps = dispatch url method body where
155+bulk (Server server) bulkOps = post url body where
155 url = joinPath [server, "_bulk"]
156 url = joinPath [server, "_bulk"]
156− method = NHTM.methodPost
157 body = Just $ collapseStream bulkOps
157 body = Just $ collapseStream bulkOps
158
158
159collapseStream :: [BulkOperation] -> L.ByteString
159collapseStream :: [BulkOperation] -> L.ByteString
@@ -201,20 +201,18 @@ getStreamChunk (BulkUpdate (IndexName indexName)
201getDocument :: Server -> IndexName -> MappingName
201getDocument :: Server -> IndexName -> MappingName
202 -> DocId -> IO Reply
202 -> DocId -> IO Reply
203getDocument (Server server) (IndexName indexName) (MappingName mappingName) (DocId docId) =
203getDocument (Server server) (IndexName indexName) (MappingName mappingName) (DocId docId) =
204− dispatch url method body where
205− url = joinPath [server, indexName, mappingName, docId]
204+ get $ joinPath [server, indexName, mappingName, docId]
206− method = NHTM.methodGet
207− body = Nothing
208
205
209documentExists :: Server -> IndexName -> MappingName
206documentExists :: Server -> IndexName -> MappingName
210 -> DocId -> IO Bool
207 -> DocId -> IO Bool
211−documentExists (Server server) (IndexName indexName) (MappingName mappingName) (DocId docId) = do
208+documentExists (Server server) (IndexName indexName)
209+ (MappingName mappingName) (DocId docId) = do
212 (reply, exists) <- existentialQuery url
210 (reply, exists) <- existentialQuery url
213 return exists where
211 return exists where
214 url = joinPath [server, indexName, mappingName, docId]
212 url = joinPath [server, indexName, mappingName, docId]
215
213
216dispatchSearch :: String -> Search -> IO Reply
214dispatchSearch :: String -> Search -> IO Reply
217−dispatchSearch url search = dispatch url NHTM.methodPost (Just (encode search))
215+dispatchSearch url search = post url (Just (encode search))
218
216
219searchAll :: Server -> Search -> IO Reply
217searchAll :: Server -> Search -> IO Reply
220searchAll (Server server) search = dispatchSearch url search where
218searchAll (Server server) search = dispatchSearch url search where
Database/Bloodhound/Types.hs+137−15
@@ -174,20 +174,32 @@ type PrefixValue = Text
174
174
175data BooleanOperator = And | Or deriving (Eq, Show)
175data BooleanOperator = And | Or deriving (Eq, Show)
176
176
177−newtype IndexName = IndexName String deriving (Eq, Generic, Show)
177+newtype IndexName = IndexName String deriving (Eq, Generic, Show)
178−newtype MappingName = MappingName String deriving (Eq, Generic, Show)
178+newtype MappingName = MappingName String deriving (Eq, Generic, Show)
179−newtype DocId = DocId String deriving (Eq, Generic, Show)
179+newtype DocId = DocId String deriving (Eq, Generic, Show)
180−newtype QueryString = QueryString Text deriving (Eq, Show)
180+newtype QueryString = QueryString Text deriving (Eq, Show)
181−newtype FieldName = FieldName Text deriving (Eq, Show)
181+newtype FieldName = FieldName Text deriving (Eq, Show)
182−newtype CacheName = CacheName Text deriving (Eq, Show)
182+newtype CacheName = CacheName Text deriving (Eq, Show)
183−newtype CacheKey = CacheKey Text deriving (Eq, Show)
183+newtype CacheKey = CacheKey Text deriving (Eq, Show)
184+newtype Existence = Existence Bool deriving (Eq, Show)
185+newtype NullValue = NullValue Bool deriving (Eq, Show)
186+newtype CutoffFrequency = CutoffFrequency Double deriving (Eq, Show)
187+newtype Analyzer = Analyzer Text deriving (Eq, Show)
188+newtype MaxExpansions = MaxExpansions Int deriving (Eq, Show)
189+newtype Lenient = Lenient Bool deriving (Eq, Show)
190+newtype Tiebreaker = Tiebreaker Double deriving (Eq, Show)
191+newtype Boost = Boost Double deriving (Eq, Show)
192+newtype MinimumMatch = MinimumMatch Int deriving (Eq, Show)
193+newtype MinimumMatchText = MinimumMatchText Text deriving (Eq, Show)
184−newtype Existence = Existence Bool deriving (Eq, Show)
194+newtype DisableCoord = DisableCoord Bool deriving (Eq, Show)
185−newtype NullValue = NullValue Bool deriving (Eq, Show)
195+newtype IgnoreTermFrequency = IgnoreTermFrequency Bool deriving (Eq, Show)
196+newtype MaxQueryTerms = MaxQueryTerms Int deriving (Eq, Show)
197+newtype Fuzziness = Fuzziness Double deriving (Eq, Show)
198+newtype PrefixLength = PrefixLength Int deriving (Eq, Show)
186
199
187unpackId :: DocId -> String
200unpackId :: DocId -> String
188unpackId (DocId docId) = docId
201unpackId (DocId docId) = docId
189
202
190−type Boost = Double
191type TrackSortScores = Bool
203type TrackSortScores = Bool
192type From = Int
204type From = Int
193type Size = Int
205type Size = Int
@@ -198,13 +210,121 @@ data Search = Search { queryBody :: Maybe Query
198 -- default False
210 -- default False
199 , trackSortScores :: TrackSortScores
211 , trackSortScores :: TrackSortScores
200 , from :: From
212 , from :: From
201− , size :: Size} deriving (Eq, Show)
213+ , size :: Size } deriving (Eq, Show)
202−
214+
203−data Query = TermQuery Term (Maybe Boost)
215+data Query = TermQuery Term (Maybe Boost)
216+ | QueryMatchQuery MatchQuery
217+ | QueryMultiMatchQuery MultiMatchQuery
218+ | QueryBoolQuery BoolQuery
219+ | QueryBoostingQuery BoostingQuery
220+ | QueryCommonTermsQuery CommonTermsQuery
204− | ConstantScoreFilter Filter Boost
221+ | ConstantScoreFilter Filter Boost
205− | ConstantScoreQuery Query Boost
222+ | ConstantScoreQuery Query Boost
223+ | QueryDisMaxQuery DisMaxQuery
224+ | QueryFilteredQuery FilteredQuery
225+ | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
226+ | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
227+ | QueryFunctionScoreQuery FunctionScoreQuery
206 deriving (Eq, Show)
228 deriving (Eq, Show)
207
229
230+data FuzzyLikeFieldQuery =
231+ FuzzyLikeFieldQuery { fuzzyLikeField :: FieldName
232+ -- anaphora is good for the soul.
233+ , fuzzyLikeFieldText :: Text
234+ , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
235+ , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
236+ , fuzzyLikeFieldFuzziness :: Fuzziness
237+ , fuzzyLikeFieldPrefixLength :: PrefixLength
238+ , fuzzyLikeFieldBoost :: Boost
239+ , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
240+ } deriving (Eq, Show)
241+
242+data FuzzyLikeThisQuery =
243+ FuzzyLikeThisQuery { fuzzyLikeFields :: [FieldName]
244+ , fuzzyLikeText :: Text
245+ , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
246+ , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
247+ , fuzzyLikeFuzziness :: Fuzziness
248+ , fuzzyLikePrefixLength :: PrefixLength
249+ , fuzzyLikeBoost :: Boost
250+ , fuzzyLikeAnalyzer :: Maybe Analyzer
251+ } deriving (Eq, Show)
252+
253+data FilteredQuery =
254+ FilteredQuery { filteredQuery :: Query
255+ , filteredFilter :: Filter } deriving (Eq, Show)
256+
257+data DisMaxQuery = DisMaxQuery { disMaxQueries :: [Query]
258+ -- default 0.0
259+ , disMaxTiebreaker :: Tiebreaker
260+ , disMaxBoost :: Maybe Boost
261+ } deriving (Eq, Show)
262+data MatchQuery =
263+ MatchQuery { matchQueryField :: FieldName
264+ , matchQueryQueryString :: QueryString
265+ , matchQueryOperator :: BooleanOperator
266+ , matchQueryZeroTerms :: ZeroTermsQuery
267+ , matchQueryCutoffFrequency :: Maybe CutoffFrequency
268+ , matchQueryMatchType :: Maybe MatchQueryType
269+ , matchQueryAnalyzer :: Maybe Analyzer
270+ , matchQueryMaxExpansions :: Maybe MaxExpansions
271+ , matchQueryLenient :: Maybe Lenient } deriving (Eq, Show)
272+
273+mkMatchQuery :: FieldName -> QueryString -> MatchQuery
274+mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing
275+
276+data MatchQueryType = MatchPhrase
277+ | MatchPhrasePrefix deriving (Eq, Show)
278+
279+data MultiMatchQuery =
280+ MultiMatchQuery { multiMatchQueryFields :: [FieldName]
281+ , multiMatchQueryString :: QueryString
282+ , multiMatchQueryOperator :: BooleanOperator
283+ , multiMatchQueryZeroTerms :: ZeroTermsQuery
284+ , multiMatchQueryTiebreaker :: Maybe Tiebreaker
285+ , multiMatchQueryType :: Maybe MultiMatchQueryType
286+ , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
287+ , multiMatchQueryAnalyzer :: Maybe Analyzer
288+ , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
289+ , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Show)
290+
291+data MultiMatchQueryType = MultiMatchBestFields
292+ | MultiMatchMostFields
293+ | MultiMatchCrossFields
294+ | MultiMatchPhrase
295+ | MultiMatchPhrasePrefix deriving (Eq, Show)
296+
297+data BoolQuery = BoolQuery { boolQueryMustMatch :: Maybe Query
298+ , boolQueryMustNotMatch :: Maybe Query
299+ , boolQueryShouldMatch :: Maybe [Query]
300+ , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
301+ , boolQueryBoost :: Maybe Boost
302+ , boolQueryDisableCoord :: Maybe DisableCoord
303+ } deriving (Eq, Show)
304+
305+data BoostingQuery = BoostingQuery { positiveQuery :: Maybe Query
306+ , negativeQuery :: Maybe Query
307+ , positiveBoost :: Maybe Boost
308+ , negativeBoost :: Maybe Boost } deriving (Eq, Show)
309+
310+data CommonTermsQuery =
311+ CommonTermsQuery { commonQuery :: QueryString
312+ , commonCutoffFrequency :: CutoffFrequency
313+ , commonLowFreqOperator :: BooleanOperator
314+ , commonHighFreqOperator :: BooleanOperator
315+ , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
316+ , commonBoost :: Maybe Boost
317+ , commonAnalyzer :: Maybe Analyzer
318+ , commonDisableCoord :: Maybe DisableCoord
319+ } deriving (Eq, Show)
320+
321+data CommonMinimumMatch = CommonMinimumMatchHighLow MinimumMatchHighLow
322+ | CommonMinimumMatch MinimumMatchText deriving (Eq, Show)
323+
324+data MinimumMatchHighLow =
325+ MinimumMatchHighLow { lowFreq :: MinimumMatch
326+ , highFreq :: MinimumMatch } deriving (Eq, Show)
327+
208data Filter = AndFilter [Filter] Cache
328data Filter = AndFilter [Filter] Cache
209 | OrFilter [Filter] Cache
329 | OrFilter [Filter] Cache
210 | NotFilter Filter Cache
330 | NotFilter Filter Cache
@@ -223,6 +343,8 @@ data Filter = AndFilter [Filter] Cache
223 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
343 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
224 deriving (Eq, Show)
344 deriving (Eq, Show)
225
345
346+data ZeroTermsQuery = ZeroTermsNone | ZeroTermsAll deriving (Eq, Show)
347+
226-- lt, lte | gt, gte
348-- lt, lte | gt, gte
227newtype LessThan = LessThan Double deriving (Eq, Show)
349newtype LessThan = LessThan Double deriving (Eq, Show)
228newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
350newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
README.org+1−1
@@ -11,7 +11,7 @@ Search doesn't have to be hard. Let the dog do it.
11
11
12** Stability
12** Stability
13
13
14−Bloodhound is alpha at the moment. The library works fine, but I don't want to mislead anyone into thinking the API is final or stable. I wouldn't call the library "complete" or representative of everything you can do in Elasticsearch but being compared to clients in other languages the story here so far is good.
14+Bloodhound is alpha at the moment. The library works fine, but I don't want to mislead anyone into thinking the API is final or stable. I wouldn't call the library "complete" or representative of everything you can do in Elasticsearch but compared to clients in other languages, the usability is good.
15
15
16* Examples
16* Examples
17
17