git.iko.soy/typeable/bloodhound
on master help sign in
Added ability to manipulate templates
a4d6c1eJohn Galt
bloodhound.cabal+2−1
@@ -38,7 +38,8 @@ library
38 uri-bytestring >= 0.1 && <0.2,
38 uri-bytestring >= 0.1 && <0.2,
39 exceptions,
39 exceptions,
40 data-default-class,
40 data-default-class,
41− blaze-builder
41+ blaze-builder,
42+ unordered-containers
42 default-language: Haskell2010
43 default-language: Haskell2010
43
44
44test-suite tests
45test-suite tests
src/Database/Bloodhound/Client.hs+32
@@ -27,6 +27,9 @@ module Database.Bloodhound.Client
27 , indexExists
27 , indexExists
28 , openIndex
28 , openIndex
29 , closeIndex
29 , closeIndex
30+ , putTemplate
31+ , templateExists
32+ , deleteTemplate
30 , putMapping
33 , putMapping
31 , deleteMapping
34 , deleteMapping
32 , indexDocument
35 , indexDocument
@@ -301,6 +304,35 @@ openIndex = openOrCloseIndexes OpenIndex
301closeIndex :: MonadBH m => IndexName -> m Reply
304closeIndex :: MonadBH m => IndexName -> m Reply
302closeIndex = openOrCloseIndexes CloseIndex
305closeIndex = openOrCloseIndexes CloseIndex
303
306
307+-- | 'putTemplate' creates a template given an 'IndexTemplate' and a 'TemplateName'.
308+-- Explained in further detail at
309+-- <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html>
310+--
311+-- >>> let idxTpl = IndexTemplate (TemplatePattern "tweet-*") (Just (IndexSettings (ShardCount 1) (ReplicaCount 1))) [toJSON TweetMapping]
312+-- >>> resp <- runBH' $ putTemplate idxTpl (TemplateName "tweet-tpl")
313+putTemplate :: MonadBH m => IndexTemplate -> TemplateName -> m Reply
314+putTemplate indexTemplate (TemplateName templateName) =
315+ bindM2 put url (return body)
316+ where url = joinPath ["_template", templateName]
317+ body = Just $ encode indexTemplate
318+
319+-- | 'templateExists' checks to see if a template exists.
320+--
321+-- >>> exists <- runBH' $ templateExists (TemplateName "tweet-tpl")
322+templateExists :: MonadBH m => TemplateName -> m Bool
323+templateExists (TemplateName templateName) = do
324+ (_, exists) <- existentialQuery =<< joinPath ["_template", templateName]
325+ return exists
326+
327+-- | 'deleteTemplate' is an HTTP DELETE and deletes a template.
328+--
329+-- >>> let idxTpl = IndexTemplate (TemplatePattern "tweet-*") (Just (IndexSettings (ShardCount 1) (ReplicaCount 1))) [toJSON TweetMapping]
330+-- >>> _ <- runBH' $ putTemplate idxTpl (TemplateName "tweet-tpl")
331+-- >>> resp <- runBH' $ deleteTemplate (TemplateName "tweet-tpl")
332+deleteTemplate :: MonadBH m => TemplateName -> m Reply
333+deleteTemplate (TemplateName templateName) =
334+ delete =<< joinPath ["_template", templateName]
335+
304-- | 'putMapping' is an HTTP PUT and has upsert semantics. Mappings are schemas
336-- | 'putMapping' is an HTTP PUT and has upsert semantics. Mappings are schemas
305-- for documents in indexes.
337-- for documents in indexes.
306--
338--
src/Database/Bloodhound/Types.hs+39−1
@@ -56,6 +56,7 @@ module Database.Bloodhound.Types
56 , Existence(..)
56 , Existence(..)
57 , NullValue(..)
57 , NullValue(..)
58 , IndexSettings(..)
58 , IndexSettings(..)
59+ , IndexTemplate(..)
59 , Server(..)
60 , Server(..)
60 , Reply
61 , Reply
61 , EsResult(..)
62 , EsResult(..)
@@ -101,6 +102,8 @@ module Database.Bloodhound.Types
101 , RegexpFlag(..)
102 , RegexpFlag(..)
102 , FieldName(..)
103 , FieldName(..)
103 , IndexName(..)
104 , IndexName(..)
105+ , TemplateName(..)
106+ , TemplatePattern(..)
104 , MappingName(..)
107 , MappingName(..)
105 , DocId(..)
108 , DocId(..)
106 , CacheName(..)
109 , CacheName(..)
@@ -222,8 +225,9 @@ import Control.Monad.Writer
222import Data.Aeson
225import Data.Aeson
223import Data.Aeson.Types (Pair, emptyObject, parseMaybe)
226import Data.Aeson.Types (Pair, emptyObject, parseMaybe)
224import qualified Data.ByteString.Lazy.Char8 as L
227import qualified Data.ByteString.Lazy.Char8 as L
225−import Data.List (nub)
228+import Data.List (nub, foldl')
226import Data.List.NonEmpty (NonEmpty (..), toList)
229import Data.List.NonEmpty (NonEmpty (..), toList)
230+import qualified Data.HashMap.Strict as HM (union)
227import qualified Data.Map.Strict as M
231import qualified Data.Map.Strict as M
228import Data.Maybe
232import Data.Maybe
229import Data.Text (Text)
233import Data.Text (Text)
@@ -348,6 +352,20 @@ data FieldType = GeoPointType
348data FieldDefinition =
352data FieldDefinition =
349 FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
353 FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
350
354
355+{-| An 'IndexTemplate' defines a template that will automatically be
356+ applied to new indices created. The templates include both
357+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
358+ controls if the template will be applied to the index created.
359+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
360+
361+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
362+-}
363+data IndexTemplate =
364+ IndexTemplate { templatePattern :: TemplatePattern
365+ , templateSettings :: Maybe IndexSettings
366+ , templateMappings :: [Value]
367+ }
368+
351data MappingField =
369data MappingField =
352 MappingField { mappingFieldName :: FieldName
370 MappingField { mappingFieldName :: FieldName
353 , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
371 , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
@@ -546,6 +564,14 @@ newtype Server = Server Text deriving (Eq, Show)
546-}
564-}
547newtype IndexName = IndexName Text deriving (Eq, Generic, Show)
565newtype IndexName = IndexName Text deriving (Eq, Generic, Show)
548
566
567+{-| 'TemplateName' is used to describe which template to query/create/delete
568+-}
569+newtype TemplateName = TemplateName Text deriving (Eq, Show, Generic)
570+
571+{-| 'TemplatePattern' represents a pattern which is matched against index names
572+-}
573+newtype TemplatePattern = TemplatePattern Text deriving (Eq, Show, Generic)
574+
549{-| 'MappingName' is part of mappings which are how ES describes and schematizes
575{-| 'MappingName' is part of mappings which are how ES describes and schematizes
550 the data in the indices.
576 the data in the indices.
551-}
577-}
@@ -1953,6 +1979,8 @@ instance ToJSON IgnoreTermFrequency
1953instance ToJSON MaxQueryTerms
1979instance ToJSON MaxQueryTerms
1954instance ToJSON TypeName
1980instance ToJSON TypeName
1955instance ToJSON IndexName
1981instance ToJSON IndexName
1982+instance ToJSON TemplateName
1983+instance ToJSON TemplatePattern
1956instance ToJSON BoostTerms
1984instance ToJSON BoostTerms
1957instance ToJSON MaxWordLength
1985instance ToJSON MaxWordLength
1958instance ToJSON MinWordLength
1986instance ToJSON MinWordLength
@@ -1991,6 +2019,16 @@ instance FromJSON Status where
1991instance ToJSON IndexSettings where
2019instance ToJSON IndexSettings where
1992 toJSON (IndexSettings s r) = object ["settings" .= object ["shards" .= s, "replicas" .= r]]
2020 toJSON (IndexSettings s r) = object ["settings" .= object ["shards" .= s, "replicas" .= r]]
1993
2021
2022+instance ToJSON IndexTemplate where
2023+ toJSON (IndexTemplate p s m) =
2024+ objectNoNulls [ "template" .= p
2025+ , "settings" .= s
2026+ , "mappings" .= foldl' merge (object []) m
2027+ ]
2028+ where
2029+ objectNoNulls = object . filter (\(_, v) -> v /= Null)
2030+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
2031+ merge _ _ = undefined
1994
2032
1995instance (FromJSON a) => FromJSON (EsResult a) where
2033instance (FromJSON a) => FromJSON (EsResult a) where
1996 parseJSON (Object v) = EsResult <$>
2034 parseJSON (Object v) = EsResult <$>
tests/tests.hs+18
@@ -309,6 +309,24 @@ main = hspec $ do
309 res' <- insertData' cfg
309 res' <- insertData' cfg
310 liftIO $ isVersionConflict res' `shouldBe` True
310 liftIO $ isVersionConflict res' `shouldBe` True
311
311
312+ describe "template API" $ do
313+ it "can create a template" $ withTestEnv $ do
314+ let idxTpl = IndexTemplate (TemplatePattern "tweet-*") (Just (IndexSettings (ShardCount 1) (ReplicaCount 1))) [toJSON TweetMapping]
315+ resp <- putTemplate idxTpl (TemplateName "tweet-tpl")
316+ liftIO $ validateStatus resp 200
317+
318+ it "can detect if a template exists" $ withTestEnv $ do
319+ exists <- templateExists (TemplateName "tweet-tpl")
320+ liftIO $ exists `shouldBe` True
321+
322+ it "can delete a template" $ withTestEnv $ do
323+ resp <- deleteTemplate (TemplateName "tweet-tpl")
324+ liftIO $ validateStatus resp 200
325+
326+ it "can detect if a template doesn't exist" $ withTestEnv $ do
327+ exists <- templateExists (TemplateName "tweet-tpl")
328+ liftIO $ exists `shouldBe` False
329+
312 describe "bulk API" $ do
330 describe "bulk API" $ do
313 it "inserts all documents we request" $ withTestEnv $ do
331 it "inserts all documents we request" $ withTestEnv $ do
314 _ <- insertData
332 _ <- insertData