@@ -1,7 +1,10 @@
1+{-# LANGUAGE DeriveDataTypeable #-}
2+{-# LANGUAGE DeriveGeneric #-}
13{-# LANGUAGE FlexibleContexts #-}
24{-# LANGUAGE FlexibleInstances #-}
35{-# LANGUAGE GeneralizedNewtypeDeriving #-}
46{-# LANGUAGE MultiParamTypeClasses #-}
7+-- {-# LANGUAGE NoMonomorphismRestriction #-}
58{-# LANGUAGE NamedFieldPuns #-}
69{-# LANGUAGE OverloadedStrings #-}
710{-# LANGUAGE RecordWildCards #-}
@@ -10,11 +13,11 @@
1013-------------------------------------------------------------------------------
1114-- |
1215-- Module : Database.Bloodhound.Types
13−-- Copyright : (C) 2014, 2018 Chris Allen
16+-- Copyright : (C) 2014, 2015, 2016 Chris Allen
1417-- License : BSD-style (see the file LICENSE)
1518-- Maintainer : Chris Allen <cma@bitemyapp.com
1619-- Stability : provisional
17−-- Portability : RecordWildCards
20+-- Portability : DeriveGeneric, RecordWildCards
1821--
1922-- Data types for describing actions and data structures performed to interact
2023-- with Elasticsearch. The two main buckets your queries against Elasticsearch
@@ -42,15 +45,11 @@ module Database.Bloodhound.Types
4245 , mkTermsAggregation
4346 , mkTermsScriptAggregation
4447 , mkDateHistogram
45− , mkCardinalityAggregation
4648 , mkDocVersion
47− , mkStatsAggregation
48− , mkExtendedStatsAggregation
4949 , docVersionNumber
5050 , toMissing
5151 , toTerms
5252 , toDateHistogram
53− , toTopHits
5453 , omitNulls
5554 , BH(..)
5655 , runBH
@@ -61,9 +60,6 @@ module Database.Bloodhound.Types
6160 , mkBHEnv
6261 , MonadBH(..)
6362 , Version(..)
64− , VersionNumber(..)
65− , MaybeNA(..)
66− , BuildHash(..)
6763 , Status(..)
6864 , Existence(..)
6965 , NullValue(..)
@@ -71,7 +67,6 @@ module Database.Bloodhound.Types
7167 , UpdatableIndexSetting(..)
7268 , IndexSettingsSummary(..)
7369 , AllocationPolicy(..)
74− , Compression(..)
7570 , ReplicaBounds(..)
7671 , Bytes(..)
7772 , gigabytes
@@ -120,8 +115,8 @@ module Database.Bloodhound.Types
120115 , Pattern(..)
121116 , ShardResult(..)
122117 , Hit(..)
123− , HitFields(..)
124118 , Filter(..)
119+ , Seminearring(..)
125120 , BoolMatch(..)
126121 , Term(..)
127122 , GeoPoint(..)
@@ -148,22 +143,11 @@ module Database.Bloodhound.Types
148143 , RegexpFlags(..)
149144 , RegexpFlag(..)
150145 , FieldName(..)
151− , ScriptFields(..)
152− , ScriptFieldName
153− , ScriptFieldValue
154146 , Script(..)
155− , ScriptLanguage(..)
156− , ScriptInline(..)
157− , ScriptId(..)
158− , ScriptParams(..)
159− , ScriptParamName
160− , ScriptParamValue
161147 , IndexName(..)
162148 , IndexSelection(..)
163− , NodeSelection(..)
164− , NodeSelector(..)
165− , ForceMergeIndexSettings(..)
166− , defaultForceMergeIndexSettings
149+ , IndexOptimizationSettings(..)
150+ , defaultIndexOptimizationSettings
167151 , TemplateName(..)
168152 , TemplatePattern(..)
169153 , MappingName(..)
@@ -187,19 +171,8 @@ module Database.Bloodhound.Types
187171 , BoolQuery(..)
188172 , BoostingQuery(..)
189173 , CommonTermsQuery(..)
190− , FunctionScoreQuery(..)
191− , BoostMode(..)
192− , ScoreMode(..)
193− , FunctionScoreFunctions(..)
194− , ComponentFunctionScoreFunction(..)
195− , FunctionScoreFunction(..)
196− , Weight(..)
197− , Seed(..)
198− , FieldValueFactor(..)
199− , Factor(..)
200− , FactorModifier(..)
201− , FactorMissingFieldValue(..)
202174 , DisMaxQuery(..)
175+ , FilteredQuery(..)
203176 , FuzzyLikeThisQuery(..)
204177 , FuzzyLikeFieldQuery(..)
205178 , FuzzyQuery(..)
@@ -215,16 +188,10 @@ module Database.Bloodhound.Types
215188 , RangeQuery(..)
216189 , RegexpQuery(..)
217190 , QueryString(..)
218− , TemplateQueryInline(..)
219− , TemplateQueryKeyValuePairs(..)
220− , WildcardQuery(..)
221191 , BooleanOperator(..)
222192 , ZeroTermsQuery(..)
223193 , CutoffFrequency(..)
224194 , Analyzer(..)
225− , Tokenizer(..)
226− , TokenFilter(..)
227− , CharFilter(..)
228195 , MaxExpansions(..)
229196 , Lenient(..)
230197 , MatchQueryType(..)
@@ -285,59 +252,12 @@ module Database.Bloodhound.Types
285252 , SnapshotNodeVerification(..)
286253 , FullNodeId(..)
287254 , NodeName(..)
288− , ClusterName(..)
289− , NodesInfo(..)
290− , NodesStats(..)
291− , NodeStats(..)
292− , NodeBreakersStats(..)
293− , NodeBreakerStats(..)
294− , NodeHTTPStats(..)
295− , NodeTransportStats(..)
296− , NodeFSStats(..)
297− , NodeDataPathStats(..)
298− , NodeFSTotalStats(..)
299− , NodeNetworkStats(..)
300− , NodeThreadPoolsStats(..)
301− , NodeThreadPoolStats(..)
302− , NodeJVMStats(..)
303− , JVMBufferPoolStats(..)
304− , JVMGCStats(..)
305− , JVMPoolStats(..)
306− , NodeProcessStats(..)
307− , NodeOSStats(..)
308− , LoadAvgs(..)
309− , NodeIndicesStats(..)
310− , EsAddress(..)
311− , PluginName(..)
312− , NodeInfo(..)
313− , NodePluginInfo(..)
314− , NodeHTTPInfo(..)
315− , NodeTransportInfo(..)
316− , BoundTransportAddress(..)
317− , NodeNetworkInfo(..)
318− , MacAddress(..)
319− , NetworkInterfaceName(..)
320− , NodeNetworkInterface(..)
321− , NodeThreadPoolsInfo(..)
322− , NodeThreadPoolInfo(..)
323− , ThreadPoolSize(..)
324− , ThreadPoolType(..)
325− , NodeJVMInfo(..)
326− , JVMMemoryPool(..)
327− , JVMGCCollector(..)
328− , JVMMemoryInfo(..)
329− , PID(..)
330− , NodeOSInfo(..)
331− , CPUInfo(..)
332− , NodeProcessInfo(..)
333255 , FsSnapshotRepo(..)
334256 , SnapshotCreateSettings(..)
335257 , defaultSnapshotCreateSettings
336258 , SnapshotSelection(..)
337259 , SnapshotPattern(..)
338260 , SnapshotInfo(..)
339− , SnapshotShardFailure(..)
340− , ShardId(..)
341261 , SnapshotName(..)
342262 , SnapshotState(..)
343263 , SnapshotRestoreSettings(..)
@@ -347,19 +267,6 @@ module Database.Bloodhound.Types
347267 , RRGroupRefNum
348268 , rrGroupRefNum
349269 , mkRRGroupRefNum
350− , RestoreIndexSettings(..)
351− , Suggest(..)
352− , SuggestType(..)
353− , PhraseSuggester(..)
354− , PhraseSuggesterHighlighter(..)
355− , PhraseSuggesterCollate(..)
356− , mkPhraseSuggester
357− , SuggestOptions(..)
358− , SuggestResponse(..)
359− , NamedSuggestionResponse(..)
360− , DirectGenerators(..)
361− , mkDirectGenerators
362− , DirectGeneratorSuggestModeTypes (..)
363270
364271 , Aggregation(..)
365272 , Aggregations
@@ -371,8 +278,6 @@ module Database.Bloodhound.Types
371278 , MissingAggregation(..)
372279 , ValueCountAggregation(..)
373280 , FilterAggregation(..)
374− , CardinalityAggregation(..)
375− , MaxAggregation(..)
376281 , DateHistogramAggregation(..)
377282 , DateRangeAggregation(..)
378283 , DateRangeAggRange(..)
@@ -380,9 +285,6 @@ module Database.Bloodhound.Types
380285 , DateMathAnchor(..)
381286 , DateMathModifier(..)
382287 , DateMathUnit(..)
383− , TopHitsAggregation(..)
384− , StatisticsAggregation(..)
385− , SearchAfterKey
386288
387289 , Highlights(..)
388290 , FieldHighlight(..)
@@ -400,32 +302,697 @@ module Database.Bloodhound.Types
400302 , TermsResult(..)
401303 , DateHistogramResult(..)
402304 , DateRangeResult(..)
403− , TopHitResult(..)
404305
405306 , EsUsername(..)
406307 , EsPassword(..)
308+ ) where
407309
408− , Analysis(..)
409− , AnalyzerDefinition(..)
410− , TokenizerDefinition(..)
411− , TokenFilterDefinition(..)
412− , CharFilterDefinition(..)
413− , Ngram(..)
414− , TokenChar(..)
415− , Shingle(..)
416− , Language(..)
417− ) where
418−
419−import Bloodhound.Import
420−
421−import Database.Bloodhound.Internal.Aggregation
422−import Database.Bloodhound.Internal.Analysis
423−import Database.Bloodhound.Internal.Client
424−import Database.Bloodhound.Internal.Highlight
425−import Database.Bloodhound.Internal.Newtypes
426−import Database.Bloodhound.Internal.Query
427−import Database.Bloodhound.Internal.Sort
428−import Database.Bloodhound.Internal.Suggest
310+import Control.Applicative as A
311+import Control.Monad.Catch
312+import Control.Monad.Except
313+import Control.Monad.Reader
314+import Control.Monad.State
315+import Control.Monad.Writer
316+import Data.Aeson
317+import Data.Aeson.Types (Pair, Parser, emptyObject,
318+ parseEither, parseMaybe)
319+import qualified Data.ByteString.Lazy.Char8 as L
320+import Data.Char
321+import Data.Hashable (Hashable)
322+import qualified Data.HashMap.Strict as HM
323+import Data.List (foldl', nub)
324+import Data.List.NonEmpty (NonEmpty (..), toList)
325+import qualified Data.Map.Strict as M
326+import Data.Maybe
327+import Data.Scientific (Scientific)
328+import Data.Text (Text)
329+import qualified Data.Text as T
330+import Data.Time.Calendar
331+import Data.Time.Clock (NominalDiffTime, UTCTime)
332+import Data.Time.Clock.POSIX
333+import qualified Data.Traversable as DT
334+import Data.Typeable (Typeable)
335+import qualified Data.Vector as V
336+import GHC.Enum
337+import GHC.Generics (Generic)
338+import Network.HTTP.Client
339+import qualified Network.HTTP.Types.Method as NHTM
340+import qualified Text.Read as TR
341+
342+import Database.Bloodhound.Types.Class
343+import Database.Bloodhound.Types.Internal
344+
345+-- $setup
346+-- >>> :set -XOverloadedStrings
347+-- >>> import Data.Aeson
348+-- >>> import Database.Bloodhound
349+-- >>> let testServer = (Server "http://localhost:9200")
350+-- >>> let testIndex = IndexName "twitter"
351+-- >>> let testMapping = MappingName "tweet"
352+-- >>> let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
353+
354+-- defaultIndexSettings is exported by Database.Bloodhound as well
355+-- no trailing slashes in servers, library handles building the path.
356+
357+-- | Create a 'BHEnv' with all optional fields defaulted. HTTP hook
358+-- will be a noop. You can use the exported fields to customize it further, e.g.:
359+--
360+-- >> (mkBHEnv myServer myManager) { bhRequestHook = customHook }
361+mkBHEnv :: Server -> Manager -> BHEnv
362+mkBHEnv s m = BHEnv s m return
363+
364+newtype BH m a = BH {
365+ unBH :: ReaderT BHEnv m a
366+ } deriving ( Functor
367+ , A.Applicative
368+ , Monad
369+ , MonadIO
370+ , MonadState s
371+ , MonadWriter w
372+ , MonadError e
373+ , Alternative
374+ , MonadPlus
375+ , MonadFix
376+ , MonadThrow
377+ , MonadCatch
378+ , MonadMask)
379+
380+instance MonadTrans BH where
381+ lift = BH . lift
382+
383+instance (MonadReader r m) => MonadReader r (BH m) where
384+ ask = lift ask
385+ local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
386+ local f (m r)
387+
388+instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
389+ getBHEnv = BH getBHEnv
390+
391+runBH :: BHEnv -> BH m a -> m a
392+runBH e f = runReaderT (unBH f) e
393+
394+{-| 'Version' is embedded in 'Status' -}
395+data Version = Version { number :: Text
396+ , build_hash :: Text
397+ , build_timestamp :: UTCTime
398+ , build_snapshot :: Bool
399+ , lucene_version :: Text } deriving (Eq, Read, Show, Generic, Typeable)
400+
401+{-| 'Status' is a data type for describing the JSON body returned by
402+ Elasticsearch when you query its status. This was deprecated in 1.2.0.
403+
404+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
405+-}
406+
407+data Status = Status { ok :: Maybe Bool
408+ , status :: Int
409+ , name :: Text
410+ , version :: Version
411+ , tagline :: Text } deriving (Eq, Read, Show, Generic)
412+
413+{-| 'IndexSettings' is used to configure the shards and replicas when you create
414+ an Elasticsearch Index.
415+
416+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
417+-}
418+
419+data IndexSettings =
420+ IndexSettings { indexShards :: ShardCount
421+ , indexReplicas :: ReplicaCount } deriving (Eq, Read, Show, Generic, Typeable)
422+
423+{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
424+defaultIndexSettings :: IndexSettings
425+defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
426+
427+
428+{-| 'IndexOptimizationSettings' is used to configure index optimization. See
429+ <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-optimize.html>
430+ for more info.
431+-}
432+data IndexOptimizationSettings =
433+ IndexOptimizationSettings { maxNumSegments :: Maybe Int
434+ -- ^ Number of segments to optimize to. 1 will fully optimize the index. If omitted, the default behavior is to only optimize if the server deems it necessary.
435+ , onlyExpungeDeletes :: Bool
436+ -- ^ Should the optimize process only expunge segments with deletes in them? If the purpose of the optimization is to free disk space, this should be set to True.
437+ , flushAfterOptimize :: Bool
438+ -- ^ Should a flush be performed after the optimize.
439+ } deriving (Eq, Show, Generic, Typeable)
440+
441+
442+{-| 'defaultIndexOptimizationSettings' implements the default settings that
443+ ElasticSearch uses for index optimization. 'maxNumSegments' is Nothing,
444+ 'onlyExpungeDeletes' is False, and flushAfterOptimize is True.
445+-}
446+defaultIndexOptimizationSettings :: IndexOptimizationSettings
447+defaultIndexOptimizationSettings = IndexOptimizationSettings Nothing False True
448+
449+{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
450+
451+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
452+-}
453+data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
454+ -- ^ The number of replicas each shard has.
455+ | AutoExpandReplicas ReplicaBounds
456+ | BlocksReadOnly Bool
457+ -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
458+ | BlocksRead Bool
459+ -- ^ Set to True to disable read operations against the index.
460+ | BlocksWrite Bool
461+ -- ^ Set to True to disable write operations against the index.
462+ | BlocksMetaData Bool
463+ -- ^ Set to True to disable metadata operations against the index.
464+ | RefreshInterval NominalDiffTime
465+ -- ^ The async refresh interval of a shard
466+ | IndexConcurrency Int
467+ | FailOnMergeFailure Bool
468+ | TranslogFlushThresholdOps Int
469+ -- ^ When to flush on operations.
470+ | TranslogFlushThresholdSize Bytes
471+ -- ^ When to flush based on translog (bytes) size.
472+ | TranslogFlushThresholdPeriod NominalDiffTime
473+ -- ^ When to flush based on a period of not flushing.
474+ | TranslogDisableFlush Bool
475+ -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
476+ | CacheFilterMaxSize (Maybe Bytes)
477+ -- ^ The maximum size of filter cache (per segment in shard).
478+ | CacheFilterExpire (Maybe NominalDiffTime)
479+ -- ^ The expire after access time for filter cache.
480+ | GatewaySnapshotInterval NominalDiffTime
481+ -- ^ The gateway snapshot interval (only applies to shared gateways).
482+ | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
483+ -- ^ A node matching any rule will be allowed to host shards from the index.
484+ | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
485+ -- ^ A node matching any rule will NOT be allowed to host shards from the index.
486+ | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
487+ -- ^ Only nodes matching all rules will be allowed to host shards from the index.
488+ | RoutingAllocationEnable AllocationPolicy
489+ -- ^ Enables shard allocation for a specific index.
490+ | RoutingAllocationShardsPerNode ShardCount
491+ -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
492+ | RecoveryInitialShards InitialShardCount
493+ -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
494+ | GCDeletes NominalDiffTime
495+ | TTLDisablePurge Bool
496+ -- ^ Disables temporarily the purge of expired docs.
497+ | TranslogFSType FSType
498+ | IndexCompoundFormat CompoundFormat
499+ | IndexCompoundOnFlush Bool
500+ | WarmerEnabled Bool
501+ deriving (Eq, Show, Generic, Typeable)
502+
503+data AllocationPolicy = AllocAll
504+ -- ^ Allows shard allocation for all shards.
505+ | AllocPrimaries
506+ -- ^ Allows shard allocation only for primary shards.
507+ | AllocNewPrimaries
508+ -- ^ Allows shard allocation only for primary shards for new indices.
509+ | AllocNone
510+ -- ^ No shard allocation is allowed
511+ deriving (Eq, Read, Show, Generic, Typeable)
512+
513+data ReplicaBounds = ReplicasBounded Int Int
514+ | ReplicasLowerBounded Int
515+ | ReplicasUnbounded
516+ deriving (Eq, Read, Show, Generic, Typeable)
517+
518+-- | A measure of bytes used for various configurations. You may want
519+-- to use smart constructors like 'gigabytes' for larger values.
520+--
521+-- >>> gigabytes 9
522+-- Bytes 9000000000
523+--
524+-- >>> megabytes 9
525+-- Bytes 9000000
526+--
527+-- >>> kilobytes 9
528+-- Bytes 9000
529+newtype Bytes = Bytes Int deriving (Eq, Read, Show, Generic, Typeable, Ord, ToJSON, FromJSON)
530+
531+gigabytes :: Int -> Bytes
532+gigabytes n = megabytes (1000 * n)
533+
534+
535+megabytes :: Int -> Bytes
536+megabytes n = kilobytes (1000 * n)
537+
538+
539+kilobytes :: Int -> Bytes
540+kilobytes n = Bytes (1000 * n)
541+
542+
543+data FSType = FSSimple
544+ | FSBuffered deriving (Eq, Read, Show, Generic, Typeable, Ord)
545+
546+data InitialShardCount = QuorumShards
547+ | QuorumMinus1Shards
548+ | FullShards
549+ | FullMinus1Shards
550+ | ExplicitShards Int
551+ deriving (Eq, Read, Show, Generic, Typeable)
552+
553+data NodeAttrFilter = NodeAttrFilter { nodeAttrFilterName :: NodeAttrName
554+ , nodeAttrFilterValues :: NonEmpty Text}
555+ deriving (Eq, Read, Show, Generic, Ord)
556+
557+newtype NodeAttrName = NodeAttrName Text deriving (Eq, Read, Show, Ord, Generic, Typeable)
558+
559+data CompoundFormat = CompoundFileFormat Bool
560+ | MergeSegmentVsTotalIndex Double
561+ -- ^ percentage between 0 and 1 where 0 is false, 1 is true
562+ deriving (Eq, Read, Show, Generic, Typeable)
563+
564+newtype NominalDiffTimeJSON = NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
565+
566+data IndexSettingsSummary = IndexSettingsSummary { sSummaryIndexName :: IndexName
567+ , sSummaryFixedSettings :: IndexSettings
568+ , sSummaryUpdateable :: [UpdatableIndexSetting]}
569+ deriving (Eq, Show, Generic, Typeable)
570+
571+{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
572+type Reply = Network.HTTP.Client.Response L.ByteString
573+type Method = NHTM.Method
574+
575+{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
576+
577+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
578+-}
579+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Read, Show, Generic, Typeable)
580+
581+data FieldType = GeoPointType
582+ | GeoShapeType
583+ | FloatType
584+ | IntegerType
585+ | LongType
586+ | ShortType
587+ | ByteType deriving (Eq, Read, Show, Generic, Typeable)
588+
589+data FieldDefinition =
590+ FieldDefinition { fieldType :: FieldType } deriving (Eq, Read, Show, Generic, Typeable)
591+
592+{-| An 'IndexTemplate' defines a template that will automatically be
593+ applied to new indices created. The templates include both
594+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
595+ controls if the template will be applied to the index created.
596+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
597+
598+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
599+-}
600+data IndexTemplate =
601+ IndexTemplate { templatePattern :: TemplatePattern
602+ , templateSettings :: Maybe IndexSettings
603+ , templateMappings :: [Value]
604+ }
605+
606+data MappingField =
607+ MappingField { mappingFieldName :: FieldName
608+ , fieldDefinition :: FieldDefinition } deriving (Eq, Read, Show, Generic, Typeable)
609+
610+{-| Support for type reification of 'Mapping's is currently incomplete, for
611+ now the mapping API verbiage expects a 'ToJSON'able blob.
612+
613+ Indexes have mappings, mappings are schemas for the documents contained in the
614+ index. I'd recommend having only one mapping per index, always having a mapping,
615+ and keeping different kinds of documents separated if possible.
616+-}
617+data Mapping = Mapping { typeName :: TypeName
618+ , mappingFields :: [MappingField] } deriving (Eq, Read, Show, Generic, Typeable)
619+
620+{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
621+ operation index, create, delete, and update. 'BulkIndex' behaves like an
622+ "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
623+
624+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk>
625+-}
626+data BulkOperation =
627+ BulkIndex IndexName MappingName DocId Value
628+ | BulkCreate IndexName MappingName DocId Value
629+ | BulkDelete IndexName MappingName DocId
630+ | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Read, Show, Generic, Typeable)
631+
632+{-| 'EsResult' describes the standard wrapper JSON document that you see in
633+ successful Elasticsearch lookups or lookups that couldn't find the document.
634+-}
635+data EsResult a = EsResult { _index :: Text
636+ , _type :: Text
637+ , _id :: Text
638+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Read, Show, Generic, Typeable)
639+
640+{-| 'EsResultFound' contains the document and its metadata inside of an
641+ 'EsResult' when the document was successfully found.
642+-}
643+data EsResultFound a = EsResultFound { _version :: DocVersion
644+ , _source :: a } deriving (Eq, Read, Show, Generic, Typeable)
645+
646+{-| 'EsError' is the generic type that will be returned when there was a
647+ problem. If you can't parse the expected response, its a good idea to
648+ try parsing this.
649+-}
650+data EsError = EsError { errorStatus :: Int
651+ , errorMessage :: Text } deriving (Eq, Read, Show, Generic, Typeable)
652+
653+{-| 'EsProtocolException' will be thrown if Bloodhound cannot parse a response
654+returned by the ElasticSearch server. If you encounter this error, please
655+verify that your domain data types and FromJSON instances are working properly
656+(for example, the 'a' of '[Hit a]' in 'SearchResult.searchHits.hits'). If you're
657+sure that your mappings are correct, then this error may be an indication of an
658+incompatibility between Bloodhound and ElasticSearch. Please open a bug report
659+and be sure to include the exception body.
660+-}
661+data EsProtocolException = EsProtocolException { esProtoExBody :: L.ByteString }
662+ deriving (Eq, Read, Show, Generic, Typeable)
663+
664+instance Exception EsProtocolException
665+
666+data IndexAlias = IndexAlias { srcIndex :: IndexName
667+ , indexAlias :: IndexAliasName } deriving (Eq, Read, Show, Generic, Typeable)
668+
669+newtype IndexAliasName = IndexAliasName { indexAliasName :: IndexName } deriving (Eq, Read, Show, Generic, ToJSON)
670+
671+data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
672+ | RemoveAlias IndexAlias deriving (Read, Show, Eq, Generic, Typeable)
673+
674+data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
675+ , aliasCreateFilter :: Maybe Filter}
676+ deriving (Read, Show, Eq, Generic, Typeable)
677+
678+data AliasRouting = AllAliasRouting RoutingValue
679+ | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
680+ deriving (Read, Show, Eq, Generic, Typeable)
681+
682+newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Read, Show, Eq, Generic, Typeable)
683+
684+newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Read, Show, Eq, Generic, ToJSON, FromJSON, Typeable)
685+
686+newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Read, Show, Eq, Generic, ToJSON, FromJSON, Typeable)
687+
688+newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Read, Show, Eq, Generic, Typeable)
689+
690+{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
691+data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
692+ , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Read, Show, Eq, Generic, Typeable)
693+
694+{-| 'DocVersion' is an integer version number for a document between 1
695+and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
696+-}
697+newtype DocVersion = DocVersion {
698+ docVersionNumber :: Int
699+ } deriving (Eq, Read, Show, Generic, Ord, ToJSON)
700+
701+-- | Smart constructor for in-range doc version
702+mkDocVersion :: Int -> Maybe DocVersion
703+mkDocVersion i
704+ | i >= (docVersionNumber minBound) && i <= (docVersionNumber maxBound) =
705+ Just $ DocVersion i
706+ | otherwise = Nothing
707+
708+
709+{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
710+own version numbers instead of ones from ES.
711+-}
712+newtype ExternalDocVersion = ExternalDocVersion DocVersion
713+ deriving (Eq, Read, Show, Generic, Ord, Bounded, Enum, ToJSON)
714+
715+{-| 'VersionControl' is specified when indexing documents as a
716+optimistic concurrency control.
717+-}
718+data VersionControl = NoVersionControl
719+ -- ^ Don't send a version. This is a pure overwrite.
720+ | InternalVersion DocVersion
721+ -- ^ Use the default ES versioning scheme. Only
722+ -- index the document if the version is the same
723+ -- as the one specified. Only applicable to
724+ -- updates, as you should be getting Version from
725+ -- a search result.
726+ | ExternalGT ExternalDocVersion
727+ -- ^ Use your own version numbering. Only index
728+ -- the document if the version is strictly higher
729+ -- OR the document doesn't exist. The given
730+ -- version will be used as the new version number
731+ -- for the stored document. N.B. All updates must
732+ -- increment this number, meaning there is some
733+ -- global, external ordering of updates.
734+ | ExternalGTE ExternalDocVersion
735+ -- ^ Use your own version numbering. Only index
736+ -- the document if the version is equal or higher
737+ -- than the stored version. Will succeed if there
738+ -- is no existing document. The given version will
739+ -- be used as the new version number for the
740+ -- stored document. Use with care, as this could
741+ -- result in data loss.
742+ | ForceVersion ExternalDocVersion
743+ -- ^ The document will always be indexed and the
744+ -- given version will be the new version. This is
745+ -- typically used for correcting errors. Use with
746+ -- care, as this could result in data loss.
747+ deriving (Read, Show, Eq, Generic, Ord)
748+
749+{-| 'DocumentParent' is used to specify a parent document.
750+-}
751+newtype DocumentParent = DocumentParent DocId
752+ deriving (Eq, Read, Show, Generic, Typeable)
753+
754+{-| 'IndexDocumentSettings' are special settings supplied when indexing
755+a document. For the best backwards compatiblity when new fields are
756+added, you should probably prefer to start with 'defaultIndexDocumentSettings'
757+-}
758+data IndexDocumentSettings =
759+ IndexDocumentSettings { idsVersionControl :: VersionControl
760+ , idsParent :: Maybe DocumentParent
761+ } deriving (Eq, Read, Show, Generic, Typeable)
762+
763+{-| Reasonable default settings. Chooses no version control and no parent.
764+-}
765+defaultIndexDocumentSettings :: IndexDocumentSettings
766+defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
767+
768+{-| 'Sort' is a synonym for a list of 'SortSpec's. Sort behavior is order
769+ dependent with later sorts acting as tie-breakers for earlier sorts.
770+-}
771+type Sort = [SortSpec]
772+
773+{-| The two main kinds of 'SortSpec' are 'DefaultSortSpec' and
774+ 'GeoDistanceSortSpec'. The latter takes a 'SortOrder', 'GeoPoint', and
775+ 'DistanceUnit' to express "nearness" to a single geographical point as a
776+ sort specification.
777+
778+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
779+-}
780+data SortSpec = DefaultSortSpec DefaultSort
781+ | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Read, Show, Generic, Typeable)
782+
783+{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
784+ 'mkSort' convenience function for when you want to specify only the most
785+ common parameters.
786+
787+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
788+-}
789+data DefaultSort =
790+ DefaultSort { sortFieldName :: FieldName
791+ , sortOrder :: SortOrder
792+ -- default False
793+ , ignoreUnmapped :: Bool
794+ , sortMode :: Maybe SortMode
795+ , missingSort :: Maybe Missing
796+ , nestedFilter :: Maybe Filter } deriving (Eq, Read, Show, Generic, Typeable)
797+
798+{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
799+ encoded into "asc" or "desc" when turned into JSON.
800+
801+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
802+-}
803+data SortOrder = Ascending
804+ | Descending deriving (Eq, Read, Show, Generic, Typeable)
805+
806+{-| 'Missing' prescribes how to handle missing fields. A missing field can be
807+ sorted last, first, or using a custom value as a substitute.
808+
809+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_missing_values>
810+-}
811+data Missing = LastMissing
812+ | FirstMissing
813+ | CustomMissing Text deriving (Eq, Read, Show, Generic, Typeable)
814+
815+{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
816+
817+http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_sort_mode_option
818+-}
819+data SortMode = SortMin
820+ | SortMax
821+ | SortSum
822+ | SortAvg deriving (Eq, Read, Show, Generic, Typeable)
823+
824+{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
825+ that you can concisely describe the usual kind of 'SortSpec's you want.
826+-}
827+mkSort :: FieldName -> SortOrder -> DefaultSort
828+mkSort fieldName sOrder = DefaultSort fieldName sOrder False Nothing Nothing Nothing
829+
830+{-| 'Cache' is for telling ES whether it should cache a 'Filter' not.
831+ 'Query's cannot be cached.
832+-}
833+type Cache = Bool -- caching on/off
834+defaultCache :: Cache
835+defaultCache = False
836+
837+{-| 'PrefixValue' is used in 'PrefixQuery' as the main query component.
838+-}
839+type PrefixValue = Text
840+
841+{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
842+ JSON encoding baked in. Used all over the place.
843+-}
844+data BooleanOperator = And | Or deriving (Eq, Read, Show, Generic, Typeable)
845+
846+{-| 'ShardCount' is part of 'IndexSettings'
847+-}
848+newtype ShardCount = ShardCount Int deriving (Eq, Read, Show, Generic, ToJSON, Typeable)
849+
850+{-| 'ReplicaCount' is part of 'IndexSettings'
851+-}
852+newtype ReplicaCount = ReplicaCount Int deriving (Eq, Read, Show, Generic, ToJSON, Typeable)
853+
854+{-| 'IndexName' is used to describe which index to query/create/delete
855+-}
856+newtype IndexName = IndexName Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
857+
858+{-| 'IndexSelection' is used for APIs which take a single index, a list of
859+ indexes, or the special @_all@ index.
860+-}
861+data IndexSelection = IndexList (NonEmpty IndexName)
862+ | AllIndexes deriving (Eq, Generic, Show, Typeable)
863+
864+{-| 'TemplateName' is used to describe which template to query/create/delete
865+-}
866+newtype TemplateName = TemplateName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
867+
868+{-| 'TemplatePattern' represents a pattern which is matched against index names
869+-}
870+newtype TemplatePattern = TemplatePattern Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
871+
872+{-| 'MappingName' is part of mappings which are how ES describes and schematizes
873+ the data in the indices.
874+-}
875+newtype MappingName = MappingName Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
876+
877+{-| 'DocId' is a generic wrapper value for expressing unique Document IDs.
878+ Can be set by the user or created by ES itself. Often used in client
879+ functions for poking at specific documents.
880+-}
881+newtype DocId = DocId Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
882+
883+{-| 'QueryString' is used to wrap query text bodies, be they human written or not.
884+-}
885+newtype QueryString = QueryString Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
886+
887+{-| 'FieldName' is used all over the place wherever a specific field within
888+ a document needs to be specified, usually in 'Query's or 'Filter's.
889+-}
890+newtype FieldName = FieldName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
891+
892+
893+{-| 'Script' is often used in place of 'FieldName' to specify more
894+complex ways of extracting a value from a document.
895+-}
896+newtype Script = Script { scriptText :: Text } deriving (Eq, Read, Show, Generic, Typeable)
897+
898+{-| 'CacheName' is used in 'RegexpFilter' for describing the
899+ 'CacheKey' keyed caching behavior.
900+-}
901+newtype CacheName = CacheName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
902+
903+{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
904+-}
905+newtype CacheKey =
906+ CacheKey Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
907+newtype Existence =
908+ Existence Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
909+newtype NullValue =
910+ NullValue Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
911+newtype CutoffFrequency =
912+ CutoffFrequency Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
913+newtype Analyzer =
914+ Analyzer Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
915+newtype MaxExpansions =
916+ MaxExpansions Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
917+
918+{-| 'Lenient', if set to true, will cause format based failures to be
919+ ignored. I don't know what the bloody default is, Elasticsearch
920+ documentation didn't say what it was. Let me know if you figure it out.
921+-}
922+newtype Lenient =
923+ Lenient Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
924+newtype Tiebreaker =
925+ Tiebreaker Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
926+newtype Boost =
927+ Boost Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
928+newtype BoostTerms =
929+ BoostTerms Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
930+
931+{-| 'MinimumMatch' controls how many should clauses in the bool query should
932+ match. Can be an absolute value (2) or a percentage (30%) or a
933+ combination of both.
934+-}
935+newtype MinimumMatch =
936+ MinimumMatch Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
937+newtype DisableCoord =
938+ DisableCoord Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
939+newtype IgnoreTermFrequency =
940+ IgnoreTermFrequency Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
941+newtype MinimumTermFrequency =
942+ MinimumTermFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
943+newtype MaxQueryTerms =
944+ MaxQueryTerms Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
945+newtype Fuzziness =
946+ Fuzziness Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
947+
948+{-| 'PrefixLength' is the prefix length used in queries, defaults to 0. -}
949+newtype PrefixLength =
950+ PrefixLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
951+newtype TypeName =
952+ TypeName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
953+newtype PercentMatch =
954+ PercentMatch Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
955+newtype StopWord =
956+ StopWord Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
957+newtype QueryPath =
958+ QueryPath Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
959+
960+{-| Allowing a wildcard at the beginning of a word (eg "*ing") is particularly
961+ heavy, because all terms in the index need to be examined, just in case
962+ they match. Leading wildcards can be disabled by setting
963+ 'AllowLeadingWildcard' to false. -}
964+newtype AllowLeadingWildcard =
965+ AllowLeadingWildcard Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
966+newtype LowercaseExpanded =
967+ LowercaseExpanded Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
968+newtype EnablePositionIncrements =
969+ EnablePositionIncrements Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
970+
971+{-| By default, wildcard terms in a query are not analyzed.
972+ Setting 'AnalyzeWildcard' to true enables best-effort analysis.
973+-}
974+newtype AnalyzeWildcard = AnalyzeWildcard Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
975+
976+{-| 'GeneratePhraseQueries' defaults to false.
977+-}
978+newtype GeneratePhraseQueries =
979+ GeneratePhraseQueries Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
980+
981+{-| 'Locale' is used for string conversions - defaults to ROOT.
982+-}
983+newtype Locale = Locale Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
984+newtype MaxWordLength = MaxWordLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
985+newtype MinWordLength = MinWordLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
986+
987+{-| 'PhraseSlop' sets the default slop for phrases, 0 means exact
988+ phrase matches. Default is 0.
989+-}
990+newtype PhraseSlop = PhraseSlop Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
991+newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
992+newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
993+
994+-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
995+newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
429996
430997{-| 'unpackId' is a silly convenience function that gets used once.
431998-}
@@ -433,6 +1000,8 @@ unpackId :: DocId -> Text
4331000unpackId (DocId docId) = docId
4341001
4351002type TrackSortScores = Bool
1003+newtype From = From Int deriving (Eq, Read, Show, Generic, ToJSON)
1004+newtype Size = Size Int deriving (Eq, Read, Show, Generic, ToJSON)
4361005
4371006data Search = Search { queryBody :: Maybe Query
4381007 , filterBody :: Maybe Filter
@@ -444,74 +1013,537 @@ data Search = Search { queryBody :: Maybe Query
4441013 , from :: From
4451014 , size :: Size
4461015 , searchType :: SearchType
447− , searchAfterKey :: Maybe SearchAfterKey
4481016 , fields :: Maybe [FieldName]
449− , scriptFields :: Maybe ScriptFields
450− , source :: Maybe Source
451− , suggestBody :: Maybe Suggest -- ^ Only one Suggestion request / response per Search is supported.
452− } deriving (Eq, Show)
453−
454−
455−instance ToJSON Search where
456− toJSON (Search mquery sFilter sort searchAggs
457− highlight sTrackSortScores sFrom sSize _ sAfter sFields
458− sScriptFields sSource sSuggest) =
459− omitNulls [ "query" .= query'
460− , "sort" .= sort
461− , "aggregations" .= searchAggs
462− , "highlight" .= highlight
463− , "from" .= sFrom
464− , "size" .= sSize
465− , "track_scores" .= sTrackSortScores
466− , "search_after" .= sAfter
467− , "fields" .= sFields
468− , "script_fields" .= sScriptFields
469− , "_source" .= sSource
470− , "suggest" .= sSuggest]
471−
472− where query' = case sFilter of
473− Nothing -> mquery
474− Just x ->
475− Just
476− . QueryBoolQuery
477− $ mkBoolQuery (maybeToList mquery)
478− [x] [] []
1017+ , source :: Maybe Source } deriving (Eq, Read, Show, Generic, Typeable)
4791018
4801019data SearchType = SearchTypeQueryThenFetch
4811020 | SearchTypeDfsQueryThenFetch
482− deriving (Eq, Show)
1021+ | SearchTypeCount
1022+ | SearchTypeScan
1023+ | SearchTypeQueryAndFetch
1024+ | SearchTypeDfsQueryAndFetch
1025+ deriving (Eq, Read, Show, Generic, Typeable)
4831026
4841027data Source =
4851028 NoSource
4861029 | SourcePatterns PatternOrPatterns
4871030 | SourceIncludeExclude Include Exclude
488− deriving (Eq, Show)
489−
490−instance ToJSON Source where
491− toJSON NoSource = toJSON False
492− toJSON (SourcePatterns patterns) = toJSON patterns
493− toJSON (SourceIncludeExclude incl excl) = object [ "includes" .= incl, "excludes" .= excl ]
1031+ deriving (Read, Show, Eq, Generic, Typeable)
4941032
4951033data PatternOrPatterns = PopPattern Pattern
496− | PopPatterns [Pattern] deriving (Eq, Read, Show)
1034+ | PopPatterns [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
4971035
498−instance ToJSON PatternOrPatterns where
499− toJSON (PopPattern pattern) = toJSON pattern
500− toJSON (PopPatterns patterns) = toJSON patterns
1036+data Include = Include [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
1037+data Exclude = Exclude [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
5011038
502−data Include = Include [Pattern] deriving (Eq, Read, Show)
503−data Exclude = Exclude [Pattern] deriving (Eq, Read, Show)
1039+newtype Pattern = Pattern Text deriving (Eq, Read, Show, Generic, Typeable)
5041040
505−instance ToJSON Include where
506− toJSON (Include patterns) = toJSON patterns
1041+data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
1042+ , highlightFields :: [FieldHighlight]
1043+ } deriving (Read, Show, Eq, Generic, Typeable)
5071044
508−instance ToJSON Exclude where
509− toJSON (Exclude patterns) = toJSON patterns
1045+data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
1046+ deriving (Read, Show, Eq, Generic, Typeable)
5101047
511−newtype Pattern = Pattern Text deriving (Eq, Read, Show)
5121048
513−instance ToJSON Pattern where
514− toJSON (Pattern pattern) = toJSON pattern
1049+data HighlightSettings = Plain PlainHighlight
1050+ | Postings PostingsHighlight
1051+ | FastVector FastVectorHighlight
1052+ deriving (Read, Show, Eq, Generic, Typeable)
1053+data PlainHighlight =
1054+ PlainHighlight { plainCommon :: Maybe CommonHighlight
1055+ , plainNonPost :: Maybe NonPostings } deriving (Read, Show, Eq, Generic, Typeable)
1056+
1057+ -- This requires that index_options are set to 'offset' in the mapping.
1058+data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Read, Show, Eq, Generic, Typeable)
1059+
1060+-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
1061+data FastVectorHighlight =
1062+ FastVectorHighlight { fvCommon :: Maybe CommonHighlight
1063+ , fvNonPostSettings :: Maybe NonPostings
1064+ , boundaryChars :: Maybe Text
1065+ , boundaryMaxScan :: Maybe Int
1066+ , fragmentOffset :: Maybe Int
1067+ , matchedFields :: [Text]
1068+ , phraseLimit :: Maybe Int
1069+ } deriving (Read, Show, Eq, Generic, Typeable)
1070+
1071+data CommonHighlight =
1072+ CommonHighlight { order :: Maybe Text
1073+ , forceSource :: Maybe Bool
1074+ , tag :: Maybe HighlightTag
1075+ , encoder :: Maybe HighlightEncoder
1076+ , noMatchSize :: Maybe Int
1077+ , highlightQuery :: Maybe Query
1078+ , requireFieldMatch :: Maybe Bool
1079+ } deriving (Read, Show, Eq, Generic, Typeable)
1080+
1081+-- Settings that are only applicable to FastVector and Plain highlighters.
1082+data NonPostings =
1083+ NonPostings { fragmentSize :: Maybe Int
1084+ , numberOfFragments :: Maybe Int} deriving (Read, Show, Eq, Generic, Typeable)
1085+
1086+data HighlightEncoder = DefaultEncoder
1087+ | HTMLEncoder
1088+ deriving (Read, Show, Eq, Generic, Typeable)
1089+
1090+-- NOTE: Should the tags use some kind of HTML type, rather than Text?
1091+data HighlightTag = TagSchema Text
1092+ | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
1093+ deriving (Read, Show, Eq, Generic, Typeable)
1094+
1095+
1096+data Query =
1097+ TermQuery Term (Maybe Boost)
1098+ | TermsQuery Text (NonEmpty Text)
1099+ | QueryMatchQuery MatchQuery
1100+ | QueryMultiMatchQuery MultiMatchQuery
1101+ | QueryBoolQuery BoolQuery
1102+ | QueryBoostingQuery BoostingQuery
1103+ | QueryCommonTermsQuery CommonTermsQuery
1104+ | ConstantScoreFilter Filter Boost
1105+ | ConstantScoreQuery Query Boost
1106+ | QueryDisMaxQuery DisMaxQuery
1107+ | QueryFilteredQuery FilteredQuery
1108+ | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
1109+ | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
1110+ | QueryFuzzyQuery FuzzyQuery
1111+ | QueryHasChildQuery HasChildQuery
1112+ | QueryHasParentQuery HasParentQuery
1113+ | IdsQuery MappingName [DocId]
1114+ | QueryIndicesQuery IndicesQuery
1115+ | MatchAllQuery (Maybe Boost)
1116+ | QueryMoreLikeThisQuery MoreLikeThisQuery
1117+ | QueryMoreLikeThisFieldQuery MoreLikeThisFieldQuery
1118+ | QueryNestedQuery NestedQuery
1119+ | QueryPrefixQuery PrefixQuery
1120+ | QueryQueryStringQuery QueryStringQuery
1121+ | QuerySimpleQueryStringQuery SimpleQueryStringQuery
1122+ | QueryRangeQuery RangeQuery
1123+ | QueryRegexpQuery RegexpQuery
1124+ deriving (Eq, Read, Show, Generic, Typeable)
1125+
1126+data RegexpQuery =
1127+ RegexpQuery { regexpQueryField :: FieldName
1128+ , regexpQuery :: Regexp
1129+ , regexpQueryFlags :: RegexpFlags
1130+ , regexpQueryBoost :: Maybe Boost
1131+ } deriving (Eq, Read, Show, Generic, Typeable)
1132+
1133+data RangeQuery =
1134+ RangeQuery { rangeQueryField :: FieldName
1135+ , rangeQueryRange :: RangeValue
1136+ , rangeQueryBoost :: Boost } deriving (Eq, Read, Show, Generic, Typeable)
1137+
1138+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
1139+mkRangeQuery f r = RangeQuery f r (Boost 1.0)
1140+
1141+data SimpleQueryStringQuery =
1142+ SimpleQueryStringQuery
1143+ { simpleQueryStringQuery :: QueryString
1144+ , simpleQueryStringField :: Maybe FieldOrFields
1145+ , simpleQueryStringOperator :: Maybe BooleanOperator
1146+ , simpleQueryStringAnalyzer :: Maybe Analyzer
1147+ , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
1148+ , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
1149+ , simpleQueryStringLocale :: Maybe Locale
1150+ } deriving (Eq, Read, Show, Generic, Typeable)
1151+
1152+data SimpleQueryFlag =
1153+ SimpleQueryAll
1154+ | SimpleQueryNone
1155+ | SimpleQueryAnd
1156+ | SimpleQueryOr
1157+ | SimpleQueryPrefix
1158+ | SimpleQueryPhrase
1159+ | SimpleQueryPrecedence
1160+ | SimpleQueryEscape
1161+ | SimpleQueryWhitespace
1162+ | SimpleQueryFuzzy
1163+ | SimpleQueryNear
1164+ | SimpleQuerySlop deriving (Eq, Read, Show, Generic, Typeable)
1165+
1166+-- use_dis_max and tie_breaker when fields are plural?
1167+data QueryStringQuery =
1168+ QueryStringQuery
1169+ { queryStringQuery :: QueryString
1170+ , queryStringDefaultField :: Maybe FieldName
1171+ , queryStringOperator :: Maybe BooleanOperator
1172+ , queryStringAnalyzer :: Maybe Analyzer
1173+ , queryStringAllowLeadingWildcard :: Maybe AllowLeadingWildcard
1174+ , queryStringLowercaseExpanded :: Maybe LowercaseExpanded
1175+ , queryStringEnablePositionIncrements :: Maybe EnablePositionIncrements
1176+ , queryStringFuzzyMaxExpansions :: Maybe MaxExpansions
1177+ , queryStringFuzziness :: Maybe Fuzziness
1178+ , queryStringFuzzyPrefixLength :: Maybe PrefixLength
1179+ , queryStringPhraseSlop :: Maybe PhraseSlop
1180+ , queryStringBoost :: Maybe Boost
1181+ , queryStringAnalyzeWildcard :: Maybe AnalyzeWildcard
1182+ , queryStringGeneratePhraseQueries :: Maybe GeneratePhraseQueries
1183+ , queryStringMinimumShouldMatch :: Maybe MinimumMatch
1184+ , queryStringLenient :: Maybe Lenient
1185+ , queryStringLocale :: Maybe Locale
1186+ } deriving (Eq, Read, Show, Generic, Typeable)
1187+
1188+mkQueryStringQuery :: QueryString -> QueryStringQuery
1189+mkQueryStringQuery qs =
1190+ QueryStringQuery qs Nothing Nothing
1191+ Nothing Nothing Nothing Nothing
1192+ Nothing Nothing Nothing Nothing
1193+ Nothing Nothing Nothing Nothing
1194+ Nothing Nothing
1195+
1196+data FieldOrFields = FofField FieldName
1197+ | FofFields (NonEmpty FieldName) deriving (Eq, Read, Show, Generic, Typeable)
1198+
1199+data PrefixQuery =
1200+ PrefixQuery
1201+ { prefixQueryField :: FieldName
1202+ , prefixQueryPrefixValue :: Text
1203+ , prefixQueryBoost :: Maybe Boost } deriving (Eq, Read, Show, Generic, Typeable)
1204+
1205+data NestedQuery =
1206+ NestedQuery
1207+ { nestedQueryPath :: QueryPath
1208+ , nestedQueryScoreType :: ScoreType
1209+ , nestedQuery :: Query } deriving (Eq, Read, Show, Generic, Typeable)
1210+
1211+data MoreLikeThisFieldQuery =
1212+ MoreLikeThisFieldQuery
1213+ { moreLikeThisFieldText :: Text
1214+ , moreLikeThisFieldFields :: FieldName
1215+ -- default 0.3 (30%)
1216+ , moreLikeThisFieldPercentMatch :: Maybe PercentMatch
1217+ , moreLikeThisFieldMinimumTermFreq :: Maybe MinimumTermFrequency
1218+ , moreLikeThisFieldMaxQueryTerms :: Maybe MaxQueryTerms
1219+ , moreLikeThisFieldStopWords :: Maybe (NonEmpty StopWord)
1220+ , moreLikeThisFieldMinDocFrequency :: Maybe MinDocFrequency
1221+ , moreLikeThisFieldMaxDocFrequency :: Maybe MaxDocFrequency
1222+ , moreLikeThisFieldMinWordLength :: Maybe MinWordLength
1223+ , moreLikeThisFieldMaxWordLength :: Maybe MaxWordLength
1224+ , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
1225+ , moreLikeThisFieldBoost :: Maybe Boost
1226+ , moreLikeThisFieldAnalyzer :: Maybe Analyzer
1227+ } deriving (Eq, Read, Show, Generic, Typeable)
1228+
1229+data MoreLikeThisQuery =
1230+ MoreLikeThisQuery
1231+ { moreLikeThisText :: Text
1232+ , moreLikeThisFields :: Maybe (NonEmpty FieldName)
1233+ -- default 0.3 (30%)
1234+ , moreLikeThisPercentMatch :: Maybe PercentMatch
1235+ , moreLikeThisMinimumTermFreq :: Maybe MinimumTermFrequency
1236+ , moreLikeThisMaxQueryTerms :: Maybe MaxQueryTerms
1237+ , moreLikeThisStopWords :: Maybe (NonEmpty StopWord)
1238+ , moreLikeThisMinDocFrequency :: Maybe MinDocFrequency
1239+ , moreLikeThisMaxDocFrequency :: Maybe MaxDocFrequency
1240+ , moreLikeThisMinWordLength :: Maybe MinWordLength
1241+ , moreLikeThisMaxWordLength :: Maybe MaxWordLength
1242+ , moreLikeThisBoostTerms :: Maybe BoostTerms
1243+ , moreLikeThisBoost :: Maybe Boost
1244+ , moreLikeThisAnalyzer :: Maybe Analyzer
1245+ } deriving (Eq, Read, Show, Generic, Typeable)
1246+
1247+data IndicesQuery =
1248+ IndicesQuery
1249+ { indicesQueryIndices :: [IndexName]
1250+ , indicesQuery :: Query
1251+ -- default "all"
1252+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Read, Show, Generic, Typeable)
1253+
1254+data HasParentQuery =
1255+ HasParentQuery
1256+ { hasParentQueryType :: TypeName
1257+ , hasParentQuery :: Query
1258+ , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Read, Show, Generic, Typeable)
1259+
1260+data HasChildQuery =
1261+ HasChildQuery
1262+ { hasChildQueryType :: TypeName
1263+ , hasChildQuery :: Query
1264+ , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Read, Show, Generic, Typeable)
1265+
1266+data ScoreType =
1267+ ScoreTypeMax
1268+ | ScoreTypeSum
1269+ | ScoreTypeAvg
1270+ | ScoreTypeNone deriving (Eq, Read, Show, Generic, Typeable)
1271+
1272+data FuzzyQuery =
1273+ FuzzyQuery { fuzzyQueryField :: FieldName
1274+ , fuzzyQueryValue :: Text
1275+ , fuzzyQueryPrefixLength :: PrefixLength
1276+ , fuzzyQueryMaxExpansions :: MaxExpansions
1277+ , fuzzyQueryFuzziness :: Fuzziness
1278+ , fuzzyQueryBoost :: Maybe Boost
1279+ } deriving (Eq, Read, Show, Generic, Typeable)
1280+
1281+data FuzzyLikeFieldQuery =
1282+ FuzzyLikeFieldQuery
1283+ { fuzzyLikeField :: FieldName
1284+ -- anaphora is good for the soul.
1285+ , fuzzyLikeFieldText :: Text
1286+ , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
1287+ , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
1288+ , fuzzyLikeFieldFuzziness :: Fuzziness
1289+ , fuzzyLikeFieldPrefixLength :: PrefixLength
1290+ , fuzzyLikeFieldBoost :: Boost
1291+ , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
1292+ } deriving (Eq, Read, Show, Generic, Typeable)
1293+
1294+data FuzzyLikeThisQuery =
1295+ FuzzyLikeThisQuery
1296+ { fuzzyLikeFields :: [FieldName]
1297+ , fuzzyLikeText :: Text
1298+ , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
1299+ , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
1300+ , fuzzyLikeFuzziness :: Fuzziness
1301+ , fuzzyLikePrefixLength :: PrefixLength
1302+ , fuzzyLikeBoost :: Boost
1303+ , fuzzyLikeAnalyzer :: Maybe Analyzer
1304+ } deriving (Eq, Read, Show, Generic, Typeable)
1305+
1306+data FilteredQuery =
1307+ FilteredQuery
1308+ { filteredQuery :: Query
1309+ , filteredFilter :: Filter } deriving (Eq, Read, Show, Generic, Typeable)
1310+
1311+data DisMaxQuery =
1312+ DisMaxQuery { disMaxQueries :: [Query]
1313+ -- default 0.0
1314+ , disMaxTiebreaker :: Tiebreaker
1315+ , disMaxBoost :: Maybe Boost
1316+ } deriving (Eq, Read, Show, Generic, Typeable)
1317+
1318+data MatchQuery =
1319+ MatchQuery { matchQueryField :: FieldName
1320+ , matchQueryQueryString :: QueryString
1321+ , matchQueryOperator :: BooleanOperator
1322+ , matchQueryZeroTerms :: ZeroTermsQuery
1323+ , matchQueryCutoffFrequency :: Maybe CutoffFrequency
1324+ , matchQueryMatchType :: Maybe MatchQueryType
1325+ , matchQueryAnalyzer :: Maybe Analyzer
1326+ , matchQueryMaxExpansions :: Maybe MaxExpansions
1327+ , matchQueryLenient :: Maybe Lenient
1328+ , matchQueryBoost :: Maybe Boost } deriving (Eq, Read, Show, Generic, Typeable)
1329+
1330+{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
1331+ enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
1332+-}
1333+mkMatchQuery :: FieldName -> QueryString -> MatchQuery
1334+mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1335+
1336+data MatchQueryType =
1337+ MatchPhrase
1338+ | MatchPhrasePrefix deriving (Eq, Read, Show, Generic, Typeable)
1339+
1340+data MultiMatchQuery =
1341+ MultiMatchQuery { multiMatchQueryFields :: [FieldName]
1342+ , multiMatchQueryString :: QueryString
1343+ , multiMatchQueryOperator :: BooleanOperator
1344+ , multiMatchQueryZeroTerms :: ZeroTermsQuery
1345+ , multiMatchQueryTiebreaker :: Maybe Tiebreaker
1346+ , multiMatchQueryType :: Maybe MultiMatchQueryType
1347+ , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
1348+ , multiMatchQueryAnalyzer :: Maybe Analyzer
1349+ , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
1350+ , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Read, Show, Generic, Typeable)
1351+
1352+{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
1353+ enabling you to provide only the list of 'FieldName's and 'QueryString' to
1354+ make a 'MultiMatchQuery'.
1355+-}
1356+
1357+mkMultiMatchQuery :: [FieldName] -> QueryString -> MultiMatchQuery
1358+mkMultiMatchQuery matchFields query =
1359+ MultiMatchQuery matchFields query
1360+ Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1361+
1362+data MultiMatchQueryType =
1363+ MultiMatchBestFields
1364+ | MultiMatchMostFields
1365+ | MultiMatchCrossFields
1366+ | MultiMatchPhrase
1367+ | MultiMatchPhrasePrefix deriving (Eq, Read, Show, Generic, Typeable)
1368+
1369+data BoolQuery =
1370+ BoolQuery { boolQueryMustMatch :: [Query]
1371+ , boolQueryMustNotMatch :: [Query]
1372+ , boolQueryShouldMatch :: [Query]
1373+ , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
1374+ , boolQueryBoost :: Maybe Boost
1375+ , boolQueryDisableCoord :: Maybe DisableCoord
1376+ } deriving (Eq, Read, Show, Generic, Typeable)
1377+
1378+mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
1379+mkBoolQuery must mustNot should =
1380+ BoolQuery must mustNot should Nothing Nothing Nothing
1381+
1382+data BoostingQuery =
1383+ BoostingQuery { positiveQuery :: Query
1384+ , negativeQuery :: Query
1385+ , negativeBoost :: Boost } deriving (Eq, Read, Show, Generic, Typeable)
1386+
1387+data CommonTermsQuery =
1388+ CommonTermsQuery { commonField :: FieldName
1389+ , commonQuery :: QueryString
1390+ , commonCutoffFrequency :: CutoffFrequency
1391+ , commonLowFreqOperator :: BooleanOperator
1392+ , commonHighFreqOperator :: BooleanOperator
1393+ , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
1394+ , commonBoost :: Maybe Boost
1395+ , commonAnalyzer :: Maybe Analyzer
1396+ , commonDisableCoord :: Maybe DisableCoord
1397+ } deriving (Eq, Read, Show, Generic, Typeable)
1398+
1399+data CommonMinimumMatch =
1400+ CommonMinimumMatchHighLow MinimumMatchHighLow
1401+ | CommonMinimumMatch MinimumMatch
1402+ deriving (Eq, Read, Show, Generic, Typeable)
1403+
1404+data MinimumMatchHighLow =
1405+ MinimumMatchHighLow { lowFreq :: MinimumMatch
1406+ , highFreq :: MinimumMatch } deriving (Eq, Read, Show, Generic, Typeable)
1407+
1408+data Filter = AndFilter [Filter] Cache
1409+ | OrFilter [Filter] Cache
1410+ | NotFilter Filter Cache
1411+ | IdentityFilter
1412+ | BoolFilter BoolMatch
1413+ | ExistsFilter FieldName -- always cached
1414+ | GeoBoundingBoxFilter GeoBoundingBoxConstraint
1415+ | GeoDistanceFilter GeoPoint Distance DistanceType OptimizeBbox Cache
1416+ | GeoDistanceRangeFilter GeoPoint DistanceRange
1417+ | GeoPolygonFilter FieldName [LatLon]
1418+ | IdsFilter MappingName [DocId]
1419+ | LimitFilter Int
1420+ | MissingFilter FieldName Existence NullValue
1421+ | PrefixFilter FieldName PrefixValue Cache
1422+ | QueryFilter Query Cache
1423+ | RangeFilter FieldName RangeValue RangeExecution Cache
1424+ | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
1425+ | TermFilter Term Cache
1426+ deriving (Eq, Read, Show, Generic, Typeable)
1427+
1428+data ZeroTermsQuery = ZeroTermsNone
1429+ | ZeroTermsAll deriving (Eq, Read, Show, Generic, Typeable)
1430+
1431+data RangeExecution = RangeExecutionIndex
1432+ | RangeExecutionFielddata deriving (Eq, Read, Show, Generic, Typeable)
1433+
1434+newtype Regexp = Regexp Text deriving (Eq, Read, Show, Generic, Typeable, FromJSON)
1435+
1436+data RegexpFlags = AllRegexpFlags
1437+ | NoRegexpFlags
1438+ | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Read, Show, Generic, Typeable)
1439+
1440+data RegexpFlag = AnyString
1441+ | Automaton
1442+ | Complement
1443+ | Empty
1444+ | Intersection
1445+ | Interval deriving (Eq, Read, Show, Generic, Typeable)
1446+
1447+newtype LessThan = LessThan Double deriving (Eq, Read, Show, Generic, Typeable)
1448+newtype LessThanEq = LessThanEq Double deriving (Eq, Read, Show, Generic, Typeable)
1449+newtype GreaterThan = GreaterThan Double deriving (Eq, Read, Show, Generic, Typeable)
1450+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Read, Show, Generic, Typeable)
1451+
1452+newtype LessThanD = LessThanD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1453+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1454+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1455+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1456+
1457+data RangeValue = RangeDateLte LessThanEqD
1458+ | RangeDateLt LessThanD
1459+ | RangeDateGte GreaterThanEqD
1460+ | RangeDateGt GreaterThanD
1461+ | RangeDateGtLt GreaterThanD LessThanD
1462+ | RangeDateGteLte GreaterThanEqD LessThanEqD
1463+ | RangeDateGteLt GreaterThanEqD LessThanD
1464+ | RangeDateGtLte GreaterThanD LessThanEqD
1465+ | RangeDoubleLte LessThanEq
1466+ | RangeDoubleLt LessThan
1467+ | RangeDoubleGte GreaterThanEq
1468+ | RangeDoubleGt GreaterThan
1469+ | RangeDoubleGtLt GreaterThan LessThan
1470+ | RangeDoubleGteLte GreaterThanEq LessThanEq
1471+ | RangeDoubleGteLt GreaterThanEq LessThan
1472+ | RangeDoubleGtLte GreaterThan LessThanEq
1473+ deriving (Eq, Read, Show, Generic, Typeable)
1474+
1475+rangeValueToPair :: RangeValue -> [Pair]
1476+rangeValueToPair rv = case rv of
1477+ RangeDateLte (LessThanEqD t) -> ["lte" .= t]
1478+ RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
1479+ RangeDateLt (LessThanD t) -> ["lt" .= t]
1480+ RangeDateGt (GreaterThanD t) -> ["gt" .= t]
1481+ RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
1482+ RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
1483+ RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
1484+ RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
1485+ RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
1486+ RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
1487+ RangeDoubleLt (LessThan t) -> ["lt" .= t]
1488+ RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
1489+ RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
1490+ RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
1491+ RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
1492+ RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1493+
1494+data Term = Term { termField :: Text
1495+ , termValue :: Text } deriving (Eq, Read, Show, Generic, Typeable)
1496+
1497+data BoolMatch = MustMatch Term Cache
1498+ | MustNotMatch Term Cache
1499+ | ShouldMatch [Term] Cache deriving (Eq, Read, Show, Generic, Typeable)
1500+
1501+-- "memory" or "indexed"
1502+data GeoFilterType = GeoFilterMemory
1503+ | GeoFilterIndexed deriving (Eq, Read, Show, Generic, Typeable)
1504+
1505+data LatLon = LatLon { lat :: Double
1506+ , lon :: Double } deriving (Eq, Read, Show, Generic, Typeable)
1507+
1508+data GeoBoundingBox =
1509+ GeoBoundingBox { topLeft :: LatLon
1510+ , bottomRight :: LatLon } deriving (Eq, Read, Show, Generic, Typeable)
1511+
1512+data GeoBoundingBoxConstraint =
1513+ GeoBoundingBoxConstraint { geoBBField :: FieldName
1514+ , constraintBox :: GeoBoundingBox
1515+ , bbConstraintcache :: Cache
1516+ , geoType :: GeoFilterType
1517+ } deriving (Eq, Read, Show, Generic, Typeable)
1518+
1519+data GeoPoint =
1520+ GeoPoint { geoField :: FieldName
1521+ , latLon :: LatLon} deriving (Eq, Read, Show, Generic, Typeable)
1522+
1523+data DistanceUnit = Miles
1524+ | Yards
1525+ | Feet
1526+ | Inches
1527+ | Kilometers
1528+ | Meters
1529+ | Centimeters
1530+ | Millimeters
1531+ | NauticalMiles deriving (Eq, Read, Show, Generic, Typeable)
1532+
1533+data DistanceType = Arc
1534+ | SloppyArc -- doesn't exist <1.0
1535+ | Plane deriving (Eq, Read, Show, Generic, Typeable)
1536+
1537+data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1538+ | NoOptimizeBbox deriving (Eq, Read, Show, Generic, Typeable)
1539+
1540+data Distance =
1541+ Distance { coefficient :: Double
1542+ , unit :: DistanceUnit } deriving (Eq, Read, Show, Generic, Typeable)
1543+
1544+data DistanceRange =
1545+ DistanceRange { distanceFrom :: Distance
1546+ , distanceTo :: Distance } deriving (Eq, Read, Show, Generic, Typeable)
5151547
5161548data SearchResult a =
5171549 SearchResult { took :: Int
@@ -519,24 +1551,2493 @@ data SearchResult a =
5191551 , shards :: ShardResult
5201552 , searchHits :: SearchHits a
5211553 , aggregations :: Maybe AggregationResults
522− , scrollId :: Maybe ScrollId
523− -- ^ Only one Suggestion request / response per
524− -- Search is supported.
525− , suggest :: Maybe NamedSuggestionResponse
526− }
527− deriving (Eq, Show)
1554+ , scrollId :: Maybe ScrollId } deriving (Eq, Read, Show, Generic, Typeable)
5281555
529−instance (FromJSON a) => FromJSON (SearchResult a) where
530− parseJSON (Object v) = SearchResult <$>
531− v .: "took" <*>
532− v .: "timed_out" <*>
533− v .: "_shards" <*>
534− v .: "hits" <*>
535− v .:? "aggregations" <*>
536− v .:? "_scroll_id" <*>
537− v .:? "suggest"
538− parseJSON _ = empty
1556+newtype ScrollId = ScrollId Text deriving (Eq, Read, Show, Generic, Ord, ToJSON, FromJSON)
1557+
1558+type Score = Maybe Double
1559+
1560+data SearchHits a =
1561+ SearchHits { hitsTotal :: Int
1562+ , maxScore :: Score
1563+ , hits :: [Hit a] } deriving (Eq, Read, Show, Generic, Typeable)
5391564
540−newtype ScrollId =
541− ScrollId Text
542− deriving (Eq, Show, Ord, ToJSON, FromJSON)
1565+
1566+instance Monoid (SearchHits a) where
1567+ mempty = SearchHits 0 Nothing mempty
1568+ mappend (SearchHits ta ma ha) (SearchHits tb mb hb) =
1569+ SearchHits (ta + tb) (max ma mb) (ha <> hb)
1570+
1571+
1572+data Hit a =
1573+ Hit { hitIndex :: IndexName
1574+ , hitType :: MappingName
1575+ , hitDocId :: DocId
1576+ , hitScore :: Score
1577+ , hitSource :: Maybe a
1578+ , hitHighlight :: Maybe HitHighlight } deriving (Eq, Read, Show, Generic, Typeable)
1579+
1580+data ShardResult =
1581+ ShardResult { shardTotal :: Int
1582+ , shardsSuccessful :: Int
1583+ , shardsFailed :: Int } deriving (Eq, Read, Show, Generic, Typeable)
1584+
1585+type HitHighlight = M.Map Text [Text]
1586+
1587+showText :: Show a => a -> Text
1588+showText = T.pack . show
1589+
1590+readMay :: Read a => String -> Maybe a
1591+readMay s = case reads s of
1592+ (a, ""):_ -> Just a
1593+ _ -> Nothing
1594+
1595+parseReadText :: Read a => Text -> Parser a
1596+parseReadText = maybe mzero return . readMay . T.unpack
1597+
1598+type Aggregations = M.Map Text Aggregation
1599+
1600+emptyAggregations :: Aggregations
1601+emptyAggregations = M.empty
1602+
1603+mkAggregations :: Text -> Aggregation -> Aggregations
1604+mkAggregations name aggregation = M.insert name aggregation emptyAggregations
1605+
1606+data TermOrder = TermOrder{ termSortField :: Text
1607+ , termSortOrder :: SortOrder } deriving (Eq, Read, Show, Generic, Typeable)
1608+
1609+data TermInclusion = TermInclusion Text
1610+ | TermPattern Text Text deriving (Eq, Read, Show, Generic, Typeable)
1611+
1612+data CollectionMode = BreadthFirst
1613+ | DepthFirst deriving (Eq, Read, Show, Generic, Typeable)
1614+
1615+data ExecutionHint = Ordinals
1616+ | GlobalOrdinals
1617+ | GlobalOrdinalsHash
1618+ | GlobalOrdinalsLowCardinality
1619+ | Map deriving (Eq, Read, Show, Generic, Typeable)
1620+
1621+data TimeInterval = Weeks
1622+ | Days
1623+ | Hours
1624+ | Minutes
1625+ | Seconds deriving Eq
1626+
1627+data Interval = Year
1628+ | Quarter
1629+ | Month
1630+ | Week
1631+ | Day
1632+ | Hour
1633+ | Minute
1634+ | Second
1635+ | FractionalInterval Float TimeInterval deriving (Eq, Read, Show, Generic, Typeable)
1636+
1637+data Aggregation = TermsAgg TermsAggregation
1638+ | DateHistogramAgg DateHistogramAggregation
1639+ | ValueCountAgg ValueCountAggregation
1640+ | FilterAgg FilterAggregation
1641+ | DateRangeAgg DateRangeAggregation
1642+ | MissingAgg MissingAggregation deriving (Eq, Read, Show, Generic, Typeable)
1643+
1644+data MissingAggregation = MissingAggregation
1645+ { maField :: Text
1646+ } deriving (Eq, Read, Show, Generic, Typeable)
1647+
1648+data TermsAggregation = TermsAggregation { term :: Either Text Text
1649+ , termInclude :: Maybe TermInclusion
1650+ , termExclude :: Maybe TermInclusion
1651+ , termOrder :: Maybe TermOrder
1652+ , termMinDocCount :: Maybe Int
1653+ , termSize :: Maybe Int
1654+ , termShardSize :: Maybe Int
1655+ , termCollectMode :: Maybe CollectionMode
1656+ , termExecutionHint :: Maybe ExecutionHint
1657+ , termAggs :: Maybe Aggregations
1658+ } deriving (Eq, Read, Show, Generic, Typeable)
1659+
1660+data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
1661+ , dateInterval :: Interval
1662+ , dateFormat :: Maybe Text
1663+ -- pre and post deprecated in 1.5
1664+ , datePreZone :: Maybe Text
1665+ , datePostZone :: Maybe Text
1666+ , datePreOffset :: Maybe Text
1667+ , datePostOffset :: Maybe Text
1668+ , dateAggs :: Maybe Aggregations
1669+ } deriving (Eq, Read, Show, Generic, Typeable)
1670+
1671+
1672+data DateRangeAggregation = DateRangeAggregation { draField :: FieldName
1673+ , draFormat :: Maybe Text
1674+ , draRanges :: NonEmpty DateRangeAggRange
1675+ } deriving (Eq, Read, Show, Generic, Typeable)
1676+
1677+data DateRangeAggRange = DateRangeFrom DateMathExpr
1678+ | DateRangeTo DateMathExpr
1679+ | DateRangeFromAndTo DateMathExpr DateMathExpr deriving (Eq, Read, Show, Generic, Typeable)
1680+
1681+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
1682+data DateMathExpr = DateMathExpr DateMathAnchor [DateMathModifier] deriving (Eq, Read, Show, Generic, Typeable)
1683+
1684+
1685+-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
1686+data DateMathAnchor = DMNow
1687+ | DMDate Day deriving (Eq, Read, Show, Generic, Typeable)
1688+
1689+data DateMathModifier = AddTime Int DateMathUnit
1690+ | SubtractTime Int DateMathUnit
1691+ | RoundDownTo DateMathUnit deriving (Eq, Read, Show, Generic, Typeable)
1692+
1693+data DateMathUnit = DMYear
1694+ | DMMonth
1695+ | DMWeek
1696+ | DMDay
1697+ | DMHour
1698+ | DMMinute
1699+ | DMSecond deriving (Eq, Read, Show, Generic, Typeable)
1700+
1701+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1702+data ValueCountAggregation = FieldValueCount FieldName
1703+ | ScriptValueCount Script deriving (Eq, Read, Show, Generic, Typeable)
1704+
1705+-- | 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.
1706+data FilterAggregation = FilterAggregation { faFilter :: Filter
1707+ , faAggs :: Maybe Aggregations} deriving (Eq, Read, Show, Generic, Typeable)
1708+
1709+mkTermsAggregation :: Text -> TermsAggregation
1710+mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1711+
1712+mkTermsScriptAggregation :: Text -> TermsAggregation
1713+mkTermsScriptAggregation t = TermsAggregation (Right t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1714+
1715+mkDateHistogram :: FieldName -> Interval -> DateHistogramAggregation
1716+mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
1717+
1718+instance ToJSON Version where
1719+ toJSON Version {..} = object ["number" .= number
1720+ ,"build_hash" .= build_hash
1721+ ,"build_timestamp" .= build_timestamp
1722+ ,"build_snapshot" .= build_snapshot
1723+ ,"lucene_version" .= lucene_version]
1724+
1725+instance FromJSON Version where
1726+ parseJSON = withObject "Version" parse
1727+ where parse o = Version
1728+ <$> o .: "number"
1729+ <*> o .: "build_hash"
1730+ <*> o .: "build_timestamp"
1731+ <*> o .: "build_snapshot"
1732+ <*> o .: "lucene_version"
1733+
1734+instance ToJSON TermOrder where
1735+ toJSON (TermOrder termSortField termSortOrder) = object [termSortField .= termSortOrder]
1736+
1737+instance ToJSON TermInclusion where
1738+ toJSON (TermInclusion x) = toJSON x
1739+ toJSON (TermPattern pattern flags) = omitNulls [ "pattern" .= pattern,
1740+ "flags" .= flags]
1741+
1742+instance ToJSON CollectionMode where
1743+ toJSON BreadthFirst = "breadth_first"
1744+ toJSON DepthFirst = "depth_first"
1745+
1746+instance ToJSON ExecutionHint where
1747+ toJSON Ordinals = "ordinals"
1748+ toJSON GlobalOrdinals = "global_ordinals"
1749+ toJSON GlobalOrdinalsHash = "global_ordinals_hash"
1750+ toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
1751+ toJSON Map = "map"
1752+
1753+instance ToJSON Interval where
1754+ toJSON Year = "year"
1755+ toJSON Quarter = "quarter"
1756+ toJSON Month = "month"
1757+ toJSON Week = "week"
1758+ toJSON Day = "day"
1759+ toJSON Hour = "hour"
1760+ toJSON Minute = "minute"
1761+ toJSON Second = "second"
1762+ toJSON (FractionalInterval fraction interval) = toJSON $ show fraction ++ show interval
1763+
1764+instance Show TimeInterval where
1765+ show Weeks = "w"
1766+ show Days = "d"
1767+ show Hours = "h"
1768+ show Minutes = "m"
1769+ show Seconds = "s"
1770+
1771+instance Read TimeInterval where
1772+ readPrec = f =<< TR.get
1773+ where
1774+ f 'w' = return Weeks
1775+ f 'd' = return Days
1776+ f 'h' = return Hours
1777+ f 'm' = return Minutes
1778+ f 's' = return Seconds
1779+ f _ = fail "TimeInterval expected one of w, d, h, m, s"
1780+
1781+instance ToJSON Aggregation where
1782+ toJSON (TermsAgg (TermsAggregation term include exclude order minDocCount size shardSize collectMode executionHint termAggs)) =
1783+ omitNulls ["terms" .= omitNulls [ toJSON' term,
1784+ "include" .= include,
1785+ "exclude" .= exclude,
1786+ "order" .= order,
1787+ "min_doc_count" .= minDocCount,
1788+ "size" .= size,
1789+ "shard_size" .= shardSize,
1790+ "collect_mode" .= collectMode,
1791+ "execution_hint" .= executionHint
1792+ ],
1793+ "aggs" .= termAggs ]
1794+ where
1795+ toJSON' x = case x of { Left y -> "field" .= y; Right y -> "script" .= y }
1796+
1797+ toJSON (DateHistogramAgg (DateHistogramAggregation field interval format preZone postZone preOffset postOffset dateHistoAggs)) =
1798+ omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
1799+ "interval" .= interval,
1800+ "format" .= format,
1801+ "pre_zone" .= preZone,
1802+ "post_zone" .= postZone,
1803+ "pre_offset" .= preOffset,
1804+ "post_offset" .= postOffset
1805+ ],
1806+ "aggs" .= dateHistoAggs ]
1807+ toJSON (ValueCountAgg a) = object ["value_count" .= v]
1808+ where v = case a of
1809+ (FieldValueCount (FieldName n)) -> object ["field" .= n]
1810+ (ScriptValueCount (Script s)) -> object ["script" .= s]
1811+ toJSON (FilterAgg (FilterAggregation filt ags)) =
1812+ omitNulls [ "filter" .= filt
1813+ , "aggs" .= ags]
1814+ toJSON (DateRangeAgg a) = object [ "date_range" .= a
1815+ ]
1816+ toJSON (MissingAgg (MissingAggregation{..})) =
1817+ object ["missing" .= object ["field" .= maField]]
1818+
1819+instance ToJSON DateRangeAggregation where
1820+ toJSON DateRangeAggregation {..} =
1821+ omitNulls [ "field" .= draField
1822+ , "format" .= draFormat
1823+ , "ranges" .= toList draRanges
1824+ ]
1825+
1826+instance ToJSON DateRangeAggRange where
1827+ toJSON (DateRangeFrom e) = object [ "from" .= e ]
1828+ toJSON (DateRangeTo e) = object [ "to" .= e ]
1829+ toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
1830+
1831+instance ToJSON DateMathExpr where
1832+ toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
1833+ where fmtA DMNow = "now"
1834+ fmtA (DMDate date) = case toGregorian date of
1835+ (y,m,d) -> showText y <> "-" <>
1836+ showText m <> "-" <>
1837+ showText d <> "||"
1838+ fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
1839+ fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
1840+ fmtMod (RoundDownTo u) = "/" <> fmtU u
1841+ fmtU DMYear = "y"
1842+ fmtU DMMonth = "M"
1843+ fmtU DMWeek = "w"
1844+ fmtU DMDay = "d"
1845+ fmtU DMHour = "h"
1846+ fmtU DMMinute = "m"
1847+ fmtU DMSecond = "s"
1848+
1849+
1850+type AggregationResults = M.Map Text Value
1851+
1852+class BucketAggregation a where
1853+ key :: a -> BucketValue
1854+ docCount :: a -> Int
1855+ aggs :: a -> Maybe AggregationResults
1856+
1857+
1858+data Bucket a = Bucket { buckets :: [a]} deriving (Read, Show)
1859+
1860+data BucketValue = TextValue Text
1861+ | ScientificValue Scientific
1862+ | BoolValue Bool deriving (Read, Show)
1863+
1864+data MissingResult = MissingResult { missingDocCount :: Int } deriving (Show)
1865+
1866+data TermsResult = TermsResult { termKey :: BucketValue
1867+ , termsDocCount :: Int
1868+ , termsAggs :: Maybe AggregationResults } deriving (Read, Show)
1869+
1870+data DateHistogramResult = DateHistogramResult { dateKey :: Int
1871+ , dateKeyStr :: Maybe Text
1872+ , dateDocCount :: Int
1873+ , dateHistogramAggs :: Maybe AggregationResults } deriving (Read, Show)
1874+
1875+data DateRangeResult = DateRangeResult { dateRangeKey :: Text
1876+ , dateRangeFrom :: Maybe UTCTime
1877+ , dateRangeFromAsString :: Maybe Text
1878+ , dateRangeTo :: Maybe UTCTime
1879+ , dateRangeToAsString :: Maybe Text
1880+ , dateRangeDocCount :: Int
1881+ , dateRangeAggs :: Maybe AggregationResults } deriving (Read, Show, Eq, Generic, Typeable)
1882+
1883+toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1884+toTerms = toAggResult
1885+
1886+toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
1887+toDateHistogram = toAggResult
1888+
1889+toMissing :: Text -> AggregationResults -> Maybe MissingResult
1890+toMissing = toAggResult
1891+
1892+toAggResult :: (FromJSON a) => Text -> AggregationResults -> Maybe a
1893+toAggResult t a = M.lookup t a >>= deserialize
1894+ where deserialize = parseMaybe parseJSON
1895+
1896+instance BucketAggregation TermsResult where
1897+ key = termKey
1898+ docCount = termsDocCount
1899+ aggs = termsAggs
1900+
1901+instance BucketAggregation DateHistogramResult where
1902+ key = TextValue . showText . dateKey
1903+ docCount = dateDocCount
1904+ aggs = dateHistogramAggs
1905+
1906+instance BucketAggregation DateRangeResult where
1907+ key = TextValue . dateRangeKey
1908+ docCount = dateRangeDocCount
1909+ aggs = dateRangeAggs
1910+
1911+instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1912+ parseJSON (Object v) = Bucket <$>
1913+ v .: "buckets"
1914+ parseJSON _ = mempty
1915+
1916+instance FromJSON BucketValue where
1917+ parseJSON (String t) = return $ TextValue t
1918+ parseJSON (Number s) = return $ ScientificValue s
1919+ parseJSON (Bool b) = return $ BoolValue b
1920+ parseJSON _ = mempty
1921+
1922+instance FromJSON MissingResult where
1923+ parseJSON = withObject "MissingResult" parse
1924+ where parse v = MissingResult <$> v .: "doc_count"
1925+
1926+instance FromJSON TermsResult where
1927+ parseJSON (Object v) = TermsResult <$>
1928+ v .: "key" <*>
1929+ v .: "doc_count" <*>
1930+ v .:? "aggregations"
1931+ parseJSON _ = mempty
1932+
1933+instance FromJSON DateHistogramResult where
1934+ parseJSON (Object v) = DateHistogramResult <$>
1935+ v .: "key" <*>
1936+ v .:? "key_as_string" <*>
1937+ v .: "doc_count" <*>
1938+ v .:? "aggregations"
1939+ parseJSON _ = mempty
1940+
1941+instance FromJSON DateRangeResult where
1942+ parseJSON = withObject "DateRangeResult" parse
1943+ where parse v = DateRangeResult <$>
1944+ v .: "key" <*>
1945+ (fmap posixMS <$> v .:? "from") <*>
1946+ v .:? "from_as_string" <*>
1947+ (fmap posixMS <$> v .:? "to") <*>
1948+ v .:? "to_as_string" <*>
1949+ v .: "doc_count" <*>
1950+ v .:? "aggregations"
1951+
1952+instance FromJSON POSIXMS where
1953+ parseJSON = withScientific "POSIXMS" (return . parse)
1954+ where parse n = let n' = truncate n :: Integer
1955+ in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
1956+
1957+instance Monoid Filter where
1958+ mempty = IdentityFilter
1959+ mappend a b = AndFilter [a, b] defaultCache
1960+
1961+instance Seminearring Filter where
1962+ a <||> b = OrFilter [a, b] defaultCache
1963+
1964+instance ToJSON Filter where
1965+ toJSON (AndFilter filters cache) =
1966+ object ["and" .=
1967+ object [ "filters" .= fmap toJSON filters
1968+ , "_cache" .= cache]]
1969+
1970+ toJSON (OrFilter filters cache) =
1971+ object ["or" .=
1972+ object [ "filters" .= fmap toJSON filters
1973+ , "_cache" .= cache]]
1974+
1975+ toJSON (NotFilter notFilter cache) =
1976+ object ["not" .=
1977+ object ["filter" .= notFilter
1978+ , "_cache" .= cache]]
1979+
1980+ toJSON (IdentityFilter) =
1981+ object ["match_all" .= object []]
1982+
1983+ toJSON (TermFilter (Term termFilterField termFilterValue) cache) =
1984+ object ["term" .= object base]
1985+ where base = [termFilterField .= termFilterValue,
1986+ "_cache" .= cache]
1987+
1988+ toJSON (ExistsFilter (FieldName fieldName)) =
1989+ object ["exists" .= object
1990+ ["field" .= fieldName]]
1991+
1992+ toJSON (BoolFilter boolMatch) =
1993+ object ["bool" .= boolMatch]
1994+
1995+ toJSON (GeoBoundingBoxFilter bbConstraint) =
1996+ object ["geo_bounding_box" .= bbConstraint]
1997+
1998+ toJSON (GeoDistanceFilter (GeoPoint (FieldName distanceGeoField) geoDistLatLon)
1999+ distance distanceType optimizeBbox cache) =
2000+ object ["geo_distance" .=
2001+ object ["distance" .= distance
2002+ , "distance_type" .= distanceType
2003+ , "optimize_bbox" .= optimizeBbox
2004+ , distanceGeoField .= geoDistLatLon
2005+ , "_cache" .= cache]]
2006+
2007+ toJSON (GeoDistanceRangeFilter (GeoPoint (FieldName gddrField) drLatLon)
2008+ (DistanceRange geoDistRangeDistFrom drDistanceTo)) =
2009+ object ["geo_distance_range" .=
2010+ object ["from" .= geoDistRangeDistFrom
2011+ , "to" .= drDistanceTo
2012+ , gddrField .= drLatLon]]
2013+
2014+ toJSON (GeoPolygonFilter (FieldName geoPolygonFilterField) latLons) =
2015+ object ["geo_polygon" .=
2016+ object [geoPolygonFilterField .=
2017+ object ["points" .= fmap toJSON latLons]]]
2018+
2019+ toJSON (IdsFilter (MappingName mappingName) values) =
2020+ object ["ids" .=
2021+ object ["type" .= mappingName
2022+ , "values" .= fmap unpackId values]]
2023+
2024+ toJSON (LimitFilter limit) =
2025+ object ["limit" .= object ["value" .= limit]]
2026+
2027+ toJSON (MissingFilter (FieldName fieldName) (Existence existence) (NullValue nullValue)) =
2028+ object ["missing" .=
2029+ object ["field" .= fieldName
2030+ , "existence" .= existence
2031+ , "null_value" .= nullValue]]
2032+
2033+ toJSON (PrefixFilter (FieldName fieldName) fieldValue cache) =
2034+ object ["prefix" .=
2035+ object [fieldName .= fieldValue
2036+ , "_cache" .= cache]]
2037+
2038+ toJSON (QueryFilter query False) =
2039+ object ["query" .= toJSON query ]
2040+ toJSON (QueryFilter query True) =
2041+ object ["fquery" .=
2042+ object [ "query" .= toJSON query
2043+ , "_cache" .= True ]]
2044+
2045+ toJSON (RangeFilter (FieldName fieldName) rangeValue rangeExecution cache) =
2046+ object ["range" .=
2047+ object [ fieldName .= object (rangeValueToPair rangeValue)
2048+ , "execution" .= rangeExecution
2049+ , "_cache" .= cache]]
2050+
2051+ toJSON (RegexpFilter (FieldName fieldName)
2052+ (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
2053+ object ["regexp" .=
2054+ object [fieldName .=
2055+ object ["value" .= regexText
2056+ , "flags" .= flags]
2057+ , "_name" .= cacheName
2058+ , "_cache" .= cache
2059+ , "_cache_key" .= cacheKey]]
2060+
2061+instance FromJSON Filter where
2062+ parseJSON = withObject "Filter" parse
2063+ where parse o = andFilter `taggedWith` "and"
2064+ <|> orFilter `taggedWith` "or"
2065+ <|> notFilter `taggedWith` "not"
2066+ <|> identityFilter `taggedWith` "match_all"
2067+ <|> boolFilter `taggedWith` "bool"
2068+ <|> existsFilter `taggedWith` "exists"
2069+ <|> geoBoundingBoxFilter `taggedWith` "geo_bounding_box"
2070+ <|> geoDistanceFilter `taggedWith` "geo_distance"
2071+ <|> geoDistanceRangeFilter `taggedWith` "geo_distance_range"
2072+ <|> geoPolygonFilter `taggedWith` "geo_polygon"
2073+ <|> idsFilter `taggedWith` "ids"
2074+ <|> limitFilter `taggedWith` "limit"
2075+ <|> missingFilter `taggedWith` "missing"
2076+ <|> prefixFilter `taggedWith` "prefix"
2077+ <|> queryFilter `taggedWith` "query"
2078+ <|> fqueryFilter `taggedWith` "fquery"
2079+ <|> rangeFilter `taggedWith` "range"
2080+ <|> regexpFilter `taggedWith` "regexp"
2081+ <|> termFilter `taggedWith` "term"
2082+ where taggedWith parser k = parser =<< o .: k
2083+ andFilter o = AndFilter <$> o .: "filters"
2084+ <*> o .:? "_cache" .!= defaultCache
2085+ orFilter o = OrFilter <$> o .: "filters"
2086+ <*> o .:? "_cache" .!= defaultCache
2087+ notFilter o = NotFilter <$> o .: "filter"
2088+ <*> o .: "_cache" .!= defaultCache
2089+ identityFilter :: Object -> Parser Filter
2090+ identityFilter m
2091+ | HM.null m = pure IdentityFilter
2092+ | otherwise = fail ("Identityfilter expected empty object but got " <> show m)
2093+ boolFilter = pure . BoolFilter
2094+ existsFilter o = ExistsFilter <$> o .: "field"
2095+ geoBoundingBoxFilter = pure . GeoBoundingBoxFilter
2096+ geoDistanceFilter o = do
2097+ case HM.toList (deleteSeveral ["distance", "distance_type", "optimize_bbox", "_cache"] o) of
2098+ [(fn, v)] -> do
2099+ gp <- GeoPoint (FieldName fn) <$> parseJSON v
2100+ GeoDistanceFilter gp <$> o .: "distance"
2101+ <*> o .: "distance_type"
2102+ <*> o .: "optimize_bbox"
2103+ <*> o .:? "_cache" .!= defaultCache
2104+ _ -> fail "Could not find GeoDistanceFilter field name"
2105+ geoDistanceRangeFilter o = do
2106+ case HM.toList (deleteSeveral ["from", "to"] o) of
2107+ [(fn, v)] -> do
2108+ gp <- GeoPoint (FieldName fn) <$> parseJSON v
2109+ rng <- DistanceRange <$> o .: "from" <*> o .: "to"
2110+ return (GeoDistanceRangeFilter gp rng)
2111+ _ -> fail "Could not find GeoDistanceRangeFilter field name"
2112+ geoPolygonFilter = fieldTagged $ \fn o -> GeoPolygonFilter fn <$> o .: "points"
2113+ idsFilter o = IdsFilter <$> o .: "type"
2114+ <*> o .: "values"
2115+ limitFilter o = LimitFilter <$> o .: "value"
2116+ missingFilter o = MissingFilter <$> o .: "field"
2117+ <*> o .: "existence"
2118+ <*> o .: "null_value"
2119+ prefixFilter o = case HM.toList (HM.delete "_cache" o) of
2120+ [(fn, String v)] -> PrefixFilter (FieldName fn) v <$> o .:? "_cache" .!= defaultCache
2121+ _ -> fail "Could not parse PrefixFilter"
2122+
2123+ queryFilter q = pure (QueryFilter q False)
2124+ fqueryFilter o = QueryFilter <$> o .: "query" <*> pure True
2125+ rangeFilter o = case HM.toList (deleteSeveral ["execution", "_cache"] o) of
2126+ [(fn, v)] -> RangeFilter (FieldName fn)
2127+ <$> parseJSON v
2128+ <*> o .: "execution"
2129+ <*> o .:? "_cache" .!= defaultCache
2130+ _ -> fail "Could not find field name for RangeFilter"
2131+ regexpFilter o = case HM.toList (deleteSeveral ["_name", "_cache", "_cache_key"] o) of
2132+ [(fn, Object o')] -> RegexpFilter (FieldName fn)
2133+ <$> o' .: "value"
2134+ <*> o' .: "flags"
2135+ <*> o .: "_name"
2136+ <*> o .:? "_cache" .!= defaultCache
2137+ <*> o .: "_cache_key"
2138+ _ -> fail "Could not find field name for RegexpFilter"
2139+ termFilter o = case HM.toList (HM.delete "_cache" o) of
2140+ [(termField, String termVal)] -> TermFilter (Term termField termVal)
2141+ <$> o .:? "_cache" .!= defaultCache
2142+ _ -> fail "Could not find term field for TermFilter"
2143+
2144+fieldTagged :: Monad m => (FieldName -> Object -> m a) -> Object -> m a
2145+fieldTagged f o = case HM.toList o of
2146+ [(k, Object o')] -> f (FieldName k) o'
2147+ _ -> fail "Expected object with 1 field-named key"
2148+
2149+instance ToJSON GeoPoint where
2150+ toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
2151+ object [ geoPointField .= geoPointLatLon ]
2152+
2153+
2154+instance ToJSON Query where
2155+ toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
2156+ object [ "term" .=
2157+ object [termQueryField .= object merged]]
2158+ where
2159+ base = [ "value" .= termQueryValue ]
2160+ boosted = maybe [] (return . ("boost" .=)) boost
2161+ merged = mappend base boosted
2162+
2163+ toJSON (TermsQuery fieldName terms) =
2164+ object [ "terms" .= object conjoined ]
2165+ where conjoined = [fieldName .= terms]
2166+
2167+ toJSON (IdsQuery idsQueryMappingName docIds) =
2168+ object [ "ids" .= object conjoined ]
2169+ where conjoined = [ "type" .= idsQueryMappingName
2170+ , "values" .= fmap toJSON docIds ]
2171+
2172+ toJSON (QueryQueryStringQuery qQueryStringQuery) =
2173+ object [ "query_string" .= qQueryStringQuery ]
2174+
2175+ toJSON (QueryMatchQuery matchQuery) =
2176+ object [ "match" .= matchQuery ]
2177+
2178+ toJSON (QueryMultiMatchQuery multiMatchQuery) =
2179+ toJSON multiMatchQuery
2180+
2181+ toJSON (QueryBoolQuery boolQuery) =
2182+ object [ "bool" .= boolQuery ]
2183+
2184+ toJSON (QueryBoostingQuery boostingQuery) =
2185+ object [ "boosting" .= boostingQuery ]
2186+
2187+ toJSON (QueryCommonTermsQuery commonTermsQuery) =
2188+ object [ "common" .= commonTermsQuery ]
2189+
2190+ toJSON (ConstantScoreFilter csFilter boost) =
2191+ object ["constant_score" .= object ["filter" .= csFilter
2192+ , "boost" .= boost]]
2193+
2194+ toJSON (ConstantScoreQuery query boost) =
2195+ object ["constant_score" .= object ["query" .= query
2196+ , "boost" .= boost]]
2197+
2198+ toJSON (QueryDisMaxQuery disMaxQuery) =
2199+ object [ "dis_max" .= disMaxQuery ]
2200+
2201+ toJSON (QueryFilteredQuery qFilteredQuery) =
2202+ object [ "filtered" .= qFilteredQuery ]
2203+
2204+ toJSON (QueryFuzzyLikeThisQuery fuzzyQuery) =
2205+ object [ "fuzzy_like_this" .= fuzzyQuery ]
2206+
2207+ toJSON (QueryFuzzyLikeFieldQuery fuzzyFieldQuery) =
2208+ object [ "fuzzy_like_this_field" .= fuzzyFieldQuery ]
2209+
2210+ toJSON (QueryFuzzyQuery fuzzyQuery) =
2211+ object [ "fuzzy" .= fuzzyQuery ]
2212+
2213+ toJSON (QueryHasChildQuery childQuery) =
2214+ object [ "has_child" .= childQuery ]
2215+
2216+ toJSON (QueryHasParentQuery parentQuery) =
2217+ object [ "has_parent" .= parentQuery ]
2218+
2219+ toJSON (QueryIndicesQuery qIndicesQuery) =
2220+ object [ "indices" .= qIndicesQuery ]
2221+
2222+ toJSON (MatchAllQuery boost) =
2223+ object [ "match_all" .= omitNulls [ "boost" .= boost ] ]
2224+
2225+ toJSON (QueryMoreLikeThisQuery query) =
2226+ object [ "more_like_this" .= query ]
2227+
2228+ toJSON (QueryMoreLikeThisFieldQuery query) =
2229+ object [ "more_like_this_field" .= query ]
2230+
2231+ toJSON (QueryNestedQuery query) =
2232+ object [ "nested" .= query ]
2233+
2234+ toJSON (QueryPrefixQuery query) =
2235+ object [ "prefix" .= query ]
2236+
2237+ toJSON (QueryRangeQuery query) =
2238+ object [ "range" .= query ]
2239+
2240+ toJSON (QueryRegexpQuery query) =
2241+ object [ "regexp" .= query ]
2242+
2243+ toJSON (QuerySimpleQueryStringQuery query) =
2244+ object [ "simple_query_string" .= query ]
2245+
2246+instance FromJSON Query where
2247+ parseJSON v = withObject "Query" parse v
2248+ where parse o = termQuery `taggedWith` "term"
2249+ <|> termsQuery `taggedWith` "terms"
2250+ <|> idsQuery `taggedWith` "ids"
2251+ <|> queryQueryStringQuery `taggedWith` "query_string"
2252+ <|> queryMatchQuery `taggedWith` "match"
2253+ <|> queryMultiMatchQuery
2254+ <|> queryBoolQuery `taggedWith` "bool"
2255+ <|> queryBoostingQuery `taggedWith` "boosting"
2256+ <|> queryCommonTermsQuery `taggedWith` "common"
2257+ <|> constantScoreFilter `taggedWith` "constant_score"
2258+ <|> constantScoreQuery `taggedWith` "constant_score"
2259+ <|> queryDisMaxQuery `taggedWith` "dis_max"
2260+ <|> queryFilteredQuery `taggedWith` "filtered"
2261+ <|> queryFuzzyLikeThisQuery `taggedWith` "fuzzy_like_this"
2262+ <|> queryFuzzyLikeFieldQuery `taggedWith` "fuzzy_like_this_field"
2263+ <|> queryFuzzyQuery `taggedWith` "fuzzy"
2264+ <|> queryHasChildQuery `taggedWith` "has_child"
2265+ <|> queryHasParentQuery `taggedWith` "has_parent"
2266+ <|> queryIndicesQuery `taggedWith` "indices"
2267+ <|> matchAllQuery `taggedWith` "match_all"
2268+ <|> queryMoreLikeThisQuery `taggedWith` "more_like_this"
2269+ <|> queryMoreLikeThisFieldQuery `taggedWith` "more_like_this_field"
2270+ <|> queryNestedQuery `taggedWith` "nested"
2271+ <|> queryPrefixQuery `taggedWith` "prefix"
2272+ <|> queryRangeQuery `taggedWith` "range"
2273+ <|> queryRegexpQuery `taggedWith` "regexp"
2274+ <|> querySimpleQueryStringQuery `taggedWith` "simple_query_string"
2275+ where taggedWith parser k = parser =<< o .: k
2276+ termQuery = fieldTagged $ \(FieldName fn) o ->
2277+ TermQuery <$> (Term fn <$> o .: "value") <*> o .:? "boost"
2278+ termsQuery o = case HM.toList o of
2279+ [(fn, vs)] -> do vals <- parseJSON vs
2280+ case vals of
2281+ x:xs -> return (TermsQuery fn (x :| xs))
2282+ _ -> fail "Expected non empty list of values"
2283+ _ -> fail "Expected object with 1 field-named key"
2284+ idsQuery o = IdsQuery <$> o .: "type"
2285+ <*> o .: "values"
2286+ queryQueryStringQuery = pure . QueryQueryStringQuery
2287+ queryMatchQuery = pure . QueryMatchQuery
2288+ queryMultiMatchQuery = QueryMultiMatchQuery <$> parseJSON v
2289+ queryBoolQuery = pure . QueryBoolQuery
2290+ queryBoostingQuery = pure . QueryBoostingQuery
2291+ queryCommonTermsQuery = pure . QueryCommonTermsQuery
2292+ constantScoreFilter o = case HM.lookup "filter" o of
2293+ Just x -> ConstantScoreFilter <$> parseJSON x
2294+ <*> o .: "boost"
2295+ _ -> fail "Does not appear to be a ConstantScoreFilter"
2296+ constantScoreQuery o = case HM.lookup "query" o of
2297+ Just x -> ConstantScoreQuery <$> parseJSON x
2298+ <*> o .: "boost"
2299+ _ -> fail "Does not appear to be a ConstantScoreQuery"
2300+ queryDisMaxQuery = pure . QueryDisMaxQuery
2301+ queryFilteredQuery = pure . QueryFilteredQuery
2302+ queryFuzzyLikeThisQuery = pure . QueryFuzzyLikeThisQuery
2303+ queryFuzzyLikeFieldQuery = pure . QueryFuzzyLikeFieldQuery
2304+ queryFuzzyQuery = pure . QueryFuzzyQuery
2305+ queryHasChildQuery = pure . QueryHasChildQuery
2306+ queryHasParentQuery = pure . QueryHasParentQuery
2307+ queryIndicesQuery = pure . QueryIndicesQuery
2308+ matchAllQuery o = MatchAllQuery <$> o .:? "boost"
2309+ queryMoreLikeThisQuery = pure . QueryMoreLikeThisQuery
2310+ queryMoreLikeThisFieldQuery = pure . QueryMoreLikeThisFieldQuery
2311+ queryNestedQuery = pure . QueryNestedQuery
2312+ queryPrefixQuery = pure . QueryPrefixQuery
2313+ queryRangeQuery = pure . QueryRangeQuery
2314+ queryRegexpQuery = pure . QueryRegexpQuery
2315+ querySimpleQueryStringQuery = pure . QuerySimpleQueryStringQuery
2316+
2317+
2318+omitNulls :: [(Text, Value)] -> Value
2319+omitNulls = object . filter notNull where
2320+ notNull (_, Null) = False
2321+ notNull (_, Array a) = (not . V.null) a
2322+ notNull _ = True
2323+
2324+
2325+instance ToJSON SimpleQueryStringQuery where
2326+ toJSON SimpleQueryStringQuery {..} =
2327+ omitNulls (base ++ maybeAdd)
2328+ where base = [ "query" .= simpleQueryStringQuery ]
2329+ maybeAdd = [ "fields" .= simpleQueryStringField
2330+ , "default_operator" .= simpleQueryStringOperator
2331+ , "analyzer" .= simpleQueryStringAnalyzer
2332+ , "flags" .= simpleQueryStringFlags
2333+ , "lowercase_expanded_terms" .= simpleQueryStringLowercaseExpanded
2334+ , "locale" .= simpleQueryStringLocale ]
2335+
2336+instance FromJSON SimpleQueryStringQuery where
2337+ parseJSON = withObject "SimpleQueryStringQuery" parse
2338+ where parse o = SimpleQueryStringQuery <$> o .: "query"
2339+ <*> o .:? "fields"
2340+ <*> o .:? "default_operator"
2341+ <*> o .:? "analyzer"
2342+ <*> (parseFlags <$> o .:? "flags")
2343+ <*> o .:? "lowercase_expanded_terms"
2344+ <*> o .:? "locale"
2345+ parseFlags (Just (x:xs)) = Just (x :| xs)
2346+ parseFlags _ = Nothing
2347+
2348+instance ToJSON FieldOrFields where
2349+ toJSON (FofField fieldName) =
2350+ toJSON fieldName
2351+ toJSON (FofFields fieldNames) =
2352+ toJSON fieldNames
2353+
2354+instance FromJSON FieldOrFields where
2355+ parseJSON v = FofField <$> parseJSON v
2356+ <|> FofFields <$> (parseNEJSON =<< parseJSON v)
2357+
2358+instance ToJSON SimpleQueryFlag where
2359+ toJSON SimpleQueryAll = "ALL"
2360+ toJSON SimpleQueryNone = "NONE"
2361+ toJSON SimpleQueryAnd = "AND"
2362+ toJSON SimpleQueryOr = "OR"
2363+ toJSON SimpleQueryPrefix = "PREFIX"
2364+ toJSON SimpleQueryPhrase = "PHRASE"
2365+ toJSON SimpleQueryPrecedence = "PRECEDENCE"
2366+ toJSON SimpleQueryEscape = "ESCAPE"
2367+ toJSON SimpleQueryWhitespace = "WHITESPACE"
2368+ toJSON SimpleQueryFuzzy = "FUZZY"
2369+ toJSON SimpleQueryNear = "NEAR"
2370+ toJSON SimpleQuerySlop = "SLOP"
2371+
2372+instance FromJSON SimpleQueryFlag where
2373+ parseJSON = withText "SimpleQueryFlag" parse
2374+ where parse "ALL" = pure SimpleQueryAll
2375+ parse "NONE" = pure SimpleQueryNone
2376+ parse "AND" = pure SimpleQueryAnd
2377+ parse "OR" = pure SimpleQueryOr
2378+ parse "PREFIX" = pure SimpleQueryPrefix
2379+ parse "PHRASE" = pure SimpleQueryPhrase
2380+ parse "PRECEDENCE" = pure SimpleQueryPrecedence
2381+ parse "ESCAPE" = pure SimpleQueryEscape
2382+ parse "WHITESPACE" = pure SimpleQueryWhitespace
2383+ parse "FUZZY" = pure SimpleQueryFuzzy
2384+ parse "NEAR" = pure SimpleQueryNear
2385+ parse "SLOP" = pure SimpleQuerySlop
2386+ parse f = fail ("Unexpected SimpleQueryFlag: " <> show f)
2387+
2388+instance ToJSON RegexpQuery where
2389+ toJSON (RegexpQuery (FieldName rqQueryField)
2390+ (Regexp regexpQueryQuery) rqQueryFlags
2391+ rqQueryBoost) =
2392+ object [ rqQueryField .= omitNulls base ]
2393+ where base = [ "value" .= regexpQueryQuery
2394+ , "flags" .= rqQueryFlags
2395+ , "boost" .= rqQueryBoost ]
2396+
2397+instance FromJSON RegexpQuery where
2398+ parseJSON = withObject "RegexpQuery" parse
2399+ where parse = fieldTagged $ \fn o ->
2400+ RegexpQuery fn
2401+ <$> o .: "value"
2402+ <*> o .: "flags"
2403+ <*> o .:? "boost"
2404+
2405+instance ToJSON QueryStringQuery where
2406+ toJSON (QueryStringQuery qsQueryString
2407+ qsDefaultField qsOperator
2408+ qsAnalyzer qsAllowWildcard
2409+ qsLowercaseExpanded qsEnablePositionIncrements
2410+ qsFuzzyMaxExpansions qsFuzziness
2411+ qsFuzzyPrefixLength qsPhraseSlop
2412+ qsBoost qsAnalyzeWildcard
2413+ qsGeneratePhraseQueries qsMinimumShouldMatch
2414+ qsLenient qsLocale) =
2415+ omitNulls base
2416+ where
2417+ base = [ "query" .= qsQueryString
2418+ , "default_field" .= qsDefaultField
2419+ , "default_operator" .= qsOperator
2420+ , "analyzer" .= qsAnalyzer
2421+ , "allow_leading_wildcard" .= qsAllowWildcard
2422+ , "lowercase_expanded_terms" .= qsLowercaseExpanded
2423+ , "enable_position_increments" .= qsEnablePositionIncrements
2424+ , "fuzzy_max_expansions" .= qsFuzzyMaxExpansions
2425+ , "fuzziness" .= qsFuzziness
2426+ , "fuzzy_prefix_length" .= qsFuzzyPrefixLength
2427+ , "phrase_slop" .= qsPhraseSlop
2428+ , "boost" .= qsBoost
2429+ , "analyze_wildcard" .= qsAnalyzeWildcard
2430+ , "auto_generate_phrase_queries" .= qsGeneratePhraseQueries
2431+ , "minimum_should_match" .= qsMinimumShouldMatch
2432+ , "lenient" .= qsLenient
2433+ , "locale" .= qsLocale ]
2434+
2435+instance FromJSON QueryStringQuery where
2436+ parseJSON = withObject "QueryStringQuery" parse
2437+ where parse o = QueryStringQuery
2438+ <$> o .: "query"
2439+ <*> o .:? "default_field"
2440+ <*> o .:? "default_operator"
2441+ <*> o .:? "analyzer"
2442+ <*> o .:? "allow_leading_wildcard"
2443+ <*> o .:? "lowercase_expanded_terms"
2444+ <*> o .:? "enable_position_increments"
2445+ <*> o .:? "fuzzy_max_expansions"
2446+ <*> o .:? "fuzziness"
2447+ <*> o .:? "fuzzy_prefix_length"
2448+ <*> o .:? "phrase_slop"
2449+ <*> o .:? "boost"
2450+ <*> o .:? "analyze_wildcard"
2451+ <*> o .:? "auto_generate_phrase_queries"
2452+ <*> o .:? "minimum_should_match"
2453+ <*> o .:? "lenient"
2454+ <*> o .:? "locale"
2455+
2456+instance ToJSON RangeQuery where
2457+ toJSON (RangeQuery (FieldName fieldName) range boost) =
2458+ object [ fieldName .= object conjoined ]
2459+ where conjoined = [ "boost" .= boost ] ++ (rangeValueToPair range)
2460+
2461+instance FromJSON RangeQuery where
2462+ parseJSON = withObject "RangeQuery" parse
2463+ where parse = fieldTagged $ \fn o ->
2464+ RangeQuery fn
2465+ <$> parseJSON (Object o)
2466+ <*> o .: "boost"
2467+
2468+instance FromJSON RangeValue where
2469+ parseJSON = withObject "RangeValue" parse
2470+ where parse o = parseDate o
2471+ <|> parseDouble o
2472+ parseDate o = do lt <- o .:? "lt"
2473+ lte <- o .:? "lte"
2474+ gt <- o .:? "gt"
2475+ gte <- o .:? "gte"
2476+ case (lt, lte, gt, gte) of
2477+ (Just a, _, Just b, _) -> return (RangeDateGtLt (GreaterThanD b) (LessThanD a))
2478+ (Just a, _, _, Just b)-> return (RangeDateGteLt (GreaterThanEqD b) (LessThanD a))
2479+ (_, Just a, Just b, _)-> return (RangeDateGtLte (GreaterThanD b) (LessThanEqD a))
2480+ (_, Just a, _, Just b)-> return (RangeDateGteLte (GreaterThanEqD b) (LessThanEqD a))
2481+ (_, _, Just a, _)-> return (RangeDateGt (GreaterThanD a))
2482+ (Just a, _, _, _)-> return (RangeDateLt (LessThanD a))
2483+ (_, _, _, Just a)-> return (RangeDateGte (GreaterThanEqD a))
2484+ (_, Just a, _, _)-> return (RangeDateLte (LessThanEqD a))
2485+ (Nothing, Nothing, Nothing, Nothing) -> mzero
2486+ parseDouble o = do lt <- o .:? "lt"
2487+ lte <- o .:? "lte"
2488+ gt <- o .:? "gt"
2489+ gte <- o .:? "gte"
2490+ case (lt, lte, gt, gte) of
2491+ (Just a, _, Just b, _) -> return (RangeDoubleGtLt (GreaterThan b) (LessThan a))
2492+ (Just a, _, _, Just b)-> return (RangeDoubleGteLt (GreaterThanEq b) (LessThan a))
2493+ (_, Just a, Just b, _)-> return (RangeDoubleGtLte (GreaterThan b) (LessThanEq a))
2494+ (_, Just a, _, Just b)-> return (RangeDoubleGteLte (GreaterThanEq b) (LessThanEq a))
2495+ (_, _, Just a, _)-> return (RangeDoubleGt (GreaterThan a))
2496+ (Just a, _, _, _)-> return (RangeDoubleLt (LessThan a))
2497+ (_, _, _, Just a)-> return (RangeDoubleGte (GreaterThanEq a))
2498+ (_, Just a, _, _)-> return (RangeDoubleLte (LessThanEq a))
2499+ (Nothing, Nothing, Nothing, Nothing) -> mzero
2500+
2501+instance ToJSON PrefixQuery where
2502+ toJSON (PrefixQuery (FieldName fieldName) queryValue boost) =
2503+ object [ fieldName .= omitNulls base ]
2504+ where base = [ "value" .= queryValue
2505+ , "boost" .= boost ]
2506+
2507+instance FromJSON PrefixQuery where
2508+ parseJSON = withObject "PrefixQuery" parse
2509+ where parse = fieldTagged $ \fn o ->
2510+ PrefixQuery fn
2511+ <$> o .: "value"
2512+ <*> o .:? "boost"
2513+
2514+instance ToJSON NestedQuery where
2515+ toJSON (NestedQuery nqPath nqScoreType nqQuery) =
2516+ object [ "path" .= nqPath
2517+ , "score_mode" .= nqScoreType
2518+ , "query" .= nqQuery ]
2519+
2520+instance FromJSON NestedQuery where
2521+ parseJSON = withObject "NestedQuery" parse
2522+ where parse o = NestedQuery
2523+ <$> o .: "path"
2524+ <*> o .: "score_mode"
2525+ <*> o .: "query"
2526+
2527+instance ToJSON MoreLikeThisFieldQuery where
2528+ toJSON (MoreLikeThisFieldQuery text (FieldName fieldName)
2529+ percent mtf mqt stopwords mindf maxdf
2530+ minwl maxwl boostTerms boost analyzer) =
2531+ object [ fieldName .= omitNulls base ]
2532+ where base = [ "like_text" .= text
2533+ , "percent_terms_to_match" .= percent
2534+ , "min_term_freq" .= mtf
2535+ , "max_query_terms" .= mqt
2536+ , "stop_words" .= stopwords
2537+ , "min_doc_freq" .= mindf
2538+ , "max_doc_freq" .= maxdf
2539+ , "min_word_length" .= minwl
2540+ , "max_word_length" .= maxwl
2541+ , "boost_terms" .= boostTerms
2542+ , "boost" .= boost
2543+ , "analyzer" .= analyzer ]
2544+
2545+instance FromJSON MoreLikeThisFieldQuery where
2546+ parseJSON = withObject "MoreLikeThisFieldQuery" parse
2547+ where parse = fieldTagged $ \fn o ->
2548+ MoreLikeThisFieldQuery
2549+ <$> o .: "like_text"
2550+ <*> pure fn
2551+ <*> o .:? "percent_terms_to_match"
2552+ <*> o .:? "min_term_freq"
2553+ <*> o .:? "max_query_terms"
2554+ -- <*> (optionalNE =<< o .:? "stop_words")
2555+ <*> o .:? "stop_words"
2556+ <*> o .:? "min_doc_freq"
2557+ <*> o .:? "max_doc_freq"
2558+ <*> o .:? "min_word_length"
2559+ <*> o .:? "max_word_length"
2560+ <*> o .:? "boost_terms"
2561+ <*> o .:? "boost"
2562+ <*> o .:? "analyzer"
2563+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
2564+
2565+instance ToJSON MoreLikeThisQuery where
2566+ toJSON (MoreLikeThisQuery text fields percent
2567+ mtf mqt stopwords mindf maxdf
2568+ minwl maxwl boostTerms boost analyzer) =
2569+ omitNulls base
2570+ where base = [ "like_text" .= text
2571+ , "fields" .= fields
2572+ , "percent_terms_to_match" .= percent
2573+ , "min_term_freq" .= mtf
2574+ , "max_query_terms" .= mqt
2575+ , "stop_words" .= stopwords
2576+ , "min_doc_freq" .= mindf
2577+ , "max_doc_freq" .= maxdf
2578+ , "min_word_length" .= minwl
2579+ , "max_word_length" .= maxwl
2580+ , "boost_terms" .= boostTerms
2581+ , "boost" .= boost
2582+ , "analyzer" .= analyzer ]
2583+
2584+instance FromJSON MoreLikeThisQuery where
2585+ parseJSON = withObject "MoreLikeThisQuery" parse
2586+ where parse o = MoreLikeThisQuery
2587+ <$> o .: "like_text"
2588+ -- <*> (optionalNE =<< o .:? "fields")
2589+ <*> o .:? "fields"
2590+ <*> o .:? "percent_terms_to_match"
2591+ <*> o .:? "min_term_freq"
2592+ <*> o .:? "max_query_terms"
2593+ -- <*> (optionalNE =<< o .:? "stop_words")
2594+ <*> o .:? "stop_words"
2595+ <*> o .:? "min_doc_freq"
2596+ <*> o .:? "max_doc_freq"
2597+ <*> o .:? "min_word_length"
2598+ <*> o .:? "max_word_length"
2599+ <*> o .:? "boost_terms"
2600+ <*> o .:? "boost"
2601+ <*> o .:? "analyzer"
2602+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
2603+
2604+instance ToJSON IndicesQuery where
2605+ toJSON (IndicesQuery indices query noMatch) =
2606+ omitNulls [ "indices" .= indices
2607+ , "no_match_query" .= noMatch
2608+ , "query" .= query ]
2609+
2610+instance FromJSON IndicesQuery where
2611+ parseJSON = withObject "IndicesQuery" parse
2612+ where parse o = IndicesQuery
2613+ <$> o .:? "indices" .!= []
2614+ <*> o .: "query"
2615+ <*> o .:? "no_match_query"
2616+
2617+instance ToJSON HasParentQuery where
2618+ toJSON (HasParentQuery queryType query scoreType) =
2619+ omitNulls [ "parent_type" .= queryType
2620+ , "score_type" .= scoreType
2621+ , "query" .= query ]
2622+
2623+instance FromJSON HasParentQuery where
2624+ parseJSON = withObject "HasParentQuery" parse
2625+ where parse o = HasParentQuery
2626+ <$> o .: "parent_type"
2627+ <*> o .: "query"
2628+ <*> o .:? "score_type"
2629+
2630+instance ToJSON HasChildQuery where
2631+ toJSON (HasChildQuery queryType query scoreType) =
2632+ omitNulls [ "query" .= query
2633+ , "score_type" .= scoreType
2634+ , "type" .= queryType ]
2635+
2636+instance FromJSON HasChildQuery where
2637+ parseJSON = withObject "HasChildQuery" parse
2638+ where parse o = HasChildQuery
2639+ <$> o .: "type"
2640+ <*> o .: "query"
2641+ <*> o .:? "score_type"
2642+
2643+instance ToJSON FuzzyQuery where
2644+ toJSON (FuzzyQuery (FieldName fieldName) queryText
2645+ prefixLength maxEx fuzziness boost) =
2646+ object [ fieldName .= omitNulls base ]
2647+ where base = [ "value" .= queryText
2648+ , "fuzziness" .= fuzziness
2649+ , "prefix_length" .= prefixLength
2650+ , "boost" .= boost
2651+ , "max_expansions" .= maxEx ]
2652+
2653+instance FromJSON FuzzyQuery where
2654+ parseJSON = withObject "FuzzyQuery" parse
2655+ where parse = fieldTagged $ \fn o ->
2656+ FuzzyQuery fn
2657+ <$> o .: "value"
2658+ <*> o .: "prefix_length"
2659+ <*> o .: "max_expansions"
2660+ <*> o .: "fuzziness"
2661+ <*> o .:? "boost"
2662+
2663+instance ToJSON FuzzyLikeFieldQuery where
2664+ toJSON (FuzzyLikeFieldQuery (FieldName fieldName)
2665+ fieldText maxTerms ignoreFreq fuzziness prefixLength
2666+ boost analyzer) =
2667+ object [ fieldName .=
2668+ omitNulls [ "like_text" .= fieldText
2669+ , "max_query_terms" .= maxTerms
2670+ , "ignore_tf" .= ignoreFreq
2671+ , "fuzziness" .= fuzziness
2672+ , "prefix_length" .= prefixLength
2673+ , "analyzer" .= analyzer
2674+ , "boost" .= boost ]]
2675+
2676+instance FromJSON FuzzyLikeFieldQuery where
2677+ parseJSON = withObject "FuzzyLikeFieldQuery" parse
2678+ where parse = fieldTagged $ \fn o ->
2679+ FuzzyLikeFieldQuery fn
2680+ <$> o .: "like_text"
2681+ <*> o .: "max_query_terms"
2682+ <*> o .: "ignore_tf"
2683+ <*> o .: "fuzziness"
2684+ <*> o .: "prefix_length"
2685+ <*> o .: "boost"
2686+ <*> o .:? "analyzer"
2687+
2688+instance ToJSON FuzzyLikeThisQuery where
2689+ toJSON (FuzzyLikeThisQuery fields text maxTerms
2690+ ignoreFreq fuzziness prefixLength boost analyzer) =
2691+ omitNulls base
2692+ where base = [ "fields" .= fields
2693+ , "like_text" .= text
2694+ , "max_query_terms" .= maxTerms
2695+ , "ignore_tf" .= ignoreFreq
2696+ , "fuzziness" .= fuzziness
2697+ , "prefix_length" .= prefixLength
2698+ , "analyzer" .= analyzer
2699+ , "boost" .= boost ]
2700+
2701+instance FromJSON FuzzyLikeThisQuery where
2702+ parseJSON = withObject "FuzzyLikeThisQuery" parse
2703+ where parse o = FuzzyLikeThisQuery
2704+ <$> o .:? "fields" .!= []
2705+ <*> o .: "like_text"
2706+ <*> o .: "max_query_terms"
2707+ <*> o .: "ignore_tf"
2708+ <*> o .: "fuzziness"
2709+ <*> o .: "prefix_length"
2710+ <*> o .: "boost"
2711+ <*> o .:? "analyzer"
2712+
2713+instance ToJSON FilteredQuery where
2714+ toJSON (FilteredQuery query fFilter) =
2715+ object [ "query" .= query
2716+ , "filter" .= fFilter ]
2717+
2718+instance FromJSON FilteredQuery where
2719+ parseJSON = withObject "FilteredQuery" parse
2720+ where parse o = FilteredQuery
2721+ <$> o .: "query"
2722+ <*> o .: "filter"
2723+
2724+instance ToJSON DisMaxQuery where
2725+ toJSON (DisMaxQuery queries tiebreaker boost) =
2726+ omitNulls base
2727+ where base = [ "queries" .= queries
2728+ , "boost" .= boost
2729+ , "tie_breaker" .= tiebreaker ]
2730+
2731+instance FromJSON DisMaxQuery where
2732+ parseJSON = withObject "DisMaxQuery" parse
2733+ where parse o = DisMaxQuery
2734+ <$> o .:? "queries" .!= []
2735+ <*> o .: "tie_breaker"
2736+ <*> o .:? "boost"
2737+
2738+instance ToJSON CommonTermsQuery where
2739+ toJSON (CommonTermsQuery (FieldName fieldName)
2740+ (QueryString query) cf lfo hfo msm
2741+ boost analyzer disableCoord) =
2742+ object [fieldName .= omitNulls base ]
2743+ where base = [ "query" .= query
2744+ , "cutoff_frequency" .= cf
2745+ , "low_freq_operator" .= lfo
2746+ , "minimum_should_match" .= msm
2747+ , "boost" .= boost
2748+ , "analyzer" .= analyzer
2749+ , "disable_coord" .= disableCoord
2750+ , "high_freq_operator" .= hfo ]
2751+
2752+instance FromJSON CommonTermsQuery where
2753+ parseJSON = withObject "CommonTermsQuery" parse
2754+ where parse = fieldTagged $ \fn o ->
2755+ CommonTermsQuery fn
2756+ <$> o .: "query"
2757+ <*> o .: "cutoff_frequency"
2758+ <*> o .: "low_freq_operator"
2759+ <*> o .: "high_freq_operator"
2760+ <*> o .:? "minimum_should_match"
2761+ <*> o .:? "boost"
2762+ <*> o .:? "analyzer"
2763+ <*> o .:? "disable_coord"
2764+
2765+instance ToJSON CommonMinimumMatch where
2766+ toJSON (CommonMinimumMatch mm) = toJSON mm
2767+ toJSON (CommonMinimumMatchHighLow (MinimumMatchHighLow lowF highF)) =
2768+ object [ "low_freq" .= lowF
2769+ , "high_freq" .= highF ]
2770+
2771+instance FromJSON CommonMinimumMatch where
2772+ parseJSON v = parseMinimum v
2773+ <|> parseMinimumHighLow v
2774+ where parseMinimum = fmap CommonMinimumMatch . parseJSON
2775+ parseMinimumHighLow = fmap CommonMinimumMatchHighLow . withObject "CommonMinimumMatchHighLow" (\o ->
2776+ MinimumMatchHighLow
2777+ <$> o .: "low_freq"
2778+ <*> o .: "high_freq")
2779+
2780+
2781+instance ToJSON BoostingQuery where
2782+ toJSON (BoostingQuery bqPositiveQuery bqNegativeQuery bqNegativeBoost) =
2783+ object [ "positive" .= bqPositiveQuery
2784+ , "negative" .= bqNegativeQuery
2785+ , "negative_boost" .= bqNegativeBoost ]
2786+
2787+instance FromJSON BoostingQuery where
2788+ parseJSON = withObject "BoostingQuery" parse
2789+ where parse o = BoostingQuery
2790+ <$> o .: "positive"
2791+ <*> o .: "negative"
2792+ <*> o .: "negative_boost"
2793+
2794+instance ToJSON BoolQuery where
2795+ toJSON (BoolQuery mustM notM shouldM bqMin boost disableCoord) =
2796+ omitNulls base
2797+ where base = [ "must" .= mustM
2798+ , "must_not" .= notM
2799+ , "should" .= shouldM
2800+ , "minimum_should_match" .= bqMin
2801+ , "boost" .= boost
2802+ , "disable_coord" .= disableCoord ]
2803+
2804+instance FromJSON BoolQuery where
2805+ parseJSON = withObject "BoolQuery" parse
2806+ where parse o = BoolQuery
2807+ <$> o .:? "must" .!= []
2808+ <*> o .:? "must_not" .!= []
2809+ <*> o .:? "should" .!= []
2810+ <*> o .:? "minimum_should_match"
2811+ <*> o .:? "boost"
2812+ <*> o .:? "disable_coord"
2813+
2814+instance ToJSON MatchQuery where
2815+ toJSON (MatchQuery (FieldName fieldName)
2816+ (QueryString mqQueryString) booleanOperator
2817+ zeroTermsQuery cutoffFrequency matchQueryType
2818+ analyzer maxExpansions lenient boost) =
2819+ object [ fieldName .= omitNulls base ]
2820+ where base = [ "query" .= mqQueryString
2821+ , "operator" .= booleanOperator
2822+ , "zero_terms_query" .= zeroTermsQuery
2823+ , "cutoff_frequency" .= cutoffFrequency
2824+ , "type" .= matchQueryType
2825+ , "analyzer" .= analyzer
2826+ , "max_expansions" .= maxExpansions
2827+ , "lenient" .= lenient
2828+ , "boost" .= boost ]
2829+
2830+instance FromJSON MatchQuery where
2831+ parseJSON = withObject "MatchQuery" parse
2832+ where parse = fieldTagged $ \fn o ->
2833+ MatchQuery fn
2834+ <$> o .: "query"
2835+ <*> o .: "operator"
2836+ <*> o .: "zero_terms_query"
2837+ <*> o .:? "cutoff_frequency"
2838+ <*> o .:? "type"
2839+ <*> o .:? "analyzer"
2840+ <*> o .:? "max_expansions"
2841+ <*> o .:? "lenient"
2842+ <*> o .:? "boost"
2843+
2844+instance ToJSON MultiMatchQuery where
2845+ toJSON (MultiMatchQuery fields (QueryString query) boolOp
2846+ ztQ tb mmqt cf analyzer maxEx lenient) =
2847+ object ["multi_match" .= omitNulls base]
2848+ where base = [ "fields" .= fmap toJSON fields
2849+ , "query" .= query
2850+ , "operator" .= boolOp
2851+ , "zero_terms_query" .= ztQ
2852+ , "tiebreaker" .= tb
2853+ , "type" .= mmqt
2854+ , "cutoff_frequency" .= cf
2855+ , "analyzer" .= analyzer
2856+ , "max_expansions" .= maxEx
2857+ , "lenient" .= lenient ]
2858+
2859+instance FromJSON MultiMatchQuery where
2860+ parseJSON = withObject "MultiMatchQuery" parse
2861+ where parse raw = do o <- raw .: "multi_match"
2862+ MultiMatchQuery
2863+ <$> o .:? "fields" .!= []
2864+ <*> o .: "query"
2865+ <*> o .: "operator"
2866+ <*> o .: "zero_terms_query"
2867+ <*> o .:? "tiebreaker"
2868+ <*> o .:? "type"
2869+ <*> o .:? "cutoff_frequency"
2870+ <*> o .:? "analyzer"
2871+ <*> o .:? "max_expansions"
2872+ <*> o .:? "lenient"
2873+
2874+instance ToJSON MultiMatchQueryType where
2875+ toJSON MultiMatchBestFields = "best_fields"
2876+ toJSON MultiMatchMostFields = "most_fields"
2877+ toJSON MultiMatchCrossFields = "cross_fields"
2878+ toJSON MultiMatchPhrase = "phrase"
2879+ toJSON MultiMatchPhrasePrefix = "phrase_prefix"
2880+
2881+instance FromJSON MultiMatchQueryType where
2882+ parseJSON = withText "MultiMatchPhrasePrefix" parse
2883+ where parse "best_fields" = pure MultiMatchBestFields
2884+ parse "most_fields" = pure MultiMatchMostFields
2885+ parse "cross_fields" = pure MultiMatchCrossFields
2886+ parse "phrase" = pure MultiMatchPhrase
2887+ parse "phrase_prefix" = pure MultiMatchPhrasePrefix
2888+ parse t = fail ("Unexpected MultiMatchPhrasePrefix: " <> show t)
2889+
2890+instance ToJSON BooleanOperator where
2891+ toJSON And = String "and"
2892+ toJSON Or = String "or"
2893+
2894+instance FromJSON BooleanOperator where
2895+ parseJSON = withText "BooleanOperator" parse
2896+ where parse "and" = pure And
2897+ parse "or" = pure Or
2898+ parse o = fail ("Unexpected BooleanOperator: " <> show o)
2899+
2900+instance ToJSON ZeroTermsQuery where
2901+ toJSON ZeroTermsNone = String "none"
2902+ toJSON ZeroTermsAll = String "all"
2903+
2904+instance FromJSON ZeroTermsQuery where
2905+ parseJSON = withText "ZeroTermsQuery" parse
2906+ where parse "none" = pure ZeroTermsNone
2907+ parse "all" = pure ZeroTermsAll
2908+ parse q = fail ("Unexpected ZeroTermsQuery: " <> show q)
2909+
2910+instance ToJSON MatchQueryType where
2911+ toJSON MatchPhrase = "phrase"
2912+ toJSON MatchPhrasePrefix = "phrase_prefix"
2913+
2914+instance FromJSON MatchQueryType where
2915+ parseJSON = withText "MatchQueryType" parse
2916+ where parse "phrase" = pure MatchPhrase
2917+ parse "phrase_prefix" = pure MatchPhrasePrefix
2918+ parse t = fail ("Unexpected MatchQueryType: " <> show t)
2919+
2920+instance FromJSON Status where
2921+ parseJSON (Object v) = Status <$>
2922+ v .:? "ok" <*>
2923+ v .: "status" <*>
2924+ v .: "name" <*>
2925+ v .: "version" <*>
2926+ v .: "tagline"
2927+ parseJSON _ = empty
2928+
2929+
2930+instance ToJSON IndexSettings where
2931+ toJSON (IndexSettings s r) = object ["settings" .=
2932+ object ["index" .=
2933+ object ["number_of_shards" .= s, "number_of_replicas" .= r]
2934+ ]
2935+ ]
2936+
2937+instance FromJSON IndexSettings where
2938+ parseJSON = withObject "IndexSettings" parse
2939+ where parse o = do s <- o .: "settings"
2940+ i <- s .: "index"
2941+ IndexSettings <$> i .: "number_of_shards"
2942+ <*> i .: "number_of_replicas"
2943+
2944+instance ToJSON UpdatableIndexSetting where
2945+ toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
2946+ toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
2947+ toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
2948+ toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
2949+ toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
2950+ toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
2951+ toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
2952+ toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
2953+ toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
2954+ toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
2955+ toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
2956+ toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
2957+ toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
2958+ toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
2959+ toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
2960+ toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
2961+ toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
2962+ toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
2963+ toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
2964+ toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
2965+ toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
2966+ toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
2967+ toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
2968+ toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
2969+ toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
2970+ toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
2971+ toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
2972+ toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
2973+
2974+instance FromJSON UpdatableIndexSetting where
2975+ parseJSON = withObject "UpdatableIndexSetting" parse
2976+ where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
2977+ <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
2978+ <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
2979+ <|> indexConcurrency `taggedAt` ["index", "concurrency"]
2980+ <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
2981+ <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
2982+ <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
2983+ <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
2984+ <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
2985+ <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
2986+ <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
2987+ <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
2988+ <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
2989+ <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
2990+ <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
2991+ <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
2992+ <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
2993+ <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
2994+ <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
2995+ <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
2996+ <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
2997+ <|> compoundFormat `taggedAt` ["index", "compound_format"]
2998+ <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
2999+ <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
3000+ <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
3001+ <|> blocksRead `taggedAt` ["blocks", "read"]
3002+ <|> blocksWrite `taggedAt` ["blocks", "write"]
3003+ <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
3004+ where taggedAt f ks = taggedAt' f (Object o) ks
3005+ taggedAt' f v [] = f =<< (parseJSON v <|> (parseJSON (unStringlyTypeJSON v)))
3006+ taggedAt' f v (k:ks) = withObject "Object" (\o -> do v' <- o .: k
3007+ taggedAt' f v' ks) v
3008+ numberOfReplicas = pure . NumberOfReplicas
3009+ autoExpandReplicas = pure . AutoExpandReplicas
3010+ refreshInterval = pure . RefreshInterval . ndtJSON
3011+ indexConcurrency = pure . IndexConcurrency
3012+ failOnMergeFailure = pure . FailOnMergeFailure
3013+ translogFlushThresholdOps = pure . TranslogFlushThresholdOps
3014+ translogFlushThresholdSize = pure . TranslogFlushThresholdSize
3015+ translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
3016+ translogDisableFlush = pure . TranslogDisableFlush
3017+ cacheFilterMaxSize = pure . CacheFilterMaxSize
3018+ cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
3019+ gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
3020+ routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
3021+ routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
3022+ routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
3023+ routingAllocationEnable = pure . RoutingAllocationEnable
3024+ routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
3025+ recoveryInitialShards = pure . RecoveryInitialShards
3026+ gcDeletes = pure . GCDeletes . ndtJSON
3027+ ttlDisablePurge = pure . TTLDisablePurge
3028+ translogFSType = pure . TranslogFSType
3029+ compoundFormat = pure . IndexCompoundFormat
3030+ compoundOnFlush = pure . IndexCompoundOnFlush
3031+ warmerEnabled = pure . WarmerEnabled
3032+ blocksReadOnly = pure . BlocksReadOnly
3033+ blocksRead = pure . BlocksRead
3034+ blocksWrite = pure . BlocksWrite
3035+ blocksMetaData = pure . BlocksMetaData
3036+
3037+instance FromJSON IndexSettingsSummary where
3038+ parseJSON = withObject "IndexSettingsSummary" parse
3039+ where parse o = case HM.toList o of
3040+ [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
3041+ <$> parseJSON v
3042+ <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
3043+ _ -> fail "Expected single-key object with index name"
3044+ redundant (NumberOfReplicas _) = True
3045+ redundant _ = False
3046+
3047+-- | For some reason in several settings APIs, all leaf values get returned
3048+-- as strings. This function attepmts to recover from this for all
3049+-- non-recursive JSON types. If nothing can be done, the value is left alone.
3050+unStringlyTypeJSON :: Value -> Value
3051+unStringlyTypeJSON (String "true") = Bool True
3052+unStringlyTypeJSON (String "false") = Bool False
3053+unStringlyTypeJSON (String "null") = Null
3054+unStringlyTypeJSON v@(String t) = case readMay (T.unpack t) of
3055+ Just n -> Number n
3056+ Nothing -> v
3057+unStringlyTypeJSON v = v
3058+
3059+
3060+parseSettings :: Object -> Parser [UpdatableIndexSetting]
3061+parseSettings o = do
3062+ o' <- o .: "index"
3063+ -- slice the index object into singleton hashmaps and try to parse each
3064+ parses <- forM (HM.toList o') $ \(k, v) -> do
3065+ -- blocks are now nested into the "index" key, which is not how they're serialized
3066+ let atRoot = Object (HM.singleton k v)
3067+ let atIndex = Object (HM.singleton "index" atRoot)
3068+ optional (parseJSON atRoot <|> parseJSON atIndex)
3069+ return (catMaybes parses)
3070+
3071+oPath :: ToJSON a => NonEmpty Text -> a -> Value
3072+oPath (k :| []) v = object [k .= v]
3073+oPath (k:| (h:t)) v = object [k .= oPath (h :| t) v]
3074+
3075+attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
3076+attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
3077+ | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
3078+
3079+parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
3080+parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
3081+ where parse o = case HM.toList o of
3082+ [] -> fail "Expected non-empty list of NodeAttrFilters"
3083+ x:xs -> DT.mapM (uncurry parse') (x :| xs)
3084+ parse' n = withText "Text" $ \t ->
3085+ case T.splitOn "," t of
3086+ fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
3087+ [] -> fail "Expected non-empty list of filter values"
3088+
3089+instance ToJSON ReplicaBounds where
3090+ toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
3091+ toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
3092+ toJSON ReplicasUnbounded = Bool False
3093+
3094+instance FromJSON ReplicaBounds where
3095+ parseJSON v = withText "ReplicaBounds" parseText v
3096+ <|> withBool "ReplicaBounds" parseBool v
3097+ where parseText t = case T.splitOn "-" t of
3098+ [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
3099+ [a, b] -> ReplicasBounded <$> parseReadText a
3100+ <*> parseReadText b
3101+ _ -> fail ("Could not parse ReplicaBounds: " <> show t)
3102+ parseBool False = pure ReplicasUnbounded
3103+ parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
3104+
3105+instance ToJSON AllocationPolicy where
3106+ toJSON AllocAll = String "all"
3107+ toJSON AllocPrimaries = String "primaries"
3108+ toJSON AllocNewPrimaries = String "new_primaries"
3109+ toJSON AllocNone = String "none"
3110+
3111+instance FromJSON AllocationPolicy where
3112+ parseJSON = withText "AllocationPolicy" parse
3113+ where parse "all" = pure AllocAll
3114+ parse "primaries" = pure AllocPrimaries
3115+ parse "new_primaries" = pure AllocNewPrimaries
3116+ parse "none" = pure AllocNone
3117+ parse t = fail ("Invlaid AllocationPolicy: " <> show t)
3118+
3119+instance ToJSON InitialShardCount where
3120+ toJSON QuorumShards = String "quorum"
3121+ toJSON QuorumMinus1Shards = String "quorum-1"
3122+ toJSON FullShards = String "full"
3123+ toJSON FullMinus1Shards = String "full-1"
3124+ toJSON (ExplicitShards x) = toJSON x
3125+
3126+instance FromJSON InitialShardCount where
3127+ parseJSON v = withText "InitialShardCount" parseText v
3128+ <|> ExplicitShards <$> parseJSON v
3129+ where parseText "quorum" = pure QuorumShards
3130+ parseText "quorum-1" = pure QuorumMinus1Shards
3131+ parseText "full" = pure FullShards
3132+ parseText "full-1" = pure FullMinus1Shards
3133+ parseText _ = mzero
3134+
3135+instance ToJSON FSType where
3136+ toJSON FSSimple = "simple"
3137+ toJSON FSBuffered = "buffered"
3138+
3139+instance FromJSON FSType where
3140+ parseJSON = withText "FSType" parse
3141+ where parse "simple" = pure FSSimple
3142+ parse "buffered" = pure FSBuffered
3143+ parse t = fail ("Invalid FSType: " <> show t)
3144+
3145+instance ToJSON CompoundFormat where
3146+ toJSON (CompoundFileFormat x) = Bool x
3147+ toJSON (MergeSegmentVsTotalIndex x) = toJSON x
3148+
3149+instance FromJSON CompoundFormat where
3150+ parseJSON v = CompoundFileFormat <$> parseJSON v
3151+ <|> MergeSegmentVsTotalIndex <$> parseJSON v
3152+
3153+instance ToJSON NominalDiffTimeJSON where
3154+ toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
3155+
3156+instance FromJSON NominalDiffTimeJSON where
3157+ parseJSON = withText "NominalDiffTime" parse
3158+ where parse t = case T.takeEnd 1 t of
3159+ "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
3160+ _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
3161+
3162+instance ToJSON IndexTemplate where
3163+ toJSON (IndexTemplate p s m) = merge
3164+ (object [ "template" .= p
3165+ , "mappings" .= foldl' merge (object []) m
3166+ ])
3167+ (toJSON s)
3168+ where
3169+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
3170+ merge o Null = o
3171+ merge _ _ = undefined
3172+
3173+instance (FromJSON a) => FromJSON (EsResult a) where
3174+ parseJSON jsonVal@(Object v) = do
3175+ found <- v .:? "found" .!= False
3176+ fr <- if found
3177+ then parseJSON jsonVal
3178+ else return Nothing
3179+ EsResult <$> v .: "_index" <*>
3180+ v .: "_type" <*>
3181+ v .: "_id" <*>
3182+ pure fr
3183+ parseJSON _ = empty
3184+
3185+instance (FromJSON a) => FromJSON (EsResultFound a) where
3186+ parseJSON (Object v) = EsResultFound <$>
3187+ v .: "_version" <*>
3188+ v .: "_source"
3189+ parseJSON _ = empty
3190+
3191+instance FromJSON EsError where
3192+ parseJSON (Object v) = EsError <$>
3193+ v .: "status" <*>
3194+ v .: "error"
3195+ parseJSON _ = empty
3196+
3197+instance FromJSON IndexAliasesSummary where
3198+ parseJSON = withObject "IndexAliasesSummary" parse
3199+ where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
3200+ go ixn = withObject "index aliases" $ \ia -> do
3201+ aliases <- ia .:? "aliases" .!= mempty
3202+ forM (HM.toList aliases) $ \(aName, v) -> do
3203+ let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
3204+ IndexAliasSummary indexAlias <$> parseJSON v
3205+
3206+
3207+instance ToJSON IndexAliasAction where
3208+ toJSON (AddAlias ia opts) = object ["add" .= (iaObj <> optsObj)]
3209+ where Object iaObj = toJSON ia
3210+ Object optsObj = toJSON opts
3211+ toJSON (RemoveAlias ia) = object ["remove" .= iaObj]
3212+ where Object iaObj = toJSON ia
3213+
3214+instance ToJSON IndexAlias where
3215+ toJSON IndexAlias {..} = object ["index" .= srcIndex
3216+ , "alias" .= indexAlias
3217+ ]
3218+
3219+instance ToJSON IndexAliasCreate where
3220+ toJSON IndexAliasCreate {..} = Object (filterObj <> routingObj)
3221+ where filterObj = maybe mempty (HM.singleton "filter" . toJSON) aliasCreateFilter
3222+ Object routingObj = maybe (Object mempty) toJSON aliasCreateRouting
3223+
3224+instance ToJSON AliasRouting where
3225+ toJSON (AllAliasRouting v) = object ["routing" .= v]
3226+ toJSON (GranularAliasRouting srch idx) = object (catMaybes prs)
3227+ where prs = [("search_routing" .=) <$> srch
3228+ ,("index_routing" .=) <$> idx]
3229+
3230+instance FromJSON AliasRouting where
3231+ parseJSON = withObject "AliasRouting" parse
3232+ where parse o = parseAll o <|> parseGranular o
3233+ parseAll o = AllAliasRouting <$> o .: "routing"
3234+ parseGranular o = do
3235+ sr <- o .:? "search_routing"
3236+ ir <- o .:? "index_routing"
3237+ if isNothing sr && isNothing ir
3238+ then fail "Both search_routing and index_routing can't be blank"
3239+ else return (GranularAliasRouting sr ir)
3240+
3241+instance FromJSON IndexAliasCreate where
3242+ parseJSON v = withObject "IndexAliasCreate" parse v
3243+ where parse o = IndexAliasCreate <$> optional (parseJSON v)
3244+ <*> o .:? "filter"
3245+
3246+instance ToJSON SearchAliasRouting where
3247+ toJSON (SearchAliasRouting rvs) = toJSON (T.intercalate "," (routingValue <$> toList rvs))
3248+
3249+instance FromJSON SearchAliasRouting where
3250+ parseJSON = withText "SearchAliasRouting" parse
3251+ where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
3252+
3253+instance ToJSON Search where
3254+ toJSON (Search query sFilter sort searchAggs highlight sTrackSortScores sFrom sSize _ sFields sSource) =
3255+ omitNulls [ "query" .= query
3256+ , "filter" .= sFilter
3257+ , "sort" .= sort
3258+ , "aggregations" .= searchAggs
3259+ , "highlight" .= highlight
3260+ , "from" .= sFrom
3261+ , "size" .= sSize
3262+ , "track_scores" .= sTrackSortScores
3263+ , "fields" .= sFields
3264+ , "_source" .= sSource]
3265+
3266+
3267+instance ToJSON Source where
3268+ toJSON NoSource = toJSON False
3269+ toJSON (SourcePatterns patterns) = toJSON patterns
3270+ toJSON (SourceIncludeExclude incl excl) = object [ "include" .= incl, "exclude" .= excl ]
3271+
3272+instance ToJSON PatternOrPatterns where
3273+ toJSON (PopPattern pattern) = toJSON pattern
3274+ toJSON (PopPatterns patterns) = toJSON patterns
3275+
3276+instance ToJSON Include where
3277+ toJSON (Include patterns) = toJSON patterns
3278+
3279+instance ToJSON Exclude where
3280+ toJSON (Exclude patterns) = toJSON patterns
3281+
3282+instance ToJSON Pattern where
3283+ toJSON (Pattern pattern) = toJSON pattern
3284+
3285+
3286+instance ToJSON FieldHighlight where
3287+ toJSON (FieldHighlight (FieldName fName) (Just fSettings)) =
3288+ object [ fName .= fSettings ]
3289+ toJSON (FieldHighlight (FieldName fName) Nothing) =
3290+ object [ fName .= emptyObject ]
3291+
3292+instance ToJSON Highlights where
3293+ toJSON (Highlights global fields) =
3294+ omitNulls (("fields" .= fields)
3295+ : highlightSettingsPairs global)
3296+
3297+instance ToJSON HighlightSettings where
3298+ toJSON hs = omitNulls (highlightSettingsPairs (Just hs))
3299+
3300+highlightSettingsPairs :: Maybe HighlightSettings -> [Pair]
3301+highlightSettingsPairs Nothing = []
3302+highlightSettingsPairs (Just (Plain plh)) = plainHighPairs (Just plh)
3303+highlightSettingsPairs (Just (Postings ph)) = postHighPairs (Just ph)
3304+highlightSettingsPairs (Just (FastVector fvh)) = fastVectorHighPairs (Just fvh)
3305+
3306+
3307+plainHighPairs :: Maybe PlainHighlight -> [Pair]
3308+plainHighPairs Nothing = []
3309+plainHighPairs (Just (PlainHighlight plCom plNonPost)) =
3310+ [ "type" .= String "plain"]
3311+ ++ commonHighlightPairs plCom
3312+ ++ nonPostingsToPairs plNonPost
3313+
3314+postHighPairs :: Maybe PostingsHighlight -> [Pair]
3315+postHighPairs Nothing = []
3316+postHighPairs (Just (PostingsHighlight pCom)) =
3317+ ("type" .= String "postings")
3318+ : commonHighlightPairs pCom
3319+
3320+fastVectorHighPairs :: Maybe FastVectorHighlight -> [Pair]
3321+fastVectorHighPairs Nothing = []
3322+fastVectorHighPairs (Just
3323+ (FastVectorHighlight fvCom fvNonPostSettings fvBoundChars
3324+ fvBoundMaxScan fvFragOff fvMatchedFields
3325+ fvPhraseLim)) =
3326+ [ "type" .= String "fvh"
3327+ , "boundary_chars" .= fvBoundChars
3328+ , "boundary_max_scan" .= fvBoundMaxScan
3329+ , "fragment_offset" .= fvFragOff
3330+ , "matched_fields" .= fvMatchedFields
3331+ , "phraseLimit" .= fvPhraseLim]
3332+ ++ commonHighlightPairs fvCom
3333+ ++ nonPostingsToPairs fvNonPostSettings
3334+
3335+deleteSeveral :: (Eq k, Hashable k) => [k] -> HM.HashMap k v -> HM.HashMap k v
3336+deleteSeveral ks hm = foldr HM.delete hm ks
3337+
3338+commonHighlightPairs :: Maybe CommonHighlight -> [Pair]
3339+commonHighlightPairs Nothing = []
3340+commonHighlightPairs (Just (CommonHighlight chScore chForceSource chTag chEncoder
3341+ chNoMatchSize chHighlightQuery
3342+ chRequireFieldMatch)) =
3343+ [ "order" .= chScore
3344+ , "force_source" .= chForceSource
3345+ , "encoder" .= chEncoder
3346+ , "no_match_size" .= chNoMatchSize
3347+ , "highlight_query" .= chHighlightQuery
3348+ , "require_fieldMatch" .= chRequireFieldMatch]
3349+ ++ highlightTagToPairs chTag
3350+
3351+
3352+nonPostingsToPairs :: Maybe NonPostings -> [Pair]
3353+nonPostingsToPairs Nothing = []
3354+nonPostingsToPairs (Just (NonPostings npFragSize npNumOfFrags)) =
3355+ [ "fragment_size" .= npFragSize
3356+ , "number_of_fragments" .= npNumOfFrags]
3357+
3358+parseNEJSON :: (FromJSON a) => [Value] -> Parser (NonEmpty a)
3359+parseNEJSON [] = fail "Expected non-empty list"
3360+parseNEJSON (x:xs) = DT.mapM parseJSON (x :| xs)
3361+
3362+
3363+instance ToJSON HighlightEncoder where
3364+ toJSON DefaultEncoder = String "default"
3365+ toJSON HTMLEncoder = String "html"
3366+
3367+highlightTagToPairs :: Maybe HighlightTag -> [Pair]
3368+highlightTagToPairs (Just (TagSchema _)) = [ "scheme" .= String "default"]
3369+highlightTagToPairs (Just (CustomTags (pre, post))) = [ "pre_tags" .= pre
3370+ , "post_tags" .= post]
3371+highlightTagToPairs Nothing = []
3372+
3373+instance ToJSON SortSpec where
3374+ toJSON (DefaultSortSpec
3375+ (DefaultSort (FieldName dsSortFieldName) dsSortOrder dsIgnoreUnmapped
3376+ dsSortMode dsMissingSort dsNestedFilter)) =
3377+ object [dsSortFieldName .= omitNulls base] where
3378+ base = [ "order" .= dsSortOrder
3379+ , "ignore_unmapped" .= dsIgnoreUnmapped
3380+ , "mode" .= dsSortMode
3381+ , "missing" .= dsMissingSort
3382+ , "nested_filter" .= dsNestedFilter ]
3383+
3384+ toJSON (GeoDistanceSortSpec gdsSortOrder (GeoPoint (FieldName field) gdsLatLon) units) =
3385+ object [ "unit" .= units
3386+ , field .= gdsLatLon
3387+ , "order" .= gdsSortOrder ]
3388+
3389+
3390+instance ToJSON SortOrder where
3391+ toJSON Ascending = String "asc"
3392+ toJSON Descending = String "desc"
3393+
3394+
3395+instance ToJSON SortMode where
3396+ toJSON SortMin = String "min"
3397+ toJSON SortMax = String "max"
3398+ toJSON SortSum = String "sum"
3399+ toJSON SortAvg = String "avg"
3400+
3401+
3402+instance ToJSON Missing where
3403+ toJSON LastMissing = String "_last"
3404+ toJSON FirstMissing = String "_first"
3405+ toJSON (CustomMissing txt) = String txt
3406+
3407+
3408+instance ToJSON ScoreType where
3409+ toJSON ScoreTypeMax = "max"
3410+ toJSON ScoreTypeAvg = "avg"
3411+ toJSON ScoreTypeSum = "sum"
3412+ toJSON ScoreTypeNone = "none"
3413+
3414+instance FromJSON ScoreType where
3415+ parseJSON = withText "ScoreType" parse
3416+ where parse "max" = pure ScoreTypeMax
3417+ parse "avg" = pure ScoreTypeAvg
3418+ parse "sum" = pure ScoreTypeSum
3419+ parse "none" = pure ScoreTypeNone
3420+ parse t = fail ("Unexpected ScoreType: " <> show t)
3421+
3422+instance ToJSON Distance where
3423+ toJSON (Distance dCoefficient dUnit) =
3424+ String boltedTogether where
3425+ coefText = showText dCoefficient
3426+ (String unitText) = toJSON dUnit
3427+ boltedTogether = mappend coefText unitText
3428+
3429+instance FromJSON Distance where
3430+ parseJSON = withText "Distance" parse
3431+ where parse t = Distance <$> parseCoeff nT
3432+ <*> parseJSON (String unitT)
3433+ where (nT, unitT) = T.span validForNumber t
3434+ -- may be a better way to do this
3435+ validForNumber '-' = True
3436+ validForNumber '.' = True
3437+ validForNumber 'e' = True
3438+ validForNumber c = isNumber c
3439+ parseCoeff "" = fail "Empty string cannot be parsed as number"
3440+ parseCoeff s = return (read (T.unpack s))
3441+
3442+instance ToJSON DistanceUnit where
3443+ toJSON Miles = String "mi"
3444+ toJSON Yards = String "yd"
3445+ toJSON Feet = String "ft"
3446+ toJSON Inches = String "in"
3447+ toJSON Kilometers = String "km"
3448+ toJSON Meters = String "m"
3449+ toJSON Centimeters = String "cm"
3450+ toJSON Millimeters = String "mm"
3451+ toJSON NauticalMiles = String "nmi"
3452+
3453+
3454+instance FromJSON DistanceUnit where
3455+ parseJSON = withText "DistanceUnit" parse
3456+ where parse "mi" = pure Miles
3457+ parse "yd" = pure Yards
3458+ parse "ft" = pure Feet
3459+ parse "in" = pure Inches
3460+ parse "km" = pure Kilometers
3461+ parse "m" = pure Meters
3462+ parse "cm" = pure Centimeters
3463+ parse "mm" = pure Millimeters
3464+ parse "nmi" = pure NauticalMiles
3465+ parse u = fail ("Unrecognized DistanceUnit: " <> show u)
3466+
3467+instance ToJSON DistanceType where
3468+ toJSON Arc = String "arc"
3469+ toJSON SloppyArc = String "sloppy_arc"
3470+ toJSON Plane = String "plane"
3471+
3472+instance FromJSON DistanceType where
3473+ parseJSON = withText "DistanceType" parse
3474+ where parse "arc" = pure Arc
3475+ parse "sloppy_arc" = pure SloppyArc
3476+ parse "plane" = pure Plane
3477+ parse t = fail ("Unrecognized DistanceType: " <> show t)
3478+
3479+
3480+instance ToJSON OptimizeBbox where
3481+ toJSON NoOptimizeBbox = String "none"
3482+ toJSON (OptimizeGeoFilterType gft) = toJSON gft
3483+
3484+instance FromJSON OptimizeBbox where
3485+ parseJSON v = withText "NoOptimizeBbox" parseNoOptimize v
3486+ <|> parseOptimize v
3487+ where parseNoOptimize "none" = pure NoOptimizeBbox
3488+ parseNoOptimize _ = mzero
3489+ parseOptimize = fmap OptimizeGeoFilterType . parseJSON
3490+
3491+instance ToJSON GeoBoundingBoxConstraint where
3492+ toJSON (GeoBoundingBoxConstraint
3493+ (FieldName gbbcGeoBBField) gbbcConstraintBox cache type') =
3494+ object [gbbcGeoBBField .= gbbcConstraintBox
3495+ , "_cache" .= cache
3496+ , "type" .= type']
3497+
3498+instance FromJSON GeoBoundingBoxConstraint where
3499+ parseJSON = withObject "GeoBoundingBoxConstraint" parse
3500+ where parse o = case HM.toList (deleteSeveral ["type", "_cache"] o) of
3501+ [(fn, v)] -> GeoBoundingBoxConstraint (FieldName fn)
3502+ <$> parseJSON v
3503+ <*> o .:? "_cache" .!= defaultCache
3504+ <*> o .: "type"
3505+ _ -> fail "Could not find field name for GeoBoundingBoxConstraint"
3506+
3507+instance ToJSON GeoFilterType where
3508+ toJSON GeoFilterMemory = String "memory"
3509+ toJSON GeoFilterIndexed = String "indexed"
3510+
3511+instance FromJSON GeoFilterType where
3512+ parseJSON = withText "GeoFilterType" parse
3513+ where parse "memory" = pure GeoFilterMemory
3514+ parse "indexed" = pure GeoFilterIndexed
3515+ parse t = fail ("Unrecognized GeoFilterType: " <> show t)
3516+
3517+instance ToJSON GeoBoundingBox where
3518+ toJSON (GeoBoundingBox gbbTopLeft gbbBottomRight) =
3519+ object ["top_left" .= gbbTopLeft
3520+ , "bottom_right" .= gbbBottomRight]
3521+
3522+instance FromJSON GeoBoundingBox where
3523+ parseJSON = withObject "GeoBoundingBox" parse
3524+ where parse o = GeoBoundingBox
3525+ <$> o .: "top_left"
3526+ <*> o .: "bottom_right"
3527+
3528+instance ToJSON LatLon where
3529+ toJSON (LatLon lLat lLon) =
3530+ object ["lat" .= lLat
3531+ , "lon" .= lLon]
3532+
3533+instance FromJSON LatLon where
3534+ parseJSON = withObject "LatLon" parse
3535+ where parse o = LatLon <$> o .: "lat"
3536+ <*> o .: "lon"
3537+
3538+-- index for smaller ranges, fielddata for longer ranges
3539+instance ToJSON RangeExecution where
3540+ toJSON RangeExecutionIndex = "index"
3541+ toJSON RangeExecutionFielddata = "fielddata"
3542+
3543+
3544+instance FromJSON RangeExecution where
3545+ parseJSON = withText "RangeExecution" parse
3546+ where parse "index" = pure RangeExecutionIndex
3547+ parse "fielddata" = pure RangeExecutionFielddata
3548+ parse t = error ("Unrecognized RangeExecution " <> show t)
3549+
3550+instance ToJSON RegexpFlags where
3551+ toJSON AllRegexpFlags = String "ALL"
3552+ toJSON NoRegexpFlags = String "NONE"
3553+ toJSON (SomeRegexpFlags (h :| fs)) = String $ T.intercalate "|" flagStrs
3554+ where flagStrs = map flagStr . nub $ h:fs
3555+ flagStr AnyString = "ANYSTRING"
3556+ flagStr Automaton = "AUTOMATON"
3557+ flagStr Complement = "COMPLEMENT"
3558+ flagStr Empty = "EMPTY"
3559+ flagStr Intersection = "INTERSECTION"
3560+ flagStr Interval = "INTERVAL"
3561+
3562+instance FromJSON RegexpFlags where
3563+ parseJSON = withText "RegexpFlags" parse
3564+ where parse "ALL" = pure AllRegexpFlags
3565+ parse "NONE" = pure NoRegexpFlags
3566+ parse t = SomeRegexpFlags <$> parseNEJSON (String <$> T.splitOn "|" t)
3567+
3568+instance FromJSON RegexpFlag where
3569+ parseJSON = withText "RegexpFlag" parse
3570+ where parse "ANYSTRING" = pure AnyString
3571+ parse "AUTOMATON" = pure Automaton
3572+ parse "COMPLEMENT" = pure Complement
3573+ parse "EMPTY" = pure Empty
3574+ parse "INTERSECTION" = pure Intersection
3575+ parse "INTERVAL" = pure Interval
3576+ parse f = fail ("Unknown RegexpFlag: " <> show f)
3577+
3578+instance ToJSON Term where
3579+ toJSON (Term field value) = object ["term" .= object
3580+ [field .= value]]
3581+
3582+instance FromJSON Term where
3583+ parseJSON = withObject "Term" parse
3584+ where parse o = do termObj <- o .: "term"
3585+ case HM.toList termObj of
3586+ [(fn, v)] -> Term fn <$> parseJSON v
3587+ _ -> fail "Expected object with 1 field-named key"
3588+
3589+instance ToJSON BoolMatch where
3590+ toJSON (MustMatch term cache) = object ["must" .= term,
3591+ "_cache" .= cache]
3592+ toJSON (MustNotMatch term cache) = object ["must_not" .= term,
3593+ "_cache" .= cache]
3594+ toJSON (ShouldMatch terms cache) = object ["should" .= fmap toJSON terms,
3595+ "_cache" .= cache]
3596+
3597+instance FromJSON BoolMatch where
3598+ parseJSON = withObject "BoolMatch" parse
3599+ where parse o = mustMatch `taggedWith` "must"
3600+ <|> mustNotMatch `taggedWith` "must_not"
3601+ <|> shouldMatch `taggedWith` "should"
3602+ where taggedWith parser k = parser =<< o .: k
3603+ mustMatch t = MustMatch t <$> o .:? "_cache" .!= defaultCache
3604+ mustNotMatch t = MustNotMatch t <$> o .:? "_cache" .!= defaultCache
3605+ shouldMatch t = ShouldMatch t <$> o .:? "_cache" .!= defaultCache
3606+
3607+instance (FromJSON a) => FromJSON (SearchResult a) where
3608+ parseJSON (Object v) = SearchResult <$>
3609+ v .: "took" <*>
3610+ v .: "timed_out" <*>
3611+ v .: "_shards" <*>
3612+ v .: "hits" <*>
3613+ v .:? "aggregations" <*>
3614+ v .:? "_scroll_id"
3615+ parseJSON _ = empty
3616+
3617+instance (FromJSON a) => FromJSON (SearchHits a) where
3618+ parseJSON (Object v) = SearchHits <$>
3619+ v .: "total" <*>
3620+ v .: "max_score" <*>
3621+ v .: "hits"
3622+ parseJSON _ = empty
3623+
3624+instance (FromJSON a) => FromJSON (Hit a) where
3625+ parseJSON (Object v) = Hit <$>
3626+ v .: "_index" <*>
3627+ v .: "_type" <*>
3628+ v .: "_id" <*>
3629+ v .: "_score" <*>
3630+ v .:? "_source" <*>
3631+ v .:? "highlight"
3632+ parseJSON _ = empty
3633+
3634+instance FromJSON ShardResult where
3635+ parseJSON (Object v) = ShardResult <$>
3636+ v .: "total" <*>
3637+ v .: "successful" <*>
3638+ v .: "failed"
3639+ parseJSON _ = empty
3640+
3641+
3642+instance FromJSON DocVersion where
3643+ parseJSON v = do
3644+ i <- parseJSON v
3645+ maybe (fail "DocVersion out of range") return $ mkDocVersion i
3646+
3647+-- This insanity is because ES *sometimes* returns Replica/Shard counts as strings
3648+instance FromJSON ReplicaCount where
3649+ parseJSON v = parseAsInt v
3650+ <|> parseAsString v
3651+ where parseAsInt = fmap ReplicaCount . parseJSON
3652+ parseAsString = withText "ReplicaCount" (fmap ReplicaCount . parseReadText)
3653+
3654+instance FromJSON ShardCount where
3655+ parseJSON v = parseAsInt v
3656+ <|> parseAsString v
3657+ where parseAsInt = fmap ShardCount . parseJSON
3658+ parseAsString = withText "ShardCount" (fmap ShardCount . parseReadText)
3659+
3660+instance Bounded DocVersion where
3661+ minBound = DocVersion 1
3662+ maxBound = DocVersion 9200000000000000000 -- 9.2e+18
3663+
3664+instance Enum DocVersion where
3665+ succ x
3666+ | x /= maxBound = DocVersion (succ $ docVersionNumber x)
3667+ | otherwise = succError "DocVersion"
3668+ pred x
3669+ | x /= minBound = DocVersion (pred $ docVersionNumber x)
3670+ | otherwise = predError "DocVersion"
3671+ toEnum i =
3672+ fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
3673+ fromEnum = docVersionNumber
3674+ enumFrom = boundedEnumFrom
3675+ enumFromThen = boundedEnumFromThen
3676+
3677+-- | Username type used for HTTP Basic authentication. See 'basicAuthHook'.
3678+newtype EsUsername = EsUsername { esUsername :: Text } deriving (Read, Show, Eq)
3679+
3680+-- | Password type used for HTTP Basic authentication. See 'basicAuthHook'.
3681+newtype EsPassword = EsPassword { esPassword :: Text } deriving (Read, Show, Eq)
3682+
3683+
3684+data SnapshotRepoSelection = SnapshotRepoList (NonEmpty SnapshotRepoPattern)
3685+ | AllSnapshotRepos deriving (Eq, Generic, Show, Typeable)
3686+
3687+
3688+-- | Either specifies an exact repo name or one with globs in it,
3689+-- e.g. @RepoPattern "foo*"@ __NOTE__:@ Patterns are not supported on ES < 1.7
3690+data SnapshotRepoPattern = ExactRepo SnapshotRepoName
3691+ | RepoPattern Text
3692+ deriving (Eq, Generic, Show, Typeable)
3693+
3694+-- | The unique name of a snapshot repository.
3695+newtype SnapshotRepoName = SnapshotRepoName { snapshotRepoName :: Text }
3696+ deriving (Eq, Ord, Generic, Show, Typeable, ToJSON, FromJSON)
3697+
3698+
3699+-- | A generic representation of a snapshot repo. This is what gets
3700+-- sent to and parsed from the server. For repo types enabled by
3701+-- plugins that aren't exported by this library, consider making a
3702+-- custom type which implements 'SnapshotRepo'. If it is a common repo
3703+-- type, consider submitting a pull request to have it included in the
3704+-- library proper
3705+data GenericSnapshotRepo = GenericSnapshotRepo {
3706+ gSnapshotRepoName :: SnapshotRepoName
3707+ , gSnapshotRepoType :: SnapshotRepoType
3708+ , gSnapshotRepoSettings :: GenericSnapshotRepoSettings
3709+ } deriving (Eq, Generic, Show, Typeable)
3710+
3711+
3712+instance SnapshotRepo GenericSnapshotRepo where
3713+ toGSnapshotRepo = id
3714+ fromGSnapshotRepo = Right
3715+
3716+
3717+newtype SnapshotRepoType = SnapshotRepoType { snapshotRepoType :: Text }
3718+ deriving (Eq, Ord, Generic, Show, Typeable, ToJSON, FromJSON)
3719+
3720+
3721+-- | Opaque representation of snapshot repo settings. Instances of
3722+-- 'SnapshotRepo' will produce this.
3723+newtype GenericSnapshotRepoSettings = GenericSnapshotRepoSettings { gSnapshotRepoSettingsObject :: Object }
3724+ deriving (Eq, Generic, Show, Typeable, ToJSON)
3725+
3726+
3727+ -- Regardless of whether you send strongly typed json, my version of
3728+ -- ES sends back stringly typed json in the settings, e.g. booleans
3729+ -- as strings, so we'll try to convert them.
3730+instance FromJSON GenericSnapshotRepoSettings where
3731+ parseJSON = fmap (GenericSnapshotRepoSettings . fmap unStringlyTypeJSON). parseJSON
3732+
3733+-- | The result of running 'verifySnapshotRepo'.
3734+newtype SnapshotVerification = SnapshotVerification { snapshotNodeVerifications :: [SnapshotNodeVerification] }
3735+ deriving (Eq, Generic, Show, Typeable)
3736+
3737+
3738+instance FromJSON SnapshotVerification where
3739+ parseJSON = withObject "SnapshotVerification" parse
3740+ where
3741+ parse o = do
3742+ o2 <- o .: "nodes"
3743+ SnapshotVerification <$> mapM (uncurry parse') (HM.toList o2)
3744+ parse' rawFullId = withObject "SnapshotNodeVerification" $ \o ->
3745+ SnapshotNodeVerification (FullNodeId rawFullId) <$> o .: "name"
3746+
3747+
3748+data SnapshotNodeVerification = SnapshotNodeVerification {
3749+ snvFullId :: FullNodeId
3750+ , snvNodeName :: NodeName
3751+ } deriving (Eq, Generic, Show, Typeable)
3752+
3753+
3754+-- | Unique, automatically-generated name assigned to nodes that are
3755+-- usually returned in node-oriented APIs.
3756+newtype FullNodeId = FullNodeId { fullNodeId :: Text }
3757+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3758+
3759+
3760+newtype NodeName = NodeName { nodeName :: Text }
3761+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3762+
3763+
3764+data SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings {
3765+ repoUpdateVerify :: Bool
3766+ -- ^ After creation/update, synchronously check that nodes can
3767+ -- write to this repo. Defaults to True. You may use False if you
3768+ -- need a faster response and plan on verifying manually later
3769+ -- with 'verifySnapshotRepo'.
3770+ } deriving (Eq, Show, Generic, Typeable)
3771+
3772+
3773+-- | Reasonable defaults for repo creation/update
3774+--
3775+-- * repoUpdateVerify True
3776+defaultSnapshotRepoUpdateSettings :: SnapshotRepoUpdateSettings
3777+defaultSnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings True
3778+
3779+
3780+-- | A filesystem-based snapshot repo that ships with elasticsearch.
3781+data FsSnapshotRepo = FsSnapshotRepo {
3782+ fsrName :: SnapshotRepoName
3783+ , fsrLocation :: FilePath
3784+ , fsrCompressMetadata :: Bool
3785+ , fsrChunkSize :: Maybe Bytes
3786+ , fsrMaxRestoreBytesPerSec :: Maybe Bytes
3787+ , fsrMaxSnapshotBytesPerSec :: Maybe Bytes
3788+ } deriving (Eq, Generic, Show, Typeable)
3789+
3790+
3791+instance SnapshotRepo FsSnapshotRepo where
3792+ toGSnapshotRepo FsSnapshotRepo {..} =
3793+ GenericSnapshotRepo fsrName fsRepoType (GenericSnapshotRepoSettings settings)
3794+ where
3795+ Object settings = object $ [ "location" .= fsrLocation
3796+ , "compress" .= fsrCompressMetadata
3797+ ] ++ optionalPairs
3798+ optionalPairs = catMaybes [ ("chunk_size" .=) <$> fsrChunkSize
3799+ , ("max_restore_bytes_per_sec" .=) <$> fsrMaxRestoreBytesPerSec
3800+ , ("max_snapshot_bytes_per_sec" .=) <$> fsrMaxSnapshotBytesPerSec
3801+ ]
3802+ fromGSnapshotRepo GenericSnapshotRepo {..}
3803+ | gSnapshotRepoType == fsRepoType = do
3804+ let o = gSnapshotRepoSettingsObject gSnapshotRepoSettings
3805+ parseRepo $ do
3806+ FsSnapshotRepo gSnapshotRepoName <$> o .: "location"
3807+ <*> o .:? "compress" .!= False
3808+ <*> o .:? "chunk_size"
3809+ <*> o .:? "max_restore_bytes_per_sec"
3810+ <*> o .:? "max_snapshot_bytes_per_sec"
3811+ | otherwise = Left (RepoTypeMismatch fsRepoType gSnapshotRepoType)
3812+
3813+
3814+parseRepo :: Parser a -> Either SnapshotRepoConversionError a
3815+parseRepo parser = case parseEither (const parser) () of
3816+ Left e -> Left (OtherRepoConversionError (T.pack e))
3817+ Right a -> Right a
3818+
3819+
3820+fsRepoType :: SnapshotRepoType
3821+fsRepoType = SnapshotRepoType "fs"
3822+
3823+-- | Law: fromGSnapshotRepo (toGSnapshotRepo r) == Right r
3824+class SnapshotRepo r where
3825+ toGSnapshotRepo :: r -> GenericSnapshotRepo
3826+ fromGSnapshotRepo :: GenericSnapshotRepo -> Either SnapshotRepoConversionError r
3827+
3828+
3829+data SnapshotRepoConversionError = RepoTypeMismatch SnapshotRepoType
3830+ -- ^ Expected type
3831+ SnapshotRepoType
3832+ -- ^ Actual type
3833+ | OtherRepoConversionError Text
3834+ deriving (Show, Eq, Generic, Typeable)
3835+
3836+
3837+instance Exception SnapshotRepoConversionError
3838+
3839+
3840+data SnapshotCreateSettings = SnapshotCreateSettings {
3841+ snapWaitForCompletion :: Bool
3842+ -- ^ Should the API call return immediately after initializing
3843+ -- the snapshot or wait until completed? Note that if this is
3844+ -- enabled it could wait a long time, so you should adjust your
3845+ -- 'ManagerSettings' accordingly to set long timeouts or
3846+ -- explicitly handle timeouts.
3847+ , snapIndices :: Maybe (NonEmpty IndexName)
3848+ -- ^ Nothing will snapshot all indices. Just [] is permissable and
3849+ -- will essentially be a no-op snapshot.
3850+ , snapIgnoreUnavailable :: Bool
3851+ -- ^ If set to True, any matched indices that don't exist will be
3852+ -- ignored. Otherwise it will be an error and fail.
3853+ , snapIncludeGlobalState :: Bool
3854+ , snapPartial :: Bool
3855+ -- ^ If some indices failed to snapshot (e.g. if not all primary
3856+ -- shards are available), should the process proceed?
3857+ } deriving (Eq, Generic, Show, Typeable)
3858+
3859+
3860+-- | Reasonable defaults for snapshot creation
3861+--
3862+-- * snapWaitForCompletion False
3863+-- * snapIndices Nothing
3864+-- * snapIgnoreUnavailable False
3865+-- * snapIncludeGlobalState True
3866+-- * snapPartial False
3867+defaultSnapshotCreateSettings :: SnapshotCreateSettings
3868+defaultSnapshotCreateSettings = SnapshotCreateSettings {
3869+ snapWaitForCompletion = False
3870+ , snapIndices = Nothing
3871+ , snapIgnoreUnavailable = False
3872+ , snapIncludeGlobalState = True
3873+ , snapPartial = False
3874+ }
3875+
3876+
3877+data SnapshotSelection = SnapshotList (NonEmpty SnapshotPattern)
3878+ | AllSnapshots deriving (Eq, Generic, Show, Typeable)
3879+
3880+
3881+-- | Either specifies an exact snapshot name or one with globs in it,
3882+-- e.g. @SnapPattern "foo*"@ __NOTE__: Patterns are not supported on
3883+-- ES < 1.7
3884+data SnapshotPattern = ExactSnap SnapshotName
3885+ | SnapPattern Text
3886+ deriving (Eq, Generic, Show, Typeable)
3887+
3888+
3889+-- | General information about the state of a snapshot. Has some
3890+-- redundancies with 'SnapshotStatus'
3891+data SnapshotInfo = SnapshotInfo {
3892+ snapInfoShards :: ShardResult
3893+ --TODO: what does failures produce? list of what?
3894+ , snapInfoDuration :: NominalDiffTime
3895+ , snapInfoEndTime :: UTCTime
3896+ , snapInfoStartTime :: UTCTime
3897+ , snapInfoState :: SnapshotState
3898+ , snapInfoIndices :: [IndexName]
3899+ , snapInfoName :: SnapshotName
3900+ } deriving (Eq, Generic, Show, Typeable)
3901+
3902+
3903+instance FromJSON SnapshotInfo where
3904+ parseJSON = withObject "SnapshotInfo" parse
3905+ where
3906+ parse o = SnapshotInfo <$> o .: "shards"
3907+ <*> (unMS <$> o .: "duration_in_millis")
3908+ <*> (posixMS <$> o .: "end_time_in_millis")
3909+ <*> (posixMS <$> o .: "start_time_in_millis")
3910+ <*> o .: "state"
3911+ <*> o .: "indices"
3912+ <*> o .: "snapshot"
3913+
3914+-- | Milliseconds
3915+newtype MS = MS NominalDiffTime
3916+
3917+
3918+-- keeps the unexported constructor warnings at bay
3919+unMS :: MS -> NominalDiffTime
3920+unMS (MS t) = t
3921+
3922+
3923+instance FromJSON MS where
3924+ parseJSON = withScientific "MS" (return . MS . parse)
3925+ where
3926+ parse n = fromInteger ((truncate n) * 1000)
3927+
3928+
3929+data SnapshotState = SnapshotInit
3930+ | SnapshotStarted
3931+ | SnapshotSuccess
3932+ | SnapshotFailed
3933+ | SnapshotAborted
3934+ | SnapshotMissing
3935+ | SnapshotWaiting
3936+ deriving (Show, Eq, Generic, Typeable)
3937+
3938+instance FromJSON SnapshotState where
3939+ parseJSON = withText "SnapshotState" parse
3940+ where
3941+ parse "INIT" = return SnapshotInit
3942+ parse "STARTED" = return SnapshotStarted
3943+ parse "SUCCESS" = return SnapshotSuccess
3944+ parse "FAILED" = return SnapshotFailed
3945+ parse "ABORTED" = return SnapshotAborted
3946+ parse "MISSING" = return SnapshotMissing
3947+ parse "WAITING" = return SnapshotWaiting
3948+ parse t = fail ("Invalid snapshot state " <> T.unpack t)
3949+
3950+
3951+newtype SnapshotName = SnapshotName { snapshotName :: Text }
3952+ deriving (Show, Eq, Ord, Generic, Typeable, ToJSON, FromJSON)
3953+
3954+
3955+data SnapshotRestoreSettings = SnapshotRestoreSettings {
3956+ snapRestoreWaitForCompletion :: Bool
3957+ -- ^ Should the API call return immediately after initializing
3958+ -- the restore or wait until completed? Note that if this is
3959+ -- enabled, it could wait a long time, so you should adjust your
3960+ -- 'ManagerSettings' accordingly to set long timeouts or
3961+ -- explicitly handle timeouts.
3962+ , snapRestoreIndices :: Maybe (NonEmpty IndexName)
3963+ -- ^ Nothing will restore all indices in the snapshot. Just [] is
3964+ -- permissable and will essentially be a no-op restore.
3965+ , snapRestoreIgnoreUnavailable :: Bool
3966+ -- ^ If set to True, any indices that do not exist will be ignored
3967+ -- during snapshot rather than failing the restore.
3968+ , snapRestoreIncludeGlobalState :: Bool
3969+ -- ^ If set to false, will ignore any global state in the snapshot
3970+ -- and will not restore it.
3971+ , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
3972+ -- ^ A regex pattern for matching indices. Used with
3973+ -- 'snapRestoreRenameReplacement', the restore can reference the
3974+ -- matched index and create a new index name upon restore.
3975+ , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
3976+ -- ^ Expression of how index renames should be constructed.
3977+ , snapRestorePartial :: Bool
3978+ -- ^ If some indices fail to restore, should the process proceed?
3979+ , snapRestoreIncludeAliases :: Bool
3980+ -- ^ Should the restore also restore the aliases captured in the
3981+ -- snapshot.
3982+ } deriving (Eq, Generic, Show, Typeable)
3983+--TODO: temporary settings changes
3984+
3985+-- | Regex-stype pattern, e.g. "index_(.+)" to match index names
3986+newtype RestoreRenamePattern = RestoreRenamePattern { rrPattern :: Text }
3987+ deriving (Show, Eq, Generic, Typeable, Ord, ToJSON)
3988+
3989+
3990+-- | A single token in a index renaming scheme for a restore. These
3991+-- are concatenated into a string before being sent to
3992+-- ElasticSearch. Check out these Java
3993+-- <https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html
3994+-- docs> to find out more if you're into that sort of thing.
3995+data RestoreRenameToken = RRTLit Text
3996+ -- ^ Just a literal string of characters
3997+ | RRSubWholeMatch
3998+ -- ^ Equivalent to $0. The entire matched pattern, not any subgroup
3999+ | RRSubGroup RRGroupRefNum
4000+ -- ^ A specific reference to a group number
4001+ deriving (Show, Eq, Generic, Typeable)
4002+
4003+
4004+-- | A group number for regex matching. Only values from 1-9 are
4005+-- supported. Construct with mkRRGroupRefNum
4006+newtype RRGroupRefNum = RRGroupRefNum { rrGroupRefNum :: Int }
4007+ deriving (Show, Eq, Generic, Typeable, Ord)
4008+
4009+instance Bounded RRGroupRefNum where
4010+ minBound = RRGroupRefNum 1
4011+ maxBound = RRGroupRefNum 9
4012+
4013+
4014+
4015+-- | Only allows valid group number references (0-9).
4016+mkRRGroupRefNum :: Int -> Maybe RRGroupRefNum
4017+mkRRGroupRefNum i
4018+ | i >= (rrGroupRefNum minBound) && i <= (rrGroupRefNum maxBound) =
4019+ Just $ RRGroupRefNum i
4020+ | otherwise = Nothing
4021+
4022+
4023+-- | Reasonable defaults for snapshot restores
4024+--
4025+-- * snapRestoreWaitForCompletion False
4026+-- * snapRestoreIndices Nothing
4027+-- * snapRestoreIgnoreUnavailable False
4028+-- * snapRestoreIncludeGlobalState True
4029+-- * snapRestoreRenamePattern Nothing
4030+-- * snapRestoreRenameReplacement Nothing
4031+-- * snapRestorePartial False
4032+-- * snapRestoreIncludeAliases True
4033+defaultSnapshotRestoreSettings :: SnapshotRestoreSettings
4034+defaultSnapshotRestoreSettings = SnapshotRestoreSettings {
4035+ snapRestoreWaitForCompletion = False
4036+ , snapRestoreIndices = Nothing
4037+ , snapRestoreIgnoreUnavailable = False
4038+ , snapRestoreIncludeGlobalState = True
4039+ , snapRestoreRenamePattern = Nothing
4040+ , snapRestoreRenameReplacement = Nothing
4041+ , snapRestorePartial = False
4042+ , snapRestoreIncludeAliases = True
4043+ }