@@ -22,6 +22,10 @@ module Database.Bloodhound.Types
22 , mkBoolQuery
22 , mkBoolQuery
23 , mkRangeQuery
23 , mkRangeQuery
24 , mkQueryStringQuery
24 , mkQueryStringQuery
25 , Version(..)
29 , Version(..)
26 , Status(..)
30 , Status(..)
27 , Existence(..)
31 , Existence(..)
@@ -142,15 +146,29 @@ module Database.Bloodhound.Types
142 , FieldOrFields(..)
146 , FieldOrFields(..)
143 , Monoid(..)
147 , Monoid(..)
144 , ToJSON(..)
148 , ToJSON(..)
156+ , AggregationResults
158+ , BucketAggregation(..)
160+ , DateHistogramResult
147import Control.Applicative
163import Control.Applicative
148import Data.Aeson
164import Data.Aeson
165+import Data.Aeson.Types (parseMaybe)
149import qualified Data.ByteString.Lazy.Char8 as L
166import qualified Data.ByteString.Lazy.Char8 as L
150import Data.List (nub)
167import Data.List (nub)
151import Data.List.NonEmpty (NonEmpty(..))
168import Data.List.NonEmpty (NonEmpty(..))
152import Data.Monoid
169import Data.Monoid
153import Data.Text (Text)
170import Data.Text (Text)
171+import qualified Data.Map.Strict as M
154import qualified Data.Text as T
172import qualified Data.Text as T
155import Data.Time.Clock (UTCTime)
173import Data.Time.Clock (UTCTime)
156import GHC.Generics (Generic)
174import GHC.Generics (Generic)
@@ -474,6 +492,7 @@ type Size = Int
474data Search = Search { queryBody :: Maybe Query
492data Search = Search { queryBody :: Maybe Query
475 , filterBody :: Maybe Filter
493 , filterBody :: Maybe Filter
476 , sortBody :: Maybe Sort
494 , sortBody :: Maybe Sort
495+ , aggBody :: Maybe Aggregations
477 -- default False
496 -- default False
478 , trackSortScores :: TrackSortScores
497 , trackSortScores :: TrackSortScores
479 , from :: From
498 , from :: From
@@ -913,10 +932,11 @@ data DistanceRange =
913 , distanceTo :: Distance } deriving (Eq, Show)
932 , distanceTo :: Distance } deriving (Eq, Show)
915data FromJSON a => SearchResult a =
934data FromJSON a => SearchResult a =
916− SearchResult { took :: Int
935+ SearchResult { took :: Int
917− , timedOut :: Bool
936+ , timedOut :: Bool
918− , shards :: ShardResult
937+ , shards :: ShardResult
919− , searchHits :: SearchHits a } deriving (Eq, Show)
938+ , searchHits :: SearchHits a
939+ , aggregations :: Maybe AggregationResults } deriving (Eq, Show)
921type Score = Double
941type Score = Double
@@ -940,6 +960,188 @@ data ShardResult =
940showText :: Show a => a -> Text
960showText :: Show a => a -> Text
941showText = T.pack . show
961showText = T.pack . show
963+type Aggregations = M.Map Text Aggregation
965+data TermOrder = TermOrder{ termSortField :: Text
966+ , termSortOrder :: SortOrder } deriving (Eq, Show)
968+data TermInclusion = TermInclusion Text
969+ | TermPattern { pattern :: Text
970+ , flags :: Text } deriving (Eq, Show)
972+data CollectionMode = BreadthFirst
973+ | DepthFirst deriving (Eq, Show)
975+data ExecutionHint = GlobalOrdinals
976+ | GlobalOrdinalsHash
977+ | GlobalOrdinalsLowCardinality
978+ | Map deriving(Eq, Show)
980+data TimeInterval = Years
987+ | Seconds deriving (Eq)
997+ | FractionalInterval { fraction :: Float
998+ , interval :: TimeInterval } deriving (Eq, Show)
1000+data Aggregation = TermsAggregation { term :: Text
1001+ , termScript :: Maybe Text
1002+ , termInclude :: Maybe TermInclusion
1003+ , termExclude :: Maybe TermInclusion
1004+ , termOrder :: Maybe TermOrder
1005+ , termMinDocCount :: Maybe Int
1006+ , termSize :: Maybe Int
1007+ , termShardSize :: Maybe Int
1008+ , termCollectMode :: Maybe CollectionMode
1009+ , termAggs :: Maybe Aggregations
1011+ | DateHistogramAggregation { dateField :: FieldName
1012+ , dateInterval :: Interval
1013+ , dateFormat :: Maybe Text
1014+ , datePreZone :: Maybe Text
1015+ , datePostZone :: Maybe Text
1016+ , datePreOffset :: Maybe Text
1017+ , datePostOffset :: Maybe Text
1018+ , dateAggs :: Maybe Aggregations
1019+ } deriving (Eq, Show)
1021+mkTermsAggregation :: Text -> Aggregation
1022+mkTermsAggregation t = TermsAggregation t Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1024+mkDateHistogram :: FieldName -> Interval -> Aggregation
1025+mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
1027+instance ToJSON TermOrder where
1028+ toJSON (TermOrder termSortField termSortOrder) = object [termSortField .= termSortOrder]
1030+instance ToJSON TermInclusion where
1031+ toJSON (TermInclusion x) = toJSON x
1032+ toJSON (TermPattern pattern flags) = omitNulls [ "pattern" .= pattern,
1035+instance ToJSON CollectionMode where
1036+ toJSON BreadthFirst = "breadth_first"
1037+ toJSON DepthFirst = "depth_first"
1039+instance ToJSON ExecutionHint where
1040+ toJSON GlobalOrdinals = "global_ordinals"
1041+ toJSON GlobalOrdinalsHash = "global_ordinals_hash"
1042+ toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
1045+instance ToJSON Interval where
1046+ toJSON Year = "year"
1047+ toJSON Quarter = "quarter"
1048+ toJSON Month = "month"
1049+ toJSON Week = "week"
1051+ toJSON Hour = "hour"
1052+ toJSON Minute = "minute"
1053+ toJSON Second = "second"
1054+ toJSON (FractionalInterval fraction interval) = toJSON $ (show fraction) ++ (show interval)
1056+instance Show TimeInterval where
1058+ show Quarters = "q"
1066+instance ToJSON Aggregation where
1067+ toJSON (TermsAggregation term termScript termInclude termExclude termOrder termMinDocCount termSize termShardSize termCollectMode termAggs) =
1068+ omitNulls ["terms" .= (omitNulls [ "field" .= (toJSON $ term) ,
1069+ "script" .= (toJSON termScript),
1070+ "include" .= (toJSON termInclude),
1071+ "exclude" .= (toJSON termExclude),
1072+ "order" .= (toJSON termOrder),
1073+ "min_doc_count" .= (toJSON termMinDocCount),
1074+ "size" .= (toJSON termSize),
1075+ "shard_size" .= (toJSON termShardSize),
1076+ "collect_mode" .= (toJSON termCollectMode)
1078+ "aggs" .= (toJSON termAggs) ]
1080+ toJSON (DateHistogramAggregation dateField dateInterval dateFormat datePreZone datePostZone datePreOffset datePostOffset dateAggs) =
1081+ omitNulls ["date_histogram" .= (omitNulls [ "field" .= (toJSON dateField),
1082+ "interval" .= (toJSON dateInterval),
1083+ "format" .= (toJSON dateFormat),
1084+ "pre_zone" .= (toJSON datePreZone),
1085+ "post_zone" .= (toJSON datePostZone),
1086+ "pre_offset" .= (toJSON datePreOffset),
1087+ "post_offset" .= (toJSON datePostOffset)
1089+ "aggs" .= (toJSON dateAggs) ]
1091+type AggregationResults = M.Map Text Value
1093+class BucketAggregation a where
1095+ docCount :: a -> Int
1096+ aggs :: a -> Maybe AggregationResults
1099+data (FromJSON a, BucketAggregation a) => Bucket a = Bucket { buckets :: [a]} deriving (Show)
1101+data TermsResult = TermsResult { termKey :: Text
1102+ , termsDocCount :: Int
1103+ , termsAggs :: Maybe AggregationResults } deriving (Show)
1105+data DateHistogramResult = DateHistogramResult { dateKey :: Int
1106+ , dateKeyStr :: Text
1107+ , dateDocCount :: Int
1108+ , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1110+toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1111+toTerms t a = M.lookup t a >>= deserialize
1112+ where deserialize = parseMaybe parseJSON
1114+toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
1115+toDateHistogram t a = M.lookup t a >>= deserialize
1116+ where deserialize = parseMaybe parseJSON
1118+instance BucketAggregation TermsResult where
1120+ docCount = termsDocCount
1123+instance BucketAggregation DateHistogramResult where
1125+ docCount = dateDocCount
1126+ aggs = dateHistogramAggs
1128+instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1129+ parseJSON (Object v) = Bucket <$>
1131+ parseJSON _ = mempty
1133+instance FromJSON TermsResult where
1134+ parseJSON (Object v) = TermsResult <$>
1136+ v .: "doc_count" <*>
1137+ v .:? "aggregations"
1139+instance FromJSON DateHistogramResult where
1140+ parseJSON (Object v) = DateHistogramResult <$>
1142+ v .: "key_as_string" <*>
1143+ v .: "doc_count" <*>
1144+ v .:? "aggregations"
944instance Monoid Filter where
1146instance Monoid Filter where
945 mempty = IdentityFilter
1147 mempty = IdentityFilter
@@ -1533,10 +1735,11 @@ instance (FromJSON a) => FromJSON (EsResult a) where
1535instance ToJSON Search where
1737instance ToJSON Search where
1536− toJSON (Search query sFilter sort sTrackSortScores sFrom sSize) =
1738+ toJSON (Search query sFilter sort aggs sTrackSortScores sFrom sSize) =
1537 omitNulls [ "query" .= query
1739 omitNulls [ "query" .= query
1538 , "filter" .= sFilter
1740 , "filter" .= sFilter
1539 , "sort" .= sort
1741 , "sort" .= sort
1742+ , "aggregations" .= aggs
1540 , "from" .= sFrom
1743 , "from" .= sFrom
1541 , "size" .= sSize
1744 , "size" .= sSize
1542 , "track_scores" .= sTrackSortScores ]
1745 , "track_scores" .= sTrackSortScores ]
@@ -1674,10 +1877,11 @@ instance ToJSON BoolMatch where
1675instance (FromJSON a) => FromJSON (SearchResult a) where
1878instance (FromJSON a) => FromJSON (SearchResult a) where
1676 parseJSON (Object v) = SearchResult <$>
1879 parseJSON (Object v) = SearchResult <$>
1677− v .: "took" <*>
1880+ v .: "took" <*>
1678− v .: "timed_out" <*>
1881+ v .: "timed_out" <*>
1679− v .: "_shards" <*>
1882+ v .: "_shards" <*>
1680− v .: "hits"
1883+ v .: "hits" <*>
1884+ v .:? "aggregations"
1681 parseJSON _ = empty
1885 parseJSON _ = empty
1683instance (FromJSON a) => FromJSON (SearchHits a) where
1887instance (FromJSON a) => FromJSON (SearchHits a) where