git.iko.soy/typeable/bloodhound
on master help sign in
Merge pull request #82 from Soostone/date-range-agg
0b116a0Chris Allen
bloodhound.cabal+1−1
@@ -71,7 +71,7 @@ test-suite tests
71 mtl,
71 mtl,
72 quickcheck-properties,
72 quickcheck-properties,
73 derive,
73 derive,
74− quickcheck-instances
74+ errors
75 default-language: Haskell2010
75 default-language: Haskell2010
76
76
77test-suite doctests
77test-suite doctests
src/Database/Bloodhound/Client.hs+89−27
@@ -24,6 +24,8 @@ module Database.Bloodhound.Client
24 withBH
24 withBH
25 , createIndex
25 , createIndex
26 , deleteIndex
26 , deleteIndex
27+ , updateIndexSettings
28+ , getIndexSettings
27 , indexExists
29 , indexExists
28 , openIndex
30 , openIndex
29 , closeIndex
31 , closeIndex
@@ -42,6 +44,8 @@ module Database.Bloodhound.Client
42 , searchByIndex
44 , searchByIndex
43 , searchByType
45 , searchByType
44 , scanSearch
46 , scanSearch
47+ , getInitialScroll
48+ , advanceScroll
45 , refreshIndex
49 , refreshIndex
46 , mkSearch
50 , mkSearch
47 , mkAggregateSearch
51 , mkAggregateSearch
@@ -70,14 +74,16 @@ import Data.Aeson
70import Data.ByteString.Lazy.Builder
74import Data.ByteString.Lazy.Builder
71import qualified Data.ByteString.Lazy.Char8 as L
75import qualified Data.ByteString.Lazy.Char8 as L
72import Data.Foldable (toList)
76import Data.Foldable (toList)
77+import qualified Data.HashMap.Strict as HM
73import Data.Ix
78import Data.Ix
74−import qualified Data.List as LS (filter)
79+import qualified Data.List as LS (filter, foldl')
75import Data.List.NonEmpty (NonEmpty (..))
80import Data.List.NonEmpty (NonEmpty (..))
76import Data.Maybe (fromMaybe, isJust)
81import Data.Maybe (fromMaybe, isJust)
77import Data.Monoid
82import Data.Monoid
78import Data.Text (Text)
83import Data.Text (Text)
79import qualified Data.Text as T
84import qualified Data.Text as T
80import qualified Data.Text.Encoding as T
85import qualified Data.Text.Encoding as T
86+import Data.Time.Clock
81import qualified Data.Vector as V
87import qualified Data.Vector as V
82import Network.HTTP.Client
88import Network.HTTP.Client
83import qualified Network.HTTP.Types.Method as NHTM
89import qualified Network.HTTP.Types.Method as NHTM
@@ -261,6 +267,7 @@ createIndex indexSettings (IndexName indexName) =
261 where url = joinPath [indexName]
267 where url = joinPath [indexName]
262 body = Just $ encode indexSettings
268 body = Just $ encode indexSettings
263
269
270+
264-- | 'deleteIndex' will delete an index given a 'Server', and an 'IndexName'.
271-- | 'deleteIndex' will delete an index given a 'Server', and an 'IndexName'.
265--
272--
266-- >>> _ <- runBH' $ createIndex defaultIndexSettings (IndexName "didimakeanindex")
273-- >>> _ <- runBH' $ createIndex defaultIndexSettings (IndexName "didimakeanindex")
@@ -273,6 +280,35 @@ deleteIndex :: MonadBH m => IndexName -> m Reply
273deleteIndex (IndexName indexName) =
280deleteIndex (IndexName indexName) =
274 delete =<< joinPath [indexName]
281 delete =<< joinPath [indexName]
275
282
283+-- | 'updateIndexSettings' will apply a non-empty list of setting updates to an index
284+--
285+-- >>> _ <- runBH' $ createIndex defaultIndexSettings (IndexName "unconfiguredindex")
286+-- >>> response <- runBH' $ updateIndexSettings (BlocksWrite False :| []) (IndexName "unconfiguredindex")
287+-- >>> respIsTwoHunna response
288+-- True
289+updateIndexSettings :: MonadBH m => NonEmpty UpdatableIndexSetting -> IndexName -> m Reply
290+updateIndexSettings updates (IndexName indexName) =
291+ bindM2 put url (return body)
292+ where url = joinPath [indexName, "_settings"]
293+ body = Just (encode jsonBody)
294+ jsonBody = Object (deepMerge [u | Object u <- toJSON <$> toList updates])
295+
296+
297+getIndexSettings :: (MonadBH m, MonadThrow m) => IndexName
298+ -> m (Either EsError IndexSettingsSummary)
299+getIndexSettings (IndexName indexName) = do
300+ parseEsResponse =<< get =<< url
301+ where url = joinPath [indexName, "_settings"]
302+
303+
304+deepMerge :: [Object] -> Object
305+deepMerge = LS.foldl' go mempty
306+ where go acc = LS.foldl' go' acc . HM.toList
307+ go' acc (k, v) = HM.insertWith merge k v acc
308+ merge (Object a) (Object b) = Object (deepMerge [a, b])
309+ merge _ b = b
310+
311+
276statusCodeIs :: Int -> Reply -> Bool
312statusCodeIs :: Int -> Reply -> Bool
277statusCodeIs n resp = NHTS.statusCode (responseStatus resp) == n
313statusCodeIs n resp = NHTS.statusCode (responseStatus resp) == n
278
314
@@ -288,8 +324,10 @@ existentialQuery url = do
288-- | Tries to parse a response body as the expected type @a@ and
324-- | Tries to parse a response body as the expected type @a@ and
289-- failing that tries to parse it as an EsError. All well-formed, JSON
325-- failing that tries to parse it as an EsError. All well-formed, JSON
290-- responses from elasticsearch should fall into these two
326-- responses from elasticsearch should fall into these two
291−-- categories. If they don't, a 'StatusCodeException' will be thrown.
327+-- categories. If they don't, a 'EsProtocolException' will be
328+-- thrown. If you encounter this, please report the full body it
329+-- reports along with your ElasticSearch verison.
292−parseEsResponse :: (MonadBH m, MonadThrow m, FromJSON a) => Reply
330+parseEsResponse :: (MonadThrow m, FromJSON a) => Reply
293 -> m (Either EsError a)
331 -> m (Either EsError a)
294parseEsResponse reply
332parseEsResponse reply
295 | respIsTwoHunna reply = case eitherDecode body of
333 | respIsTwoHunna reply = case eitherDecode body of
@@ -297,14 +335,11 @@ parseEsResponse reply
297 Left _ -> tryParseError
335 Left _ -> tryParseError
298 | otherwise = tryParseError
336 | otherwise = tryParseError
299 where body = responseBody reply
337 where body = responseBody reply
300− stat = responseStatus reply
301− hdrs = responseHeaders reply
302− cookies = responseCookieJar reply
303 tryParseError = case eitherDecode body of
338 tryParseError = case eitherDecode body of
304 Right e -> return (Left e)
339 Right e -> return (Left e)
305 -- this case should not be possible
340 -- this case should not be possible
306 Left _ -> explode
341 Left _ -> explode
307− explode = throwM (StatusCodeException stat hdrs cookies)
342+ explode = throwM (EsProtocolException body)
308
343
309-- | 'indexExists' enables you to check if an index exists. Returns 'Bool'
344-- | 'indexExists' enables you to check if an index exists. Returns 'Bool'
310-- in IO
345-- in IO
@@ -608,8 +643,12 @@ searchByType (IndexName indexName)
608 (MappingName mappingName) = bindM2 dispatchSearch url . return
643 (MappingName mappingName) = bindM2 dispatchSearch url . return
609 where url = joinPath [indexName, mappingName, "_search"]
644 where url = joinPath [indexName, mappingName, "_search"]
610
645
646+-- | For a given scearch, request a scroll for efficient streaming of
647+-- search results. Note that the search is put into 'SearchTypeScan'
648+-- mode and thus results will not be sorted. Combine this with
649+-- 'advanceScroll' to efficiently stream through the full result set
611−scanSearch' :: MonadBH m => IndexName -> MappingName -> Search -> m (Maybe ScrollId)
650+getInitialScroll :: MonadBH m => IndexName -> MappingName -> Search -> m (Maybe ScrollId)
612−scanSearch' (IndexName indexName) (MappingName mappingName) search = do
651+getInitialScroll (IndexName indexName) (MappingName mappingName) search = do
613 let url = joinPath [indexName, mappingName, "_search"]
652 let url = joinPath [indexName, mappingName, "_search"]
614 search' = search { searchType = SearchTypeScan }
653 search' = search { searchType = SearchTypeScan }
615 resp' <- bindM2 dispatchSearch url (return search')
654 resp' <- bindM2 dispatchSearch url (return search')
@@ -617,31 +656,54 @@ scanSearch' (IndexName indexName) (MappingName mappingName) search = do
617 msid = maybe Nothing scrollId msr
656 msid = maybe Nothing scrollId msr
618 return msid
657 return msid
619
658
620−scroll' :: (FromJSON a, MonadBH m) => Maybe ScrollId -> m ([Hit a], Maybe ScrollId)
659+scroll' :: (FromJSON a, MonadBH m, MonadThrow m) => Maybe ScrollId -> m ([Hit a], Maybe ScrollId)
621scroll' Nothing = return ([], Nothing)
660scroll' Nothing = return ([], Nothing)
622scroll' (Just sid) = do
661scroll' (Just sid) = do
662+ res <- advanceScroll sid 60
663+ case res of
664+ Right SearchResult {..} -> return (hits searchHits, scrollId)
665+ Left _ -> return ([], Nothing)
666+
667+-- | Use the given scroll to fetch the next page of documents. If
668+-- there are still further pages, there will be a value in the
669+-- 'scrollId' field of the 'SearchResult'
670+advanceScroll
671+ :: ( FromJSON a
672+ , MonadBH m
673+ , MonadThrow m
674+ )
675+ => ScrollId
676+ -> NominalDiffTime
677+ -- ^ How long should the snapshot of data be kept around? This timeout is updated every time 'advanceScroll' is used, so don't feel the need to set it to the entire duration of your search processing. Note that durations < 1s will be rounded up. Also note that 'NominalDiffTime' is an instance of Num so literals like 60 will be interpreted as seconds. 60s is a reasonable default.
678+ -> m (Either EsError (SearchResult a))
679+advanceScroll (ScrollId sid) scroll = do
623− url <- joinPath ["_search/scroll?scroll=1m"]
680+ url <- joinPath ["_search/scroll?scroll=" <> scrollTime]
624− resp' <- post url (Just . L.fromStrict $ T.encodeUtf8 sid)
681+ parseEsResponse =<< post url (Just . L.fromStrict $ T.encodeUtf8 sid)
625− let msr = decode' $ responseBody resp' :: FromJSON a => Maybe (SearchResult a)
682+ where scrollTime = showText secs <> "s"
626− resp = case msr of
683+ secs :: Integer
627− Just sr -> (hits $ searchHits sr, scrollId sr)
684+ secs = round scroll
628− _ -> ([], Nothing)
685+
629− return resp
630−
631−simpleAccumilator :: (FromJSON a, MonadBH m) => [Hit a] -> ([Hit a], Maybe ScrollId) -> m ([Hit a], Maybe ScrollId)
686+simpleAccumulator :: (FromJSON a, MonadBH m, MonadThrow m) => [Hit a] -> ([Hit a], Maybe ScrollId) -> m ([Hit a], Maybe ScrollId)
632−simpleAccumilator oldHits (newHits, Nothing) = return (oldHits ++ newHits, Nothing)
687+simpleAccumulator oldHits (newHits, Nothing) = return (oldHits ++ newHits, Nothing)
633−simpleAccumilator oldHits ([], _) = return (oldHits, Nothing)
688+simpleAccumulator oldHits ([], _) = return (oldHits, Nothing)
634−simpleAccumilator oldHits (newHits, msid) = do
689+simpleAccumulator oldHits (newHits, msid) = do
635 (newHits', msid') <- scroll' msid
690 (newHits', msid') <- scroll' msid
636− simpleAccumilator (oldHits ++ newHits) (newHits', msid')
691+ simpleAccumulator (oldHits ++ newHits) (newHits', msid')
637
692
638-- | 'scanSearch' uses the 'scan&scroll' API of elastic,
693-- | 'scanSearch' uses the 'scan&scroll' API of elastic,
639−-- for a given 'IndexName' and 'MappingName',
694+-- for a given 'IndexName' and 'MappingName'. Note that this will
695+-- consume the entire search result set and will be doing O(n) list
696+-- appends so this may not be suitable for large result sets. In that
697+-- case, 'getInitialScroll' and 'advanceScroll' are good low level
698+-- tools. You should be able to hook them up trivially to conduit,
699+-- pipes, or your favorite streaming IO abstraction of choice. Note
700+-- that ordering on the search would destroy performance and thus is
701+-- ignored.
640−scanSearch :: (FromJSON a, MonadBH m) => IndexName -> MappingName -> Search -> m [Hit a]
702+scanSearch :: (FromJSON a, MonadBH m, MonadThrow m) => IndexName -> MappingName -> Search -> m [Hit a]
641scanSearch indexName mappingName search = do
703scanSearch indexName mappingName search = do
642− msid <- scanSearch' indexName mappingName search
704+ msid <- getInitialScroll indexName mappingName search
643 (hits, msid') <- scroll' msid
705 (hits, msid') <- scroll' msid
644− (totalHits, _) <- simpleAccumilator [] (hits, msid')
706+ (totalHits, _) <- simpleAccumulator [] (hits, msid')
645 return totalHits
707 return totalHits
646
708
647-- | 'mkSearch' is a helper function for defaulting additional fields of a 'Search'
709-- | 'mkSearch' is a helper function for defaulting additional fields of a 'Search'
src/Database/Bloodhound/Types.hs+607−135
@@ -58,12 +58,23 @@ module Database.Bloodhound.Types
58 , Existence(..)
58 , Existence(..)
59 , NullValue(..)
59 , NullValue(..)
60 , IndexSettings(..)
60 , IndexSettings(..)
61+ , UpdatableIndexSetting(..)
62+ , IndexSettingsSummary(..)
63+ , AllocationPolicy(..)
64+ , ReplicaBounds(..)
65+ , Bytes(..)
66+ , FSType(..)
67+ , InitialShardCount(..)
68+ , NodeAttrFilter(..)
69+ , NodeAttrName(..)
70+ , CompoundFormat(..)
61 , IndexTemplate(..)
71 , IndexTemplate(..)
62 , Server(..)
72 , Server(..)
63 , Reply
73 , Reply
64 , EsResult(..)
74 , EsResult(..)
65 , EsResultFound(..)
75 , EsResultFound(..)
66 , EsError(..)
76 , EsError(..)
77+ , EsProtocolException(..)
67 , IndexAlias(..)
78 , IndexAlias(..)
68 , IndexAliasName(..)
79 , IndexAliasName(..)
69 , IndexAliasAction(..)
80 , IndexAliasAction(..)
@@ -83,7 +94,7 @@ module Database.Bloodhound.Types
83 , Search(..)
94 , Search(..)
84 , SearchType(..)
95 , SearchType(..)
85 , SearchResult(..)
96 , SearchResult(..)
86− , ScrollId
97+ , ScrollId(..)
87 , SearchHits(..)
98 , SearchHits(..)
88 , TrackSortScores
99 , TrackSortScores
89 , From(..)
100 , From(..)
@@ -225,6 +236,12 @@ module Database.Bloodhound.Types
225 , ValueCountAggregation(..)
236 , ValueCountAggregation(..)
226 , FilterAggregation(..)
237 , FilterAggregation(..)
227 , DateHistogramAggregation(..)
238 , DateHistogramAggregation(..)
239+ , DateRangeAggregation(..)
240+ , DateRangeAggRange(..)
241+ , DateMathExpr(..)
242+ , DateMathAnchor(..)
243+ , DateMathModifier(..)
244+ , DateMathUnit(..)
228
245
229 , Highlights(..)
246 , Highlights(..)
230 , FieldHighlight(..)
247 , FieldHighlight(..)
@@ -240,6 +257,7 @@ module Database.Bloodhound.Types
240
257
241 , TermsResult(..)
258 , TermsResult(..)
242 , DateHistogramResult(..)
259 , DateHistogramResult(..)
260+ , DateRangeResult(..)
243 ) where
261 ) where
244
262
245import Control.Applicative
263import Control.Applicative
@@ -261,7 +279,9 @@ import qualified Data.Map.Strict as M
261import Data.Maybe
279import Data.Maybe
262import Data.Text (Text)
280import Data.Text (Text)
263import qualified Data.Text as T
281import qualified Data.Text as T
264−import Data.Time.Clock (UTCTime)
282+import Data.Time.Calendar
283+import Data.Time.Clock (NominalDiffTime, UTCTime)
284+import Data.Time.Clock.POSIX
265import qualified Data.Traversable as DT
285import qualified Data.Traversable as DT
266import Data.Typeable (Typeable)
286import Data.Typeable (Typeable)
267import qualified Data.Vector as V
287import qualified Data.Vector as V
@@ -349,7 +369,7 @@ data Status = Status { ok :: Maybe Bool
349 , status :: Int
369 , status :: Int
350 , name :: Text
370 , name :: Text
351 , version :: Version
371 , version :: Version
352− , tagline :: Text } deriving (Eq, Show)
372+ , tagline :: Text } deriving (Eq, Show, Generic)
353
373
354{-| 'IndexSettings' is used to configure the shards and replicas when you create
374{-| 'IndexSettings' is used to configure the shards and replicas when you create
355 an Elasticsearch Index.
375 an Elasticsearch Index.
@@ -359,12 +379,111 @@ data Status = Status { ok :: Maybe Bool
359
379
360data IndexSettings =
380data IndexSettings =
361 IndexSettings { indexShards :: ShardCount
381 IndexSettings { indexShards :: ShardCount
362− , indexReplicas :: ReplicaCount } deriving (Eq, Show)
382+ , indexReplicas :: ReplicaCount } deriving (Eq, Show, Generic, Typeable)
363
383
364{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
384{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
365defaultIndexSettings :: IndexSettings
385defaultIndexSettings :: IndexSettings
366defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
386defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
367
387
388+{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
389+
390+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
391+-}
392+data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
393+ -- ^ The number of replicas each shard has.
394+ | AutoExpandReplicas ReplicaBounds
395+ | BlocksReadOnly Bool
396+ -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
397+ | BlocksRead Bool
398+ -- ^ Set to True to disable read operations against the index.
399+ | BlocksWrite Bool
400+ -- ^ Set to True to disable write operations against the index.
401+ | BlocksMetaData Bool
402+ -- ^ Set to True to disable metadata operations against the index.
403+ | RefreshInterval NominalDiffTime
404+ -- ^ The async refresh interval of a shard
405+ | IndexConcurrency Int
406+ | FailOnMergeFailure Bool
407+ | TranslogFlushThresholdOps Int
408+ -- ^ When to flush on operations.
409+ | TranslogFlushThresholdSize Bytes
410+ -- ^ When to flush based on translog (bytes) size.
411+ | TranslogFlushThresholdPeriod NominalDiffTime
412+ -- ^ When to flush based on a period of not flushing.
413+ | TranslogDisableFlush Bool
414+ -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
415+ | CacheFilterMaxSize (Maybe Bytes)
416+ -- ^ The maximum size of filter cache (per segment in shard).
417+ | CacheFilterExpire (Maybe NominalDiffTime)
418+ -- ^ The expire after access time for filter cache.
419+ | GatewaySnapshotInterval NominalDiffTime
420+ -- ^ The gateway snapshot interval (only applies to shared gateways).
421+ | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
422+ -- ^ A node matching any rule will be allowed to host shards from the index.
423+ | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
424+ -- ^ A node matching any rule will NOT be allowed to host shards from the index.
425+ | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
426+ -- ^ Only nodes matching all rules will be allowed to host shards from the index.
427+ | RoutingAllocationEnable AllocationPolicy
428+ -- ^ Enables shard allocation for a specific index.
429+ | RoutingAllocationShardsPerNode ShardCount
430+ -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
431+ | RecoveryInitialShards InitialShardCount
432+ -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
433+ | GCDeletes NominalDiffTime
434+ | TTLDisablePurge Bool
435+ -- ^ Disables temporarily the purge of expired docs.
436+ | TranslogFSType FSType
437+ | IndexCompoundFormat CompoundFormat
438+ | IndexCompoundOnFlush Bool
439+ | WarmerEnabled Bool
440+ deriving (Eq, Show, Generic, Typeable)
441+
442+data AllocationPolicy = AllocAll
443+ -- ^ Allows shard allocation for all shards.
444+ | AllocPrimaries
445+ -- ^ Allows shard allocation only for primary shards.
446+ | AllocNewPrimaries
447+ -- ^ Allows shard allocation only for primary shards for new indices.
448+ | AllocNone
449+ -- ^ No shard allocation is allowed
450+ deriving (Eq, Show, Generic, Typeable)
451+
452+data ReplicaBounds = ReplicasBounded Int Int
453+ | ReplicasLowerBounded Int
454+ | ReplicasUnbounded
455+ deriving (Eq, Show, Generic, Typeable)
456+
457+newtype Bytes = Bytes Int deriving (Eq, Show, Generic, Typeable, Ord, ToJSON, FromJSON)
458+
459+data FSType = FSSimple
460+ | FSBuffered deriving (Eq, Show, Generic, Typeable, Ord)
461+
462+data InitialShardCount = QuorumShards
463+ | QuorumMinus1Shards
464+ | FullShards
465+ | FullMinus1Shards
466+ | ExplicitShards Int
467+ deriving (Eq, Show, Generic, Typeable)
468+
469+data NodeAttrFilter = NodeAttrFilter { nodeAttrFilterName :: NodeAttrName
470+ , nodeAttrFilterValues :: NonEmpty Text}
471+ deriving (Eq, Show, Generic, Ord)
472+
473+newtype NodeAttrName = NodeAttrName Text deriving (Eq, Show, Ord, Generic, Typeable)
474+
475+data CompoundFormat = CompoundFileFormat Bool
476+ | MergeSegmentVsTotalIndex Double
477+ -- ^ percentage between 0 and 1 where 0 is false, 1 is true
478+ deriving (Eq, Show, Generic, Typeable)
479+
480+newtype NominalDiffTimeJSON = NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
481+
482+data IndexSettingsSummary = IndexSettingsSummary { sSummaryIndexName :: IndexName
483+ , sSummaryFixedSettings :: IndexSettings
484+ , sSummaryUpdateable :: [UpdatableIndexSetting]}
485+ deriving (Eq, Show, Generic, Typeable)
486+
368{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
487{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
369type Reply = Network.HTTP.Client.Response L.ByteString
488type Reply = Network.HTTP.Client.Response L.ByteString
370type Method = NHTM.Method
489type Method = NHTM.Method
@@ -373,7 +492,7 @@ type Method = NHTM.Method
373
492
374 <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
493 <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
375-}
494-}
376−data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show)
495+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show, Generic, Typeable)
377
496
378data FieldType = GeoPointType
497data FieldType = GeoPointType
379 | GeoShapeType
498 | GeoShapeType
@@ -381,10 +500,10 @@ data FieldType = GeoPointType
381 | IntegerType
500 | IntegerType
382 | LongType
501 | LongType
383 | ShortType
502 | ShortType
384− | ByteType deriving (Eq, Show)
503+ | ByteType deriving (Eq, Show, Generic, Typeable)
385
504
386data FieldDefinition =
505data FieldDefinition =
387− FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
506+ FieldDefinition { fieldType :: FieldType } deriving (Eq, Show, Generic, Typeable)
388
507
389{-| An 'IndexTemplate' defines a template that will automatically be
508{-| An 'IndexTemplate' defines a template that will automatically be
390 applied to new indices created. The templates include both
509 applied to new indices created. The templates include both
@@ -402,7 +521,7 @@ data IndexTemplate =
402
521
403data MappingField =
522data MappingField =
404 MappingField { mappingFieldName :: FieldName
523 MappingField { mappingFieldName :: FieldName
405− , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
524+ , fieldDefinition :: FieldDefinition } deriving (Eq, Show, Generic, Typeable)
406
525
407{-| Support for type reification of 'Mapping's is currently incomplete, for
526{-| Support for type reification of 'Mapping's is currently incomplete, for
408 now the mapping API verbiage expects a 'ToJSON'able blob.
527 now the mapping API verbiage expects a 'ToJSON'able blob.
@@ -412,7 +531,7 @@ data MappingField =
412 and keeping different kinds of documents separated if possible.
531 and keeping different kinds of documents separated if possible.
413-}
532-}
414data Mapping = Mapping { typeName :: TypeName
533data Mapping = Mapping { typeName :: TypeName
415− , mappingFields :: [MappingField] } deriving (Eq, Show)
534+ , mappingFields :: [MappingField] } deriving (Eq, Show, Generic, Typeable)
416
535
417{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
536{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
418 operation index, create, delete, and update. 'BulkIndex' behaves like an
537 operation index, create, delete, and update. 'BulkIndex' behaves like an
@@ -424,7 +543,7 @@ data BulkOperation =
424 BulkIndex IndexName MappingName DocId Value
543 BulkIndex IndexName MappingName DocId Value
425 | BulkCreate IndexName MappingName DocId Value
544 | BulkCreate IndexName MappingName DocId Value
426 | BulkDelete IndexName MappingName DocId
545 | BulkDelete IndexName MappingName DocId
427− | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Show)
546+ | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Show, Generic, Typeable)
428
547
429{-| 'EsResult' describes the standard wrapper JSON document that you see in
548{-| 'EsResult' describes the standard wrapper JSON document that you see in
430 successful Elasticsearch lookups or lookups that couldn't find the document.
549 successful Elasticsearch lookups or lookups that couldn't find the document.
@@ -432,55 +551,65 @@ data BulkOperation =
432data EsResult a = EsResult { _index :: Text
551data EsResult a = EsResult { _index :: Text
433 , _type :: Text
552 , _type :: Text
434 , _id :: Text
553 , _id :: Text
435− , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show)
554+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show, Generic, Typeable)
436
555
437{-| 'EsResultFound' contains the document and its metadata inside of an
556{-| 'EsResultFound' contains the document and its metadata inside of an
438 'EsResult' when the document was successfully found.
557 'EsResult' when the document was successfully found.
439-}
558-}
440data EsResultFound a = EsResultFound { _version :: DocVersion
559data EsResultFound a = EsResultFound { _version :: DocVersion
441− , _source :: a } deriving (Eq, Show)
560+ , _source :: a } deriving (Eq, Show, Generic, Typeable)
442
561
443{-| 'EsError' is the generic type that will be returned when there was a
562{-| 'EsError' is the generic type that will be returned when there was a
444 problem. If you can't parse the expected response, its a good idea to
563 problem. If you can't parse the expected response, its a good idea to
445 try parsing this.
564 try parsing this.
446-}
565-}
447data EsError = EsError { errorStatus :: Int
566data EsError = EsError { errorStatus :: Int
448− , errorMessage :: Text } deriving (Eq, Show)
567+ , errorMessage :: Text } deriving (Eq, Show, Generic, Typeable)
568+
569+{-| 'EsProtocolException' ideally should never be thrown. It will only
570+be thrown when Bloodhound can't parse a response returned by the
571+ElasticSearch server. This should be reported as a bug to the bug
572+tracker. Be sure to include the body.
573+-}
574+data EsProtocolException = EsProtocolException { esProtoExBody :: L.ByteString }
575+ deriving (Eq, Show, Generic, Typeable)
576+
577+instance Exception EsProtocolException
449
578
450data IndexAlias = IndexAlias { srcIndex :: IndexName
579data IndexAlias = IndexAlias { srcIndex :: IndexName
451− , indexAlias :: IndexAliasName } deriving (Eq, Show)
580+ , indexAlias :: IndexAliasName } deriving (Eq, Show, Generic, Typeable)
452
581
453−newtype IndexAliasName = IndexAliasName { indexAliasName :: IndexName } deriving (Eq, Show, ToJSON)
582+newtype IndexAliasName = IndexAliasName { indexAliasName :: IndexName } deriving (Eq, Show, Generic, ToJSON)
454
583
455data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
584data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
456− | RemoveAlias IndexAlias deriving (Show, Eq, Typeable)
585+ | RemoveAlias IndexAlias deriving (Show, Eq, Generic, Typeable)
457
586
458data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
587data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
459 , aliasCreateFilter :: Maybe Filter}
588 , aliasCreateFilter :: Maybe Filter}
460− deriving (Show, Eq, Typeable)
589+ deriving (Show, Eq, Generic, Typeable)
461
590
462data AliasRouting = AllAliasRouting RoutingValue
591data AliasRouting = AllAliasRouting RoutingValue
463 | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
592 | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
464− deriving (Show, Eq, Typeable)
593+ deriving (Show, Eq, Generic, Typeable)
465
594
466−newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Show, Eq, Typeable)
595+newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Show, Eq, Generic, Typeable)
467
596
468−newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Show, Eq, ToJSON, FromJSON, Typeable)
597+newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Show, Eq, Generic, ToJSON, FromJSON, Typeable)
469
598
470−newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Show, Eq, ToJSON, FromJSON, Typeable)
599+newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Show, Eq, Generic, ToJSON, FromJSON, Typeable)
471
600
472−newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Show, Eq, Typeable)
601+newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Show, Eq, Generic, Typeable)
473
602
474{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
603{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
475data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
604data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
476− , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Show, Eq)
605+ , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Show, Eq, Generic, Typeable)
477
606
478{-| 'DocVersion' is an integer version number for a document between 1
607{-| 'DocVersion' is an integer version number for a document between 1
479and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
608and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
480-}
609-}
481newtype DocVersion = DocVersion {
610newtype DocVersion = DocVersion {
482 docVersionNumber :: Int
611 docVersionNumber :: Int
483− } deriving (Eq, Show, Ord, ToJSON)
612+ } deriving (Eq, Show, Generic, Ord, ToJSON)
484
613
485-- | Smart constructor for in-range doc version
614-- | Smart constructor for in-range doc version
486mkDocVersion :: Int -> Maybe DocVersion
615mkDocVersion :: Int -> Maybe DocVersion
@@ -494,7 +623,7 @@ mkDocVersion i
494own version numbers instead of ones from ES.
623own version numbers instead of ones from ES.
495-}
624-}
496newtype ExternalDocVersion = ExternalDocVersion DocVersion
625newtype ExternalDocVersion = ExternalDocVersion DocVersion
497− deriving (Eq, Show, Ord, Bounded, Enum, ToJSON)
626+ deriving (Eq, Show, Generic, Ord, Bounded, Enum, ToJSON)
498
627
499{-| 'VersionControl' is specified when indexing documents as a
628{-| 'VersionControl' is specified when indexing documents as a
500optimistic concurrency control.
629optimistic concurrency control.
@@ -528,12 +657,12 @@ data VersionControl = NoVersionControl
528 -- given version will be the new version. This is
657 -- given version will be the new version. This is
529 -- typically used for correcting errors. Use with
658 -- typically used for correcting errors. Use with
530 -- care, as this could result in data loss.
659 -- care, as this could result in data loss.
531− deriving (Show, Eq, Ord)
660+ deriving (Show, Eq, Generic, Ord)
532
661
533{-| 'DocumentParent' is used to specify a parent document.
662{-| 'DocumentParent' is used to specify a parent document.
534-}
663-}
535newtype DocumentParent = DocumentParent DocId
664newtype DocumentParent = DocumentParent DocId
536− deriving (Eq, Show)
665+ deriving (Eq, Show, Generic, Typeable)
537
666
538{-| 'IndexDocumentSettings' are special settings supplied when indexing
667{-| 'IndexDocumentSettings' are special settings supplied when indexing
539a document. For the best backwards compatiblity when new fields are
668a document. For the best backwards compatiblity when new fields are
@@ -542,7 +671,7 @@ added, you should probably prefer to start with 'defaultIndexDocumentSettings'
542data IndexDocumentSettings =
671data IndexDocumentSettings =
543 IndexDocumentSettings { idsVersionControl :: VersionControl
672 IndexDocumentSettings { idsVersionControl :: VersionControl
544 , idsParent :: Maybe DocumentParent
673 , idsParent :: Maybe DocumentParent
545− } deriving (Eq, Show)
674+ } deriving (Eq, Show, Generic, Typeable)
546
675
547{-| Reasonable default settings. Chooses no version control and no parent.
676{-| Reasonable default settings. Chooses no version control and no parent.
548-}
677-}
@@ -562,7 +691,7 @@ type Sort = [SortSpec]
562<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
691<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
563-}
692-}
564data SortSpec = DefaultSortSpec DefaultSort
693data SortSpec = DefaultSortSpec DefaultSort
565− | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Show)
694+ | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Show, Generic, Typeable)
566
695
567{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
696{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
568 'mkSort' convenience function for when you want to specify only the most
697 'mkSort' convenience function for when you want to specify only the most
@@ -577,7 +706,7 @@ data DefaultSort =
577 , ignoreUnmapped :: Bool
706 , ignoreUnmapped :: Bool
578 , sortMode :: Maybe SortMode
707 , sortMode :: Maybe SortMode
579 , missingSort :: Maybe Missing
708 , missingSort :: Maybe Missing
580− , nestedFilter :: Maybe Filter } deriving (Eq, Show)
709+ , nestedFilter :: Maybe Filter } deriving (Eq, Show, Generic, Typeable)
581
710
582{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
711{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
583 encoded into "asc" or "desc" when turned into JSON.
712 encoded into "asc" or "desc" when turned into JSON.
@@ -585,7 +714,7 @@ data DefaultSort =
585<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
714<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
586-}
715-}
587data SortOrder = Ascending
716data SortOrder = Ascending
588− | Descending deriving (Eq, Show)
717+ | Descending deriving (Eq, Show, Generic, Typeable)
589
718
590{-| 'Missing' prescribes how to handle missing fields. A missing field can be
719{-| 'Missing' prescribes how to handle missing fields. A missing field can be
591 sorted last, first, or using a custom value as a substitute.
720 sorted last, first, or using a custom value as a substitute.
@@ -594,7 +723,7 @@ data SortOrder = Ascending
594-}
723-}
595data Missing = LastMissing
724data Missing = LastMissing
596 | FirstMissing
725 | FirstMissing
597− | CustomMissing Text deriving (Eq, Show)
726+ | CustomMissing Text deriving (Eq, Show, Generic, Typeable)
598
727
599{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
728{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
600
729
@@ -603,7 +732,7 @@ http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-req
603data SortMode = SortMin
732data SortMode = SortMin
604 | SortMax
733 | SortMax
605 | SortSum
734 | SortSum
606− | SortAvg deriving (Eq, Show)
735+ | SortAvg deriving (Eq, Show, Generic, Typeable)
607
736
608{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
737{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
609 that you can concisely describe the usual kind of 'SortSpec's you want.
738 that you can concisely describe the usual kind of 'SortSpec's you want.
@@ -625,19 +754,19 @@ type PrefixValue = Text
625{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
754{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
626 JSON encoding baked in. Used all over the place.
755 JSON encoding baked in. Used all over the place.
627-}
756-}
628−data BooleanOperator = And | Or deriving (Eq, Show, Typeable)
757+data BooleanOperator = And | Or deriving (Eq, Show, Generic, Typeable)
629
758
630{-| 'ShardCount' is part of 'IndexSettings'
759{-| 'ShardCount' is part of 'IndexSettings'
631-}
760-}
632−newtype ShardCount = ShardCount Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
761+newtype ShardCount = ShardCount Int deriving (Eq, Show, Generic, ToJSON, Typeable)
633
762
634{-| 'ReplicaCount' is part of 'IndexSettings'
763{-| 'ReplicaCount' is part of 'IndexSettings'
635-}
764-}
636−newtype ReplicaCount = ReplicaCount Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
765+newtype ReplicaCount = ReplicaCount Int deriving (Eq, Show, Generic, ToJSON, Typeable)
637
766
638{-| 'Server' is used with the client functions to point at the ES instance
767{-| 'Server' is used with the client functions to point at the ES instance
639-}
768-}
640−newtype Server = Server Text deriving (Eq, Show)
769+newtype Server = Server Text deriving (Eq, Show, Generic, Typeable)
641
770
642{-| 'IndexName' is used to describe which index to query/create/delete
771{-| 'IndexName' is used to describe which index to query/create/delete
643-}
772-}
@@ -669,27 +798,27 @@ newtype QueryString = QueryString Text deriving (Eq, Generic, Show, ToJSON, From
669{-| 'FieldName' is used all over the place wherever a specific field within
798{-| 'FieldName' is used all over the place wherever a specific field within
670 a document needs to be specified, usually in 'Query's or 'Filter's.
799 a document needs to be specified, usually in 'Query's or 'Filter's.
671-}
800-}
672−newtype FieldName = FieldName Text deriving (Eq, Show, ToJSON, FromJSON, Typeable)
801+newtype FieldName = FieldName Text deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
673
802
674
803
675{-| 'Script' is often used in place of 'FieldName' to specify more
804{-| 'Script' is often used in place of 'FieldName' to specify more
676complex ways of extracting a value from a document.
805complex ways of extracting a value from a document.
677-}
806-}
678−newtype Script = Script { scriptText :: Text } deriving (Eq, Show)
807+newtype Script = Script { scriptText :: Text } deriving (Eq, Show, Generic, Typeable)
679
808
680{-| 'CacheName' is used in 'RegexpFilter' for describing the
809{-| 'CacheName' is used in 'RegexpFilter' for describing the
681 'CacheKey' keyed caching behavior.
810 'CacheKey' keyed caching behavior.
682-}
811-}
683−newtype CacheName = CacheName Text deriving (Eq, Show, ToJSON, FromJSON, Typeable)
812+newtype CacheName = CacheName Text deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
684
813
685{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
814{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
686-}
815-}
687newtype CacheKey =
816newtype CacheKey =
688− CacheKey Text deriving (Eq, Show, ToJSON, FromJSON, Typeable)
817+ CacheKey Text deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
689newtype Existence =
818newtype Existence =
690− Existence Bool deriving (Eq, Show, ToJSON, FromJSON, Typeable)
819+ Existence Bool deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
691newtype NullValue =
820newtype NullValue =
692− NullValue Bool deriving (Eq, Show, ToJSON, FromJSON, Typeable)
821+ NullValue Bool deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
693newtype CutoffFrequency =
822newtype CutoffFrequency =
694 CutoffFrequency Double deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
823 CutoffFrequency Double deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
695newtype Analyzer =
824newtype Analyzer =
@@ -773,14 +902,17 @@ newtype PhraseSlop = PhraseSlop Int deriving (Eq, Show, Generic, ToJSO
773newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
902newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
774newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
903newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, Generic, ToJSON, FromJSON, Typeable)
775
904
905+-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
906+newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
907+
776{-| 'unpackId' is a silly convenience function that gets used once.
908{-| 'unpackId' is a silly convenience function that gets used once.
777-}
909-}
778unpackId :: DocId -> Text
910unpackId :: DocId -> Text
779unpackId (DocId docId) = docId
911unpackId (DocId docId) = docId
780
912
781type TrackSortScores = Bool
913type TrackSortScores = Bool
782−newtype From = From Int deriving (Eq, Show, ToJSON)
914+newtype From = From Int deriving (Eq, Show, Generic, ToJSON)
783−newtype Size = Size Int deriving (Eq, Show, ToJSON)
915+newtype Size = Size Int deriving (Eq, Show, Generic, ToJSON)
784
916
785data Search = Search { queryBody :: Maybe Query
917data Search = Search { queryBody :: Maybe Query
786 , filterBody :: Maybe Filter
918 , filterBody :: Maybe Filter
@@ -793,7 +925,7 @@ data Search = Search { queryBody :: Maybe Query
793 , size :: Size
925 , size :: Size
794 , searchType :: SearchType
926 , searchType :: SearchType
795 , fields :: Maybe [FieldName]
927 , fields :: Maybe [FieldName]
796− , source :: Maybe Source } deriving (Eq, Show)
928+ , source :: Maybe Source } deriving (Eq, Show, Generic, Typeable)
797
929
798data SearchType = SearchTypeQueryThenFetch
930data SearchType = SearchTypeQueryThenFetch
799 | SearchTypeDfsQueryThenFetch
931 | SearchTypeDfsQueryThenFetch
@@ -801,40 +933,40 @@ data SearchType = SearchTypeQueryThenFetch
801 | SearchTypeScan
933 | SearchTypeScan
802 | SearchTypeQueryAndFetch
934 | SearchTypeQueryAndFetch
803 | SearchTypeDfsQueryAndFetch
935 | SearchTypeDfsQueryAndFetch
804− deriving (Eq, Show)
936+ deriving (Eq, Show, Generic, Typeable)
805
937
806data Source =
938data Source =
807 NoSource
939 NoSource
808 | SourcePatterns PatternOrPatterns
940 | SourcePatterns PatternOrPatterns
809 | SourceIncludeExclude Include Exclude
941 | SourceIncludeExclude Include Exclude
810− deriving (Show, Eq)
942+ deriving (Show, Eq, Generic, Typeable)
811
943
812data PatternOrPatterns = PopPattern Pattern
944data PatternOrPatterns = PopPattern Pattern
813− | PopPatterns [Pattern] deriving (Eq, Show)
945+ | PopPatterns [Pattern] deriving (Eq, Show, Generic, Typeable)
814
946
815−data Include = Include [Pattern] deriving (Eq, Show)
947+data Include = Include [Pattern] deriving (Eq, Show, Generic, Typeable)
816−data Exclude = Exclude [Pattern] deriving (Eq, Show)
948+data Exclude = Exclude [Pattern] deriving (Eq, Show, Generic, Typeable)
817
949
818−newtype Pattern = Pattern Text deriving (Eq, Show)
950+newtype Pattern = Pattern Text deriving (Eq, Show, Generic, Typeable)
819
951
820data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
952data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
821 , highlightFields :: [FieldHighlight]
953 , highlightFields :: [FieldHighlight]
822− } deriving (Show, Eq)
954+ } deriving (Show, Eq, Generic, Typeable)
823
955
824data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
956data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
825− deriving (Show, Eq)
957+ deriving (Show, Eq, Generic, Typeable)
826
958
827
959
828data HighlightSettings = Plain PlainHighlight
960data HighlightSettings = Plain PlainHighlight
829 | Postings PostingsHighlight
961 | Postings PostingsHighlight
830 | FastVector FastVectorHighlight
962 | FastVector FastVectorHighlight
831− deriving (Show, Eq)
963+ deriving (Show, Eq, Generic, Typeable)
832data PlainHighlight =
964data PlainHighlight =
833 PlainHighlight { plainCommon :: Maybe CommonHighlight
965 PlainHighlight { plainCommon :: Maybe CommonHighlight
834− , plainNonPost :: Maybe NonPostings } deriving (Show, Eq)
966+ , plainNonPost :: Maybe NonPostings } deriving (Show, Eq, Generic, Typeable)
835
967
836 -- This requires that index_options are set to 'offset' in the mapping.
968 -- This requires that index_options are set to 'offset' in the mapping.
837−data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Show, Eq)
969+data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Show, Eq, Generic, Typeable)
838
970
839-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
971-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
840data FastVectorHighlight =
972data FastVectorHighlight =
@@ -845,7 +977,7 @@ data FastVectorHighlight =
845 , fragmentOffset :: Maybe Int
977 , fragmentOffset :: Maybe Int
846 , matchedFields :: [Text]
978 , matchedFields :: [Text]
847 , phraseLimit :: Maybe Int
979 , phraseLimit :: Maybe Int
848− } deriving (Show, Eq)
980+ } deriving (Show, Eq, Generic, Typeable)
849
981
850data CommonHighlight =
982data CommonHighlight =
851 CommonHighlight { order :: Maybe Text
983 CommonHighlight { order :: Maybe Text
@@ -855,21 +987,21 @@ data CommonHighlight =
855 , noMatchSize :: Maybe Int
987 , noMatchSize :: Maybe Int
856 , highlightQuery :: Maybe Query
988 , highlightQuery :: Maybe Query
857 , requireFieldMatch :: Maybe Bool
989 , requireFieldMatch :: Maybe Bool
858− } deriving (Show, Eq)
990+ } deriving (Show, Eq, Generic, Typeable)
859
991
860-- Settings that are only applicable to FastVector and Plain highlighters.
992-- Settings that are only applicable to FastVector and Plain highlighters.
861data NonPostings =
993data NonPostings =
862 NonPostings { fragmentSize :: Maybe Int
994 NonPostings { fragmentSize :: Maybe Int
863− , numberOfFragments :: Maybe Int} deriving (Show, Eq)
995+ , numberOfFragments :: Maybe Int} deriving (Show, Eq, Generic, Typeable)
864
996
865data HighlightEncoder = DefaultEncoder
997data HighlightEncoder = DefaultEncoder
866 | HTMLEncoder
998 | HTMLEncoder
867− deriving (Show, Eq)
999+ deriving (Show, Eq, Generic, Typeable)
868
1000
869-- NOTE: Should the tags use some kind of HTML type, rather than Text?
1001-- NOTE: Should the tags use some kind of HTML type, rather than Text?
870data HighlightTag = TagSchema Text
1002data HighlightTag = TagSchema Text
871 | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
1003 | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
872− deriving (Show, Eq)
1004+ deriving (Show, Eq, Generic, Typeable)
873
1005
874
1006
875data Query =
1007data Query =
@@ -900,19 +1032,19 @@ data Query =
900 | QuerySimpleQueryStringQuery SimpleQueryStringQuery
1032 | QuerySimpleQueryStringQuery SimpleQueryStringQuery
901 | QueryRangeQuery RangeQuery
1033 | QueryRangeQuery RangeQuery
902 | QueryRegexpQuery RegexpQuery
1034 | QueryRegexpQuery RegexpQuery
903− deriving (Eq, Show, Typeable)
1035+ deriving (Eq, Show, Generic, Typeable)
904
1036
905data RegexpQuery =
1037data RegexpQuery =
906 RegexpQuery { regexpQueryField :: FieldName
1038 RegexpQuery { regexpQueryField :: FieldName
907 , regexpQuery :: Regexp
1039 , regexpQuery :: Regexp
908 , regexpQueryFlags :: RegexpFlags
1040 , regexpQueryFlags :: RegexpFlags
909 , regexpQueryBoost :: Maybe Boost
1041 , regexpQueryBoost :: Maybe Boost
910− } deriving (Eq, Show, Typeable)
1042+ } deriving (Eq, Show, Generic, Typeable)
911
1043
912data RangeQuery =
1044data RangeQuery =
913 RangeQuery { rangeQueryField :: FieldName
1045 RangeQuery { rangeQueryField :: FieldName
914 , rangeQueryRange :: RangeValue
1046 , rangeQueryRange :: RangeValue
915− , rangeQueryBoost :: Boost } deriving (Eq, Show, Typeable)
1047+ , rangeQueryBoost :: Boost } deriving (Eq, Show, Generic, Typeable)
916
1048
917mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
1049mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
918mkRangeQuery f r = RangeQuery f r (Boost 1.0)
1050mkRangeQuery f r = RangeQuery f r (Boost 1.0)
@@ -926,7 +1058,7 @@ data SimpleQueryStringQuery =
926 , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
1058 , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
927 , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
1059 , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
928 , simpleQueryStringLocale :: Maybe Locale
1060 , simpleQueryStringLocale :: Maybe Locale
929− } deriving (Eq, Show, Typeable)
1061+ } deriving (Eq, Show, Generic, Typeable)
930
1062
931data SimpleQueryFlag =
1063data SimpleQueryFlag =
932 SimpleQueryAll
1064 SimpleQueryAll
@@ -940,7 +1072,7 @@ data SimpleQueryFlag =
940 | SimpleQueryWhitespace
1072 | SimpleQueryWhitespace
941 | SimpleQueryFuzzy
1073 | SimpleQueryFuzzy
942 | SimpleQueryNear
1074 | SimpleQueryNear
943− | SimpleQuerySlop deriving (Eq, Show, Typeable)
1075+ | SimpleQuerySlop deriving (Eq, Show, Generic, Typeable)
944
1076
945-- use_dis_max and tie_breaker when fields are plural?
1077-- use_dis_max and tie_breaker when fields are plural?
946data QueryStringQuery =
1078data QueryStringQuery =
@@ -962,7 +1094,7 @@ data QueryStringQuery =
962 , queryStringMinimumShouldMatch :: Maybe MinimumMatch
1094 , queryStringMinimumShouldMatch :: Maybe MinimumMatch
963 , queryStringLenient :: Maybe Lenient
1095 , queryStringLenient :: Maybe Lenient
964 , queryStringLocale :: Maybe Locale
1096 , queryStringLocale :: Maybe Locale
965− } deriving (Eq, Show, Typeable)
1097+ } deriving (Eq, Show, Generic, Typeable)
966
1098
967mkQueryStringQuery :: QueryString -> QueryStringQuery
1099mkQueryStringQuery :: QueryString -> QueryStringQuery
968mkQueryStringQuery qs =
1100mkQueryStringQuery qs =
@@ -973,19 +1105,19 @@ mkQueryStringQuery qs =
973 Nothing Nothing
1105 Nothing Nothing
974
1106
975data FieldOrFields = FofField FieldName
1107data FieldOrFields = FofField FieldName
976− | FofFields (NonEmpty FieldName) deriving (Eq, Show, Typeable)
1108+ | FofFields (NonEmpty FieldName) deriving (Eq, Show, Generic, Typeable)
977
1109
978data PrefixQuery =
1110data PrefixQuery =
979 PrefixQuery
1111 PrefixQuery
980 { prefixQueryField :: FieldName
1112 { prefixQueryField :: FieldName
981 , prefixQueryPrefixValue :: Text
1113 , prefixQueryPrefixValue :: Text
982− , prefixQueryBoost :: Maybe Boost } deriving (Eq, Show, Typeable)
1114+ , prefixQueryBoost :: Maybe Boost } deriving (Eq, Show, Generic, Typeable)
983
1115
984data NestedQuery =
1116data NestedQuery =
985 NestedQuery
1117 NestedQuery
986 { nestedQueryPath :: QueryPath
1118 { nestedQueryPath :: QueryPath
987 , nestedQueryScoreType :: ScoreType
1119 , nestedQueryScoreType :: ScoreType
988− , nestedQuery :: Query } deriving (Eq, Show, Typeable)
1120+ , nestedQuery :: Query } deriving (Eq, Show, Generic, Typeable)
989
1121
990data MoreLikeThisFieldQuery =
1122data MoreLikeThisFieldQuery =
991 MoreLikeThisFieldQuery
1123 MoreLikeThisFieldQuery
@@ -1003,7 +1135,7 @@ data MoreLikeThisFieldQuery =
1003 , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
1135 , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
1004 , moreLikeThisFieldBoost :: Maybe Boost
1136 , moreLikeThisFieldBoost :: Maybe Boost
1005 , moreLikeThisFieldAnalyzer :: Maybe Analyzer
1137 , moreLikeThisFieldAnalyzer :: Maybe Analyzer
1006− } deriving (Eq, Show, Typeable)
1138+ } deriving (Eq, Show, Generic, Typeable)
1007
1139
1008data MoreLikeThisQuery =
1140data MoreLikeThisQuery =
1009 MoreLikeThisQuery
1141 MoreLikeThisQuery
@@ -1021,32 +1153,32 @@ data MoreLikeThisQuery =
1021 , moreLikeThisBoostTerms :: Maybe BoostTerms
1153 , moreLikeThisBoostTerms :: Maybe BoostTerms
1022 , moreLikeThisBoost :: Maybe Boost
1154 , moreLikeThisBoost :: Maybe Boost
1023 , moreLikeThisAnalyzer :: Maybe Analyzer
1155 , moreLikeThisAnalyzer :: Maybe Analyzer
1024− } deriving (Eq, Show, Typeable)
1156+ } deriving (Eq, Show, Generic, Typeable)
1025
1157
1026data IndicesQuery =
1158data IndicesQuery =
1027 IndicesQuery
1159 IndicesQuery
1028 { indicesQueryIndices :: [IndexName]
1160 { indicesQueryIndices :: [IndexName]
1029 , indicesQuery :: Query
1161 , indicesQuery :: Query
1030 -- default "all"
1162 -- default "all"
1031− , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show, Typeable)
1163+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show, Generic, Typeable)
1032
1164
1033data HasParentQuery =
1165data HasParentQuery =
1034 HasParentQuery
1166 HasParentQuery
1035 { hasParentQueryType :: TypeName
1167 { hasParentQueryType :: TypeName
1036 , hasParentQuery :: Query
1168 , hasParentQuery :: Query
1037− , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Show, Typeable)
1169+ , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Show, Generic, Typeable)
1038
1170
1039data HasChildQuery =
1171data HasChildQuery =
1040 HasChildQuery
1172 HasChildQuery
1041 { hasChildQueryType :: TypeName
1173 { hasChildQueryType :: TypeName
1042 , hasChildQuery :: Query
1174 , hasChildQuery :: Query
1043− , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Show, Typeable)
1175+ , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Show, Generic, Typeable)
1044
1176
1045data ScoreType =
1177data ScoreType =
1046 ScoreTypeMax
1178 ScoreTypeMax
1047 | ScoreTypeSum
1179 | ScoreTypeSum
1048 | ScoreTypeAvg
1180 | ScoreTypeAvg
1049− | ScoreTypeNone deriving (Eq, Show, Typeable)
1181+ | ScoreTypeNone deriving (Eq, Show, Generic, Typeable)
1050
1182
1051data FuzzyQuery =
1183data FuzzyQuery =
1052 FuzzyQuery { fuzzyQueryField :: FieldName
1184 FuzzyQuery { fuzzyQueryField :: FieldName
@@ -1055,7 +1187,7 @@ data FuzzyQuery =
1055 , fuzzyQueryMaxExpansions :: MaxExpansions
1187 , fuzzyQueryMaxExpansions :: MaxExpansions
1056 , fuzzyQueryFuzziness :: Fuzziness
1188 , fuzzyQueryFuzziness :: Fuzziness
1057 , fuzzyQueryBoost :: Maybe Boost
1189 , fuzzyQueryBoost :: Maybe Boost
1058− } deriving (Eq, Show, Typeable)
1190+ } deriving (Eq, Show, Generic, Typeable)
1059
1191
1060data FuzzyLikeFieldQuery =
1192data FuzzyLikeFieldQuery =
1061 FuzzyLikeFieldQuery
1193 FuzzyLikeFieldQuery
@@ -1068,7 +1200,7 @@ data FuzzyLikeFieldQuery =
1068 , fuzzyLikeFieldPrefixLength :: PrefixLength
1200 , fuzzyLikeFieldPrefixLength :: PrefixLength
1069 , fuzzyLikeFieldBoost :: Boost
1201 , fuzzyLikeFieldBoost :: Boost
1070 , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
1202 , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
1071− } deriving (Eq, Show, Typeable)
1203+ } deriving (Eq, Show, Generic, Typeable)
1072
1204
1073data FuzzyLikeThisQuery =
1205data FuzzyLikeThisQuery =
1074 FuzzyLikeThisQuery
1206 FuzzyLikeThisQuery
@@ -1080,19 +1212,19 @@ data FuzzyLikeThisQuery =
1080 , fuzzyLikePrefixLength :: PrefixLength
1212 , fuzzyLikePrefixLength :: PrefixLength
1081 , fuzzyLikeBoost :: Boost
1213 , fuzzyLikeBoost :: Boost
1082 , fuzzyLikeAnalyzer :: Maybe Analyzer
1214 , fuzzyLikeAnalyzer :: Maybe Analyzer
1083− } deriving (Eq, Show, Typeable)
1215+ } deriving (Eq, Show, Generic, Typeable)
1084
1216
1085data FilteredQuery =
1217data FilteredQuery =
1086 FilteredQuery
1218 FilteredQuery
1087 { filteredQuery :: Query
1219 { filteredQuery :: Query
1088− , filteredFilter :: Filter } deriving (Eq, Show, Typeable)
1220+ , filteredFilter :: Filter } deriving (Eq, Show, Generic, Typeable)
1089
1221
1090data DisMaxQuery =
1222data DisMaxQuery =
1091 DisMaxQuery { disMaxQueries :: [Query]
1223 DisMaxQuery { disMaxQueries :: [Query]
1092 -- default 0.0
1224 -- default 0.0
1093 , disMaxTiebreaker :: Tiebreaker
1225 , disMaxTiebreaker :: Tiebreaker
1094 , disMaxBoost :: Maybe Boost
1226 , disMaxBoost :: Maybe Boost
1095− } deriving (Eq, Show, Typeable)
1227+ } deriving (Eq, Show, Generic, Typeable)
1096
1228
1097data MatchQuery =
1229data MatchQuery =
1098 MatchQuery { matchQueryField :: FieldName
1230 MatchQuery { matchQueryField :: FieldName
@@ -1104,7 +1236,7 @@ data MatchQuery =
1104 , matchQueryAnalyzer :: Maybe Analyzer
1236 , matchQueryAnalyzer :: Maybe Analyzer
1105 , matchQueryMaxExpansions :: Maybe MaxExpansions
1237 , matchQueryMaxExpansions :: Maybe MaxExpansions
1106 , matchQueryLenient :: Maybe Lenient
1238 , matchQueryLenient :: Maybe Lenient
1107− , matchQueryBoost :: Maybe Boost } deriving (Eq, Show, Typeable)
1239+ , matchQueryBoost :: Maybe Boost } deriving (Eq, Show, Generic, Typeable)
1108
1240
1109{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
1241{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
1110 enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
1242 enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
@@ -1114,7 +1246,7 @@ mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothi
1114
1246
1115data MatchQueryType =
1247data MatchQueryType =
1116 MatchPhrase
1248 MatchPhrase
1117− | MatchPhrasePrefix deriving (Eq, Show, Typeable)
1249+ | MatchPhrasePrefix deriving (Eq, Show, Generic, Typeable)
1118
1250
1119data MultiMatchQuery =
1251data MultiMatchQuery =
1120 MultiMatchQuery { multiMatchQueryFields :: [FieldName]
1252 MultiMatchQuery { multiMatchQueryFields :: [FieldName]
@@ -1126,7 +1258,7 @@ data MultiMatchQuery =
1126 , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
1258 , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
1127 , multiMatchQueryAnalyzer :: Maybe Analyzer
1259 , multiMatchQueryAnalyzer :: Maybe Analyzer
1128 , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
1260 , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
1129− , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Show, Typeable)
1261+ , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Show, Generic, Typeable)
1130
1262
1131{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
1263{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
1132 enabling you to provide only the list of 'FieldName's and 'QueryString' to
1264 enabling you to provide only the list of 'FieldName's and 'QueryString' to
@@ -1143,7 +1275,7 @@ data MultiMatchQueryType =
1143 | MultiMatchMostFields
1275 | MultiMatchMostFields
1144 | MultiMatchCrossFields
1276 | MultiMatchCrossFields
1145 | MultiMatchPhrase
1277 | MultiMatchPhrase
1146− | MultiMatchPhrasePrefix deriving (Eq, Show, Typeable)
1278+ | MultiMatchPhrasePrefix deriving (Eq, Show, Generic, Typeable)
1147
1279
1148data BoolQuery =
1280data BoolQuery =
1149 BoolQuery { boolQueryMustMatch :: [Query]
1281 BoolQuery { boolQueryMustMatch :: [Query]
@@ -1152,7 +1284,7 @@ data BoolQuery =
1152 , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
1284 , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
1153 , boolQueryBoost :: Maybe Boost
1285 , boolQueryBoost :: Maybe Boost
1154 , boolQueryDisableCoord :: Maybe DisableCoord
1286 , boolQueryDisableCoord :: Maybe DisableCoord
1155− } deriving (Eq, Show, Typeable)
1287+ } deriving (Eq, Show, Generic, Typeable)
1156
1288
1157mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
1289mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
1158mkBoolQuery must mustNot should =
1290mkBoolQuery must mustNot should =
@@ -1161,7 +1293,7 @@ mkBoolQuery must mustNot should =
1161data BoostingQuery =
1293data BoostingQuery =
1162 BoostingQuery { positiveQuery :: Query
1294 BoostingQuery { positiveQuery :: Query
1163 , negativeQuery :: Query
1295 , negativeQuery :: Query
1164− , negativeBoost :: Boost } deriving (Eq, Show, Typeable)
1296+ , negativeBoost :: Boost } deriving (Eq, Show, Generic, Typeable)
1165
1297
1166data CommonTermsQuery =
1298data CommonTermsQuery =
1167 CommonTermsQuery { commonField :: FieldName
1299 CommonTermsQuery { commonField :: FieldName
@@ -1173,16 +1305,16 @@ data CommonTermsQuery =
1173 , commonBoost :: Maybe Boost
1305 , commonBoost :: Maybe Boost
1174 , commonAnalyzer :: Maybe Analyzer
1306 , commonAnalyzer :: Maybe Analyzer
1175 , commonDisableCoord :: Maybe DisableCoord
1307 , commonDisableCoord :: Maybe DisableCoord
1176− } deriving (Eq, Show, Typeable)
1308+ } deriving (Eq, Show, Generic, Typeable)
1177
1309
1178data CommonMinimumMatch =
1310data CommonMinimumMatch =
1179 CommonMinimumMatchHighLow MinimumMatchHighLow
1311 CommonMinimumMatchHighLow MinimumMatchHighLow
1180 | CommonMinimumMatch MinimumMatch
1312 | CommonMinimumMatch MinimumMatch
1181− deriving (Eq, Show, Typeable)
1313+ deriving (Eq, Show, Generic, Typeable)
1182
1314
1183data MinimumMatchHighLow =
1315data MinimumMatchHighLow =
1184 MinimumMatchHighLow { lowFreq :: MinimumMatch
1316 MinimumMatchHighLow { lowFreq :: MinimumMatch
1185− , highFreq :: MinimumMatch } deriving (Eq, Show, Typeable)
1317+ , highFreq :: MinimumMatch } deriving (Eq, Show, Generic, Typeable)
1186
1318
1187data Filter = AndFilter [Filter] Cache
1319data Filter = AndFilter [Filter] Cache
1188 | OrFilter [Filter] Cache
1320 | OrFilter [Filter] Cache
@@ -1202,36 +1334,36 @@ data Filter = AndFilter [Filter] Cache
1202 | RangeFilter FieldName RangeValue RangeExecution Cache
1334 | RangeFilter FieldName RangeValue RangeExecution Cache
1203 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
1335 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
1204 | TermFilter Term Cache
1336 | TermFilter Term Cache
1205− deriving (Eq, Show, Typeable)
1337+ deriving (Eq, Show, Generic, Typeable)
1206
1338
1207data ZeroTermsQuery = ZeroTermsNone
1339data ZeroTermsQuery = ZeroTermsNone
1208− | ZeroTermsAll deriving (Eq, Show, Typeable)
1340+ | ZeroTermsAll deriving (Eq, Show, Generic, Typeable)
1209
1341
1210data RangeExecution = RangeExecutionIndex
1342data RangeExecution = RangeExecutionIndex
1211− | RangeExecutionFielddata deriving (Eq, Show, Typeable)
1343+ | RangeExecutionFielddata deriving (Eq, Show, Generic, Typeable)
1212
1344
1213−newtype Regexp = Regexp Text deriving (Eq, Show, FromJSON)
1345+newtype Regexp = Regexp Text deriving (Eq, Show, Generic, Typeable, FromJSON)
1214
1346
1215data RegexpFlags = AllRegexpFlags
1347data RegexpFlags = AllRegexpFlags
1216 | NoRegexpFlags
1348 | NoRegexpFlags
1217− | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Show)
1349+ | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Show, Generic, Typeable)
1218
1350
1219data RegexpFlag = AnyString
1351data RegexpFlag = AnyString
1220 | Automaton
1352 | Automaton
1221 | Complement
1353 | Complement
1222 | Empty
1354 | Empty
1223 | Intersection
1355 | Intersection
1224− | Interval deriving (Eq, Show)
1356+ | Interval deriving (Eq, Show, Generic, Typeable)
1225
1357
1226−newtype LessThan = LessThan Double deriving (Eq, Show)
1358+newtype LessThan = LessThan Double deriving (Eq, Show, Generic, Typeable)
1227−newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
1359+newtype LessThanEq = LessThanEq Double deriving (Eq, Show, Generic, Typeable)
1228−newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
1360+newtype GreaterThan = GreaterThan Double deriving (Eq, Show, Generic, Typeable)
1229−newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
1361+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show, Generic, Typeable)
1230
1362
1231−newtype LessThanD = LessThanD UTCTime deriving (Eq, Show)
1363+newtype LessThanD = LessThanD UTCTime deriving (Eq, Show, Generic, Typeable)
1232−newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show)
1364+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show, Generic, Typeable)
1233−newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show)
1365+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show, Generic, Typeable)
1234−newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show)
1366+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show, Generic, Typeable)
1235
1367
1236data RangeValue = RangeDateLte LessThanEqD
1368data RangeValue = RangeDateLte LessThanEqD
1237 | RangeDateLt LessThanD
1369 | RangeDateLt LessThanD
@@ -1249,7 +1381,7 @@ data RangeValue = RangeDateLte LessThanEqD
1249 | RangeDoubleGteLte GreaterThanEq LessThanEq
1381 | RangeDoubleGteLte GreaterThanEq LessThanEq
1250 | RangeDoubleGteLt GreaterThanEq LessThan
1382 | RangeDoubleGteLt GreaterThanEq LessThan
1251 | RangeDoubleGtLte GreaterThan LessThanEq
1383 | RangeDoubleGtLte GreaterThan LessThanEq
1252− deriving (Eq, Show)
1384+ deriving (Eq, Show, Generic, Typeable)
1253
1385
1254rangeValueToPair :: RangeValue -> [Pair]
1386rangeValueToPair :: RangeValue -> [Pair]
1255rangeValueToPair rv = case rv of
1387rangeValueToPair rv = case rv of
@@ -1271,33 +1403,33 @@ rangeValueToPair rv = case rv of
1271 RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1403 RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1272
1404
1273data Term = Term { termField :: Text
1405data Term = Term { termField :: Text
1274− , termValue :: Text } deriving (Eq, Show, Typeable)
1406+ , termValue :: Text } deriving (Eq, Show, Generic, Typeable)
1275
1407
1276data BoolMatch = MustMatch Term Cache
1408data BoolMatch = MustMatch Term Cache
1277 | MustNotMatch Term Cache
1409 | MustNotMatch Term Cache
1278− | ShouldMatch [Term] Cache deriving (Eq, Show, Typeable)
1410+ | ShouldMatch [Term] Cache deriving (Eq, Show, Generic, Typeable)
1279
1411
1280-- "memory" or "indexed"
1412-- "memory" or "indexed"
1281data GeoFilterType = GeoFilterMemory
1413data GeoFilterType = GeoFilterMemory
1282− | GeoFilterIndexed deriving (Eq, Show, Typeable)
1414+ | GeoFilterIndexed deriving (Eq, Show, Generic, Typeable)
1283
1415
1284data LatLon = LatLon { lat :: Double
1416data LatLon = LatLon { lat :: Double
1285− , lon :: Double } deriving (Eq, Show, Typeable)
1417+ , lon :: Double } deriving (Eq, Show, Generic, Typeable)
1286
1418
1287data GeoBoundingBox =
1419data GeoBoundingBox =
1288 GeoBoundingBox { topLeft :: LatLon
1420 GeoBoundingBox { topLeft :: LatLon
1289− , bottomRight :: LatLon } deriving (Eq, Show, Typeable)
1421+ , bottomRight :: LatLon } deriving (Eq, Show, Generic, Typeable)
1290
1422
1291data GeoBoundingBoxConstraint =
1423data GeoBoundingBoxConstraint =
1292 GeoBoundingBoxConstraint { geoBBField :: FieldName
1424 GeoBoundingBoxConstraint { geoBBField :: FieldName
1293 , constraintBox :: GeoBoundingBox
1425 , constraintBox :: GeoBoundingBox
1294 , bbConstraintcache :: Cache
1426 , bbConstraintcache :: Cache
1295 , geoType :: GeoFilterType
1427 , geoType :: GeoFilterType
1296− } deriving (Eq, Show, Typeable)
1428+ } deriving (Eq, Show, Generic, Typeable)
1297
1429
1298data GeoPoint =
1430data GeoPoint =
1299 GeoPoint { geoField :: FieldName
1431 GeoPoint { geoField :: FieldName
1300− , latLon :: LatLon} deriving (Eq, Show, Typeable)
1432+ , latLon :: LatLon} deriving (Eq, Show, Generic, Typeable)
1301
1433
1302data DistanceUnit = Miles
1434data DistanceUnit = Miles
1303 | Yards
1435 | Yards
@@ -1307,22 +1439,22 @@ data DistanceUnit = Miles
1307 | Meters
1439 | Meters
1308 | Centimeters
1440 | Centimeters
1309 | Millimeters
1441 | Millimeters
1310− | NauticalMiles deriving (Eq, Show, Typeable)
1442+ | NauticalMiles deriving (Eq, Show, Generic, Typeable)
1311
1443
1312data DistanceType = Arc
1444data DistanceType = Arc
1313 | SloppyArc -- doesn't exist <1.0
1445 | SloppyArc -- doesn't exist <1.0
1314− | Plane deriving (Eq, Show, Typeable)
1446+ | Plane deriving (Eq, Show, Generic, Typeable)
1315
1447
1316data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1448data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1317− | NoOptimizeBbox deriving (Eq, Show, Typeable)
1449+ | NoOptimizeBbox deriving (Eq, Show, Generic, Typeable)
1318
1450
1319data Distance =
1451data Distance =
1320 Distance { coefficient :: Double
1452 Distance { coefficient :: Double
1321− , unit :: DistanceUnit } deriving (Eq, Show, Typeable)
1453+ , unit :: DistanceUnit } deriving (Eq, Show, Generic, Typeable)
1322
1454
1323data DistanceRange =
1455data DistanceRange =
1324 DistanceRange { distanceFrom :: Distance
1456 DistanceRange { distanceFrom :: Distance
1325− , distanceTo :: Distance } deriving (Eq, Show)
1457+ , distanceTo :: Distance } deriving (Eq, Show, Generic, Typeable)
1326
1458
1327data SearchResult a =
1459data SearchResult a =
1328 SearchResult { took :: Int
1460 SearchResult { took :: Int
@@ -1330,16 +1462,16 @@ data SearchResult a =
1330 , shards :: ShardResult
1462 , shards :: ShardResult
1331 , searchHits :: SearchHits a
1463 , searchHits :: SearchHits a
1332 , aggregations :: Maybe AggregationResults
1464 , aggregations :: Maybe AggregationResults
1333− , scrollId :: Maybe ScrollId } deriving (Eq, Show)
1465+ , scrollId :: Maybe ScrollId } deriving (Eq, Show, Generic, Typeable)
1334
1466
1335−type ScrollId = Text -- Fixme: Newtype
1467+newtype ScrollId = ScrollId Text deriving (Eq, Show, Generic, Ord, ToJSON, FromJSON)
1336
1468
1337type Score = Maybe Double
1469type Score = Maybe Double
1338
1470
1339data SearchHits a =
1471data SearchHits a =
1340 SearchHits { hitsTotal :: Int
1472 SearchHits { hitsTotal :: Int
1341 , maxScore :: Score
1473 , maxScore :: Score
1342− , hits :: [Hit a] } deriving (Eq, Show)
1474+ , hits :: [Hit a] } deriving (Eq, Show, Generic, Typeable)
1343
1475
1344
1476
1345instance Monoid (SearchHits a) where
1477instance Monoid (SearchHits a) where
@@ -1354,18 +1486,26 @@ data Hit a =
1354 , hitDocId :: DocId
1486 , hitDocId :: DocId
1355 , hitScore :: Score
1487 , hitScore :: Score
1356 , hitSource :: Maybe a
1488 , hitSource :: Maybe a
1357− , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show)
1489+ , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show, Generic, Typeable)
1358
1490
1359data ShardResult =
1491data ShardResult =
1360 ShardResult { shardTotal :: Int
1492 ShardResult { shardTotal :: Int
1361 , shardsSuccessful :: Int
1493 , shardsSuccessful :: Int
1362− , shardsFailed :: Int } deriving (Eq, Show, Generic)
1494+ , shardsFailed :: Int } deriving (Eq, Show, Generic, Typeable)
1363
1495
1364type HitHighlight = M.Map Text [Text]
1496type HitHighlight = M.Map Text [Text]
1365
1497
1366showText :: Show a => a -> Text
1498showText :: Show a => a -> Text
1367showText = T.pack . show
1499showText = T.pack . show
1368
1500
1501+readMay :: Read a => String -> Maybe a
1502+readMay s = case reads s of
1503+ (a, ""):_ -> Just a
1504+ _ -> Nothing
1505+
1506+parseReadText :: Read a => Text -> Parser a
1507+parseReadText = maybe mzero return . readMay . T.unpack
1508+
1369type Aggregations = M.Map Text Aggregation
1509type Aggregations = M.Map Text Aggregation
1370
1510
1371emptyAggregations :: Aggregations
1511emptyAggregations :: Aggregations
@@ -1375,19 +1515,19 @@ mkAggregations :: Text -> Aggregation -> Aggregations
1375mkAggregations name aggregation = M.insert name aggregation emptyAggregations
1515mkAggregations name aggregation = M.insert name aggregation emptyAggregations
1376
1516
1377data TermOrder = TermOrder{ termSortField :: Text
1517data TermOrder = TermOrder{ termSortField :: Text
1378− , termSortOrder :: SortOrder } deriving (Eq, Show)
1518+ , termSortOrder :: SortOrder } deriving (Eq, Show, Generic, Typeable)
1379
1519
1380data TermInclusion = TermInclusion Text
1520data TermInclusion = TermInclusion Text
1381− | TermPattern Text Text deriving (Eq, Show)
1521+ | TermPattern Text Text deriving (Eq, Show, Generic, Typeable)
1382
1522
1383data CollectionMode = BreadthFirst
1523data CollectionMode = BreadthFirst
1384− | DepthFirst deriving (Eq, Show)
1524+ | DepthFirst deriving (Eq, Show, Generic, Typeable)
1385
1525
1386data ExecutionHint = Ordinals
1526data ExecutionHint = Ordinals
1387 | GlobalOrdinals
1527 | GlobalOrdinals
1388 | GlobalOrdinalsHash
1528 | GlobalOrdinalsHash
1389 | GlobalOrdinalsLowCardinality
1529 | GlobalOrdinalsLowCardinality
1390− | Map deriving (Eq, Show)
1530+ | Map deriving (Eq, Show, Generic, Typeable)
1391
1531
1392data TimeInterval = Weeks
1532data TimeInterval = Weeks
1393 | Days
1533 | Days
@@ -1403,12 +1543,13 @@ data Interval = Year
1403 | Hour
1543 | Hour
1404 | Minute
1544 | Minute
1405 | Second
1545 | Second
1406− | FractionalInterval Float TimeInterval deriving (Eq, Show)
1546+ | FractionalInterval Float TimeInterval deriving (Eq, Show, Generic, Typeable)
1407
1547
1408data Aggregation = TermsAgg TermsAggregation
1548data Aggregation = TermsAgg TermsAggregation
1409 | DateHistogramAgg DateHistogramAggregation
1549 | DateHistogramAgg DateHistogramAggregation
1410 | ValueCountAgg ValueCountAggregation
1550 | ValueCountAgg ValueCountAggregation
1411− | FilterAgg FilterAggregation deriving (Eq, Show)
1551+ | FilterAgg FilterAggregation
1552+ | DateRangeAgg DateRangeAggregation deriving (Eq, Show, Generic, Typeable)
1412
1553
1413
1554
1414data TermsAggregation = TermsAggregation { term :: Either Text Text
1555data TermsAggregation = TermsAggregation { term :: Either Text Text
@@ -1421,7 +1562,7 @@ data TermsAggregation = TermsAggregation { term :: Either Text Text
1421 , termCollectMode :: Maybe CollectionMode
1562 , termCollectMode :: Maybe CollectionMode
1422 , termExecutionHint :: Maybe ExecutionHint
1563 , termExecutionHint :: Maybe ExecutionHint
1423 , termAggs :: Maybe Aggregations
1564 , termAggs :: Maybe Aggregations
1424− } deriving (Eq, Show)
1565+ } deriving (Eq, Show, Generic, Typeable)
1425
1566
1426data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
1567data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
1427 , dateInterval :: Interval
1568 , dateInterval :: Interval
@@ -1432,15 +1573,45 @@ data DateHistogramAggregation = DateHistogramAggregation { dateField :: Fie
1432 , datePreOffset :: Maybe Text
1573 , datePreOffset :: Maybe Text
1433 , datePostOffset :: Maybe Text
1574 , datePostOffset :: Maybe Text
1434 , dateAggs :: Maybe Aggregations
1575 , dateAggs :: Maybe Aggregations
1435− } deriving (Eq, Show)
1576+ } deriving (Eq, Show, Generic, Typeable)
1577+
1578+
1579+data DateRangeAggregation = DateRangeAggregation { draField :: FieldName
1580+ , draFormat :: Maybe Text
1581+ , draRanges :: NonEmpty DateRangeAggRange
1582+ } deriving (Eq, Show, Generic, Typeable)
1583+
1584+data DateRangeAggRange = DateRangeFrom DateMathExpr
1585+ | DateRangeTo DateMathExpr
1586+ | DateRangeFromAndTo DateMathExpr DateMathExpr deriving (Eq, Show, Generic, Typeable)
1587+
1588+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
1589+data DateMathExpr = DateMathExpr DateMathAnchor [DateMathModifier] deriving (Eq, Show, Generic, Typeable)
1590+
1591+
1592+-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
1593+data DateMathAnchor = DMNow
1594+ | DMDate Day deriving (Eq, Show, Generic, Typeable)
1595+
1596+data DateMathModifier = AddTime Int DateMathUnit
1597+ | SubtractTime Int DateMathUnit
1598+ | RoundDownTo DateMathUnit deriving (Eq, Show, Generic, Typeable)
1599+
1600+data DateMathUnit = DMYear
1601+ | DMMonth
1602+ | DMWeek
1603+ | DMDay
1604+ | DMHour
1605+ | DMMinute
1606+ | DMSecond deriving (Eq, Show, Generic, Typeable)
1436
1607
1437-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1608-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1438data ValueCountAggregation = FieldValueCount FieldName
1609data ValueCountAggregation = FieldValueCount FieldName
1439− | ScriptValueCount Script deriving (Eq, Show)
1610+ | ScriptValueCount Script deriving (Eq, Show, Generic, Typeable)
1440
1611
1441-- | Single-bucket filter aggregations. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filter-aggregation.html#search-aggregations-bucket-filter-aggregation> for more information.
1612-- | Single-bucket filter aggregations. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filter-aggregation.html#search-aggregations-bucket-filter-aggregation> for more information.
1442data FilterAggregation = FilterAggregation { faFilter :: Filter
1613data FilterAggregation = FilterAggregation { faFilter :: Filter
1443− , faAggs :: Maybe Aggregations} deriving (Eq, Show)
1614+ , faAggs :: Maybe Aggregations} deriving (Eq, Show, Generic, Typeable)
1444
1615
1445mkTermsAggregation :: Text -> TermsAggregation
1616mkTermsAggregation :: Text -> TermsAggregation
1446mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1617mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
@@ -1537,6 +1708,39 @@ instance ToJSON Aggregation where
1537 toJSON (FilterAgg (FilterAggregation filt ags)) =
1708 toJSON (FilterAgg (FilterAggregation filt ags)) =
1538 omitNulls [ "filter" .= filt
1709 omitNulls [ "filter" .= filt
1539 , "aggs" .= ags]
1710 , "aggs" .= ags]
1711+ toJSON (DateRangeAgg a) = object [ "date_range" .= a
1712+ ]
1713+
1714+instance ToJSON DateRangeAggregation where
1715+ toJSON DateRangeAggregation {..} =
1716+ omitNulls [ "field" .= draField
1717+ , "format" .= draFormat
1718+ , "ranges" .= toList draRanges
1719+ ]
1720+
1721+instance ToJSON DateRangeAggRange where
1722+ toJSON (DateRangeFrom e) = object [ "from" .= e ]
1723+ toJSON (DateRangeTo e) = object [ "to" .= e ]
1724+ toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
1725+
1726+instance ToJSON DateMathExpr where
1727+ toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
1728+ where fmtA DMNow = "now"
1729+ fmtA (DMDate date) = case toGregorian date of
1730+ (y,m,d) -> showText y <> "-" <>
1731+ showText m <> "-" <>
1732+ showText d <> "||"
1733+ fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
1734+ fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
1735+ fmtMod (RoundDownTo u) = "/" <> fmtU u
1736+ fmtU DMYear = "y"
1737+ fmtU DMMonth = "M"
1738+ fmtU DMWeek = "w"
1739+ fmtU DMDay = "d"
1740+ fmtU DMHour = "h"
1741+ fmtU DMMinute = "m"
1742+ fmtU DMSecond = "s"
1743+
1540
1744
1541type AggregationResults = M.Map Text Value
1745type AggregationResults = M.Map Text Value
1542
1746
@@ -1557,6 +1761,14 @@ data DateHistogramResult = DateHistogramResult { dateKey :: Int
1557 , dateDocCount :: Int
1761 , dateDocCount :: Int
1558 , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1762 , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1559
1763
1764+data DateRangeResult = DateRangeResult { dateRangeKey :: Text
1765+ , dateRangeFrom :: Maybe UTCTime
1766+ , dateRangeFromAsString :: Maybe Text
1767+ , dateRangeTo :: Maybe UTCTime
1768+ , dateRangeToAsString :: Maybe Text
1769+ , dateRangeDocCount :: Int
1770+ , dateRangeAggs :: Maybe AggregationResults } deriving (Show, Eq, Generic, Typeable)
1771+
1560toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1772toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1561toTerms t a = M.lookup t a >>= deserialize
1773toTerms t a = M.lookup t a >>= deserialize
1562 where deserialize = parseMaybe parseJSON
1774 where deserialize = parseMaybe parseJSON
@@ -1575,6 +1787,11 @@ instance BucketAggregation DateHistogramResult where
1575 docCount = dateDocCount
1787 docCount = dateDocCount
1576 aggs = dateHistogramAggs
1788 aggs = dateHistogramAggs
1577
1789
1790+instance BucketAggregation DateRangeResult where
1791+ key = dateRangeKey
1792+ docCount = dateRangeDocCount
1793+ aggs = dateRangeAggs
1794+
1578instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1795instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1579 parseJSON (Object v) = Bucket <$>
1796 parseJSON (Object v) = Bucket <$>
1580 v .: "buckets"
1797 v .: "buckets"
@@ -1595,6 +1812,22 @@ instance FromJSON DateHistogramResult where
1595 v .:? "aggregations"
1812 v .:? "aggregations"
1596 parseJSON _ = mempty
1813 parseJSON _ = mempty
1597
1814
1815+instance FromJSON DateRangeResult where
1816+ parseJSON = withObject "DateRangeResult" parse
1817+ where parse v = DateRangeResult <$>
1818+ v .: "key" <*>
1819+ (fmap posixMS <$> v .:? "from") <*>
1820+ v .:? "from_as_string" <*>
1821+ (fmap posixMS <$> v .:? "to") <*>
1822+ v .:? "to_as_string" <*>
1823+ v .: "doc_count" <*>
1824+ v .:? "aggregations"
1825+
1826+instance FromJSON POSIXMS where
1827+ parseJSON = withScientific "POSIXMS" (return . parse)
1828+ where parse n = let n' = truncate n :: Integer
1829+ in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
1830+
1598instance Monoid Filter where
1831instance Monoid Filter where
1599 mempty = IdentityFilter
1832 mempty = IdentityFilter
1600 mappend a b = AndFilter [a, b] defaultCache
1833 mappend a b = AndFilter [a, b] defaultCache
@@ -1890,7 +2123,7 @@ instance FromJSON Query where
1890 <|> idsQuery `taggedWith` "ids"
2123 <|> idsQuery `taggedWith` "ids"
1891 <|> queryQueryStringQuery `taggedWith` "query_string"
2124 <|> queryQueryStringQuery `taggedWith` "query_string"
1892 <|> queryMatchQuery `taggedWith` "match"
2125 <|> queryMatchQuery `taggedWith` "match"
1893− <|> queryMultiMatchQuery --TODO: is this a precedence issue?
2126+ <|> queryMultiMatchQuery
1894 <|> queryBoolQuery `taggedWith` "bool"
2127 <|> queryBoolQuery `taggedWith` "bool"
1895 <|> queryBoostingQuery `taggedWith` "boosting"
2128 <|> queryBoostingQuery `taggedWith` "boosting"
1896 <|> queryCommonTermsQuery `taggedWith` "common"
2129 <|> queryCommonTermsQuery `taggedWith` "common"
@@ -2571,6 +2804,232 @@ instance ToJSON IndexSettings where
2571 ]
2804 ]
2572 ]
2805 ]
2573
2806
2807+instance FromJSON IndexSettings where
2808+ parseJSON = withObject "IndexSettings" parse
2809+ where parse o = do s <- o .: "settings"
2810+ i <- s .: "index"
2811+ IndexSettings <$> i .: "number_of_shards"
2812+ <*> i .: "number_of_replicas"
2813+
2814+instance ToJSON UpdatableIndexSetting where
2815+ toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
2816+ toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
2817+ toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
2818+ toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
2819+ toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
2820+ toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
2821+ toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
2822+ toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
2823+ toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
2824+ toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
2825+ toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
2826+ toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
2827+ toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
2828+ toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
2829+ toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
2830+ toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
2831+ toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
2832+ toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
2833+ toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
2834+ toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
2835+ toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
2836+ toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
2837+ toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
2838+ toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
2839+ toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
2840+ toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
2841+ toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
2842+ toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
2843+
2844+instance FromJSON UpdatableIndexSetting where
2845+ parseJSON = withObject "UpdatableIndexSetting" parse
2846+ where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
2847+ <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
2848+ <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
2849+ <|> indexConcurrency `taggedAt` ["index", "concurrency"]
2850+ <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
2851+ <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
2852+ <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
2853+ <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
2854+ <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
2855+ <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
2856+ <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
2857+ <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
2858+ <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
2859+ <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
2860+ <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
2861+ <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
2862+ <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
2863+ <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
2864+ <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
2865+ <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
2866+ <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
2867+ <|> compoundFormat `taggedAt` ["index", "compound_format"]
2868+ <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
2869+ <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
2870+ <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
2871+ <|> blocksRead `taggedAt` ["blocks", "read"]
2872+ <|> blocksWrite `taggedAt` ["blocks", "write"]
2873+ <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
2874+ where taggedAt f ks = taggedAt' f (Object o) ks
2875+ taggedAt' f v [] = f =<< (parseJSON v <|> (parseJSON =<< unStringlyTypeJSON v))
2876+ taggedAt' f v (k:ks) = withObject "Object" (\o -> do v' <- o .: k
2877+ taggedAt' f v' ks) v
2878+ numberOfReplicas = pure . NumberOfReplicas
2879+ autoExpandReplicas = pure . AutoExpandReplicas
2880+ refreshInterval = pure . RefreshInterval . ndtJSON
2881+ indexConcurrency = pure . IndexConcurrency
2882+ failOnMergeFailure = pure . FailOnMergeFailure
2883+ translogFlushThresholdOps = pure . TranslogFlushThresholdOps
2884+ translogFlushThresholdSize = pure . TranslogFlushThresholdSize
2885+ translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
2886+ translogDisableFlush = pure . TranslogDisableFlush
2887+ cacheFilterMaxSize = pure . CacheFilterMaxSize
2888+ cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
2889+ gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
2890+ routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
2891+ routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
2892+ routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
2893+ routingAllocationEnable = pure . RoutingAllocationEnable
2894+ routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
2895+ recoveryInitialShards = pure . RecoveryInitialShards
2896+ gcDeletes = pure . GCDeletes . ndtJSON
2897+ ttlDisablePurge = pure . TTLDisablePurge
2898+ translogFSType = pure . TranslogFSType
2899+ compoundFormat = pure . IndexCompoundFormat
2900+ compoundOnFlush = pure . IndexCompoundOnFlush
2901+ warmerEnabled = pure . WarmerEnabled
2902+ blocksReadOnly = pure . BlocksReadOnly
2903+ blocksRead = pure . BlocksRead
2904+ blocksWrite = pure . BlocksWrite
2905+ blocksMetaData = pure . BlocksMetaData
2906+
2907+instance FromJSON IndexSettingsSummary where
2908+ parseJSON = withObject "IndexSettingsSummary" parse
2909+ where parse o = case HM.toList o of
2910+ [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
2911+ <$> parseJSON v
2912+ <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
2913+ _ -> fail "Expected single-key object with index name"
2914+ redundant (NumberOfReplicas _) = True
2915+ redundant _ = False
2916+
2917+-- | For some reason in the settings API, all leaf values get returned
2918+-- as strings. This function attepmts to recover from this for all
2919+-- non-recursive JSON types. If nothing can be done or the same value
2920+-- would be return, it returns 'mzero'
2921+unStringlyTypeJSON :: MonadPlus m => Value -> m Value
2922+unStringlyTypeJSON (String "true") = return (Bool True)
2923+unStringlyTypeJSON (String "false") = return (Bool False)
2924+unStringlyTypeJSON (String "null") = return Null
2925+unStringlyTypeJSON (String t) = case readMay (T.unpack t) of
2926+ Just n -> return (Number n)
2927+ Nothing -> mzero
2928+unStringlyTypeJSON _ = mzero
2929+
2930+
2931+parseSettings :: Object -> Parser [UpdatableIndexSetting]
2932+parseSettings o = do
2933+ o' <- o .: "index"
2934+ -- slice the index object into singleton hashmaps and try to parse each
2935+ parses <- forM (HM.toList o') $ \(k, v) -> do
2936+ -- blocks are now nested into the "index" key, which is not how they're serialized
2937+ let atRoot = Object (HM.singleton k v)
2938+ let atIndex = Object (HM.singleton "index" atRoot)
2939+ optional (parseJSON atRoot <|> parseJSON atIndex)
2940+ return (catMaybes parses)
2941+
2942+oPath :: ToJSON a => NonEmpty Text -> a -> Value
2943+oPath (k :| []) v = object [k .= v]
2944+oPath (k:| (h:t)) v = object [k .= oPath (h :| t) v]
2945+
2946+attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
2947+attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
2948+ | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
2949+
2950+parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
2951+parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
2952+ where parse o = case HM.toList o of
2953+ [] -> fail "Expected non-empty list of NodeAttrFilters"
2954+ x:xs -> DT.mapM (uncurry parse') (x :| xs)
2955+ parse' n = withText "Text" $ \t ->
2956+ case T.splitOn "," t of
2957+ fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
2958+ [] -> fail "Expected non-empty list of filter values"
2959+
2960+instance ToJSON ReplicaBounds where
2961+ toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
2962+ toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
2963+ toJSON ReplicasUnbounded = Bool False
2964+
2965+instance FromJSON ReplicaBounds where
2966+ parseJSON v = withText "ReplicaBounds" parseText v
2967+ <|> withBool "ReplicaBounds" parseBool v
2968+ where parseText t = case T.splitOn "-" t of
2969+ [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
2970+ [a, b] -> ReplicasBounded <$> parseReadText a
2971+ <*> parseReadText b
2972+ _ -> fail ("Could not parse ReplicaBounds: " <> show t)
2973+ parseBool False = pure ReplicasUnbounded
2974+ parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
2975+
2976+instance ToJSON AllocationPolicy where
2977+ toJSON AllocAll = String "all"
2978+ toJSON AllocPrimaries = String "primaries"
2979+ toJSON AllocNewPrimaries = String "new_primaries"
2980+ toJSON AllocNone = String "none"
2981+
2982+instance FromJSON AllocationPolicy where
2983+ parseJSON = withText "AllocationPolicy" parse
2984+ where parse "all" = pure AllocAll
2985+ parse "primaries" = pure AllocPrimaries
2986+ parse "new_primaries" = pure AllocNewPrimaries
2987+ parse "none" = pure AllocNone
2988+ parse t = fail ("Invlaid AllocationPolicy: " <> show t)
2989+
2990+instance ToJSON InitialShardCount where
2991+ toJSON QuorumShards = String "quorum"
2992+ toJSON QuorumMinus1Shards = String "quorum-1"
2993+ toJSON FullShards = String "full"
2994+ toJSON FullMinus1Shards = String "full-1"
2995+ toJSON (ExplicitShards x) = toJSON x
2996+
2997+instance FromJSON InitialShardCount where
2998+ parseJSON v = withText "InitialShardCount" parseText v
2999+ <|> ExplicitShards <$> parseJSON v
3000+ where parseText "quorum" = pure QuorumShards
3001+ parseText "quorum-1" = pure QuorumMinus1Shards
3002+ parseText "full" = pure FullShards
3003+ parseText "full-1" = pure FullMinus1Shards
3004+ parseText _ = mzero
3005+
3006+instance ToJSON FSType where
3007+ toJSON FSSimple = "simple"
3008+ toJSON FSBuffered = "buffered"
3009+
3010+instance FromJSON FSType where
3011+ parseJSON = withText "FSType" parse
3012+ where parse "simple" = pure FSSimple
3013+ parse "buffered" = pure FSBuffered
3014+ parse t = fail ("Invalid FSType: " <> show t)
3015+
3016+instance ToJSON CompoundFormat where
3017+ toJSON (CompoundFileFormat x) = Bool x
3018+ toJSON (MergeSegmentVsTotalIndex x) = toJSON x
3019+
3020+instance FromJSON CompoundFormat where
3021+ parseJSON v = CompoundFileFormat <$> parseJSON v
3022+ <|> MergeSegmentVsTotalIndex <$> parseJSON v
3023+
3024+instance ToJSON NominalDiffTimeJSON where
3025+ toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
3026+
3027+instance FromJSON NominalDiffTimeJSON where
3028+ parseJSON = withText "NominalDiffTime" parse
3029+ where parse t = case T.takeEnd 1 t of
3030+ "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
3031+ _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
3032+
2574instance ToJSON IndexTemplate where
3033instance ToJSON IndexTemplate where
2575 toJSON (IndexTemplate p s m) = merge
3034 toJSON (IndexTemplate p s m) = merge
2576 (object [ "template" .= p
3035 (object [ "template" .= p
@@ -2610,7 +3069,7 @@ instance FromJSON IndexAliasesSummary where
2610 parseJSON = withObject "IndexAliasesSummary" parse
3069 parseJSON = withObject "IndexAliasesSummary" parse
2611 where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
3070 where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
2612 go ixn = withObject "index aliases" $ \ia -> do
3071 go ixn = withObject "index aliases" $ \ia -> do
2613− aliases <- ia .: "aliases"
3072+ aliases <- ia .:? "aliases" .!= mempty
2614 forM (HM.toList aliases) $ \(aName, v) -> do
3073 forM (HM.toList aliases) $ \(aName, v) -> do
2615 let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
3074 let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
2616 IndexAliasSummary indexAlias <$> parseJSON v
3075 IndexAliasSummary indexAlias <$> parseJSON v
@@ -3056,6 +3515,19 @@ instance FromJSON DocVersion where
3056 i <- parseJSON v
3515 i <- parseJSON v
3057 maybe (fail "DocVersion out of range") return $ mkDocVersion i
3516 maybe (fail "DocVersion out of range") return $ mkDocVersion i
3058
3517
3518+-- This insanity is because ES *sometimes* returns Replica/Shard counts as strings
3519+instance FromJSON ReplicaCount where
3520+ parseJSON v = parseAsInt v
3521+ <|> parseAsString v
3522+ where parseAsInt = fmap ReplicaCount . parseJSON
3523+ parseAsString = withText "ReplicaCount" (fmap ReplicaCount . parseReadText)
3524+
3525+instance FromJSON ShardCount where
3526+ parseJSON v = parseAsInt v
3527+ <|> parseAsString v
3528+ where parseAsInt = fmap ShardCount . parseJSON
3529+ parseAsString = withText "ShardCount" (fmap ShardCount . parseReadText)
3530+
3059instance Bounded DocVersion where
3531instance Bounded DocVersion where
3060 minBound = DocVersion 1
3532 minBound = DocVersion 1
3061 maxBound = DocVersion 9200000000000000000 -- 9.2e+18
3533 maxBound = DocVersion 9200000000000000000 -- 9.2e+18
tests/tests.hs+309−11
@@ -1,17 +1,22 @@
1+{-# LANGUAGE DefaultSignatures #-}
1{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE TemplateHaskell #-}
7{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeOperators #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
9{-# OPTIONS_GHC -fno-warn-orphans #-}
7module Main where
10module Main where
8
11
9import Control.Applicative
12import Control.Applicative
13+import Control.Error
10import Control.Exception
14import Control.Exception
11import Control.Monad
15import Control.Monad
12import Control.Monad.Reader
16import Control.Monad.Reader
13import Data.Aeson
17import Data.Aeson
14import Data.Aeson.Types (parseEither)
18import Data.Aeson.Types (parseEither)
19+import qualified Data.ByteString.Lazy.Char8 as BL8
15import Data.DeriveTH
20import Data.DeriveTH
16import qualified Data.HashMap.Strict as HM
21import qualified Data.HashMap.Strict as HM
17import Data.List (nub)
22import Data.List (nub)
@@ -23,13 +28,13 @@ import Data.Monoid
23import Data.Proxy
28import Data.Proxy
24import Data.Text (Text)
29import Data.Text (Text)
25import qualified Data.Text as T
30import qualified Data.Text as T
26−import Data.Time.Calendar (Day (..))
31+import Data.Time.Calendar (Day (..), fromGregorian)
27−import Data.Time.Clock (UTCTime (..),
32+import Data.Time.Clock (NominalDiffTime, UTCTime (..),
28 secondsToDiffTime)
33 secondsToDiffTime)
29import Data.Typeable
34import Data.Typeable
30import qualified Data.Vector as V
35import qualified Data.Vector as V
31import Database.Bloodhound
36import Database.Bloodhound
32−import GHC.Generics (Generic)
37+import GHC.Generics as G
33import Network.HTTP.Client hiding (Proxy)
38import Network.HTTP.Client hiding (Proxy)
34import qualified Network.HTTP.Types.Status as NHTS
39import qualified Network.HTTP.Types.Status as NHTS
35import Prelude hiding (filter)
40import Prelude hiding (filter)
@@ -38,7 +43,6 @@ import Test.QuickCheck.Property.Monoid (T (..), eq, prop_Monoid)
38
43
39import Test.Hspec.QuickCheck (prop)
44import Test.Hspec.QuickCheck (prop)
40import Test.QuickCheck
45import Test.QuickCheck
41−import Test.QuickCheck.Instances ()
42
46
43testServer :: Server
47testServer :: Server
44testServer = Server "http://localhost:9200"
48testServer = Server "http://localhost:9200"
@@ -109,9 +113,13 @@ is v = testServerBranch >>= \x -> return $ x == Just (serverBranch v)
109when' :: Monad m => m Bool -> m () -> m ()
113when' :: Monad m => m Bool -> m () -> m ()
110when' b f = b >>= \x -> when x f
114when' b f = b >>= \x -> when x f
111
115
116+(==~) :: (ApproxEq a, Show a) => a -> a -> Property
117+a ==~ b = counterexample (show a ++ " !=~ " ++ show b) (a =~ b)
118+
112−propJSON :: forall a. (Arbitrary a, ToJSON a, FromJSON a, Show a, Eq a, Typeable a) => Proxy a -> Spec
119+propJSON :: forall a. (Arbitrary a, ToJSON a, FromJSON a, Show a, ApproxEq a, Typeable a) => Proxy a -> Spec
113propJSON _ = prop testName $ \(a :: a) ->
120propJSON _ = prop testName $ \(a :: a) ->
121+ let jsonStr = "via " <> BL8.unpack (encode a)
114− parseEither parseJSON (toJSON a) === Right a
122+ in counterexample jsonStr (parseEither parseJSON (toJSON a) ==~ Right a)
115 where testName = show ty <> " FromJSON/ToJSON roundtrips"
123 where testName = show ty <> " FromJSON/ToJSON roundtrips"
116 ty = typeOf (undefined :: a)
124 ty = typeOf (undefined :: a)
117
125
@@ -276,30 +284,224 @@ instance FromJSON BulkTest where
276instance ToJSON BulkTest where
284instance ToJSON BulkTest where
277 toJSON = genericToJSON defaultOptions
285 toJSON = genericToJSON defaultOptions
278
286
287+class GApproxEq f where
288+ gApproxEq :: f a -> f a -> Bool
289+
290+-- | Unit type
291+instance GApproxEq U1 where
292+ gApproxEq U1 U1 = True
293+
294+-- | Sum type, ensure same constructors, recurse
295+instance (GApproxEq a, GApproxEq b) => GApproxEq (a :+: b) where
296+ gApproxEq (L1 a) (L1 b) = gApproxEq a b
297+ gApproxEq (R1 a) (R1 b) = gApproxEq a b
298+ gApproxEq _ _ = False
299+
300+-- | Product type, ensure each field is approx eq
301+instance (GApproxEq a, GApproxEq b) => GApproxEq (a :*: b) where
302+ gApproxEq (a1 :*: b1) (a2 :*: b2) = gApproxEq a1 a2 && gApproxEq b1 b2
303+
304+-- | Value type, actually check the values for approx equality
305+instance (ApproxEq a) => GApproxEq (K1 i a) where
306+ gApproxEq (K1 a) (K1 b) = a =~ b
307+
308+instance (GApproxEq f) => GApproxEq (M1 i t f) where
309+ gApproxEq (M1 a) (M1 b) = gApproxEq a b
310+
311+-- | Typeclass for "equal where it matters". Use this to specify
312+-- less-strict equivalence for things such as lists that can wind up
313+-- in an unpredictable order
314+class ApproxEq a where
315+ (=~) :: a -> a -> Bool
316+ default (=~) :: (Generic a, GApproxEq (Rep a)) => a -> a -> Bool
317+ a =~ b = gApproxEq (G.from a) (G.from b)
318+
319+instance ApproxEq NominalDiffTime where (=~) = (==)
320+instance ApproxEq UTCTime where (=~) = (==)
321+instance ApproxEq Text where (=~) = (==)
322+instance ApproxEq Bool where (=~) = (==)
323+instance ApproxEq Int where (=~) = (==)
324+instance ApproxEq Double where (=~) = (==)
325+instance ApproxEq a => ApproxEq (NonEmpty a)
326+instance ApproxEq a => ApproxEq (Maybe a)
327+instance ApproxEq GeoPoint
328+instance ApproxEq Regexp
329+instance ApproxEq RangeValue
330+instance ApproxEq LessThan
331+instance ApproxEq LessThanEq
332+instance ApproxEq LessThanD
333+instance ApproxEq LessThanEqD
334+instance ApproxEq GreaterThan
335+instance ApproxEq GreaterThanEq
336+instance ApproxEq GreaterThanD
337+instance ApproxEq GreaterThanEqD
338+instance ApproxEq MinimumMatchHighLow
339+instance ApproxEq RegexpFlag
340+instance ApproxEq RegexpFlags
341+instance ApproxEq NullValue
342+instance ApproxEq Version
343+instance ApproxEq DistanceRange
344+instance ApproxEq IndexName
345+instance ApproxEq MappingName
346+instance ApproxEq DocId
347+instance ApproxEq IndexAliasRouting
348+instance ApproxEq RoutingValue
349+instance ApproxEq ShardCount
350+instance ApproxEq ReplicaCount
351+instance ApproxEq TemplateName
352+instance ApproxEq TemplatePattern
353+instance ApproxEq QueryString
354+instance ApproxEq FieldName
355+instance ApproxEq CacheName
356+instance ApproxEq CacheKey
357+instance ApproxEq Existence
358+instance ApproxEq CutoffFrequency
359+instance ApproxEq Analyzer
360+instance ApproxEq Lenient
361+instance ApproxEq Tiebreaker
362+instance ApproxEq Boost
363+instance ApproxEq BoostTerms
364+instance ApproxEq MaxExpansions
365+instance ApproxEq MinimumMatch
366+instance ApproxEq DisableCoord
367+instance ApproxEq IgnoreTermFrequency
368+instance ApproxEq MinimumTermFrequency
369+instance ApproxEq MaxQueryTerms
370+instance ApproxEq Fuzziness
371+instance ApproxEq PrefixLength
372+instance ApproxEq TypeName
373+instance ApproxEq PercentMatch
374+instance ApproxEq StopWord
375+instance ApproxEq QueryPath
376+instance ApproxEq AllowLeadingWildcard
377+instance ApproxEq LowercaseExpanded
378+instance ApproxEq EnablePositionIncrements
379+instance ApproxEq AnalyzeWildcard
380+instance ApproxEq GeneratePhraseQueries
381+instance ApproxEq Locale
382+instance ApproxEq MaxWordLength
383+instance ApproxEq MinWordLength
384+instance ApproxEq PhraseSlop
385+instance ApproxEq MinDocFrequency
386+instance ApproxEq MaxDocFrequency
387+instance ApproxEq Filter
388+instance ApproxEq Query
389+instance ApproxEq SimpleQueryStringQuery
390+instance ApproxEq FieldOrFields
391+instance ApproxEq SimpleQueryFlag
392+instance ApproxEq RegexpQuery
393+instance ApproxEq QueryStringQuery
394+instance ApproxEq RangeQuery
395+instance ApproxEq PrefixQuery
396+instance ApproxEq NestedQuery
397+instance ApproxEq MoreLikeThisFieldQuery
398+instance ApproxEq MoreLikeThisQuery
399+instance ApproxEq IndicesQuery
400+instance ApproxEq HasParentQuery
401+instance ApproxEq HasChildQuery
402+instance ApproxEq FuzzyQuery
403+instance ApproxEq FuzzyLikeFieldQuery
404+instance ApproxEq FuzzyLikeThisQuery
405+instance ApproxEq FilteredQuery
406+instance ApproxEq DisMaxQuery
407+instance ApproxEq CommonTermsQuery
408+instance ApproxEq CommonMinimumMatch
409+instance ApproxEq BoostingQuery
410+instance ApproxEq BoolQuery
411+instance ApproxEq MatchQuery
412+instance ApproxEq MultiMatchQueryType
413+instance ApproxEq BooleanOperator
414+instance ApproxEq ZeroTermsQuery
415+instance ApproxEq MatchQueryType
416+instance ApproxEq AliasRouting
417+instance ApproxEq IndexAliasCreate
418+instance ApproxEq SearchAliasRouting
419+instance ApproxEq ScoreType
420+instance ApproxEq Distance
421+instance ApproxEq DistanceUnit
422+instance ApproxEq DistanceType
423+instance ApproxEq OptimizeBbox
424+instance ApproxEq GeoBoundingBoxConstraint
425+instance ApproxEq GeoFilterType
426+instance ApproxEq GeoBoundingBox
427+instance ApproxEq LatLon
428+instance ApproxEq RangeExecution
429+instance ApproxEq FSType
430+instance ApproxEq CompoundFormat
431+instance ApproxEq InitialShardCount
432+instance ApproxEq Bytes
433+instance ApproxEq ReplicaBounds
434+instance ApproxEq Term
435+instance ApproxEq BoolMatch
436+instance ApproxEq MultiMatchQuery
437+instance ApproxEq IndexSettings
438+instance ApproxEq AllocationPolicy
439+instance ApproxEq Char
440+instance ApproxEq a => ApproxEq [a] where
441+ as =~ bs = and (zipWith (=~) as bs)
442+instance (ApproxEq l, ApproxEq r) => ApproxEq (Either l r) where
443+ Left a =~ Left b = a =~ b
444+ Right a =~ Right b = a =~ b
445+ _ =~ _ = False
446+instance ApproxEq NodeAttrFilter
447+instance ApproxEq NodeAttrName
448+
449+-- | Due to the way nodeattrfilters get serialized here, they may come
450+-- out in a different order, but they are morally equivalent
451+instance ApproxEq UpdatableIndexSetting where
452+ RoutingAllocationInclude a =~ RoutingAllocationInclude b =
453+ NE.sort a =~ NE.sort b
454+ RoutingAllocationExclude a =~ RoutingAllocationExclude b =
455+ NE.sort a =~ NE.sort b
456+ RoutingAllocationRequire a =~ RoutingAllocationRequire b =
457+ NE.sort a =~ NE.sort b
458+ a =~ b = a == b
459+
460+
279noDuplicates :: Eq a => [a] -> Bool
461noDuplicates :: Eq a => [a] -> Bool
280noDuplicates xs = nub xs == xs
462noDuplicates xs = nub xs == xs
281
463
464+instance Arbitrary NominalDiffTime where
465+ arbitrary = fromInteger <$> arbitrary
466+
467+instance (Arbitrary k, Ord k, Arbitrary v) => Arbitrary (M.Map k v) where
468+ arbitrary = M.fromList <$> arbitrary
469+
470+instance Arbitrary Text where
471+ arbitrary = T.pack <$> arbitrary
472+
473+instance Arbitrary UTCTime where
474+ arbitrary = UTCTime
475+ <$> arbitrary
476+ <*> (fromRational . toRational <$> choose (0::Double, 86400))
477+
478+instance Arbitrary Day where
479+ arbitrary = ModifiedJulianDay <$> (2000 +) <$> arbitrary
480+ shrink = (ModifiedJulianDay <$>) . shrink . toModifiedJulianDay
481+
282instance Arbitrary a => Arbitrary (NonEmpty a) where
482instance Arbitrary a => Arbitrary (NonEmpty a) where
283 arbitrary = liftA2 (:|) arbitrary arbitrary
483 arbitrary = liftA2 (:|) arbitrary arbitrary
284
484
285arbitraryScore :: Gen Score
485arbitraryScore :: Gen Score
286arbitraryScore = fmap getPositive <$> arbitrary
486arbitraryScore = fmap getPositive <$> arbitrary
287
487
288−instance Arbitrary a => Arbitrary (Hit a) where
488+instance (Arbitrary a, Typeable a) => Arbitrary (Hit a) where
289 arbitrary = Hit <$> arbitrary
489 arbitrary = Hit <$> arbitrary
290 <*> arbitrary
490 <*> arbitrary
291 <*> arbitrary
491 <*> arbitrary
292 <*> arbitraryScore
492 <*> arbitraryScore
293 <*> arbitrary
493 <*> arbitrary
294 <*> arbitrary
494 <*> arbitrary
495+ shrink = genericShrink
295
496
296
497
297−instance Arbitrary a => Arbitrary (SearchHits a) where
498+instance (Arbitrary a, Typeable a) => Arbitrary (SearchHits a) where
298 arbitrary = reduceSize $ do
499 arbitrary = reduceSize $ do
299 tot <- getPositive <$> arbitrary
500 tot <- getPositive <$> arbitrary
300 score <- arbitraryScore
501 score <- arbitraryScore
301 hs <- arbitrary
502 hs <- arbitrary
302 return $ SearchHits tot score hs
503 return $ SearchHits tot score hs
504+ shrink = genericShrink
303
505
304reduceSize :: Gen a -> Gen a
506reduceSize :: Gen a -> Gen a
305reduceSize f = sized $ \n -> resize (n `div` 2) f
507reduceSize f = sized $ \n -> resize (n `div` 2) f
@@ -337,9 +539,13 @@ instance Arbitrary AliasRouting where
337 <$> (Just <$> arbitrary)
539 <$> (Just <$> arbitrary)
338 <*> (Just <$> arbitrary)
540 <*> (Just <$> arbitrary)
339 allAlias = AllAliasRouting <$> arbitrary
541 allAlias = AllAliasRouting <$> arbitrary
542+ shrink = genericShrink
543+
340
544
341instance Arbitrary FieldName where
545instance Arbitrary FieldName where
342 arbitrary = FieldName . T.pack <$> listOf1 arbitraryAlphaNum
546 arbitrary = FieldName . T.pack <$> listOf1 arbitraryAlphaNum
547+ shrink = genericShrink
548+
343
549
344instance Arbitrary RegexpFlags where
550instance Arbitrary RegexpFlags where
345 arbitrary = oneof [ pure AllRegexpFlags
551 arbitrary = oneof [ pure AllRegexpFlags
@@ -347,9 +553,12 @@ instance Arbitrary RegexpFlags where
347 , SomeRegexpFlags <$> genUniqueFlags
553 , SomeRegexpFlags <$> genUniqueFlags
348 ]
554 ]
349 where genUniqueFlags = NE.fromList . nub <$> listOf1 arbitrary
555 where genUniqueFlags = NE.fromList . nub <$> listOf1 arbitrary
556+ shrink = genericShrink
557+
350
558
351instance Arbitrary IndexAliasCreate where
559instance Arbitrary IndexAliasCreate where
352 arbitrary = IndexAliasCreate <$> arbitrary <*> reduceSize arbitrary
560 arbitrary = IndexAliasCreate <$> arbitrary <*> reduceSize arbitrary
561+ shrink = genericShrink
353
562
354instance Arbitrary Query where
563instance Arbitrary Query where
355 arbitrary = reduceSize $ oneof [ TermQuery <$> arbitrary <*> arbitrary
564 arbitrary = reduceSize $ oneof [ TermQuery <$> arbitrary <*> arbitrary
@@ -380,6 +589,7 @@ instance Arbitrary Query where
380 , QueryRangeQuery <$> arbitrary
589 , QueryRangeQuery <$> arbitrary
381 , QueryRegexpQuery <$> arbitrary
590 , QueryRegexpQuery <$> arbitrary
382 ]
591 ]
592+ shrink = genericShrink
383
593
384instance Arbitrary Filter where
594instance Arbitrary Filter where
385 arbitrary = reduceSize $ oneof [ AndFilter <$> arbitrary <*> arbitrary
595 arbitrary = reduceSize $ oneof [ AndFilter <$> arbitrary <*> arbitrary
@@ -400,6 +610,29 @@ instance Arbitrary Filter where
400 , RangeFilter <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
610 , RangeFilter <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
401 , RegexpFilter <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
611 , RegexpFilter <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
402 , TermFilter <$> arbitrary <*> arbitrary]
612 , TermFilter <$> arbitrary <*> arbitrary]
613+ shrink = genericShrink
614+
615+instance Arbitrary ReplicaBounds where
616+ arbitrary = oneof [ replicasBounded
617+ , replicasLowerBounded
618+ , pure ReplicasUnbounded
619+ ]
620+ where replicasBounded = do Positive a <- arbitrary
621+ Positive b <- arbitrary
622+ return (ReplicasBounded a b)
623+ replicasLowerBounded = do Positive a <- arbitrary
624+ return (ReplicasLowerBounded a)
625+
626+instance Arbitrary NodeAttrName where
627+ arbitrary = NodeAttrName . T.pack . getNonEmpty <$> arbitrary
628+
629+
630+instance Arbitrary NodeAttrFilter where
631+ arbitrary = do
632+ n <- arbitrary
633+ s:ss <- listOf1 (listOf1 arbitraryAlphaNum)
634+ let ts = T.pack <$> s :| ss
635+ return (NodeAttrFilter n ts)
403
636
404$(derive makeArbitrary ''IndexName)
637$(derive makeArbitrary ''IndexName)
405$(derive makeArbitrary ''MappingName)
638$(derive makeArbitrary ''MappingName)
@@ -499,6 +732,13 @@ $(derive makeArbitrary ''RangeExecution)
499$(derive makeArbitrary ''RegexpFlag)
732$(derive makeArbitrary ''RegexpFlag)
500$(derive makeArbitrary ''BoolMatch)
733$(derive makeArbitrary ''BoolMatch)
501$(derive makeArbitrary ''Term)
734$(derive makeArbitrary ''Term)
735+$(derive makeArbitrary ''IndexSettings)
736+$(derive makeArbitrary ''UpdatableIndexSetting)
737+$(derive makeArbitrary ''Bytes)
738+$(derive makeArbitrary ''AllocationPolicy)
739+$(derive makeArbitrary ''InitialShardCount)
740+$(derive makeArbitrary ''FSType)
741+$(derive makeArbitrary ''CompoundFormat)
502
742
503
743
504main :: IO ()
744main :: IO ()
@@ -917,6 +1157,43 @@ main = hspec $ do
917 fmap aggregations res `shouldBe` Right (Just (M.fromList [ docCountPair "bitemyapps" 1
1157 fmap aggregations res `shouldBe` Right (Just (M.fromList [ docCountPair "bitemyapps" 1
918 , docCountPair "notmyapps" 1
1158 , docCountPair "notmyapps" 1
919 ]))
1159 ]))
1160+ it "can execute date_range aggregations" $ withTestEnv $ do
1161+ let now = fromGregorian 2015 3 14
1162+ let ltAMonthAgo = UTCTime (fromGregorian 2015 3 1) 0
1163+ let ltAWeekAgo = UTCTime (fromGregorian 2015 3 10) 0
1164+ let oldDoc = exampleTweet { postDate = ltAMonthAgo }
1165+ let newDoc = exampleTweet { postDate = ltAWeekAgo }
1166+ _ <- indexDocument testIndex testMapping defaultIndexDocumentSettings oldDoc (DocId "1")
1167+ _ <- indexDocument testIndex testMapping defaultIndexDocumentSettings newDoc (DocId "2")
1168+ _ <- refreshIndex testIndex
1169+ let thisMonth = DateRangeFrom (DateMathExpr (DMDate now) [SubtractTime 1 DMMonth])
1170+ let thisWeek = DateRangeFrom (DateMathExpr (DMDate now) [SubtractTime 1 DMWeek])
1171+ let agg = DateRangeAggregation (FieldName "postDate") Nothing (thisMonth :| [thisWeek])
1172+ let ags = mkAggregations "date_ranges" (DateRangeAgg agg)
1173+ let search = mkAggregateSearch Nothing ags
1174+ res <- searchTweets search
1175+ liftIO $ hitsTotal . searchHits <$> res `shouldBe` Right 2
1176+ let bucks = do magrs <- fmapL show (aggregations <$> res)
1177+ agrs <- note "no aggregations returned" magrs
1178+ rawBucks <- note "no date_ranges aggregation" $ M.lookup "date_ranges" agrs
1179+ parseEither parseJSON rawBucks
1180+ let fromMonthT = UTCTime (fromGregorian 2015 2 14) 0
1181+ let fromWeekT = UTCTime (fromGregorian 2015 3 7) 0
1182+ liftIO $ buckets <$> bucks `shouldBe` Right [ DateRangeResult "2015-02-14T00:00:00.000Z-*"
1183+ (Just fromMonthT)
1184+ (Just "2015-02-14T00:00:00.000Z")
1185+ Nothing
1186+ Nothing
1187+ 2
1188+ Nothing
1189+ , DateRangeResult "2015-03-07T00:00:00.000Z-*"
1190+ (Just fromWeekT)
1191+ (Just "2015-03-07T00:00:00.000Z")
1192+ Nothing
1193+ Nothing
1194+ 1
1195+ Nothing
1196+ ]
920
1197
921 it "returns date histogram aggregation results" $ withTestEnv $ do
1198 it "returns date histogram aggregation results" $ withTestEnv $ do
922 _ <- insertData
1199 _ <- insertData
@@ -1073,7 +1350,7 @@ main = hspec $ do
1073 withTestEnv $ do
1350 withTestEnv $ do
1074 resetIndex
1351 resetIndex
1075 resp <- updateIndexAliases (action :| [])
1352 resp <- updateIndexAliases (action :| [])
1076− liftIO $ NHTS.statusCode (responseStatus resp) `shouldBe` 200
1353+ liftIO $ validateStatus resp 200
1077 let cleanup = withTestEnv (updateIndexAliases (RemoveAlias alias :| []))
1354 let cleanup = withTestEnv (updateIndexAliases (RemoveAlias alias :| []))
1078 (do aliases <- withTestEnv getIndexAliases
1355 (do aliases <- withTestEnv getIndexAliases
1079 let expected = IndexAliasSummary alias create
1356 let expected = IndexAliasSummary alias create
@@ -1094,7 +1371,7 @@ main = hspec $ do
1094 withTestEnv $ do
1371 withTestEnv $ do
1095 resetIndex
1372 resetIndex
1096 resp <- updateIndexAliases (action :| [])
1373 resp <- updateIndexAliases (action :| [])
1097− liftIO $ NHTS.statusCode (responseStatus resp) `shouldBe` 200
1374+ liftIO $ validateStatus resp 200
1098 let cleanup = withTestEnv (updateIndexAliases (RemoveAlias alias :| []))
1375 let cleanup = withTestEnv (updateIndexAliases (RemoveAlias alias :| []))
1099 (do aliases <- withTestEnv getIndexAliases
1376 (do aliases <- withTestEnv getIndexAliases
1100 let expected = IndexAliasSummary alias create
1377 let expected = IndexAliasSummary alias create
@@ -1103,6 +1380,20 @@ main = hspec $ do
1103 L.find ((== alias) . indexAliasSummaryAlias) summs `shouldBe` Just expected
1380 L.find ((== alias) . indexAliasSummaryAlias) summs `shouldBe` Just expected
1104 Left e -> expectationFailure ("Expected an IndexAliasesSummary but got " <> show e)) `finally` cleanup
1381 Left e -> expectationFailure ("Expected an IndexAliasesSummary but got " <> show e)) `finally` cleanup
1105
1382
1383+ describe "Index Settings" $ do
1384+ it "persists settings" $ withTestEnv $ do
1385+ _ <- deleteExampleIndex
1386+ _ <- createExampleIndex
1387+ let updates = BlocksWrite False :| []
1388+ updateResp <- updateIndexSettings updates testIndex
1389+ liftIO $ validateStatus updateResp 200
1390+ getResp <- getIndexSettings testIndex
1391+ liftIO $
1392+ getResp `shouldBe` Right (IndexSettingsSummary
1393+ testIndex
1394+ (IndexSettings (ShardCount 1) (ReplicaCount 0))
1395+ (NE.toList updates))
1396+
1106 describe "JSON instances" $ do
1397 describe "JSON instances" $ do
1107 propJSON (Proxy :: Proxy Version)
1398 propJSON (Proxy :: Proxy Version)
1108 propJSON (Proxy :: Proxy IndexName)
1399 propJSON (Proxy :: Proxy IndexName)
@@ -1126,7 +1417,6 @@ main = hspec $ do
1126 propJSON (Proxy :: Proxy Tiebreaker)
1417 propJSON (Proxy :: Proxy Tiebreaker)
1127 propJSON (Proxy :: Proxy Boost)
1418 propJSON (Proxy :: Proxy Boost)
1128 propJSON (Proxy :: Proxy BoostTerms)
1419 propJSON (Proxy :: Proxy BoostTerms)
1129− propJSON (Proxy :: Proxy MaxExpansions)
1130 propJSON (Proxy :: Proxy MinimumMatch)
1420 propJSON (Proxy :: Proxy MinimumMatch)
1131 propJSON (Proxy :: Proxy DisableCoord)
1421 propJSON (Proxy :: Proxy DisableCoord)
1132 propJSON (Proxy :: Proxy IgnoreTermFrequency)
1422 propJSON (Proxy :: Proxy IgnoreTermFrequency)
@@ -1199,3 +1489,11 @@ main = hspec $ do
1199 propJSON (Proxy :: Proxy BoolMatch)
1489 propJSON (Proxy :: Proxy BoolMatch)
1200 propJSON (Proxy :: Proxy Term)
1490 propJSON (Proxy :: Proxy Term)
1201 propJSON (Proxy :: Proxy MultiMatchQuery)
1491 propJSON (Proxy :: Proxy MultiMatchQuery)
1492+ propJSON (Proxy :: Proxy IndexSettings)
1493+ propJSON (Proxy :: Proxy UpdatableIndexSetting)
1494+ propJSON (Proxy :: Proxy ReplicaBounds)
1495+ propJSON (Proxy :: Proxy Bytes)
1496+ propJSON (Proxy :: Proxy AllocationPolicy)
1497+ propJSON (Proxy :: Proxy InitialShardCount)
1498+ propJSON (Proxy :: Proxy FSType)
1499+ propJSON (Proxy :: Proxy CompoundFormat)