@@ -225,6 +225,12 @@ module Database.Bloodhound.Types
225 , ValueCountAggregation(..)
225 , ValueCountAggregation(..)
226 , FilterAggregation(..)
226 , FilterAggregation(..)
227 , DateHistogramAggregation(..)
227 , DateHistogramAggregation(..)
228+ , DateRangeAggregation(..)
229+ , DateRangeAggRange(..)
231+ , DateMathAnchor(..)
232+ , DateMathModifier(..)
229 , Highlights(..)
235 , Highlights(..)
230 , FieldHighlight(..)
236 , FieldHighlight(..)
@@ -240,6 +246,7 @@ module Database.Bloodhound.Types
241 , TermsResult(..)
247 , TermsResult(..)
242 , DateHistogramResult(..)
248 , DateHistogramResult(..)
249+ , DateRangeResult(..)
245import Control.Applicative
252import Control.Applicative
@@ -261,7 +268,9 @@ import qualified Data.Map.Strict as M
261import Data.Maybe
268import Data.Maybe
262import Data.Text (Text)
269import Data.Text (Text)
263import qualified Data.Text as T
270import qualified Data.Text as T
271+import Data.Time.Calendar
264import Data.Time.Clock (UTCTime)
272import Data.Time.Clock (UTCTime)
273+import Data.Time.Clock.POSIX
265import qualified Data.Traversable as DT
274import qualified Data.Traversable as DT
266import Data.Typeable (Typeable)
275import Data.Typeable (Typeable)
267import qualified Data.Vector as V
276import qualified Data.Vector as V
@@ -773,6 +782,9 @@ newtype PhraseSlop = PhraseSlop Int deriving (Eq, Show, Generic, ToJSO
773newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
782newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
774newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
783newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
785+-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
786+newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
776{-| 'unpackId' is a silly convenience function that gets used once.
788{-| 'unpackId' is a silly convenience function that gets used once.
778unpackId :: DocId -> Text
790unpackId :: DocId -> Text
@@ -1408,7 +1420,8 @@ data Interval = Year
1408data Aggregation = TermsAgg TermsAggregation
1420data Aggregation = TermsAgg TermsAggregation
1409 | DateHistogramAgg DateHistogramAggregation
1421 | DateHistogramAgg DateHistogramAggregation
1410 | ValueCountAgg ValueCountAggregation
1422 | ValueCountAgg ValueCountAggregation
1411− | FilterAgg FilterAggregation deriving (Eq, Show)
1423+ | FilterAgg FilterAggregation
1424+ | DateRangeAgg DateRangeAggregation deriving (Eq, Show)
1414data TermsAggregation = TermsAggregation { term :: Either Text Text
1427data TermsAggregation = TermsAggregation { term :: Either Text Text
@@ -1434,6 +1447,36 @@ data DateHistogramAggregation = DateHistogramAggregation { dateField :: Fie
1434 , dateAggs :: Maybe Aggregations
1447 , dateAggs :: Maybe Aggregations
1435 } deriving (Eq, Show)
1448 } deriving (Eq, Show)
1451+data DateRangeAggregation = DateRangeAggregation { draField :: FieldName
1452+ , draFormat :: Maybe Text
1453+ , draRanges :: NonEmpty DateRangeAggRange
1454+ } deriving (Eq, Show)
1456+data DateRangeAggRange = DateRangeFrom DateMathExpr
1457+ | DateRangeTo DateMathExpr
1458+ | DateRangeFromAndTo DateMathExpr DateMathExpr deriving (Eq, Show)
1460+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
1461+data DateMathExpr = DateMathExpr DateMathAnchor [DateMathModifier] deriving (Eq, Show)
1464+-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
1465+data DateMathAnchor = DMNow
1466+ | DMDate Day deriving (Eq, Show)
1468+data DateMathModifier = AddTime Int DateMathUnit
1469+ | SubtractTime Int DateMathUnit
1470+ | RoundDownTo DateMathUnit deriving (Eq, Show)
1472+data DateMathUnit = DMYear
1478+ | DMSecond deriving (Eq, Show)
1437-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1480-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1438data ValueCountAggregation = FieldValueCount FieldName
1481data ValueCountAggregation = FieldValueCount FieldName
1439 | ScriptValueCount Script deriving (Eq, Show)
1482 | ScriptValueCount Script deriving (Eq, Show)
@@ -1537,6 +1580,39 @@ instance ToJSON Aggregation where
1537 toJSON (FilterAgg (FilterAggregation filt ags)) =
1580 toJSON (FilterAgg (FilterAggregation filt ags)) =
1538 omitNulls [ "filter" .= filt
1581 omitNulls [ "filter" .= filt
1539 , "aggs" .= ags]
1582 , "aggs" .= ags]
1583+ toJSON (DateRangeAgg a) = object [ "date_range" .= a
1586+instance ToJSON DateRangeAggregation where
1587+ toJSON DateRangeAggregation {..} =
1588+ omitNulls [ "field" .= draField
1589+ , "format" .= draFormat
1590+ , "ranges" .= toList draRanges
1593+instance ToJSON DateRangeAggRange where
1594+ toJSON (DateRangeFrom e) = object [ "from" .= e ]
1595+ toJSON (DateRangeTo e) = object [ "to" .= e ]
1596+ toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
1598+instance ToJSON DateMathExpr where
1599+ toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
1600+ where fmtA DMNow = "now"
1601+ fmtA (DMDate date) = case toGregorian date of
1602+ (y,m,d) -> showText y <> "-" <>
1603+ showText m <> "-" <>
1605+ fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
1606+ fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
1607+ fmtMod (RoundDownTo u) = "/" <> fmtU u
1613+ fmtU DMMinute = "m"
1614+ fmtU DMSecond = "s"
1541type AggregationResults = M.Map Text Value
1617type AggregationResults = M.Map Text Value
@@ -1557,6 +1633,14 @@ data DateHistogramResult = DateHistogramResult { dateKey :: Int
1557 , dateDocCount :: Int
1633 , dateDocCount :: Int
1558 , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1634 , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1636+data DateRangeResult = DateRangeResult { dateRangeKey :: Text
1637+ , dateRangeFrom :: Maybe UTCTime
1638+ , dateRangeFromAsString :: Maybe Text
1639+ , dateRangeTo :: Maybe UTCTime
1640+ , dateRangeToAsString :: Maybe Text
1641+ , dateRangeDocCount :: Int
1642+ , dateRangeAggs :: Maybe AggregationResults } deriving (Show, Eq)
1560toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1644toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1561toTerms t a = M.lookup t a >>= deserialize
1645toTerms t a = M.lookup t a >>= deserialize
1562 where deserialize = parseMaybe parseJSON
1646 where deserialize = parseMaybe parseJSON
@@ -1575,6 +1659,11 @@ instance BucketAggregation DateHistogramResult where
1575 docCount = dateDocCount
1659 docCount = dateDocCount
1576 aggs = dateHistogramAggs
1660 aggs = dateHistogramAggs
1662+instance BucketAggregation DateRangeResult where
1664+ docCount = dateRangeDocCount
1665+ aggs = dateRangeAggs
1578instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1667instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1579 parseJSON (Object v) = Bucket <$>
1668 parseJSON (Object v) = Bucket <$>
1580 v .: "buckets"
1669 v .: "buckets"
@@ -1595,6 +1684,22 @@ instance FromJSON DateHistogramResult where
1595 v .:? "aggregations"
1684 v .:? "aggregations"
1596 parseJSON _ = mempty
1685 parseJSON _ = mempty
1687+instance FromJSON DateRangeResult where
1688+ parseJSON = withObject "DateRangeResult" parse
1689+ where parse v = DateRangeResult <$>
1691+ (fmap posixMS <$> v .:? "from") <*>
1692+ v .:? "from_as_string" <*>
1693+ (fmap posixMS <$> v .:? "to") <*>
1694+ v .:? "to_as_string" <*>
1695+ v .: "doc_count" <*>
1696+ v .:? "aggregations"
1698+instance FromJSON POSIXMS where
1699+ parseJSON = withScientific "POSIXMS" (return . parse)
1700+ where parse n = let n' = truncate n :: Integer
1701+ in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
1598instance Monoid Filter where
1703instance Monoid Filter where
1599 mempty = IdentityFilter
1704 mempty = IdentityFilter
1600 mappend a b = AndFilter [a, b] defaultCache
1705 mappend a b = AndFilter [a, b] defaultCache
@@ -1890,7 +1995,7 @@ instance FromJSON Query where
1890 <|> idsQuery `taggedWith` "ids"
1995 <|> idsQuery `taggedWith` "ids"
1891 <|> queryQueryStringQuery `taggedWith` "query_string"
1996 <|> queryQueryStringQuery `taggedWith` "query_string"
1892 <|> queryMatchQuery `taggedWith` "match"
1997 <|> queryMatchQuery `taggedWith` "match"
1893− <|> queryMultiMatchQuery --TODO: is this a precedence issue?
1998+ <|> queryMultiMatchQuery
1894 <|> queryBoolQuery `taggedWith` "bool"
1999 <|> queryBoolQuery `taggedWith` "bool"
1895 <|> queryBoostingQuery `taggedWith` "boosting"
2000 <|> queryBoostingQuery `taggedWith` "boosting"
1896 <|> queryCommonTermsQuery `taggedWith` "common"
2001 <|> queryCommonTermsQuery `taggedWith` "common"