@@ -203,6 +203,7 @@ module Database.V5.Bloodhound.Types
203203 , CutoffFrequency(..)
204204 , Analyzer(..)
205205 , Tokenizer(..)
206+ , TokenFilter(..)
206207 , MaxExpansions(..)
207208 , Lenient(..)
208209 , MatchQueryType(..)
@@ -381,8 +382,11 @@ module Database.V5.Bloodhound.Types
381382 , Analysis(..)
382383 , AnalyzerDefinition(..)
383384 , TokenizerDefinition(..)
385+ , TokenFilterDefinition(..)
384386 , Ngram(..)
385387 , TokenChar(..)
388+ , Shingle(..)
389+ , Language(..)
386390 ) where
387391
388392import Control.Applicative as A
@@ -597,32 +601,244 @@ data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
597601data Analysis = Analysis
598602 { analysisAnalyzer :: M.Map Text AnalyzerDefinition
599603 , analysisTokenizer :: M.Map Text TokenizerDefinition
604+ , analysisTokenFilter :: M.Map Text TokenFilterDefinition
600605 } deriving (Eq,Show,Generic,Typeable)
601606
602607instance ToJSON Analysis where
603− toJSON (Analysis analyzer tokenizer) = object
608+ toJSON (Analysis analyzer tokenizer tokenFilter) = object
604609 [ "analyzer" .= analyzer
605610 , "tokenizer" .= tokenizer
611+ , "filter" .= tokenFilter
606612 ]
607613
608614instance FromJSON Analysis where
609615 parseJSON = withObject "Analysis" $ \m -> Analysis
610616 <$> m .: "analyzer"
611− <*> m .: "tokenizer"
617+ <*> m .:? "tokenizer" .!= M.empty
618+ <*> m .:? "filter" .!= M.empty
612619
613620data AnalyzerDefinition = AnalyzerDefinition
614621 { analyzerDefinitionTokenizer :: Maybe Tokenizer
622+ , analyzerDefinitionFilter :: [TokenFilter]
615623 } deriving (Eq,Show,Generic,Typeable)
616624
617625instance ToJSON AnalyzerDefinition where
618− toJSON (AnalyzerDefinition tokenizer) = object $ catMaybes
626+ toJSON (AnalyzerDefinition tokenizer tokenFilter) = object $ catMaybes
619627 [ fmap ("tokenizer" .=) tokenizer
628+ , Just $ "filter" .= tokenFilter
620629 ]
621630
622631instance FromJSON AnalyzerDefinition where
623632 parseJSON = withObject "AnalyzerDefinition" $ \m -> AnalyzerDefinition
624633 <$> m .:? "tokenizer"
634+ <*> m .:? "filter" .!= []
625635
636+-- | Token filters are used to create custom analyzers.
637+data TokenFilterDefinition
638+ = TokenFilterDefinitionLowercase Language
639+ | TokenFilterDefinitionUppercase Language
640+ | TokenFilterDefinitionApostrophe
641+ | TokenFilterDefinitionReverse
642+ | TokenFilterDefinitionSnowball Language
643+ | TokenFilterDefinitionShingle Shingle
644+ deriving (Eq,Show,Generic)
645+
646+instance ToJSON TokenFilterDefinition where
647+ toJSON x = case x of
648+ TokenFilterDefinitionLowercase lang -> object
649+ [ "type" .= ("lowercase" :: Text)
650+ , "language" .= languageToText lang
651+ ]
652+ TokenFilterDefinitionUppercase lang -> object
653+ [ "type" .= ("uppercase" :: Text)
654+ , "language" .= languageToText lang
655+ ]
656+ TokenFilterDefinitionApostrophe -> object
657+ [ "type" .= ("apostrophe" :: Text)
658+ ]
659+ TokenFilterDefinitionReverse -> object
660+ [ "type" .= ("reverse" :: Text)
661+ ]
662+ TokenFilterDefinitionSnowball lang -> object
663+ [ "type" .= ("snowball" :: Text)
664+ , "language" .= languageToText lang
665+ ]
666+ TokenFilterDefinitionShingle s -> object
667+ [ "type" .= ("shingle" :: Text)
668+ , "max_shingle_size" .= shingleMaxSize s
669+ , "min_shingle_size" .= shingleMinSize s
670+ , "output_unigrams" .= shingleOutputUnigrams s
671+ , "output_unigrams_if_no_shingles" .= shingleOutputUnigramsIfNoShingles s
672+ , "token_separator" .= shingleTokenSeparator s
673+ , "filler_token" .= shingleFillerToken s
674+ ]
675+
676+instance FromJSON TokenFilterDefinition where
677+ parseJSON = withObject "TokenFilterDefinition" $ \m -> do
678+ t <- m .: "type"
679+ case (t :: Text) of
680+ "reverse" -> return TokenFilterDefinitionReverse
681+ "apostrophe" -> return TokenFilterDefinitionApostrophe
682+ "lowercase" -> TokenFilterDefinitionLowercase
683+ <$> m .:? "language" .!= English
684+ "uppercase" -> TokenFilterDefinitionUppercase
685+ <$> m .:? "language" .!= English
686+ "snowball" -> TokenFilterDefinitionSnowball
687+ <$> m .:? "language" .!= English
688+ "shingle" -> fmap TokenFilterDefinitionShingle $ Shingle
689+ <$> (fmap.fmap) unStringlyTypedInt (m .:? "max_shingle_size") .!= 2
690+ <*> (fmap.fmap) unStringlyTypedInt (m .:? "min_shingle_size") .!= 2
691+ <*> (fmap.fmap) unStringlyTypedBool (m .:? "output_unigrams") .!= True
692+ <*> (fmap.fmap) unStringlyTypedBool (m .:? "output_unigrams_if_no_shingles") .!= False
693+ <*> m .:? "token_separator" .!= " "
694+ <*> m .:? "filler_token" .!= "_"
695+ _ -> fail ("unrecognized token filter type: " ++ T.unpack t)
696+
697+-- | The set of languages that can be passed to various analyzers,
698+-- filters, etc. in ElasticSearch. Most data types in this module
699+-- that have a 'Language' field are actually only actually to
700+-- handle a subset of these languages. Consult the official
701+-- ElasticSearch documentation to see what is actually supported.
702+data Language
703+ = Arabic
704+ | Armenian
705+ | Basque
706+ | Bengali
707+ | Brazilian
708+ | Bulgarian
709+ | Catalan
710+ | Cjk
711+ | Czech
712+ | Danish
713+ | Dutch
714+ | English
715+ | Finnish
716+ | French
717+ | Galician
718+ | German
719+ | German2
720+ | Greek
721+ | Hindi
722+ | Hungarian
723+ | Indonesian
724+ | Irish
725+ | Italian
726+ | Kp
727+ | Latvian
728+ | Lithuanian
729+ | Lovins
730+ | Norwegian
731+ | Persian
732+ | Porter
733+ | Portuguese
734+ | Romanian
735+ | Russian
736+ | Sorani
737+ | Spanish
738+ | Swedish
739+ | Thai
740+ | Turkish
741+ deriving (Show,Eq,Generic)
742+
743+instance ToJSON Language where
744+ toJSON = Data.Aeson.String . languageToText
745+
746+instance FromJSON Language where
747+ parseJSON = withText "Language" $ \t -> case languageFromText t of
748+ Nothing -> fail "not a supported ElasticSearch language"
749+ Just lang -> return lang
750+
751+languageToText :: Language -> Text
752+languageToText x = case x of
753+ Arabic -> "arabic"
754+ Armenian -> "armenian"
755+ Basque -> "basque"
756+ Bengali -> "bengali"
757+ Brazilian -> "brazilian"
758+ Bulgarian -> "bulgarian"
759+ Catalan -> "catalan"
760+ Cjk -> "cjk"
761+ Czech -> "czech"
762+ Danish -> "danish"
763+ Dutch -> "dutch"
764+ English -> "english"
765+ Finnish -> "finnish"
766+ French -> "french"
767+ Galician -> "galician"
768+ German -> "german"
769+ German2 -> "german2"
770+ Greek -> "greek"
771+ Hindi -> "hindi"
772+ Hungarian -> "hungarian"
773+ Indonesian -> "indonesian"
774+ Irish -> "irish"
775+ Italian -> "italian"
776+ Kp -> "kp"
777+ Latvian -> "latvian"
778+ Lithuanian -> "lithuanian"
779+ Lovins -> "lovins"
780+ Norwegian -> "norwegian"
781+ Persian -> "persian"
782+ Porter -> "porter"
783+ Portuguese -> "portuguese"
784+ Romanian -> "romanian"
785+ Russian -> "russian"
786+ Sorani -> "sorani"
787+ Spanish -> "spanish"
788+ Swedish -> "swedish"
789+ Thai -> "thai"
790+ Turkish -> "turkish"
791+
792+languageFromText :: Text -> Maybe Language
793+languageFromText x = case x of
794+ "arabic" -> Just Arabic
795+ "armenian" -> Just Armenian
796+ "basque" -> Just Basque
797+ "bengali" -> Just Bengali
798+ "brazilian" -> Just Brazilian
799+ "bulgarian" -> Just Bulgarian
800+ "catalan" -> Just Catalan
801+ "cjk" -> Just Cjk
802+ "czech" -> Just Czech
803+ "danish" -> Just Danish
804+ "dutch" -> Just Dutch
805+ "english" -> Just English
806+ "finnish" -> Just Finnish
807+ "french" -> Just French
808+ "galician" -> Just Galician
809+ "german" -> Just German
810+ "german2" -> Just German2
811+ "greek" -> Just Greek
812+ "hindi" -> Just Hindi
813+ "hungarian" -> Just Hungarian
814+ "indonesian" -> Just Indonesian
815+ "irish" -> Just Irish
816+ "italian" -> Just Italian
817+ "kp" -> Just Kp
818+ "latvian" -> Just Latvian
819+ "lithuanian" -> Just Lithuanian
820+ "lovins" -> Just Lovins
821+ "norwegian" -> Just Norwegian
822+ "persian" -> Just Persian
823+ "porter" -> Just Porter
824+ "portuguese" -> Just Portuguese
825+ "romanian" -> Just Romanian
826+ "russian" -> Just Russian
827+ "sorani" -> Just Sorani
828+ "spanish" -> Just Spanish
829+ "swedish" -> Just Swedish
830+ "thai" -> Just Thai
831+ "turkish" -> Just Turkish
832+ _ -> Nothing
833+
834+data Shingle = Shingle
835+ { shingleMaxSize :: Int
836+ , shingleMinSize :: Int
837+ , shingleOutputUnigrams :: Bool
838+ , shingleOutputUnigramsIfNoShingles :: Bool
839+ , shingleTokenSeparator :: Text
840+ , shingleFillerToken :: Text
841+ } deriving (Eq,Show,Generic,Typeable)
626842
627843data TokenizerDefinition
628844 = TokenizerDefinitionNgram Ngram
@@ -1110,6 +1326,8 @@ newtype Analyzer =
11101326 Analyzer Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
11111327newtype Tokenizer =
11121328 Tokenizer Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1329+newtype TokenFilter =
1330+ TokenFilter Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
11131331newtype MaxExpansions =
11141332 MaxExpansions Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
11151333
@@ -4751,6 +4969,11 @@ newtype StringlyTypedInt = StringlyTypedInt { unStringlyTypedInt :: Int }
47514969instance FromJSON StringlyTypedInt where
47524970 parseJSON = fmap StringlyTypedInt . parseJSON . unStringlyTypeJSON
47534971
4972+newtype StringlyTypedBool = StringlyTypedBool { unStringlyTypedBool :: Bool }
4973+
4974+instance FromJSON StringlyTypedBool where
4975+ parseJSON = fmap StringlyTypedBool . parseJSON . unStringlyTypeJSON
4976+
47544977instance FromJSON NodeFSTotalStats where
47554978 parseJSON = withObject "NodeFSTotalStats" parse
47564979 where