git.iko.soy/typeable/bloodhound
on master help sign in
Add value_count aggregation support
463fdc8Michael Xavier
src/Database/Bloodhound/Types.hs+16−1
@@ -103,6 +103,7 @@ module Database.Bloodhound.Types
103 , RegexpFlags(..)
103 , RegexpFlags(..)
104 , RegexpFlag(..)
104 , RegexpFlag(..)
105 , FieldName(..)
105 , FieldName(..)
106+ , Script(..)
106 , IndexName(..)
107 , IndexName(..)
107 , TemplateName(..)
108 , TemplateName(..)
108 , TemplatePattern(..)
109 , TemplatePattern(..)
@@ -201,6 +202,7 @@ module Database.Bloodhound.Types
201 , Bucket(..)
202 , Bucket(..)
202 , BucketAggregation(..)
203 , BucketAggregation(..)
203 , TermsAggregation(..)
204 , TermsAggregation(..)
205+ , ValueCountAggregation(..)
204 , DateHistogramAggregation(..)
206 , DateHistogramAggregation(..)
205
207
206 , Highlights(..)
208 , Highlights(..)
@@ -607,6 +609,12 @@ newtype QueryString = QueryString Text deriving (Eq, Generic, Show)
607-}
609-}
608newtype FieldName = FieldName Text deriving (Eq, Show)
610newtype FieldName = FieldName Text deriving (Eq, Show)
609
611
612+
613+{-| 'Script' is often used in place of 'FieldName' to specify more
614+complex ways of extracting a value from a document.
615+-}
616+newtype Script = Script { scriptText :: Text } deriving (Eq, Show)
617+
610{-| 'CacheName' is used in 'RegexpFilter' for describing the
618{-| 'CacheName' is used in 'RegexpFilter' for describing the
611 'CacheKey' keyed caching behavior.
619 'CacheKey' keyed caching behavior.
612-}
620-}
@@ -1310,7 +1318,8 @@ data Interval = Year
1310 | FractionalInterval Float TimeInterval deriving (Eq, Show)
1318 | FractionalInterval Float TimeInterval deriving (Eq, Show)
1311
1319
1312data Aggregation = TermsAgg TermsAggregation
1320data Aggregation = TermsAgg TermsAggregation
1313− | DateHistogramAgg DateHistogramAggregation deriving (Eq, Show)
1321+ | DateHistogramAgg DateHistogramAggregation
1322+ | ValueCountAgg ValueCountAggregation deriving (Eq, Show)
1314
1323
1315
1324
1316data TermsAggregation = TermsAggregation { term :: Either Text Text
1325data TermsAggregation = TermsAggregation { term :: Either Text Text
@@ -1336,6 +1345,8 @@ data DateHistogramAggregation = DateHistogramAggregation { dateField :: Fie
1336 , dateAggs :: Maybe Aggregations
1345 , dateAggs :: Maybe Aggregations
1337 } deriving (Eq, Show)
1346 } deriving (Eq, Show)
1338
1347
1348+data ValueCountAggregation = FieldValueCount FieldName
1349+ | ScriptValueCount Script deriving (Eq, Show)
1339
1350
1340mkTermsAggregation :: Text -> TermsAggregation
1351mkTermsAggregation :: Text -> TermsAggregation
1341mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1352mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
@@ -1409,6 +1420,10 @@ instance ToJSON Aggregation where
1409 "post_offset" .= postOffset
1420 "post_offset" .= postOffset
1410 ],
1421 ],
1411 "aggs" .= dateHistoAggs ]
1422 "aggs" .= dateHistoAggs ]
1423+ toJSON (ValueCountAgg a) = object ["value_count" .= v]
1424+ where v = case a of
1425+ (FieldValueCount (FieldName n)) -> object ["field" .= n]
1426+ (ScriptValueCount (Script s)) -> object ["script" .= s]
1412
1427
1413type AggregationResults = M.Map Text Value
1428type AggregationResults = M.Map Text Value
1414
1429
tests/tests.hs+21−8
@@ -14,6 +14,7 @@ import Data.List (nub)
14import Data.List.NonEmpty (NonEmpty (..))
14import Data.List.NonEmpty (NonEmpty (..))
15import qualified Data.List.NonEmpty as NE
15import qualified Data.List.NonEmpty as NE
16import qualified Data.Map.Strict as M
16import qualified Data.Map.Strict as M
17+import Data.Monoid
17import Data.Text (Text)
18import Data.Text (Text)
18import qualified Data.Text as T
19import qualified Data.Text as T
19import Data.Time.Calendar (Day (..))
20import Data.Time.Calendar (Day (..))
@@ -174,15 +175,16 @@ insertOther = do
174
175
175searchTweet :: Search -> BH IO (Either String Tweet)
176searchTweet :: Search -> BH IO (Either String Tweet)
176searchTweet search = do
177searchTweet search = do
177− reply <- searchByIndex testIndex search
178+ result <- searchTweets search
178− let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
179 let myTweet = fmap (hitSource . head . hits . searchHits) result
179 let myTweet = fmap (hitSource . head . hits . searchHits) result
180 return myTweet
180 return myTweet
181
181
182+searchTweets :: Search -> BH IO (Either String (SearchResult Tweet))
183+searchTweets search = eitherDecode . responseBody <$> searchByIndex testIndex search
184+
182searchExpectNoResults :: Search -> BH IO ()
185searchExpectNoResults :: Search -> BH IO ()
183searchExpectNoResults search = do
186searchExpectNoResults search = do
184− reply <- searchByIndex testIndex search
187+ result <- searchTweets search
185− let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
186 let emptyHits = fmap (hits . searchHits) result
188 let emptyHits = fmap (hits . searchHits) result
187 liftIO $
189 liftIO $
188 emptyHits `shouldBe` Right []
190 emptyHits `shouldBe` Right []
@@ -214,8 +216,7 @@ searchTermsAggHint hints = do
214
216
215searchTweetHighlight :: Search -> BH IO (Either String (Maybe HitHighlight))
217searchTweetHighlight :: Search -> BH IO (Either String (Maybe HitHighlight))
216searchTweetHighlight search = do
218searchTweetHighlight search = do
217− reply <- searchByIndex testIndex search
219+ result <- searchTweets search
218− let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
219 let myHighlight = fmap (hitHighlight . head . hits . searchHits) result
220 let myHighlight = fmap (hitHighlight . head . hits . searchHits) result
220 return myHighlight
221 return myHighlight
221
222
@@ -435,8 +436,7 @@ main = hspec $ do
435 let search = Search Nothing
436 let search = Search Nothing
436 (Just IdentityFilter) (Just [sortSpec]) Nothing Nothing
437 (Just IdentityFilter) (Just [sortSpec]) Nothing Nothing
437 False (From 0) (Size 10) Nothing
438 False (From 0) (Size 10) Nothing
438− reply <- searchByIndex testIndex search
439+ result <- searchTweets search
439− let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
440 let myTweet = fmap (hitSource . head . hits . searchHits) result
440 let myTweet = fmap (hitSource . head . hits . searchHits) result
441 liftIO $
441 liftIO $
442 myTweet `shouldBe` Right otherTweet
442 myTweet `shouldBe` Right otherTweet
@@ -628,6 +628,19 @@ main = hspec $ do
628 _ <- insertData
628 _ <- insertData
629 searchTermsAggHint [GlobalOrdinals, GlobalOrdinalsHash, GlobalOrdinalsLowCardinality, Map]
629 searchTermsAggHint [GlobalOrdinals, GlobalOrdinalsHash, GlobalOrdinalsLowCardinality, Map]
630
630
631+
632+ it "can execute value_count aggregations" $ withTestEnv $ do
633+ _ <- insertData
634+ _ <- insertOther
635+ let ags = mkAggregations "user_count" (ValueCountAgg (FieldValueCount (FieldName "user"))) <>
636+ mkAggregations "bogus_count" (ValueCountAgg (FieldValueCount (FieldName "bogus")))
637+ let search = mkAggregateSearch Nothing ags
638+ let countPair k n = (k, object ["value" .= Number n])
639+ res <- searchTweets search
640+ liftIO $
641+ fmap aggregations res `shouldBe` Right (Just (M.fromList [ countPair "user_count" 2
642+ , countPair "bogus_count" 0
643+ ]))
631 -- Interaction of date serialization and date histogram aggregation is broken.
644 -- Interaction of date serialization and date histogram aggregation is broken.
632 -- it "returns date histogram aggregation results" $ withTestEnv $ do
645 -- it "returns date histogram aggregation results" $ withTestEnv $ do
633 -- _ <- insertData
646 -- _ <- insertData