git.iko.soy/typeable/bloodhound
on master help sign in
killed off a redundant important, getting documentation rolling
1aea945Chris Allen
README.org+205−1
@@ -5,14 +5,218 @@
5
5
6* Elasticsearch client and query DSL for Haskell
6* Elasticsearch client and query DSL for Haskell
7
7
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+
8** Stability
12** Stability
9
13
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.
11
15
12* Examples
16* Examples
13
17
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+
14* Possible future functionality
206* Possible future functionality
15
207
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+
16** GeoShapeFilter
220** GeoShapeFilter
17
221
18http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-filter.html
222http://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
4
4
5import Database.Bloodhound.Client
5import Database.Bloodhound.Client
6import Data.Aeson
6import Data.Aeson
7−import Data.DeriveTH
8import Data.Either (Either(..))
7import Data.Either (Either(..))
9import Data.Maybe (fromJust)
8import Data.Maybe (fromJust)
10import Data.Time.Calendar (Day(..))
9import Data.Time.Calendar (Day(..))