git.iko.soy/typeable/bloodhound
on master help sign in
Merge branch 'faster-compile-script-merge' into we-need-faster-compile
commitf7d31499d5cc411b8f02544ea602539af0646c57 authorChris Allen <cma@bitemyapp.com> dateThu 8 Feb 2018, 17:01 (9 years ago) parentf37754d94bf2e4
9 files changed+484−56
.travis.yml+1−1 bloodhound.cabal+2 src/Bloodhound/Import.hs+14 src/Database/Bloodhound/Common/Script.hs+222 src/Database/V5/Bloodhound/Client.hs+3−3 src/Database/V5/Bloodhound/Types.hs+61−29 src/Database/V5/Bloodhound/Types/Internal/Newtypes.hs+9−6 src/Database/V5/Bloodhound/Types/Internal/Query.hs+81−13 tests/V5/tests.hs+91−4
.travis.yml+1−1
@@ -40,7 +40,7 @@ script:
4040 - stack update --no-terminal
4141 - stack build -j2 --fast --no-terminal
4242 - travis_wait 45 sleep 1800 &
43− - stack test --fast --no-terminal bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:$ESFLAG
43+ - stack test --fast --no-terminal bloodhound:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:$ESFLAG
4444
4545cache:
4646 timeout: 2000
bloodhound.cabal+2
@@ -44,6 +44,7 @@ library
4444 Database.V5.Bloodhound.Types
4545 Database.V5.Bloodhound.Types.Class
4646 other-modules: Bloodhound.Import
47+ Database.Bloodhound.Common.Script
4748 Database.V5.Bloodhound.Types.Internal.Analysis
4849 Database.V5.Bloodhound.Types.Internal.Client
4950 Database.V5.Bloodhound.Types.Internal.Newtypes
@@ -64,6 +65,7 @@ library
6465 http-types >= 0.8 && <0.13,
6566 vector >= 0.10.9 && <0.13,
6667 scientific >= 0.3.0.0 && <0.4.0.0,
68+ bifunctors,
6769 exceptions,
6870 data-default-class,
6971 blaze-builder,
src/Bloodhound/Import.hs+14
@@ -2,6 +2,8 @@ module Bloodhound.Import
22 ( module X
33 , LByteString
44 , Method
5+ , omitNulls
6+ , parseNEJSON
57 , parseReadText
68 , readMay
79 , showText
@@ -62,6 +64,8 @@ import Data.Time.Clock.POSIX as X
6264
6365import qualified Data.ByteString.Lazy as BL
6466import qualified Data.Text as T
67+import qualified Data.Traversable as DT
68+import qualified Data.Vector as V
6569import qualified Network.HTTP.Types.Method as NHTM
6670
6771type LByteString = BL.ByteString
@@ -78,3 +82,13 @@ parseReadText = maybe mzero return . readMay . T.unpack
7882
7983showText :: Show a => a -> Text
8084showText = T.pack . show
85+
86+omitNulls :: [(Text, Value)] -> Value
87+omitNulls = object . filter notNull where
88+ notNull (_, Null) = False
89+ notNull (_, Array a) = (not . V.null) a
90+ notNull _ = True
91+
92+parseNEJSON :: (FromJSON a) => [Value] -> Parser (NonEmpty a)
93+parseNEJSON [] = fail "Expected non-empty list"
94+parseNEJSON (x:xs) = DT.mapM parseJSON (x :| xs)
src/Database/Bloodhound/Common/Script.hsnew file+222
@@ -0,0 +1,222 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.Bloodhound.Common.Script where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.HashMap.Strict as HM
9+
10+import Database.V5.Bloodhound.Types.Internal.Newtypes
11+
12+newtype ScriptFields =
13+ ScriptFields (HM.HashMap ScriptFieldName ScriptFieldValue)
14+ deriving (Eq, Show)
15+
16+type ScriptFieldName = Text
17+type ScriptFieldValue = Value
18+
19+data Script =
20+ Script { scriptLanguage :: Maybe ScriptLanguage
21+ , scriptInline :: Maybe ScriptInline
22+ , scriptStored :: Maybe ScriptId
23+ , scriptParams :: Maybe ScriptParams
24+ } deriving (Eq, Show)
25+
26+newtype ScriptLanguage =
27+ ScriptLanguage Text deriving (Eq, Show, FromJSON, ToJSON)
28+
29+newtype ScriptInline =
30+ ScriptInline Text deriving (Eq, Show, FromJSON, ToJSON)
31+
32+newtype ScriptId =
33+ ScriptId Text deriving (Eq, Show, FromJSON, ToJSON)
34+
35+newtype ScriptParams =
36+ ScriptParams (HM.HashMap ScriptParamName ScriptParamValue)
37+ deriving (Eq, Show)
38+
39+type ScriptParamName = Text
40+type ScriptParamValue = Value
41+
42+data BoostMode =
43+ BoostModeMultiply
44+ | BoostModeReplace
45+ | BoostModeSum
46+ | BoostModeAvg
47+ | BoostModeMax
48+ | BoostModeMin deriving (Eq, Show)
49+
50+data ScoreMode =
51+ ScoreModeMultiply
52+ | ScoreModeSum
53+ | ScoreModeAvg
54+ | ScoreModeFirst
55+ | ScoreModeMax
56+ | ScoreModeMin deriving (Eq, Show)
57+
58+data FunctionScoreFunction =
59+ FunctionScoreFunctionScript Script
60+ | FunctionScoreFunctionRandom Seed
61+ | FunctionScoreFunctionFieldValueFactor FieldValueFactor
62+ deriving (Eq, Show)
63+
64+newtype Weight =
65+ Weight Float deriving (Eq, Show, FromJSON, ToJSON)
66+
67+newtype Seed =
68+ Seed Float deriving (Eq, Show, FromJSON, ToJSON)
69+
70+data FieldValueFactor =
71+ FieldValueFactor { fieldValueFactorField :: FieldName
72+ , fieldValueFactor :: Maybe Factor
73+ , fieldValueFactorModifier :: Maybe FactorModifier
74+ , fieldValueFactorMissing :: Maybe FactorMissingFieldValue
75+ } deriving (Eq, Show)
76+
77+newtype Factor =
78+ Factor Float deriving (Eq, Show, FromJSON, ToJSON)
79+
80+data FactorModifier =
81+ FactorModifierNone
82+ | FactorModifierLog
83+ | FactorModifierLog1p
84+ | FactorModifierLog2p
85+ | FactorModifierLn
86+ | FactorModifierLn1p
87+ | FactorModifierLn2p
88+ | FactorModifierSquare
89+ | FactorModifierSqrt
90+ | FactorModifierReciprocal deriving (Eq, Show)
91+
92+newtype FactorMissingFieldValue =
93+ FactorMissingFieldValue Float deriving (Eq, Show, FromJSON, ToJSON)
94+
95+instance ToJSON BoostMode where
96+ toJSON BoostModeMultiply = "multiply"
97+ toJSON BoostModeReplace = "replace"
98+ toJSON BoostModeSum = "sum"
99+ toJSON BoostModeAvg = "avg"
100+ toJSON BoostModeMax = "max"
101+ toJSON BoostModeMin = "min"
102+
103+instance FromJSON BoostMode where
104+ parseJSON = withText "BoostMode" parse
105+ where parse "multiply" = pure BoostModeMultiply
106+ parse "replace" = pure BoostModeReplace
107+ parse "sum" = pure BoostModeSum
108+ parse "avg" = pure BoostModeAvg
109+ parse "max" = pure BoostModeMax
110+ parse "min" = pure BoostModeMin
111+ parse bm = fail ("Unexpected BoostMode: " <> show bm)
112+
113+instance ToJSON ScoreMode where
114+ toJSON ScoreModeMultiply = "multiply"
115+ toJSON ScoreModeSum = "sum"
116+ toJSON ScoreModeFirst = "first"
117+ toJSON ScoreModeAvg = "avg"
118+ toJSON ScoreModeMax = "max"
119+ toJSON ScoreModeMin = "min"
120+
121+instance FromJSON ScoreMode where
122+ parseJSON = withText "ScoreMode" parse
123+ where parse "multiply" = pure ScoreModeMultiply
124+ parse "sum" = pure ScoreModeSum
125+ parse "first" = pure ScoreModeFirst
126+ parse "avg" = pure ScoreModeAvg
127+ parse "max" = pure ScoreModeMax
128+ parse "min" = pure ScoreModeMin
129+ parse sm = fail ("Unexpected ScoreMode: " <> show sm)
130+
131+functionScoreFunctionPair :: FunctionScoreFunction -> (Text, Value)
132+functionScoreFunctionPair (FunctionScoreFunctionScript functionScoreScript) =
133+ ("script_score", toJSON functionScoreScript)
134+
135+functionScoreFunctionPair (FunctionScoreFunctionRandom seed) =
136+ ("random_score", omitNulls [ "seed" .= seed ])
137+
138+functionScoreFunctionPair (FunctionScoreFunctionFieldValueFactor fieldValueFactor) =
139+ ("field_value_factor", toJSON fieldValueFactor)
140+
141+parseFunctionScoreFunction :: Object -> Parser FunctionScoreFunction
142+parseFunctionScoreFunction o =
143+ singleScript `taggedWith` "script_score"
144+ <|> singleRandom `taggedWith` "random_score"
145+ <|> singleFieldValueFactor `taggedWith` "field_value_factor"
146+ where taggedWith parser k = parser =<< o .: k
147+ singleScript = pure . FunctionScoreFunctionScript
148+ singleRandom o' = FunctionScoreFunctionRandom <$> o' .: "seed"
149+ singleFieldValueFactor = pure . FunctionScoreFunctionFieldValueFactor
150+
151+instance ToJSON ScriptFields where
152+ toJSON (ScriptFields x) = Object x
153+
154+instance FromJSON ScriptFields where
155+ parseJSON (Object o) = pure (ScriptFields o)
156+ parseJSON _ = fail "error parsing ScriptFields"
157+
158+instance ToJSON Script where
159+ toJSON (Script lang inline stored params) =
160+ object [ "script" .= omitNulls base ]
161+ where base = [ "lang" .= lang
162+ , "inline" .= inline
163+ , "stored" .= stored
164+ , "params" .= params ]
165+
166+instance FromJSON Script where
167+ parseJSON = withObject "Script" parse
168+ where parse o = o .: "script" >>= \o' ->
169+ Script
170+ <$> o' .:? "lang"
171+ <*> o' .:? "inline"
172+ <*> o' .:? "stored"
173+ <*> o' .:? "params"
174+
175+instance ToJSON ScriptParams where
176+ toJSON (ScriptParams x) = Object x
177+
178+instance FromJSON ScriptParams where
179+ parseJSON (Object o) = pure (ScriptParams o)
180+ parseJSON _ = fail "error parsing ScriptParams"
181+
182+instance ToJSON FieldValueFactor where
183+ toJSON (FieldValueFactor field factor modifier missing) =
184+ omitNulls base
185+ where base = [ "field" .= field
186+ , "factor" .= factor
187+ , "modifier" .= modifier
188+ , "missing" .= missing ]
189+
190+instance FromJSON FieldValueFactor where
191+ parseJSON = withObject "FieldValueFactor" parse
192+ where parse o = FieldValueFactor
193+ <$> o .: "field"
194+ <*> o .:? "factor"
195+ <*> o .:? "modifier"
196+ <*> o .:? "missing"
197+
198+instance ToJSON FactorModifier where
199+ toJSON FactorModifierNone = "none"
200+ toJSON FactorModifierLog = "log"
201+ toJSON FactorModifierLog1p = "log1p"
202+ toJSON FactorModifierLog2p = "log2p"
203+ toJSON FactorModifierLn = "ln"
204+ toJSON FactorModifierLn1p = "ln1p"
205+ toJSON FactorModifierLn2p = "ln2p"
206+ toJSON FactorModifierSquare = "square"
207+ toJSON FactorModifierSqrt = "sqrt"
208+ toJSON FactorModifierReciprocal = "reciprocal"
209+
210+instance FromJSON FactorModifier where
211+ parseJSON = withText "FactorModifier" parse
212+ where parse "none" = pure FactorModifierNone
213+ parse "log" = pure FactorModifierLog
214+ parse "log1p" = pure FactorModifierLog1p
215+ parse "log2p" = pure FactorModifierLog2p
216+ parse "ln" = pure FactorModifierLn
217+ parse "ln1p" = pure FactorModifierLn1p
218+ parse "ln2p" = pure FactorModifierLn2p
219+ parse "square" = pure FactorModifierSquare
220+ parse "sqrt" = pure FactorModifierSqrt
221+ parse "reciprocal" = pure FactorModifierReciprocal
222+ parse fm = fail ("Unexpected FactorModifier: " <> show fm)
src/Database/V5/Bloodhound/Client.hs+3−3
@@ -1092,7 +1092,7 @@ scanSearch indexName mappingName search = do
10921092-- >>> mkSearch (Just query) Nothing
10931093-- Search {queryBody = Just (TermQuery (Term {termField = "user", termValue = "bitemyapp"}) Nothing), filterBody = Nothing, sortBody = Nothing, aggBody = Nothing, highlight = Nothing, trackSortScores = False, from = From 0, size = Size 10, searchType = SearchTypeQueryThenFetch, fields = Nothing, source = Nothing}
10941094mkSearch :: Maybe Query -> Maybe Filter -> Search
1095−mkSearch query filter = Search query filter Nothing Nothing Nothing False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing
1095+mkSearch query filter = Search query filter Nothing Nothing Nothing False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
10961096
10971097-- | 'mkAggregateSearch' is a helper function that defaults everything in a 'Search' except for
10981098-- the 'Query' and the 'Aggregation'.
@@ -1102,7 +1102,7 @@ mkSearch query filter = Search query filter Nothing Nothing Nothing False (From
11021102-- TermsAgg (TermsAggregation {term = Left "user", termInclude = Nothing, termExclude = Nothing, termOrder = Nothing, termMinDocCount = Nothing, termSize = Nothing, termShardSize = Nothing, termCollectMode = Just BreadthFirst, termExecutionHint = Nothing, termAggs = Nothing})
11031103-- >>> let myAggregation = mkAggregateSearch Nothing $ mkAggregations "users" terms
11041104mkAggregateSearch :: Maybe Query -> Aggregations -> Search
1105−mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSearchAggs) Nothing False (From 0) (Size 0) SearchTypeQueryThenFetch Nothing Nothing Nothing
1105+mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSearchAggs) Nothing False (From 0) (Size 0) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
11061106
11071107-- | 'mkHighlightSearch' is a helper function that defaults everything in a 'Search' except for
11081108-- the 'Query' and the 'Aggregation'.
@@ -1111,7 +1111,7 @@ mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSear
11111111-- >>> let testHighlight = Highlights Nothing [FieldHighlight (FieldName "message") Nothing]
11121112-- >>> let search = mkHighlightSearch (Just query) testHighlight
11131113mkHighlightSearch :: Maybe Query -> Highlights -> Search
1114−mkHighlightSearch query searchHighlights = Search query Nothing Nothing Nothing (Just searchHighlights) False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing
1114+mkHighlightSearch query searchHighlights = Search query Nothing Nothing Nothing (Just searchHighlights) False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
11151115
11161116-- | 'pageSearch' is a helper function that takes a search and assigns the from
11171117-- and size fields for the search. The from parameter defines the offset
src/Database/V5/Bloodhound/Types.hs+61−29
@@ -122,6 +122,7 @@ module Database.V5.Bloodhound.Types
122122 , Pattern(..)
123123 , ShardResult(..)
124124 , Hit(..)
125+ , HitFields(..)
125126 , Filter(..)
126127 , Seminearring(..)
127128 , BoolMatch(..)
@@ -150,7 +151,16 @@ module Database.V5.Bloodhound.Types
150151 , RegexpFlags(..)
151152 , RegexpFlag(..)
152153 , FieldName(..)
154+ , ScriptFields(..)
155+ , ScriptFieldName
156+ , ScriptFieldValue
153157 , Script(..)
158+ , ScriptLanguage(..)
159+ , ScriptInline(..)
160+ , ScriptId(..)
161+ , ScriptParams(..)
162+ , ScriptParamName
163+ , ScriptParamValue
154164 , IndexName(..)
155165 , IndexSelection(..)
156166 , NodeSelection(..)
@@ -180,6 +190,18 @@ module Database.V5.Bloodhound.Types
180190 , BoolQuery(..)
181191 , BoostingQuery(..)
182192 , CommonTermsQuery(..)
193+ , FunctionScoreQuery(..)
194+ , BoostMode(..)
195+ , ScoreMode(..)
196+ , FunctionScoreFunctions(..)
197+ , ComponentFunctionScoreFunction(..)
198+ , FunctionScoreFunction(..)
199+ , Weight(..)
200+ , Seed(..)
201+ , FieldValueFactor(..)
202+ , Factor(..)
203+ , FactorModifier(..)
204+ , FactorMissingFieldValue(..)
183205 , DisMaxQuery(..)
184206 , FuzzyLikeThisQuery(..)
185207 , FuzzyLikeFieldQuery(..)
@@ -521,6 +543,7 @@ data Search = Search { queryBody :: Maybe Query
521543 , size :: Size
522544 , searchType :: SearchType
523545 , fields :: Maybe [FieldName]
546+ , scriptFields :: Maybe ScriptFields
524547 , source :: Maybe Source
525548 , suggestBody :: Maybe Suggest -- ^ Only one Suggestion request / response per Search is supported.
526549 } deriving (Eq, Show)
@@ -607,9 +630,6 @@ data HighlightTag =
607630 | CustomTags ([Text], [Text])
608631 deriving (Eq, Show)
609632
610−
611−
612−
613633data SearchResult a =
614634 SearchResult { took :: Int
615635 , timedOut :: Bool
@@ -617,7 +637,8 @@ data SearchResult a =
617637 , searchHits :: SearchHits a
618638 , aggregations :: Maybe AggregationResults
619639 , scrollId :: Maybe ScrollId
620− , suggest :: Maybe NamedSuggestionResponse -- ^ Only one Suggestion request / response per Search is supported.
640+ -- ^ Only one Suggestion request / response per Search is supported.
641+ , suggest :: Maybe NamedSuggestionResponse
621642 }
622643 deriving (Eq, Show)
623644
@@ -625,8 +646,6 @@ newtype ScrollId =
625646 ScrollId Text
626647 deriving (Eq, Show, Ord, ToJSON, FromJSON)
627648
628−type Score = Maybe Double
629−
630649data SearchHits a =
631650 SearchHits { hitsTotal :: Int
632651 , maxScore :: Score
@@ -645,8 +664,13 @@ data Hit a =
645664 , hitDocId :: DocId
646665 , hitScore :: Score
647666 , hitSource :: Maybe a
667+ , hitFields :: Maybe HitFields
648668 , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show)
649669
670+newtype HitFields =
671+ HitFields (M.Map Text [Value])
672+ deriving (Eq, Show)
673+
650674type HitHighlight = M.Map Text [Text]
651675
652676
@@ -856,7 +880,9 @@ instance ToJSON Aggregation where
856880 ]
857881 ]
858882
859− toJSON (DateHistogramAgg (DateHistogramAggregation field interval format preZone postZone preOffset postOffset dateHistoAggs)) =
883+ toJSON (DateHistogramAgg
884+ (DateHistogramAggregation field interval format
885+ preZone postZone preOffset postOffset dateHistoAggs)) =
860886 omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
861887 "interval" .= interval,
862888 "format" .= format,
@@ -868,8 +894,10 @@ instance ToJSON Aggregation where
868894 "aggs" .= dateHistoAggs ]
869895 toJSON (ValueCountAgg a) = object ["value_count" .= v]
870896 where v = case a of
871− (FieldValueCount (FieldName n)) -> object ["field" .= n]
872− (ScriptValueCount (Script s)) -> object ["script" .= s]
897+ (FieldValueCount (FieldName n)) ->
898+ object ["field" .= n]
899+ (ScriptValueCount s) ->
900+ object ["script" .= s]
873901 toJSON (FilterAgg (FilterAggregation filt ags)) =
874902 omitNulls [ "filter" .= filt
875903 , "aggs" .= ags]
@@ -1058,11 +1086,6 @@ instance ToJSON GeoPoint where
10581086 toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
10591087 object [ geoPointField .= geoPointLatLon ]
10601088
1061−
1062−
1063−
1064−
1065−
10661089instance FromJSON Status where
10671090 parseJSON (Object v) = Status <$>
10681091 v .: "name" <*>
@@ -1393,21 +1416,28 @@ instance FromJSON SearchAliasRouting where
13931416 where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
13941417
13951418instance ToJSON Search where
1396− toJSON (Search mquery sFilter sort searchAggs highlight sTrackSortScores sFrom sSize _ sFields sSource sSuggest) =
1397− omitNulls [ "query" .= query'
1398− , "sort" .= sort
1399− , "aggregations" .= searchAggs
1400− , "highlight" .= highlight
1401− , "from" .= sFrom
1402− , "size" .= sSize
1403− , "track_scores" .= sTrackSortScores
1404− , "fields" .= sFields
1405− , "_source" .= sSource
1406− , "suggest" .= sSuggest]
1419+ toJSON (Search mquery sFilter sort searchAggs
1420+ highlight sTrackSortScores sFrom sSize _ sFields
1421+ sScriptFields sSource sSuggest) =
1422+ omitNulls [ "query" .= query'
1423+ , "sort" .= sort
1424+ , "aggregations" .= searchAggs
1425+ , "highlight" .= highlight
1426+ , "from" .= sFrom
1427+ , "size" .= sSize
1428+ , "track_scores" .= sTrackSortScores
1429+ , "fields" .= sFields
1430+ , "script_fields" .= sScriptFields
1431+ , "_source" .= sSource
1432+ , "suggest" .= sSuggest]
14071433
14081434 where query' = case sFilter of
14091435 Nothing -> mquery
1410− Just x -> Just . QueryBoolQuery $ mkBoolQuery (maybeToList mquery) [x] [] []
1436+ Just x ->
1437+ Just
1438+ . QueryBoolQuery
1439+ $ mkBoolQuery (maybeToList mquery)
1440+ [x] [] []
14111441
14121442instance ToJSON Source where
14131443 toJSON NoSource = toJSON False
@@ -1568,10 +1598,14 @@ instance (FromJSON a) => FromJSON (Hit a) where
15681598 v .: "_type" <*>
15691599 v .: "_id" <*>
15701600 v .: "_score" <*>
1571− v .:? "_source" <*>
1601+ v .:? "_source" <*>
1602+ v .:? "fields" <*>
15721603 v .:? "highlight"
15731604 parseJSON _ = empty
15741605
1606+instance FromJSON HitFields where
1607+ parseJSON x
1608+ = HitFields <$> parseJSON x
15751609
15761610instance FromJSON DocVersion where
15771611 parseJSON v = do
@@ -1579,8 +1613,6 @@ instance FromJSON DocVersion where
15791613 maybe (fail "DocVersion out of range") return $ mkDocVersion i
15801614
15811615
1582−
1583−
15841616data Suggest = Suggest { suggestText :: Text
15851617 , suggestName :: Text
15861618 , suggestType :: SuggestType
src/Database/V5/Bloodhound/Types/Internal/Newtypes.hs+9−6
@@ -5,6 +5,9 @@ module Database.V5.Bloodhound.Types.Internal.Newtypes where
55
66import Bloodhound.Import
77
8+-- Slight misnomer.
9+type Score = Maybe Double
10+
811{-| 'MappingName' is part of mappings which are how ES describes and schematizes
912 the data in the indices.
1013-}
@@ -41,12 +44,12 @@ newtype QueryString =
4144 deriving (Eq, Show, ToJSON, FromJSON)
4245
4346
44−{-| 'Script' is often used in place of 'FieldName' to specify more
45−complex ways of extracting a value from a document.
46−-}
47−newtype Script =
48− Script { scriptText :: Text }
49− deriving (Eq, Show)
47+-- {-| 'Script' is often used in place of 'FieldName' to specify more
48+-- complex ways of extracting a value from a document.
49+-- -}
50+-- newtype Script =
51+-- Script { scriptText :: Text }
52+-- deriving (Eq, Show)
5053
5154{-| 'CacheName' is used in 'RegexpFilter' for describing the
5255 'CacheKey' keyed caching behavior.
src/Database/V5/Bloodhound/Types/Internal/Query.hs+81−13
@@ -2,7 +2,10 @@
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE RecordWildCards #-}
44
5−module Database.V5.Bloodhound.Types.Internal.Query where
5+module Database.V5.Bloodhound.Types.Internal.Query
6+ ( module X
7+ , module Database.V5.Bloodhound.Types.Internal.Query
8+ ) where
69
710import Bloodhound.Import
811
@@ -13,10 +16,11 @@ import qualified Data.Text as T
1316import qualified Data.Traversable as DT
1417import qualified Data.Vector as V
1518
19+import Database.Bloodhound.Common.Script as X
1620import Database.V5.Bloodhound.Types.Internal.Newtypes
1721
1822data Query =
19− TermQuery Term (Maybe Boost)
23+ TermQuery Term (Maybe Boost)
2024 | TermsQuery Text (NonEmpty Text)
2125 | QueryMatchQuery MatchQuery
2226 | QueryMultiMatchQuery MultiMatchQuery
@@ -24,6 +28,7 @@ data Query =
2428 | QueryBoostingQuery BoostingQuery
2529 | QueryCommonTermsQuery CommonTermsQuery
2630 | ConstantScoreQuery Query Boost
31+ | QueryFunctionScoreQuery FunctionScoreQuery
2732 | QueryDisMaxQuery DisMaxQuery
2833 | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
2934 | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
@@ -42,8 +47,8 @@ data Query =
4247 | QueryRangeQuery RangeQuery
4348 | QueryRegexpQuery RegexpQuery
4449 | QueryExistsQuery FieldName
45− | QueryMatchNoneQuery
4650 | QueryTemplateQueryInline TemplateQueryInline
51+ | QueryMatchNoneQuery
4752 deriving (Eq, Show)
4853
4954-- | As of Elastic 2.0, 'Filters' are just 'Queries' housed in a
@@ -505,6 +510,74 @@ type Cache = Bool -- caching on/off
505510defaultCache :: Cache
506511defaultCache = False
507512
513+
514+data FunctionScoreQuery =
515+ FunctionScoreQuery { functionScoreQuery :: Maybe Query
516+ , functionScoreBoost :: Maybe Boost
517+ , functionScoreFunctions :: FunctionScoreFunctions
518+ , functionScoreMaxBoost :: Maybe Boost
519+ , functionScoreBoostMode :: Maybe BoostMode
520+ , functionScoreMinScore :: Score
521+ , functionScoreScoreMode :: Maybe ScoreMode
522+ } deriving (Eq, Show)
523+
524+instance ToJSON FunctionScoreQuery where
525+ toJSON (FunctionScoreQuery query boost fns maxBoost boostMode minScore scoreMode) =
526+ omitNulls base
527+ where base = functionScoreFunctionsPair fns :
528+ [ "query" .= query
529+ , "boost" .= boost
530+ , "max_boost" .= maxBoost
531+ , "boost_mode" .= boostMode
532+ , "min_score" .= minScore
533+ , "score_mode" .= scoreMode ]
534+
535+instance FromJSON FunctionScoreQuery where
536+ parseJSON = withObject "FunctionScoreQuery" parse
537+ where parse o = FunctionScoreQuery
538+ <$> o .:? "query"
539+ <*> o .:? "boost"
540+ <*> (singleFunction o
541+ <|> multipleFunctions `taggedWith` "functions")
542+ <*> o .:? "max_boost"
543+ <*> o .:? "boost_mode"
544+ <*> o .:? "min_score"
545+ <*> o .:? "score_mode"
546+ where taggedWith parser k = parser =<< o .: k
547+ singleFunction = fmap FunctionScoreSingle . parseFunctionScoreFunction
548+ multipleFunctions = pure . FunctionScoreMultiple
549+
550+data FunctionScoreFunctions =
551+ FunctionScoreSingle FunctionScoreFunction
552+ | FunctionScoreMultiple (NonEmpty ComponentFunctionScoreFunction) deriving (Eq, Show)
553+
554+data ComponentFunctionScoreFunction =
555+ ComponentFunctionScoreFunction { componentScoreFunctionFilter :: Maybe Filter
556+ , componentScoreFunction :: FunctionScoreFunction
557+ , componentScoreFunctionWeight :: Maybe Weight
558+ } deriving (Eq, Show)
559+
560+instance ToJSON ComponentFunctionScoreFunction where
561+ toJSON (ComponentFunctionScoreFunction filter fn weight) =
562+ omitNulls base
563+ where base = functionScoreFunctionPair fn :
564+ [ "filter" .= filter
565+ , "weight" .= weight ]
566+
567+instance FromJSON ComponentFunctionScoreFunction where
568+ parseJSON = withObject "ComponentFunctionScoreFunction" parse
569+ where parse o = ComponentFunctionScoreFunction
570+ <$> o .:? "filter"
571+ <*> parseFunctionScoreFunction o
572+ <*> o .:? "weight"
573+
574+functionScoreFunctionsPair :: FunctionScoreFunctions -> (Text, Value)
575+functionScoreFunctionsPair (FunctionScoreSingle fn)
576+ = functionScoreFunctionPair fn
577+functionScoreFunctionsPair (FunctionScoreMultiple componentFns) =
578+ ("functions", toJSON componentFns)
579+
580+
508581instance ToJSON Query where
509582 toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
510583 object [ "term" .=
@@ -545,6 +618,9 @@ instance ToJSON Query where
545618 object ["constant_score" .= object ["query" .= query
546619 , "boost" .= boost]]
547620
621+ toJSON (QueryFunctionScoreQuery functionScoreQuery) =
622+ object [ "function_score" .= functionScoreQuery ]
623+
548624 toJSON (QueryDisMaxQuery disMaxQuery) =
549625 object [ "dis_max" .= disMaxQuery ]
550626
@@ -612,6 +688,7 @@ instance FromJSON Query where
612688 <|> queryBoostingQuery `taggedWith` "boosting"
613689 <|> queryCommonTermsQuery `taggedWith` "common"
614690 <|> constantScoreQuery `taggedWith` "constant_score"
691+ <|> queryFunctionScoreQuery `taggedWith` "function_score"
615692 <|> queryDisMaxQuery `taggedWith` "dis_max"
616693 <|> queryFuzzyLikeThisQuery `taggedWith` "fuzzy_like_this"
617694 <|> queryFuzzyLikeFieldQuery `taggedWith` "fuzzy_like_this_field"
@@ -649,6 +726,7 @@ instance FromJSON Query where
649726 Just x -> ConstantScoreQuery <$> parseJSON x
650727 <*> o .: "boost"
651728 _ -> fail "Does not appear to be a ConstantScoreQuery"
729+ queryFunctionScoreQuery = pure . QueryFunctionScoreQuery
652730 queryDisMaxQuery = pure . QueryDisMaxQuery
653731 queryFuzzyLikeThisQuery = pure . QueryFuzzyLikeThisQuery
654732 queryFuzzyLikeFieldQuery = pure . QueryFuzzyLikeFieldQuery
@@ -667,21 +745,11 @@ instance FromJSON Query where
667745 -- queryExistsQuery o = QueryExistsQuery <$> o .: "field"
668746 queryTemplateQueryInline = pure . QueryTemplateQueryInline
669747
670−omitNulls :: [(Text, Value)] -> Value
671−omitNulls = object . filter notNull where
672− notNull (_, Null) = False
673− notNull (_, Array a) = (not . V.null) a
674− notNull _ = True
675−
676748fieldTagged :: Monad m => (FieldName -> Object -> m a) -> Object -> m a
677749fieldTagged f o = case HM.toList o of
678750 [(k, Object o')] -> f (FieldName k) o'
679751 _ -> fail "Expected object with 1 field-named key"
680752
681−parseNEJSON :: (FromJSON a) => [Value] -> Parser (NonEmpty a)
682−parseNEJSON [] = fail "Expected non-empty list"
683−parseNEJSON (x:xs) = DT.mapM parseJSON (x :| xs)
684−
685753instance ToJSON SimpleQueryStringQuery where
686754 toJSON SimpleQueryStringQuery {..} =
687755 omitNulls (base ++ maybeAdd)
tests/V5/tests.hs+91−4
@@ -19,7 +19,7 @@
1919module Main where
2020
2121import Control.Applicative
22−import Control.Error
22+import Control.Error hiding (Script)
2323import Control.Exception (evaluate)
2424import Control.Monad
2525import Control.Monad.Catch
@@ -540,8 +540,11 @@ instance (Arbitrary a, Typeable a) => Arbitrary (Hit a) where
540540 <*> arbitraryScore
541541 <*> arbitrary
542542 <*> arbitrary
543+ <*> arbitrary
543544
544−
545+instance Arbitrary HitFields where
546+ arbitrary = pure (HitFields M.empty)
547+ shrink = const []
545548
546549instance (Arbitrary a, Typeable a) => Arbitrary (SearchHits a) where
547550 arbitrary = reduceSize $ do
@@ -609,6 +612,21 @@ instance Arbitrary a => Arbitrary (NonEmpty a) where
609612 qcNonEmptyToNonEmpty
610613 <$> arbitrary
611614#endif
615+instance Arbitrary ScriptFields where
616+ arbitrary =
617+ pure $ ScriptFields $
618+ HM.fromList []
619+
620+ shrink = const []
621+
622+instance Arbitrary ScriptParams where
623+ arbitrary =
624+ pure $ ScriptParams $
625+ HM.fromList [ ("a", Number 42)
626+ , ("b", String "forty two")
627+ ]
628+
629+ shrink = const []
612630
613631instance Arbitrary RegexpFlags where
614632 arbitrary = oneof [ pure AllRegexpFlags
@@ -944,6 +962,40 @@ instance Arbitrary SuggestType where arbitrary = arbitrarySuggestType
944962makeArbitrary ''Suggest
945963instance Arbitrary Suggest where arbitrary = arbitrarySuggest
946964
965+makeArbitrary ''FunctionScoreQuery
966+instance Arbitrary FunctionScoreQuery where arbitrary = arbitraryFunctionScoreQuery
967+
968+makeArbitrary ''FunctionScoreFunction
969+instance Arbitrary FunctionScoreFunction where arbitrary = arbitraryFunctionScoreFunction
970+makeArbitrary ''FunctionScoreFunctions
971+instance Arbitrary FunctionScoreFunctions where arbitrary = arbitraryFunctionScoreFunctions
972+makeArbitrary ''ComponentFunctionScoreFunction
973+instance Arbitrary ComponentFunctionScoreFunction where arbitrary = arbitraryComponentFunctionScoreFunction
974+makeArbitrary ''Script
975+instance Arbitrary Script where arbitrary = arbitraryScript
976+makeArbitrary ''ScriptLanguage
977+instance Arbitrary ScriptLanguage where arbitrary = arbitraryScriptLanguage
978+makeArbitrary ''ScriptInline
979+instance Arbitrary ScriptInline where arbitrary = arbitraryScriptInline
980+makeArbitrary ''ScriptId
981+instance Arbitrary ScriptId where arbitrary = arbitraryScriptId
982+makeArbitrary ''ScoreMode
983+instance Arbitrary ScoreMode where arbitrary = arbitraryScoreMode
984+makeArbitrary ''BoostMode
985+instance Arbitrary BoostMode where arbitrary = arbitraryBoostMode
986+makeArbitrary ''Seed
987+instance Arbitrary Seed where arbitrary = arbitrarySeed
988+makeArbitrary ''FieldValueFactor
989+instance Arbitrary FieldValueFactor where arbitrary = arbitraryFieldValueFactor
990+makeArbitrary ''Weight
991+instance Arbitrary Weight where arbitrary = arbitraryWeight
992+makeArbitrary ''Factor
993+instance Arbitrary Factor where arbitrary = arbitraryFactor
994+makeArbitrary ''FactorMissingFieldValue
995+instance Arbitrary FactorMissingFieldValue where arbitrary = arbitraryFactorMissingFieldValue
996+makeArbitrary ''FactorModifier
997+instance Arbitrary FactorModifier where arbitrary = arbitraryFactorModifier
998+
947999newtype UpdatableIndexSetting' =
9481000 UpdatableIndexSetting' UpdatableIndexSetting
9491001 deriving (Show, Eq, ToJSON, FromJSON, ApproxEq, Typeable)
@@ -1218,7 +1270,7 @@ main = hspec $ do
12181270 let search = Search Nothing
12191271 Nothing (Just [sortSpec]) Nothing Nothing
12201272 False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing
1221− Nothing
1273+ Nothing Nothing
12221274 result <- searchTweets search
12231275 let myTweet = grabFirst result
12241276 liftIO $
@@ -1635,7 +1687,7 @@ main = hspec $ do
16351687 resetIndex
16361688 resp <- updateIndexAliases (action :| [])
16371689 liftIO $ validateStatus resp 200
1638− deleteIndexAlias aname
1690+ _ <- deleteIndexAlias aname
16391691 getIndexAliases
16401692 -- let expected = IndexAliasSummary alias create
16411693 case aliases of
@@ -1740,6 +1792,30 @@ main = hspec $ do
17401792 Left e -> liftIO $ expectationFailure ("Expected an search suggestion but got " <> show e)
17411793 Right sr -> liftIO $ (suggestOptionsText . head . suggestResponseOptions . head . nsrResponses <$> suggest sr) `shouldBe` expectedText
17421794
1795+ describe "Script" $ do
1796+ it "returns a transformed document based on the script field" $ withTestEnv $ do
1797+ _ <- insertData
1798+ let query = MatchAllQuery Nothing
1799+ sfv = toJSON $
1800+ Script
1801+ (Just (ScriptLanguage "painless"))
1802+ (Just (ScriptInline "doc['age'].value * 2"))
1803+ Nothing
1804+ Nothing
1805+ sf = ScriptFields $
1806+ HM.fromList [("test1", sfv)]
1807+ search' = mkSearch (Just query) Nothing
1808+ search = search' { scriptFields = Just sf }
1809+ resp <- searchByIndex testIndex search
1810+ parsed <- parseEsResponse resp :: BH IO (Either EsError (SearchResult Value))
1811+ case parsed of
1812+ Left e ->
1813+ liftIO $ expectationFailure ("Expected a script-transformed result but got: " <> show e)
1814+ Right sr -> do
1815+ let Just results =
1816+ hitFields (head (hits (searchHits sr)))
1817+ liftIO $ results `shouldBe` (HitFields (M.fromList [("test1", [Number 20000.0])]))
1818+
17431819 describe "Exact isomorphism JSON instances" $ do
17441820 propJSON (Proxy :: Proxy Version)
17451821 propJSON (Proxy :: Proxy IndexName)
@@ -1753,6 +1829,11 @@ main = hspec $ do
17531829 propJSON (Proxy :: Proxy TemplatePattern)
17541830 propJSON (Proxy :: Proxy QueryString)
17551831 propJSON (Proxy :: Proxy FieldName)
1832+ propJSON (Proxy :: Proxy Script)
1833+ propJSON (Proxy :: Proxy ScriptLanguage)
1834+ propJSON (Proxy :: Proxy ScriptInline)
1835+ propJSON (Proxy :: Proxy ScriptId)
1836+ propJSON (Proxy :: Proxy ScriptParams)
17561837 propJSON (Proxy :: Proxy CacheName)
17571838 propJSON (Proxy :: Proxy CacheKey)
17581839 propJSON (Proxy :: Proxy Existence)
@@ -1803,6 +1884,12 @@ main = hspec $ do
18031884 propJSON (Proxy :: Proxy FuzzyQuery)
18041885 propJSON (Proxy :: Proxy FuzzyLikeFieldQuery)
18051886 propJSON (Proxy :: Proxy FuzzyLikeThisQuery)
1887+ propJSON (Proxy :: Proxy FunctionScoreQuery)
1888+ propJSON (Proxy :: Proxy BoostMode)
1889+ propJSON (Proxy :: Proxy ScoreMode)
1890+ propJSON (Proxy :: Proxy ComponentFunctionScoreFunction)
1891+ propJSON (Proxy :: Proxy FieldValueFactor)
1892+ propJSON (Proxy :: Proxy FactorModifier)
18061893 propJSON (Proxy :: Proxy DisMaxQuery)
18071894 propJSON (Proxy :: Proxy CommonTermsQuery)
18081895 propJSON (Proxy :: Proxy CommonMinimumMatch)