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