| Copyright | (C) 2014 Chris Allen |
|---|---|
| License | BSD-style (see the file LICENSE) |
| Maintainer | Chris Allen <cma@bitemyapp.com |
| Stability | provisional |
| Portability | OverloadedStrings |
| Safe Haskell | None |
| Language | Haskell2010 |
Database.Bloodhound.Client
Contents
Description
Client side functions for talking to Elasticsearch servers.
- createIndex :: Server -> IndexSettings -> IndexName -> IO Reply
- deleteIndex :: Server -> IndexName -> IO Reply
- indexExists :: Server -> IndexName -> IO Bool
- openIndex :: Server -> IndexName -> IO Reply
- closeIndex :: Server -> IndexName -> IO Reply
- putMapping :: ToJSON a => Server -> IndexName -> MappingName -> a -> IO Reply
- deleteMapping :: Server -> IndexName -> MappingName -> IO Reply
- indexDocument :: ToJSON doc => Server -> IndexName -> MappingName -> doc -> DocId -> IO Reply
- getDocument :: Server -> IndexName -> MappingName -> DocId -> IO Reply
- documentExists :: Server -> IndexName -> MappingName -> DocId -> IO Bool
- deleteDocument :: Server -> IndexName -> MappingName -> DocId -> IO Reply
- searchAll :: Server -> Search -> IO Reply
- searchByIndex :: Server -> IndexName -> Search -> IO Reply
- searchByType :: Server -> IndexName -> MappingName -> Search -> IO Reply
- refreshIndex :: Server -> IndexName -> IO Reply
- mkSearch :: Maybe Query -> Maybe Filter -> Search
- mkAggregateSearch :: Maybe Query -> Aggregations -> Search
- mkHighlightSearch :: Maybe Query -> Highlights -> Search
- bulk :: Server -> Vector BulkOperation -> IO Reply
- pageSearch :: Int -> Int -> Search -> Search
- mkShardCount :: Int -> Maybe ShardCount
- mkReplicaCount :: Int -> Maybe ReplicaCount
- getStatus :: Server -> IO (Maybe Status)
- encodeBulkOperations :: Vector BulkOperation -> ByteString
- encodeBulkOperation :: BulkOperation -> ByteString
Bloodhound client functions
The examples in this module assume the following code has been run. The :{ and :} will only work in GHCi. You'll only need the data types and typeclass instances for the functions that make use of them.
>>>:set -XOverloadedStrings>>>:set -XDeriveGeneric>>>import Database.Bloodhound>>>import Test.DocTest.Prop (assert)>>>let testServer = (Server "http://localhost:9200")>>>let testIndex = IndexName "twitter">>>let testMapping = MappingName "tweet">>>let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)>>>data TweetMapping = TweetMapping deriving (Eq, Show)>>>_ <- deleteIndex testServer testIndex>>>_ <- deleteMapping testServer testIndex testMapping>>>import GHC.Generics>>>import Data.Time.Calendar (Day (..))>>>import Data.Time.Clock (UTCTime (..), secondsToDiffTime)>>>:{instance ToJSON TweetMapping where toJSON TweetMapping = object ["tweet" .= object ["properties" .= object ["location" .= object ["type" .= ("geo_point" :: Text)]]]] data Location = Location { lat :: Double , lon :: Double } deriving (Eq, Generic, Show) data Tweet = Tweet { user :: Text , postDate :: UTCTime , message :: Text , age :: Int , location :: Location } deriving (Eq, Generic, Show) exampleTweet = Tweet { user = "bitemyapp" , postDate = UTCTime (ModifiedJulianDay 55000) (secondsToDiffTime 10) , message = "Use haskell!" , age = 10000 , location = Location 40.12 (-71.34) } instance ToJSON Tweet instance FromJSON Tweet instance ToJSON Location instance FromJSON Location data BulkTest = BulkTest { name :: Text } deriving (Eq, Generic, Show) instance FromJSON BulkTest instance ToJSON BulkTest :}
createIndex :: Server -> IndexSettings -> IndexName -> IO Reply Source
createIndex will create an index given a Server, IndexSettings, and an IndexName.
>>>response <- createIndex testServer defaultIndexSettings (IndexName "didimakeanindex")>>>respIsTwoHunna responseTrue>>>indexExists testServer (IndexName "didimakeanindex")True
deleteIndex :: Server -> IndexName -> IO Reply Source
deleteIndex will delete an index given a Server, and an IndexName.
>>>response <- createIndex testServer defaultIndexSettings (IndexName "didimakeanindex")>>>response <- deleteIndex testServer (IndexName "didimakeanindex")>>>respIsTwoHunna responseTrue>>>indexExists testServer testIndexFalse
indexExists :: Server -> IndexName -> IO Bool Source
indexExists enables you to check if an index exists. Returns Bool
in IO
>>>exists <- indexExists testServer testIndex
openIndex :: Server -> IndexName -> IO Reply Source
openIndex opens an index given a Server and an IndexName. Explained in further detail at
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html
>>>reply <- openIndex testServer testIndex
closeIndex :: Server -> IndexName -> IO Reply Source
closeIndex closes an index given a Server and an IndexName. Explained in further detail at
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html
>>>reply <- closeIndex testServer testIndex
putMapping :: ToJSON a => Server -> IndexName -> MappingName -> a -> IO Reply Source
putMapping is an HTTP PUT and has upsert semantics. Mappings are schemas
for documents in indexes.
>>>_ <- createIndex testServer defaultIndexSettings testIndex>>>resp <- putMapping testServer testIndex testMapping TweetMapping>>>print respResponse {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}
deleteMapping :: Server -> IndexName -> MappingName -> IO Reply Source
deleteMapping is an HTTP DELETE and deletes a mapping for a given index.
Mappings are schemas for documents in indexes.
>>>_ <- createIndex testServer defaultIndexSettings testIndex>>>_ <- putMapping testServer testIndex testMapping TweetMapping>>>resp <- deleteMapping testServer testIndex testMapping>>>print respResponse {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}
indexDocument :: ToJSON doc => Server -> IndexName -> MappingName -> doc -> DocId -> IO Reply Source
indexDocument is the primary way to save a single document in
Elasticsearch. The document itself is simply something we can
convert into a JSON Value. The DocId will function as the
primary key for the document.
>>>resp <- indexDocument testServer testIndex testMapping exampleTweet (DocId "1")>>>print respResponse {responseStatus = Status {statusCode = 201, statusMessage = "Created"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","74")], responseBody = "{\"_index\":\"twitter\",\"_type\":\"tweet\",\"_id\":\"1\",\"_version\":1,\"created\":true}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
getDocument :: Server -> IndexName -> MappingName -> DocId -> IO Reply Source
getDocument is a straight-forward way to fetch a single document from
Elasticsearch using a Server, IndexName, MappingName, and a DocId.
The DocId is the primary key for your Elasticsearch document.
>>>yourDoc <- getDocument testServer testIndex testMapping (DocId "1")
documentExists :: Server -> IndexName -> MappingName -> DocId -> IO Bool Source
documentExists enables you to check if a document exists. Returns Bool
in IO
>>>exists <- documentExists testServer testIndex testMapping (DocId "1")
deleteDocument :: Server -> IndexName -> MappingName -> DocId -> IO Reply Source
deleteDocument is the primary way to delete a single document.
>>>_ <- deleteDocument testServer testIndex testMapping (DocId "1")
searchByIndex :: Server -> IndexName -> Search -> IO Reply Source
searchByIndex, given a Search and an IndexName, will perform that search
against all mappings within an index on an Elasticsearch server.
>>>let query = TermQuery (Term "user" "bitemyapp") Nothing>>>let search = mkSearch (Just query) Nothing>>>reply <- searchByIndex testServer testIndex search
searchByType :: Server -> IndexName -> MappingName -> Search -> IO Reply Source
searchByType, given a Search, IndexName, and MappingName, will perform that
search against a specific mapping within an index on an Elasticsearch server.
>>>let query = TermQuery (Term "user" "bitemyapp") Nothing>>>let search = mkSearch (Just query) Nothing>>>reply <- searchByType testServer testIndex testMapping search
refreshIndex :: Server -> IndexName -> IO Reply Source
refreshIndex will force a refresh on an index. You must
do this if you want to read what you wrote.
>>>_ <- createIndex testServer defaultIndexSettings testIndex>>>_ <- refreshIndex testServer testIndex
mkSearch :: Maybe Query -> Maybe Filter -> Search Source
mkSearch is a helper function for defaulting additional fields of a Search
to Nothing in case you only care about your Query and Filter. Use record update
syntax if you want to add things like aggregations or highlights while still using
this helper function.
>>>let query = TermQuery (Term "user" "bitemyapp") Nothing>>>mkSearch (Just query) NothingSearch {queryBody = Just (TermQuery (Term {termField = "user", termValue = "bitemyapp"}) Nothing), filterBody = Nothing, sortBody = Nothing, aggBody = Nothing, highlight = Nothing, trackSortScores = False, from = 0, size = 10}
mkAggregateSearch :: Maybe Query -> Aggregations -> Search Source
mkAggregateSearch is a helper function that defaults everything in a Search except for
the Query and the Aggregation.
>>>let terms = TermsAgg $ (mkTermsAggregation "user") { termCollectMode = Just BreadthFirst }>>>termsTermsAgg (TermsAggregation {term = Left "user", termInclude = Nothing, termExclude = Nothing, termOrder = Nothing, termMinDocCount = Nothing, termSize = Nothing, termShardSize = Nothing, termCollectMode = Just BreadthFirst, termExecutionHint = Nothing, termAggs = Nothing})>>>let myAggregation = mkAggregateSearch Nothing $ mkAggregations "users" terms
mkHighlightSearch :: Maybe Query -> Highlights -> Search Source
mkHighlightSearch is a helper function that defaults everything in a Search except for
the Query and the Aggregation.
>>>let query = QueryMatchQuery $ mkMatchQuery (FieldName "_all") (QueryString "haskell")>>>let testHighlight = Highlights Nothing [FieldHighlight (FieldName "message") Nothing]>>>let search = mkHighlightSearch (Just query) testHighlight
bulk :: Server -> Vector BulkOperation -> IO Reply Source
bulk uses
Elasticsearch's bulk API
to perform bulk operations. The BulkOperation data type encodes the
indexupdatedelete/create operations. You pass a Vector of BulkOperations
and a Server to bulk in order to send those operations up to your Elasticsearch
server to be performed. I changed from [BulkOperation] to a Vector due to memory overhead.
>>>let stream = V.fromList [BulkIndex testIndex testMapping (DocId "2") (toJSON (BulkTest "blah"))]>>>_ <- bulk testServer stream>>>_ <- refreshIndex testServer testIndex
pageSearch :: Int -> Int -> Search -> Search Source
pageSearch is a helper function that takes a search and assigns the page from and to
fields for the search.
>>>let query = QueryMatchQuery $ mkMatchQuery (FieldName "_all") (QueryString "haskell")>>>let search = mkSearch (Just query) Nothing>>>searchSearch {queryBody = Just (QueryMatchQuery (MatchQuery {matchQueryField = FieldName "_all", matchQueryQueryString = QueryString "haskell", matchQueryOperator = Or, matchQueryZeroTerms = ZeroTermsNone, matchQueryCutoffFrequency = Nothing, matchQueryMatchType = Nothing, matchQueryAnalyzer = Nothing, matchQueryMaxExpansions = Nothing, matchQueryLenient = Nothing})), filterBody = Nothing, sortBody = Nothing, aggBody = Nothing, highlight = Nothing, trackSortScores = False, from = 0, size = 10}>>>pageSearch 10 100 searchSearch {queryBody = Just (QueryMatchQuery (MatchQuery {matchQueryField = FieldName "_all", matchQueryQueryString = QueryString "haskell", matchQueryOperator = Or, matchQueryZeroTerms = ZeroTermsNone, matchQueryCutoffFrequency = Nothing, matchQueryMatchType = Nothing, matchQueryAnalyzer = Nothing, matchQueryMaxExpansions = Nothing, matchQueryLenient = Nothing})), filterBody = Nothing, sortBody = Nothing, aggBody = Nothing, highlight = Nothing, trackSortScores = False, from = 10, size = 100}
mkShardCount :: Int -> Maybe ShardCount Source
mkShardCount is a straight-forward smart constructor for ShardCount
which rejects Int values below 1 and above 1000.
>>>mkShardCount 10Just (ShardCount 10)
mkReplicaCount :: Int -> Maybe ReplicaCount Source
mkReplicaCount is a straight-forward smart constructor for ReplicaCount
which rejects Int values below 1 and above 1000.
>>>mkReplicaCount 10Just (ReplicaCount 10)
getStatus :: Server -> IO (Maybe Status) Source
getStatus fetches the Status of a Server
>>>getStatus testServerJust (Status {ok = Nothing, status = 200, name = "Arena", version = Version {number = "1.4.1", build_hash = "89d3241d670db65f994242c8e8383b169779e2d4", build_timestamp = 2014-11-26 15:49:29 UTC, build_snapshot = False, lucene_version = "4.10.2"}, tagline = "You Know, for Search"})
encodeBulkOperations :: Vector BulkOperation -> ByteString Source
encodeBulkOperations is a convenience function for dumping a vector of BulkOperation
into an ByteString
>>>let bulkOps = V.fromList [BulkIndex testIndex testMapping (DocId "2") (toJSON (BulkTest "blah"))]>>>encodeBulkOperations bulkOps"\n{\"index\":{\"_type\":\"tweet\",\"_id\":\"2\",\"_index\":\"twitter\"}}\n{\"name\":\"blah\"}\n"
encodeBulkOperation :: BulkOperation -> ByteString Source
encodeBulkOperation is a convenience function for dumping a single BulkOperation
into an ByteString
>>>let bulkOp = BulkIndex testIndex testMapping (DocId "2") (toJSON (BulkTest "blah"))>>>encodeBulkOperation bulkOp"{\"index\":{\"_type\":\"tweet\",\"_id\":\"2\",\"_index\":\"twitter\"}}\n{\"name\":\"blah\"}"