git.iko.soy/typeable/bloodhound
on master help sign in
Merged #230
commit2ddaedd05e4c19eacbf4935f26583c1ae1e50199 authorChris Allen <cma@bitemyapp.com> dateSat 7 Apr 2018, 17:16 (9 years ago) parent380da47
7 files changed+127−8
changelog.md+10−2 src/Database/V5/Bloodhound/Internal/Analysis.hs+70−2 src/Database/V5/Bloodhound/Internal/Newtypes.hs+6−1 src/Database/V5/Bloodhound/Internal/Suggest.hs+6−1 src/Database/V5/Bloodhound/Types.hs+2 tests/V5/Test/Generators.hs+18−1 tests/V5/Test/Indices.hs+15−1
changelog.md+10−2
@@ -1,8 +1,10 @@
110.16.0.0
22========
33- @bitemyapp
4− - Reorganized modules internally, ripped out Generic,
5− rewrote part of the test suite
4+ - Reorganized V5 modules internally, ripped out Generic,
5+ rewrote part of the test suite.
6+- @mxavier
7+ - Reorganized V1 modules, mirroring the work done on V5.
68- @andrewthad
79 - Added support for autogenerated elasticsearch ids in the bulk API
810 - Added support for token filters
@@ -11,6 +13,12 @@
1113 - Added support for scripts fields and function score queries
1214- @bermanjosh
1315 - Added support for direct generators
16+- @rvl
17+ - Add [character filters][] to the `AnalyzerDefinition` type.
18+ - Add the "stemmer" and "stop" [token filters][] to `TokenFilterDefinition`.
19+
20+[Character Filters]: https://www.elastic.co/guide/en/elasticsearch/reference/5.6/analysis-charfilters.html
21+[Token Filters]: https://www.elastic.co/guide/en/elasticsearch/reference/5.6/analysis-tokenfilters.html
1422
15230.15.0.2
1624========
src/Database/V5/Bloodhound/Internal/Analysis.hs+70−2
@@ -6,6 +6,7 @@ module Database.V5.Bloodhound.Internal.Analysis where
66import Bloodhound.Import
77
88import qualified Data.Map.Strict as M
9+import Data.String
910import qualified Data.Text as T
1011
1112import Database.V5.Bloodhound.Internal.Newtypes
@@ -15,13 +16,15 @@ data Analysis = Analysis
1516 { analysisAnalyzer :: M.Map Text AnalyzerDefinition
1617 , analysisTokenizer :: M.Map Text TokenizerDefinition
1718 , analysisTokenFilter :: M.Map Text TokenFilterDefinition
19+ , analysisCharFilter :: M.Map Text CharFilterDefinition
1820 } deriving (Eq, Show)
1921
2022instance ToJSON Analysis where
21− toJSON (Analysis analyzer tokenizer tokenFilter) = object
23+ toJSON (Analysis analyzer tokenizer tokenFilter charFilter) = object
2224 [ "analyzer" .= analyzer
2325 , "tokenizer" .= tokenizer
2426 , "filter" .= tokenFilter
27+ , "char_filter" .= charFilter
2528 ]
2629
2730instance FromJSON Analysis where
@@ -29,6 +32,7 @@ instance FromJSON Analysis where
2932 <$> m .: "analyzer"
3033 <*> m .:? "tokenizer" .!= M.empty
3134 <*> m .:? "filter" .!= M.empty
35+ <*> m .:? "char_filter" .!= M.empty
3236
3337newtype Tokenizer =
3438 Tokenizer Text
@@ -37,18 +41,57 @@ newtype Tokenizer =
3741data AnalyzerDefinition = AnalyzerDefinition
3842 { analyzerDefinitionTokenizer :: Maybe Tokenizer
3943 , analyzerDefinitionFilter :: [TokenFilter]
44+ , analyzerDefinitionCharFilter :: [CharFilter]
4045 } deriving (Eq,Show)
4146
4247instance ToJSON AnalyzerDefinition where
43− toJSON (AnalyzerDefinition tokenizer tokenFilter) = object $ catMaybes
48+ toJSON (AnalyzerDefinition tokenizer tokenFilter charFilter) =
49+ object $ catMaybes
4450 [ fmap ("tokenizer" .=) tokenizer
4551 , Just $ "filter" .= tokenFilter
52+ , Just $ "char_filter" .= charFilter
4653 ]
4754
4855instance FromJSON AnalyzerDefinition where
4956 parseJSON = withObject "AnalyzerDefinition" $ \m -> AnalyzerDefinition
5057 <$> m .:? "tokenizer"
5158 <*> m .:? "filter" .!= []
59+ <*> m .:? "char_filter" .!= []
60+
61+-- | Character filters are used to preprocess the stream of characters
62+-- before it is passed to the tokenizer.
63+data CharFilterDefinition
64+ = CharFilterDefinitionMapping (M.Map Text Text)
65+ | CharFilterDefinitionPatternReplace
66+ { charFilterDefinitionPatternReplacePattern :: Text
67+ , charFilterDefinitionPatternReplaceReplacement :: Text
68+ , charFilterDefinitionPatternReplaceFlags :: Maybe Text
69+ }
70+ deriving (Eq, Show)
71+
72+instance ToJSON CharFilterDefinition where
73+ toJSON (CharFilterDefinitionMapping ms) = object
74+ [ "type" .= ("mapping" :: Text)
75+ , "mappings" .= [a <> " => " <> b | (a, b) <- M.toList ms] ]
76+ toJSON (CharFilterDefinitionPatternReplace pat repl flags) = object $
77+ [ "type" .= ("pattern_replace" :: Text)
78+ , "pattern" .= pat
79+ , "replacement" .= repl
80+ ] ++ maybe [] (\f -> ["flags" .= f]) flags
81+
82+instance FromJSON CharFilterDefinition where
83+ parseJSON = withObject "CharFilterDefinition" $ \m -> do
84+ t <- m .: "type"
85+ case (t :: Text) of
86+ "mapping" -> CharFilterDefinitionMapping . M.fromList <$> ms
87+ where
88+ ms = m .: "mappings" >>= mapM parseMapping
89+ parseMapping kv = case T.splitOn "=>" kv of
90+ (k:vs) -> pure (T.strip k, T.strip $ T.concat vs)
91+ _ -> fail "mapping is not of the format key => value"
92+ "pattern_replace" -> CharFilterDefinitionPatternReplace
93+ <$> m .: "pattern" <*> m .: "replacement" <*> m .:? "flags"
94+ _ -> fail ("unrecognized character filter type: " ++ T.unpack t)
5295
5396newtype TokenizerDefinition =
5497 TokenizerDefinitionNgram Ngram
@@ -112,6 +155,8 @@ data TokenFilterDefinition
112155 | TokenFilterDefinitionReverse
113156 | TokenFilterDefinitionSnowball Language
114157 | TokenFilterDefinitionShingle Shingle
158+ | TokenFilterDefinitionStemmer Language
159+ | TokenFilterDefinitionStop (Either Language [StopWord])
115160 deriving (Eq, Show)
116161
117162instance ToJSON TokenFilterDefinition where
@@ -143,6 +188,16 @@ instance ToJSON TokenFilterDefinition where
143188 , "token_separator" .= shingleTokenSeparator s
144189 , "filler_token" .= shingleFillerToken s
145190 ]
191+ TokenFilterDefinitionStemmer lang -> object
192+ [ "type" .= ("stemmer" :: Text)
193+ , "language" .= languageToText lang
194+ ]
195+ TokenFilterDefinitionStop stop -> object
196+ [ "type" .= ("stop" :: Text)
197+ , "stopwords" .= case stop of
198+ Left lang -> String $ "_" <> languageToText lang <> "_"
199+ Right stops -> toJSON stops
200+ ]
146201
147202instance FromJSON TokenFilterDefinition where
148203 parseJSON = withObject "TokenFilterDefinition" $ \m -> do
@@ -163,6 +218,19 @@ instance FromJSON TokenFilterDefinition where
163218 <*> (fmap.fmap) unStringlyTypedBool (m .:? "output_unigrams_if_no_shingles") .!= False
164219 <*> m .:? "token_separator" .!= " "
165220 <*> m .:? "filler_token" .!= "_"
221+ "stemmer" -> TokenFilterDefinitionStemmer
222+ <$> m .: "language"
223+ "stop" -> do
224+ stop <- m .: "stopwords"
225+ stop' <- case stop of
226+ String lang ->
227+ fmap Left
228+ . parseJSON
229+ . String
230+ . T.drop 1
231+ . T.dropEnd 1 $ lang
232+ _ -> Right <$> parseJSON stop
233+ return (TokenFilterDefinitionStop stop')
166234 _ -> fail ("unrecognized token filter type: " ++ T.unpack t)
167235
168236-- | The set of languages that can be passed to various analyzers,
src/Database/V5/Bloodhound/Internal/Newtypes.hs+6−1
@@ -231,4 +231,9 @@ instance FromJSON MS where
231231 parse n = fromInteger (truncate n * 1000)
232232
233233newtype TokenFilter =
234− TokenFilter Text deriving (Eq, Show, FromJSON, ToJSON)
234+ TokenFilter Text
235+ deriving (Eq, Show, FromJSON, ToJSON)
236+
237+newtype CharFilter =
238+ CharFilter Text
239+ deriving (Eq, Show, FromJSON, ToJSON)
src/Database/V5/Bloodhound/Internal/Suggest.hs+6−1
@@ -262,4 +262,9 @@ instance FromJSON DirectGenerators where
262262 <*> o .:? "post_filter"
263263
264264mkDirectGenerators :: FieldName -> DirectGenerators
265−mkDirectGenerators fn = DirectGenerators fn Nothing DirectGeneratorSuggestModeMissing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
265+mkDirectGenerators fn =
266+ DirectGenerators
267+ fn
268+ Nothing
269+ DirectGeneratorSuggestModeMissing
270+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
src/Database/V5/Bloodhound/Types.hs+2
@@ -224,6 +224,7 @@ module Database.V5.Bloodhound.Types
224224 , Analyzer(..)
225225 , Tokenizer(..)
226226 , TokenFilter(..)
227+ , CharFilter(..)
227228 , MaxExpansions(..)
228229 , Lenient(..)
229230 , MatchQueryType(..)
@@ -406,6 +407,7 @@ module Database.V5.Bloodhound.Types
406407 , AnalyzerDefinition(..)
407408 , TokenizerDefinition(..)
408409 , TokenFilterDefinition(..)
410+ , CharFilterDefinition(..)
409411 , Ngram(..)
410412 , TokenChar(..)
411413 , Shingle(..)
tests/V5/Test/Generators.hs+18−1
@@ -435,8 +435,25 @@ makeArbitrary ''Language
435435instance Arbitrary Language where arbitrary = arbitraryLanguage
436436makeArbitrary ''Shingle
437437instance Arbitrary Shingle where arbitrary = arbitraryShingle
438+
439+makeArbitrary ''CharFilter
440+instance Arbitrary CharFilter where arbitrary = arbitraryCharFilter
438441makeArbitrary ''AnalyzerDefinition
439442instance Arbitrary AnalyzerDefinition where arbitrary = arbitraryAnalyzerDefinition
443+
444+-- TODO: This should have a proper generator that doesn't
445+-- create garbage that has to be filtered out.
446+instance Arbitrary CharFilterDefinition where
447+ arbitrary =
448+ oneof [ CharFilterDefinitionMapping
449+ . chomp <$> arbitrary
450+ , CharFilterDefinitionPatternReplace
451+ <$> arbitrary <*> arbitrary <*> arbitrary
452+ ]
453+ where chomp =
454+ M.map T.strip
455+ . M.mapKeys (T.replace "=>" "" . T.strip)
456+
440457makeArbitrary ''Analysis
441458instance Arbitrary Analysis where arbitrary = arbitraryAnalysis
442459makeArbitrary ''Tokenizer
@@ -533,4 +550,4 @@ instance Arbitrary UpdatableIndexSetting' where
533550 NE.fromList . L.nubBy sameAttrName . NE.toList
534551 sameAttrName a b =
535552 nodeAttrFilterName a == nodeAttrFilterName b
536− -- shrink (UpdatableIndexSetting' x) = map UpdatableIndexSetting' (shrink x)
553+ shrink (UpdatableIndexSetting' x) = map UpdatableIndexSetting' (shrink x)
tests/V5/Test/Indices.hs+15−1
@@ -99,6 +99,9 @@ spec = do
9999 , "ex_filter_shingle"
100100 ]
101101 )
102+ (map CharFilter
103+ ["html_strip", "ex_mapping", "ex_pattern_replace"]
104+ )
102105 )
103106 )
104107 (M.singleton "ex_tokenizer"
@@ -113,7 +116,18 @@ spec = do
113116 , ("ex_filter_reverse",TokenFilterDefinitionReverse)
114117 , ("ex_filter_snowball",TokenFilterDefinitionSnowball English)
115118 , ("ex_filter_shingle",TokenFilterDefinitionShingle (Shingle 3 3 True False " " "_"))
116− ]
119+ , ("ex_filter_stemmer",TokenFilterDefinitionStemmer German)
120+ , ("ex_filter_stop1", TokenFilterDefinitionStop (Left French))
121+ , ("ex_filter_stop2",
122+ TokenFilterDefinitionStop
123+ (Right
124+ $ map StopWord ["a", "is", "the"]))
125+ ]
126+ )
127+ (M.fromList
128+ [ ("ex_mapping", CharFilterDefinitionMapping (M.singleton "١" "1"))
129+ , ("ex_pattern_replace", CharFilterDefinitionPatternReplace "(\\d+)-(?=\\d)" "$1_" Nothing)
130+ ]
117131 )
118132 updates = [AnalysisSetting analysis]
119133 createResp <- createIndexWith (updates ++ [NumberOfReplicas (ReplicaCount 0)]) 1 testIndex