git.iko.soy/typeable/bloodhound
on master help sign in
killed off a redundant important, getting documentation rolling
commit1aea945f12bafd836066a8d3bbe6adf32f05e7ad authorChris Allen <cma@bitemyapp.com> dateSun 13 Apr 2014, 20:00 (13 years ago) parent8c8892f
2 files changed+205−2
README.org+205−1 tests/tests.hs−1
README.org+205−1
@@ -5,14 +5,218 @@
55
66* Elasticsearch client and query DSL for Haskell
77
8+** Why?
9+
10+Because you're tired of obnoxious errors like [this](http://i.imgur.com/FKtZYIP.png) and want types to guide your use of the API.
11+
812** Stability
913
10−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 personally familiar with multiple clients in other languages I think the story there 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 being compared to clients in other languages the story here so far is good.
1115
1216* Examples
1317
18+** Index Operations
19+
20+*** Create Index
21+
22+#+BEGIN_SRC haskell
23+
24+-- Formatted for use in ghci, so there are "let"s in front of the decls.
25+
26+-- :set -XDeriveGeneric
27+import Database.Bloodhound.Client
28+import Data.Aeson
29+import Data.Either (Either(..))
30+import Data.Maybe (fromJust)
31+import Data.Time.Calendar (Day(..))
32+import Data.Time.Clock (secondsToDiffTime, UTCTime(..))
33+import Data.Text (Text)
34+import GHC.Generics (Generic)
35+import Network.HTTP.Conduit
36+import qualified Network.HTTP.Types.Status as NHTS
37+
38+-- no trailing slashes in servers, library handles building the path.
39+let testServer = (Server "http://localhost:9200")
40+let testIndex = IndexName "twitter"
41+let testMapping = MappingName "tweet"
42+
43+-- defaultIndexSettings is exported by Database.Bloodhound.Client as well.
44+let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
45+
46+-- createIndex returns IO Reply
47+
48+-- response :: Reply, Reply is a synonym for Network.HTTP.Conduit.Response
49+response <- createIndex testServer defaultIndexSettings testIndex
50+
51+#+END_SRC
52+
53+*** Delete Index
54+
55+#+BEGIN_SRC haskell
56+
57+-- response :: Reply
58+response <- deleteIndex testServer testIndex
59+
60+-- print response if it was a success
61+Response {responseStatus = Status {statusCode = 200, statusMessage = "OK"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","21")], responseBody = "{\"acknowledged\":true}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
62+
63+-- if the index to be deleted didn't exist anyway
64+Response {responseStatus = Status {statusCode = 404, statusMessage = "Not Found"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","65")], responseBody = "{\"error\":\"IndexMissingException[[twitter] missing]\",\"status\":404}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
65+
66+#+END_SRC
67+
68+*** Refresh Index
69+
70+**** Note, you *have* to do this if you expect to read what you just wrote
71+
72+#+BEGIN_SRC haskell
73+
74+resp <- refreshIndex testServer testIndex
75+
76+-- print resp on success
77+Response {responseStatus = Status {statusCode = 200, statusMessage = "OK"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","50")], responseBody = "{\"_shards\":{\"total\":10,\"successful\":5,\"failed\":0}}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
78+
79+#+END_SRC
80+
81+** Mapping Operations
82+
83+*** Create Mapping
84+
85+#+BEGIN_SRC haskell
86+
87+-- don't forget imports and the like at the top.
88+
89+data TweetMapping = TweetMapping deriving (Eq, Show)
90+
91+-- I know writing the JSON directly/manually sucks.
92+-- I don't have a real data type for Mappings yet.
93+-- Let me know if this is something you need.
94+
95+:{
96+instance ToJSON TweetMapping where
97+ toJSON TweetMapping =
98+ object ["tweet" .=
99+ object ["properties" .=
100+ object ["location" .=
101+ object ["type" .= ("geo_point" :: Text)]]]]
102+:}
103+
104+resp <- createMapping testServer testIndex testMapping TweetMapping
105+
106+#+END_SRC
107+
108+*** Delete Mapping
109+
110+#+BEGIN_SRC haskell
111+
112+resp <- deleteMapping testServer testIndex testMapping
113+
114+#+END_SRC
115+
116+** Document Operations
117+
118+*** Indexing Documents
119+
120+#+BEGIN_SRC haskell
121+
122+-- don't forget the imports and derive generic setting for ghci at the beginning of the examples.
123+
124+:{
125+data Location = Location { lat :: Double
126+ , lon :: Double } deriving (Eq, Generic, Show)
127+
128+data Tweet = Tweet { user :: Text
129+ , postDate :: UTCTime
130+ , message :: Text
131+ , age :: Int
132+ , location :: Location } deriving (Eq, Generic, Show)
133+
134+exampleTweet = Tweet { user = "bitemyapp"
135+ , postDate = UTCTime
136+ (ModifiedJulianDay 55000)
137+ (secondsToDiffTime 10)
138+ , message = "Use haskell!"
139+ , age = 10000
140+ , location = Location 40.12 (-71.34) }
141+
142+-- automagic (generic) derivation of instances because we're lazy.
143+instance ToJSON Tweet
144+instance FromJSON Tweet
145+instance ToJSON Location
146+instance FromJSON Location
147+:}
148+
149+-- Should be able to toJSON and encode the data structures like this:
150+-- λ> toJSON $ Location 10.0 10.0
151+-- Object fromList [("lat",Number 10.0),("lon",Number 10.0)]
152+-- λ> encode $ Location 10.0 10.0
153+-- "{\"lat\":10,\"lon\":10}"
154+
155+resp <- indexDocument testServer testIndex testMapping exampleTweet (DocId "1")
156+
157+-- print resp on success
158+Response {responseStatus = Status {statusCode = 200, statusMessage = "OK"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","75")], responseBody = "{\"_index\":\"twitter\",\"_type\":\"tweet\",\"_id\":\"1\",\"_version\":2,\"created\":false}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
159+
160+#+END_SRC
161+
162+*** Deleting Documents
163+
164+#+BEGIN_SRC haskell
165+
166+resp <- deleteDocument testServer testIndex testMapping (DocId "1")
167+
168+#+END_SRC
169+
170+*** Getting Documents
171+
172+#+BEGIN_SRC haskell
173+
174+-- n.b., you'll need the earlier imports. responseBody is from http-conduit
175+
176+resp <- getDocument testServer testIndex testMapping (DocId "1")
177+
178+-- responseBody :: Response body -> body
179+let body = responseBody resp
180+
181+-- you have two options, you use decode and just get Maybe (EsResult Tweet)
182+-- or you can use eitherDecode and get Either String (EsResult Tweet)
183+
184+let maybeResult = decode body :: Maybe (EsResult Tweet)
185+-- the explicit typing is so Aeson knows how to parse the JSON.
186+
187+-- use either if you want to know why something failed to parse.
188+-- (string errors, sadly)
189+let eitherResult = decode body :: Either String (EsResult Tweet)
190+
191+-- print eitherResult should look like:
192+Right (EsResult {_index = "twitter", _type = "tweet", _id = "1", _version = 2, found = Just True, _source = Tweet {user = "bitemyapp", postDate = 2009-06-18 00:00:10 UTC, message = "Use haskell!", age = 10000, location = Location {lat = 40.12, lon = -71.34}}})
193+
194+-- _source in EsResult is parametric, we dispatch the type by passing in what we expect (Tweet) as a parameter to EsResult.
195+
196+#+END_SRC
197+
198+** Search
199+
200+*** Querying
201+
202+
203+*** Filtering
204+
205+
14206* Possible future functionality
15207
208+** Node discovery and failover
209+
210+Might require TCP support.
211+
212+** Support for TCP access to Elasticsearch
213+
214+Pretend to be a transport client?
215+
216+** Bulk cluster-join merge
217+
218+Might require making a lucene index on disk with the appropriate format.
219+
16220** GeoShapeFilter
17221
18222http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-filter.html
tests/tests.hs−1
@@ -4,7 +4,6 @@ module Main where
44
55import Database.Bloodhound.Client
66import Data.Aeson
7−import Data.DeriveTH
87import Data.Either (Either(..))
98import Data.Maybe (fromJust)
109import Data.Time.Calendar (Day(..))