@@ -1,8 +1,8 @@
1+{-# LANGUAGE DeriveGeneric #-}
12{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE GeneralizedNewtypeDeriving #-}
45{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE NamedFieldPuns #-}
66{-# LANGUAGE OverloadedStrings #-}
77{-# LANGUAGE RecordWildCards #-}
88{-# LANGUAGE UndecidableInstances #-}
@@ -10,11 +10,11 @@
1010-------------------------------------------------------------------------------
1111-- |
1212-- Module : Database.Bloodhound.Types
13−-- Copyright : (C) 2014, 2018 Chris Allen
13+-- Copyright : (C) 2014 Chris Allen
1414-- License : BSD-style (see the file LICENSE)
1515-- Maintainer : Chris Allen <cma@bitemyapp.com
1616-- Stability : provisional
17−-- Portability : RecordWildCards
17+-- Portability : DeriveGeneric, RecordWildCards
1818--
1919-- Data types for describing actions and data structures performed to interact
2020-- with Elasticsearch. The two main buckets your queries against Elasticsearch
@@ -42,63 +42,26 @@ module Database.Bloodhound.Types
4242 , mkTermsAggregation
4343 , mkTermsScriptAggregation
4444 , mkDateHistogram
45− , mkCardinalityAggregation
4645 , mkDocVersion
47− , mkStatsAggregation
48− , mkExtendedStatsAggregation
4946 , docVersionNumber
50− , toMissing
5147 , toTerms
5248 , toDateHistogram
53− , toTopHits
5449 , omitNulls
5550 , BH(..)
5651 , runBH
57− , BHEnv
58− , bhServer
59− , bhManager
60− , bhRequestHook
61− , mkBHEnv
52+ , BHEnv(..)
6253 , MonadBH(..)
6354 , Version(..)
64− , VersionNumber(..)
65− , MaybeNA(..)
66− , BuildHash(..)
6755 , Status(..)
6856 , Existence(..)
6957 , NullValue(..)
7058 , IndexSettings(..)
71− , UpdatableIndexSetting(..)
72− , IndexSettingsSummary(..)
73− , AllocationPolicy(..)
74− , Compression(..)
75− , ReplicaBounds(..)
76− , Bytes(..)
77− , gigabytes
78− , megabytes
79− , kilobytes
80− , FSType(..)
81− , InitialShardCount(..)
82− , NodeAttrFilter(..)
83− , NodeAttrName(..)
84− , CompoundFormat(..)
8559 , IndexTemplate(..)
8660 , Server(..)
8761 , Reply
8862 , EsResult(..)
8963 , EsResultFound(..)
9064 , EsError(..)
91− , EsProtocolException(..)
92− , IndexAlias(..)
93− , IndexAliasName(..)
94− , IndexAliasAction(..)
95− , IndexAliasCreate(..)
96− , IndexAliasSummary(..)
97− , IndexAliasesSummary(..)
98− , AliasRouting(..)
99− , SearchAliasRouting(..)
100− , IndexAliasRouting(..)
101− , RoutingValue(..)
10265 , DocVersion
10366 , ExternalDocVersion(..)
10467 , VersionControl(..)
@@ -108,7 +71,7 @@ module Database.Bloodhound.Types
10871 , Search(..)
10972 , SearchType(..)
11073 , SearchResult(..)
111− , ScrollId(..)
74+ , ScrollId
11275 , SearchHits(..)
11376 , TrackSortScores
11477 , From(..)
@@ -120,8 +83,8 @@ module Database.Bloodhound.Types
12083 , Pattern(..)
12184 , ShardResult(..)
12285 , Hit(..)
123− , HitFields(..)
12486 , Filter(..)
87+ , Seminearring(..)
12588 , BoolMatch(..)
12689 , Term(..)
12790 , GeoPoint(..)
@@ -148,22 +111,8 @@ module Database.Bloodhound.Types
148111 , RegexpFlags(..)
149112 , RegexpFlag(..)
150113 , FieldName(..)
151− , ScriptFields(..)
152− , ScriptFieldName
153− , ScriptFieldValue
154114 , Script(..)
155− , ScriptLanguage(..)
156− , ScriptInline(..)
157− , ScriptId(..)
158− , ScriptParams(..)
159− , ScriptParamName
160− , ScriptParamValue
161115 , IndexName(..)
162− , IndexSelection(..)
163− , NodeSelection(..)
164− , NodeSelector(..)
165− , ForceMergeIndexSettings(..)
166− , defaultForceMergeIndexSettings
167116 , TemplateName(..)
168117 , TemplatePattern(..)
169118 , MappingName(..)
@@ -187,19 +136,8 @@ module Database.Bloodhound.Types
187136 , BoolQuery(..)
188137 , BoostingQuery(..)
189138 , 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(..)
202139 , DisMaxQuery(..)
140+ , FilteredQuery(..)
203141 , FuzzyLikeThisQuery(..)
204142 , FuzzyLikeFieldQuery(..)
205143 , FuzzyQuery(..)
@@ -215,16 +153,10 @@ module Database.Bloodhound.Types
215153 , RangeQuery(..)
216154 , RegexpQuery(..)
217155 , QueryString(..)
218− , TemplateQueryInline(..)
219− , TemplateQueryKeyValuePairs(..)
220− , WildcardQuery(..)
221156 , BooleanOperator(..)
222157 , ZeroTermsQuery(..)
223158 , CutoffFrequency(..)
224159 , Analyzer(..)
225− , Tokenizer(..)
226− , TokenFilter(..)
227− , CharFilter(..)
228160 , MaxExpansions(..)
229161 , Lenient(..)
230162 , MatchQueryType(..)
@@ -271,118 +203,16 @@ module Database.Bloodhound.Types
271203 , CollectionMode(..)
272204 , TermOrder(..)
273205 , 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 (..)
363206
364207 , Aggregation(..)
365208 , Aggregations
366209 , AggregationResults
367− , BucketValue(..)
368210 , Bucket(..)
369211 , BucketAggregation(..)
370212 , TermsAggregation(..)
371− , MissingAggregation(..)
372213 , ValueCountAggregation(..)
373214 , FilterAggregation(..)
374− , CardinalityAggregation(..)
375− , MaxAggregation(..)
376215 , DateHistogramAggregation(..)
377− , DateRangeAggregation(..)
378− , DateRangeAggRange(..)
379− , DateMathExpr(..)
380− , DateMathAnchor(..)
381− , DateMathModifier(..)
382− , DateMathUnit(..)
383− , TopHitsAggregation(..)
384− , StatisticsAggregation(..)
385− , SearchAfterKey
386216
387217 , Highlights(..)
388218 , FieldHighlight(..)
@@ -396,36 +226,510 @@ module Database.Bloodhound.Types
396226 , HighlightTag(..)
397227 , HitHighlight
398228
399− , MissingResult(..)
400229 , TermsResult(..)
401230 , DateHistogramResult(..)
402− , DateRangeResult(..)
403− , TopHitResult(..)
404−
405− , EsUsername(..)
406− , EsPassword(..)
407−
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
231+ ) where
232+
233+import Control.Applicative
234+import Control.Monad.Catch
235+import Control.Monad.Except
236+import Control.Monad.Reader
237+import Control.Monad.State
238+import Control.Monad.Writer
239+import Data.Aeson
240+import Data.Aeson.Types (Pair, emptyObject, parseMaybe)
241+import qualified Data.ByteString.Lazy.Char8 as L
242+import qualified Data.HashMap.Strict as HM (union)
243+import Data.List (foldl', nub)
244+import Data.List.NonEmpty (NonEmpty (..), toList)
245+import qualified Data.Map.Strict as M
246+import Data.Maybe
247+import Data.Text (Text)
248+import qualified Data.Text as T
249+import Data.Time.Clock (UTCTime)
250+import qualified Data.Vector as V
251+import GHC.Enum
252+import GHC.Generics (Generic)
253+import Network.HTTP.Client
254+import qualified Network.HTTP.Types.Method as NHTM
255+
256+import Database.Bloodhound.Types.Class
257+
258+-- $setup
259+-- >>> :set -XOverloadedStrings
260+-- >>> import Database.Bloodhound
261+-- >>> let testServer = (Server "http://localhost:9200")
262+-- >>> let testIndex = IndexName "twitter"
263+-- >>> let testMapping = MappingName "tweet"
264+-- >>> let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
265+
266+-- defaultIndexSettings is exported by Database.Bloodhound as well
267+-- no trailing slashes in servers, library handles building the path.
268+
269+{-| Common environment for Elasticsearch calls. Connections will be
270+ pipelined according to the provided HTTP connection manager.
271+-}
272+data BHEnv = BHEnv { bhServer :: Server
273+ , bhManager :: Manager
274+ }
275+
276+{-| All API calls to Elasticsearch operate within
277+ MonadBH. The idea is that it can be easily embedded in your
278+ own monad transformer stack. A default instance for a ReaderT and
279+ alias 'BH' is provided for the simple case.
280+-}
281+class (Functor m, Applicative m, MonadIO m) => MonadBH m where
282+ getBHEnv :: m BHEnv
283+
284+newtype BH m a = BH {
285+ unBH :: ReaderT BHEnv m a
286+ } deriving ( Functor
287+ , Applicative
288+ , Monad
289+ , MonadIO
290+ , MonadState s
291+ , MonadWriter w
292+ , MonadError e
293+ , Alternative
294+ , MonadPlus
295+ , MonadFix
296+ , MonadThrow
297+ , MonadCatch
298+ , MonadMask)
299+
300+instance MonadTrans BH where
301+ lift = BH . lift
302+
303+instance (MonadReader r m) => MonadReader r (BH m) where
304+ ask = lift ask
305+ local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
306+ local f (m r)
307+
308+instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
309+ getBHEnv = BH getBHEnv
310+
311+
312+instance (Functor m, Applicative m, MonadIO m) => MonadBH (ReaderT BHEnv m) where
313+ getBHEnv = ask
314+
315+runBH :: BHEnv -> BH m a -> m a
316+runBH e f = runReaderT (unBH f) e
317+
318+{-| 'Version' is embedded in 'Status' -}
319+data Version = Version { number :: Text
320+ , build_hash :: Text
321+ , build_timestamp :: UTCTime
322+ , build_snapshot :: Bool
323+ , lucene_version :: Text } deriving (Eq, Show, Generic)
324+
325+{-| 'Status' is a data type for describing the JSON body returned by
326+ Elasticsearch when you query its status. This was deprecated in 1.2.0.
327+
328+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
329+-}
330+
331+data Status = Status { ok :: Maybe Bool
332+ , status :: Int
333+ , name :: Text
334+ , version :: Version
335+ , tagline :: Text } deriving (Eq, Show)
336+
337+{-| 'IndexSettings' is used to configure the shards and replicas when you create
338+ an Elasticsearch Index.
339+
340+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
341+-}
342+
343+data IndexSettings =
344+ IndexSettings { indexShards :: ShardCount
345+ , indexReplicas :: ReplicaCount } deriving (Eq, Show)
346+
347+{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
348+defaultIndexSettings :: IndexSettings
349+defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
350+
351+{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
352+type Reply = Network.HTTP.Client.Response L.ByteString
353+type Method = NHTM.Method
354+
355+{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
356+
357+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
358+-}
359+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show)
360+
361+data FieldType = GeoPointType
362+ | GeoShapeType
363+ | FloatType
364+ | IntegerType
365+ | LongType
366+ | ShortType
367+ | ByteType deriving (Eq, Show)
368+
369+data FieldDefinition =
370+ FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
371+
372+{-| An 'IndexTemplate' defines a template that will automatically be
373+ applied to new indices created. The templates include both
374+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
375+ controls if the template will be applied to the index created.
376+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
377+
378+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
379+-}
380+data IndexTemplate =
381+ IndexTemplate { templatePattern :: TemplatePattern
382+ , templateSettings :: Maybe IndexSettings
383+ , templateMappings :: [Value]
384+ }
385+
386+data MappingField =
387+ MappingField { mappingFieldName :: FieldName
388+ , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
389+
390+{-| Support for type reification of 'Mapping's is currently incomplete, for
391+ now the mapping API verbiage expects a 'ToJSON'able blob.
392+
393+ Indexes have mappings, mappings are schemas for the documents contained in the
394+ index. I'd recommend having only one mapping per index, always having a mapping,
395+ and keeping different kinds of documents separated if possible.
396+-}
397+data Mapping = Mapping { typeName :: TypeName
398+ , mappingFields :: [MappingField] } deriving (Eq, Show)
399+
400+{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
401+ operation index, create, delete, and update. 'BulkIndex' behaves like an
402+ "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
403+
404+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk>
405+-}
406+data BulkOperation =
407+ BulkIndex IndexName MappingName DocId Value
408+ | BulkCreate IndexName MappingName DocId Value
409+ | BulkDelete IndexName MappingName DocId
410+ | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Show)
411+
412+{-| 'EsResult' describes the standard wrapper JSON document that you see in
413+ successful Elasticsearch lookups or lookups that couldn't find the document.
414+-}
415+data EsResult a = EsResult { _index :: Text
416+ , _type :: Text
417+ , _id :: Text
418+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show)
419+
420+{-| 'EsResultFound' contains the document and its metadata inside of an
421+ 'EsResult' when the document was successfully found.
422+-}
423+data EsResultFound a = EsResultFound { _version :: DocVersion
424+ , _source :: a } deriving (Eq, Show)
425+
426+{-| 'EsError' is the generic type that will be returned when there was a
427+ problem. If you can't parse the expected response, its a good idea to
428+ try parsing this.
429+-}
430+data EsError = EsError { errorStatus :: Int
431+ , errorMessage :: Text } deriving (Eq, Show)
432+
433+
434+{-| 'DocVersion' is an integer version number for a document between 1
435+and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
436+-}
437+newtype DocVersion = DocVersion {
438+ docVersionNumber :: Int
439+ } deriving (Eq, Show, Ord, ToJSON)
440+
441+-- | Smart constructor for in-range doc version
442+mkDocVersion :: Int -> Maybe DocVersion
443+mkDocVersion i
444+ | i >= (docVersionNumber minBound) && i <= (docVersionNumber maxBound) =
445+ Just $ DocVersion i
446+ | otherwise = Nothing
447+
448+
449+{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
450+own version numbers instead of ones from ES.
451+-}
452+newtype ExternalDocVersion = ExternalDocVersion DocVersion
453+ deriving (Eq, Show, Ord, Bounded, Enum, ToJSON)
454+
455+{-| 'VersionControl' is specified when indexing documents as a
456+optimistic concurrency control.
457+-}
458+data VersionControl = NoVersionControl
459+ -- ^ Don't send a version. This is a pure overwrite.
460+ | InternalVersion DocVersion
461+ -- ^ Use the default ES versioning scheme. Only
462+ -- index the document if the version is the same
463+ -- as the one specified. Only applicable to
464+ -- updates, as you should be getting Version from
465+ -- a search result.
466+ | ExternalGT ExternalDocVersion
467+ -- ^ Use your own version numbering. Only index
468+ -- the document if the version is strictly higher
469+ -- OR the document doesn't exist. The given
470+ -- version will be used as the new version number
471+ -- for the stored document. N.B. All updates must
472+ -- increment this number, meaning there is some
473+ -- global, external ordering of updates.
474+ | ExternalGTE ExternalDocVersion
475+ -- ^ Use your own version numbering. Only index
476+ -- the document if the version is equal or higher
477+ -- than the stored version. Will succeed if there
478+ -- is no existing document. The given version will
479+ -- be used as the new version number for the
480+ -- stored document. Use with care, as this could
481+ -- result in data loss.
482+ | ForceVersion ExternalDocVersion
483+ -- ^ The document will always be indexed and the
484+ -- given version will be the new version. This is
485+ -- typically used for correcting errors. Use with
486+ -- care, as this could result in data loss.
487+ deriving (Show, Eq, Ord)
488+
489+{-| 'DocumentParent' is used to specify a parent document.
490+-}
491+newtype DocumentParent = DocumentParent DocId
492+ deriving (Eq, Show)
493+
494+{-| 'IndexDocumentSettings' are special settings supplied when indexing
495+a document. For the best backwards compatiblity when new fields are
496+added, you should probably prefer to start with 'defaultIndexDocumentSettings'
497+-}
498+data IndexDocumentSettings =
499+ IndexDocumentSettings { idsVersionControl :: VersionControl
500+ , idsParent :: Maybe DocumentParent
501+ } deriving (Eq, Show)
502+
503+{-| Reasonable default settings. Chooses no version control and no parent.
504+-}
505+defaultIndexDocumentSettings :: IndexDocumentSettings
506+defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
507+
508+{-| 'Sort' is a synonym for a list of 'SortSpec's. Sort behavior is order
509+ dependent with later sorts acting as tie-breakers for earlier sorts.
510+-}
511+type Sort = [SortSpec]
512+
513+{-| The two main kinds of 'SortSpec' are 'DefaultSortSpec' and
514+ 'GeoDistanceSortSpec'. The latter takes a 'SortOrder', 'GeoPoint', and
515+ 'DistanceUnit' to express "nearness" to a single geographical point as a
516+ sort specification.
517+
518+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
519+-}
520+data SortSpec = DefaultSortSpec DefaultSort
521+ | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Show)
522+
523+{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
524+ 'mkSort' convenience function for when you want to specify only the most
525+ common parameters.
526+
527+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
528+-}
529+data DefaultSort =
530+ DefaultSort { sortFieldName :: FieldName
531+ , sortOrder :: SortOrder
532+ -- default False
533+ , ignoreUnmapped :: Bool
534+ , sortMode :: Maybe SortMode
535+ , missingSort :: Maybe Missing
536+ , nestedFilter :: Maybe Filter } deriving (Eq, Show)
537+
538+{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
539+ encoded into "asc" or "desc" when turned into JSON.
540+
541+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
542+-}
543+data SortOrder = Ascending
544+ | Descending deriving (Eq, Show)
545+
546+{-| 'Missing' prescribes how to handle missing fields. A missing field can be
547+ sorted last, first, or using a custom value as a substitute.
548+
549+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_missing_values>
550+-}
551+data Missing = LastMissing
552+ | FirstMissing
553+ | CustomMissing Text deriving (Eq, Show)
554+
555+{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
556+
557+http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_sort_mode_option
558+-}
559+data SortMode = SortMin
560+ | SortMax
561+ | SortSum
562+ | SortAvg deriving (Eq, Show)
563+
564+{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
565+ that you can concisely describe the usual kind of 'SortSpec's you want.
566+-}
567+mkSort :: FieldName -> SortOrder -> DefaultSort
568+mkSort fieldName sOrder = DefaultSort fieldName sOrder False Nothing Nothing Nothing
569+
570+{-| 'Cache' is for telling ES whether it should cache a 'Filter' not.
571+ 'Query's cannot be cached.
572+-}
573+type Cache = Bool -- caching on/off
574+defaultCache :: Cache
575+defaultCache = False
576+
577+{-| 'PrefixValue' is used in 'PrefixQuery' as the main query component.
578+-}
579+type PrefixValue = Text
580+
581+{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
582+ JSON encoding baked in. Used all over the place.
583+-}
584+data BooleanOperator = And | Or deriving (Eq, Show)
585+
586+{-| 'ShardCount' is part of 'IndexSettings'
587+-}
588+newtype ShardCount = ShardCount Int deriving (Eq, Show, Generic)
589+
590+{-| 'ReplicaCount' is part of 'IndexSettings'
591+-}
592+newtype ReplicaCount = ReplicaCount Int deriving (Eq, Show, Generic)
593+
594+{-| 'Server' is used with the client functions to point at the ES instance
595+-}
596+newtype Server = Server Text deriving (Eq, Show)
597+
598+{-| 'IndexName' is used to describe which index to query/create/delete
599+-}
600+newtype IndexName = IndexName Text deriving (Eq, Generic, Show)
601+
602+{-| 'TemplateName' is used to describe which template to query/create/delete
603+-}
604+newtype TemplateName = TemplateName Text deriving (Eq, Show, Generic)
605+
606+{-| 'TemplatePattern' represents a pattern which is matched against index names
607+-}
608+newtype TemplatePattern = TemplatePattern Text deriving (Eq, Show, Generic)
609+
610+{-| 'MappingName' is part of mappings which are how ES describes and schematizes
611+ the data in the indices.
612+-}
613+newtype MappingName = MappingName Text deriving (Eq, Generic, Show)
614+
615+{-| 'DocId' is a generic wrapper value for expressing unique Document IDs.
616+ Can be set by the user or created by ES itself. Often used in client
617+ functions for poking at specific documents.
618+-}
619+newtype DocId = DocId Text deriving (Eq, Generic, Show)
620+
621+{-| 'QueryString' is used to wrap query text bodies, be they human written or not.
622+-}
623+newtype QueryString = QueryString Text deriving (Eq, Generic, Show)
624+
625+{-| 'FieldName' is used all over the place wherever a specific field within
626+ a document needs to be specified, usually in 'Query's or 'Filter's.
627+-}
628+newtype FieldName = FieldName Text deriving (Eq, Show)
629+
630+
631+{-| 'Script' is often used in place of 'FieldName' to specify more
632+complex ways of extracting a value from a document.
633+-}
634+newtype Script = Script { scriptText :: Text } deriving (Eq, Show)
635+
636+{-| 'CacheName' is used in 'RegexpFilter' for describing the
637+ 'CacheKey' keyed caching behavior.
638+-}
639+newtype CacheName = CacheName Text deriving (Eq, Show)
640+
641+{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
642+-}
643+newtype CacheKey =
644+ CacheKey Text deriving (Eq, Show)
645+newtype Existence =
646+ Existence Bool deriving (Eq, Show)
647+newtype NullValue =
648+ NullValue Bool deriving (Eq, Show)
649+newtype CutoffFrequency =
650+ CutoffFrequency Double deriving (Eq, Show, Generic)
651+newtype Analyzer =
652+ Analyzer Text deriving (Eq, Show, Generic)
653+newtype MaxExpansions =
654+ MaxExpansions Int deriving (Eq, Show, Generic)
655+
656+{-| 'Lenient', if set to true, will cause format based failures to be
657+ ignored. I don't know what the bloody default is, Elasticsearch
658+ documentation didn't say what it was. Let me know if you figure it out.
659+-}
660+newtype Lenient =
661+ Lenient Bool deriving (Eq, Show, Generic)
662+newtype Tiebreaker =
663+ Tiebreaker Double deriving (Eq, Show, Generic)
664+newtype Boost =
665+ Boost Double deriving (Eq, Show, Generic)
666+newtype BoostTerms =
667+ BoostTerms Double deriving (Eq, Show, Generic)
668+
669+{-| 'MinimumMatch' controls how many should clauses in the bool query should
670+ match. Can be an absolute value (2) or a percentage (30%) or a
671+ combination of both.
672+-}
673+newtype MinimumMatch =
674+ MinimumMatch Int deriving (Eq, Show, Generic)
675+newtype MinimumMatchText =
676+ MinimumMatchText Text deriving (Eq, Show)
677+newtype DisableCoord =
678+ DisableCoord Bool deriving (Eq, Show, Generic)
679+newtype IgnoreTermFrequency =
680+ IgnoreTermFrequency Bool deriving (Eq, Show, Generic)
681+newtype MinimumTermFrequency =
682+ MinimumTermFrequency Int deriving (Eq, Show, Generic)
683+newtype MaxQueryTerms =
684+ MaxQueryTerms Int deriving (Eq, Show, Generic)
685+newtype Fuzziness =
686+ Fuzziness Double deriving (Eq, Show, Generic)
687+
688+{-| 'PrefixLength' is the prefix length used in queries, defaults to 0. -}
689+newtype PrefixLength =
690+ PrefixLength Int deriving (Eq, Show, Generic)
691+newtype TypeName =
692+ TypeName Text deriving (Eq, Show, Generic)
693+newtype PercentMatch =
694+ PercentMatch Double deriving (Eq, Show, Generic)
695+newtype StopWord =
696+ StopWord Text deriving (Eq, Show, Generic)
697+newtype QueryPath =
698+ QueryPath Text deriving (Eq, Show, Generic)
699+
700+{-| Allowing a wildcard at the beginning of a word (eg "*ing") is particularly
701+ heavy, because all terms in the index need to be examined, just in case
702+ they match. Leading wildcards can be disabled by setting
703+ 'AllowLeadingWildcard' to false. -}
704+newtype AllowLeadingWildcard =
705+ AllowLeadingWildcard Bool deriving (Eq, Show, Generic)
706+newtype LowercaseExpanded =
707+ LowercaseExpanded Bool deriving (Eq, Show, Generic)
708+newtype EnablePositionIncrements =
709+ EnablePositionIncrements Bool deriving (Eq, Show, Generic)
710+
711+{-| By default, wildcard terms in a query are not analyzed.
712+ Setting 'AnalyzeWildcard' to true enables best-effort analysis.
713+-}
714+newtype AnalyzeWildcard = AnalyzeWildcard Bool deriving (Eq, Show, Generic)
715+
716+{-| 'GeneratePhraseQueries' defaults to false.
717+-}
718+newtype GeneratePhraseQueries =
719+ GeneratePhraseQueries Bool deriving (Eq, Show, Generic)
720+
721+{-| 'Locale' is used for string conversions - defaults to ROOT.
722+-}
723+newtype Locale = Locale Text deriving (Eq, Show, Generic)
724+newtype MaxWordLength = MaxWordLength Int deriving (Eq, Show, Generic)
725+newtype MinWordLength = MinWordLength Int deriving (Eq, Show, Generic)
726+
727+{-| 'PhraseSlop' sets the default slop for phrases, 0 means exact
728+ phrase matches. Default is 0.
729+-}
730+newtype PhraseSlop = PhraseSlop Int deriving (Eq, Show, Generic)
731+newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, Generic)
732+newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, Generic)
429733
430734{-| 'unpackId' is a silly convenience function that gets used once.
431735-}
@@ -433,6 +737,8 @@ unpackId :: DocId -> Text
433737unpackId (DocId docId) = docId
434738
435739type TrackSortScores = Bool
740+newtype From = From Int deriving (Eq, Show, ToJSON)
741+newtype Size = Size Int deriving (Eq, Show, ToJSON)
436742
437743data Search = Search { queryBody :: Maybe Query
438744 , filterBody :: Maybe Filter
@@ -444,87 +750,1644 @@ data Search = Search { queryBody :: Maybe Query
444750 , from :: From
445751 , size :: Size
446752 , searchType :: SearchType
447− , searchAfterKey :: Maybe SearchAfterKey
448753 , 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] [] []
754+ , source :: Maybe Source } deriving (Eq, Show)
479755
480756data SearchType = SearchTypeQueryThenFetch
481757 | SearchTypeDfsQueryThenFetch
758+ | SearchTypeCount
759+ | SearchTypeScan
760+ | SearchTypeQueryAndFetch
761+ | SearchTypeDfsQueryAndFetch
482762 deriving (Eq, Show)
483763
484764data Source =
485765 NoSource
486766 | SourcePatterns PatternOrPatterns
487767 | SourceIncludeExclude Include Exclude
488− deriving (Eq, Show)
768+ deriving (Show, Eq)
769+
770+data PatternOrPatterns = PopPattern Pattern
771+ | PopPatterns [Pattern] deriving (Eq, Show)
772+
773+data Include = Include [Pattern] deriving (Eq, Show)
774+data Exclude = Exclude [Pattern] deriving (Eq, Show)
775+
776+newtype Pattern = Pattern Text deriving (Eq, Show)
777+
778+data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
779+ , highlightFields :: [FieldHighlight]
780+ } deriving (Show, Eq)
781+
782+data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
783+ deriving (Show, Eq)
784+
785+
786+data HighlightSettings = Plain PlainHighlight
787+ | Postings PostingsHighlight
788+ | FastVector FastVectorHighlight
789+ deriving (Show, Eq)
790+data PlainHighlight =
791+ PlainHighlight { plainCommon :: Maybe CommonHighlight
792+ , plainNonPost :: Maybe NonPostings } deriving (Show, Eq)
793+
794+ -- This requires that index_options are set to 'offset' in the mapping.
795+data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Show, Eq)
796+
797+-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
798+data FastVectorHighlight =
799+ FastVectorHighlight { fvCommon :: Maybe CommonHighlight
800+ , fvNonPostSettings :: Maybe NonPostings
801+ , boundaryChars :: Maybe Text
802+ , boundaryMaxScan :: Maybe Int
803+ , fragmentOffset :: Maybe Int
804+ , matchedFields :: [Text]
805+ , phraseLimit :: Maybe Int
806+ } deriving (Show, Eq)
807+
808+data CommonHighlight =
809+ CommonHighlight { order :: Maybe Text
810+ , forceSource :: Maybe Bool
811+ , tag :: Maybe HighlightTag
812+ , encoder :: Maybe HighlightEncoder
813+ , noMatchSize :: Maybe Int
814+ , highlightQuery :: Maybe Query
815+ , requireFieldMatch :: Maybe Bool
816+ } deriving (Show, Eq)
817+
818+-- Settings that are only applicable to FastVector and Plain highlighters.
819+data NonPostings =
820+ NonPostings { fragmentSize :: Maybe Int
821+ , numberOfFragments :: Maybe Int} deriving (Show, Eq)
822+
823+data HighlightEncoder = DefaultEncoder
824+ | HTMLEncoder
825+ deriving (Show, Eq)
826+
827+-- NOTE: Should the tags use some kind of HTML type, rather than Text?
828+data HighlightTag = TagSchema Text
829+ | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
830+ deriving (Show, Eq)
831+
832+
833+data Query =
834+ TermQuery Term (Maybe Boost)
835+ | TermsQuery (NonEmpty Term)
836+ | QueryMatchQuery MatchQuery
837+ | QueryMultiMatchQuery MultiMatchQuery
838+ | QueryBoolQuery BoolQuery
839+ | QueryBoostingQuery BoostingQuery
840+ | QueryCommonTermsQuery CommonTermsQuery
841+ | ConstantScoreFilter Filter Boost
842+ | ConstantScoreQuery Query Boost
843+ | QueryDisMaxQuery DisMaxQuery
844+ | QueryFilteredQuery FilteredQuery
845+ | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
846+ | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
847+ | QueryFuzzyQuery FuzzyQuery
848+ | QueryHasChildQuery HasChildQuery
849+ | QueryHasParentQuery HasParentQuery
850+ | IdsQuery MappingName [DocId]
851+ | QueryIndicesQuery IndicesQuery
852+ | MatchAllQuery (Maybe Boost)
853+ | QueryMoreLikeThisQuery MoreLikeThisQuery
854+ | QueryMoreLikeThisFieldQuery MoreLikeThisFieldQuery
855+ | QueryNestedQuery NestedQuery
856+ | QueryPrefixQuery PrefixQuery
857+ | QueryQueryStringQuery QueryStringQuery
858+ | QuerySimpleQueryStringQuery SimpleQueryStringQuery
859+ | QueryRangeQuery RangeQuery
860+ | QueryRegexpQuery RegexpQuery
861+ deriving (Eq, Show)
862+
863+data RegexpQuery =
864+ RegexpQuery { regexpQueryField :: FieldName
865+ , regexpQuery :: Regexp
866+ , regexpQueryFlags :: RegexpFlags
867+ , regexpQueryBoost :: Maybe Boost
868+ } deriving (Eq, Show)
869+
870+data RangeQuery =
871+ RangeQuery { rangeQueryField :: FieldName
872+ , rangeQueryRange :: RangeValue
873+ , rangeQueryBoost :: Boost } deriving (Eq, Show)
874+
875+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
876+mkRangeQuery f r = RangeQuery f r (Boost 1.0)
877+
878+data SimpleQueryStringQuery =
879+ SimpleQueryStringQuery
880+ { simpleQueryStringQuery :: QueryString
881+ , simpleQueryStringField :: Maybe FieldOrFields
882+ , simpleQueryStringOperator :: Maybe BooleanOperator
883+ , simpleQueryStringAnalyzer :: Maybe Analyzer
884+ , simpleQueryStringFlags :: Maybe [SimpleQueryFlag]
885+ , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
886+ , simpleQueryStringLocale :: Maybe Locale
887+ } deriving (Eq, Show)
888+
889+data SimpleQueryFlag =
890+ SimpleQueryAll
891+ | SimpleQueryNone
892+ | SimpleQueryAnd
893+ | SimpleQueryOr
894+ | SimpleQueryPrefix
895+ | SimpleQueryPhrase
896+ | SimpleQueryPrecedence
897+ | SimpleQueryEscape
898+ | SimpleQueryWhitespace
899+ | SimpleQueryFuzzy
900+ | SimpleQueryNear
901+ | SimpleQuerySlop deriving (Eq, Show)
902+
903+-- use_dis_max and tie_breaker when fields are plural?
904+data QueryStringQuery =
905+ QueryStringQuery
906+ { queryStringQuery :: QueryString
907+ , queryStringDefaultField :: Maybe FieldName
908+ , queryStringOperator :: Maybe BooleanOperator
909+ , queryStringAnalyzer :: Maybe Analyzer
910+ , queryStringAllowLeadingWildcard :: Maybe AllowLeadingWildcard
911+ , queryStringLowercaseExpanded :: Maybe LowercaseExpanded
912+ , queryStringEnablePositionIncrements :: Maybe EnablePositionIncrements
913+ , queryStringFuzzyMaxExpansions :: Maybe MaxExpansions
914+ , queryStringFuzziness :: Maybe Fuzziness
915+ , queryStringFuzzyPrefixLength :: Maybe PrefixLength
916+ , queryStringPhraseSlop :: Maybe PhraseSlop
917+ , queryStringBoost :: Maybe Boost
918+ , queryStringAnalyzeWildcard :: Maybe AnalyzeWildcard
919+ , queryStringGeneratePhraseQueries :: Maybe GeneratePhraseQueries
920+ , queryStringMinimumShouldMatch :: Maybe MinimumMatch
921+ , queryStringLenient :: Maybe Lenient
922+ , queryStringLocale :: Maybe Locale
923+ } deriving (Eq, Show)
924+
925+mkQueryStringQuery :: QueryString -> QueryStringQuery
926+mkQueryStringQuery qs =
927+ QueryStringQuery qs Nothing Nothing
928+ Nothing Nothing Nothing Nothing
929+ Nothing Nothing Nothing Nothing
930+ Nothing Nothing Nothing Nothing
931+ Nothing Nothing
932+
933+data FieldOrFields = FofField FieldName
934+ | FofFields [FieldName] deriving (Eq, Show)
935+
936+data PrefixQuery =
937+ PrefixQuery
938+ { prefixQueryField :: FieldName
939+ , prefixQueryPrefixValue :: Text
940+ , prefixQueryBoost :: Maybe Boost } deriving (Eq, Show)
941+
942+data NestedQuery =
943+ NestedQuery
944+ { nestedQueryPath :: QueryPath
945+ , nestedQueryScoreType :: ScoreType
946+ , nestedQuery :: Query } deriving (Eq, Show)
947+
948+data MoreLikeThisFieldQuery =
949+ MoreLikeThisFieldQuery
950+ { moreLikeThisFieldText :: Text
951+ , moreLikeThisFieldFields :: FieldName
952+ -- default 0.3 (30%)
953+ , moreLikeThisFieldPercentMatch :: Maybe PercentMatch
954+ , moreLikeThisFieldMinimumTermFreq :: Maybe MinimumTermFrequency
955+ , moreLikeThisFieldMaxQueryTerms :: Maybe MaxQueryTerms
956+ , moreLikeThisFieldStopWords :: Maybe [StopWord]
957+ , moreLikeThisFieldMinDocFrequency :: Maybe MinDocFrequency
958+ , moreLikeThisFieldMaxDocFrequency :: Maybe MaxDocFrequency
959+ , moreLikeThisFieldMinWordLength :: Maybe MinWordLength
960+ , moreLikeThisFieldMaxWordLength :: Maybe MaxWordLength
961+ , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
962+ , moreLikeThisFieldBoost :: Maybe Boost
963+ , moreLikeThisFieldAnalyzer :: Maybe Analyzer
964+ } deriving (Eq, Show)
965+
966+data MoreLikeThisQuery =
967+ MoreLikeThisQuery
968+ { moreLikeThisText :: Text
969+ , moreLikeThisFields :: Maybe [FieldName]
970+ -- default 0.3 (30%)
971+ , moreLikeThisPercentMatch :: Maybe PercentMatch
972+ , moreLikeThisMinimumTermFreq :: Maybe MinimumTermFrequency
973+ , moreLikeThisMaxQueryTerms :: Maybe MaxQueryTerms
974+ , moreLikeThisStopWords :: Maybe [StopWord]
975+ , moreLikeThisMinDocFrequency :: Maybe MinDocFrequency
976+ , moreLikeThisMaxDocFrequency :: Maybe MaxDocFrequency
977+ , moreLikeThisMinWordLength :: Maybe MinWordLength
978+ , moreLikeThisMaxWordLength :: Maybe MaxWordLength
979+ , moreLikeThisBoostTerms :: Maybe BoostTerms
980+ , moreLikeThisBoost :: Maybe Boost
981+ , moreLikeThisAnalyzer :: Maybe Analyzer
982+ } deriving (Eq, Show)
983+
984+data IndicesQuery =
985+ IndicesQuery
986+ { indicesQueryIndices :: [IndexName]
987+ , indicesQuery :: Query
988+ -- default "all"
989+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show)
990+
991+data HasParentQuery =
992+ HasParentQuery
993+ { hasParentQueryType :: TypeName
994+ , hasParentQuery :: Query
995+ , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
996+
997+data HasChildQuery =
998+ HasChildQuery
999+ { hasChildQueryType :: TypeName
1000+ , hasChildQuery :: Query
1001+ , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
1002+
1003+data ScoreType =
1004+ ScoreTypeMax
1005+ | ScoreTypeSum
1006+ | ScoreTypeAvg
1007+ | ScoreTypeNone deriving (Eq, Show)
1008+
1009+data FuzzyQuery =
1010+ FuzzyQuery { fuzzyQueryField :: FieldName
1011+ , fuzzyQueryValue :: Text
1012+ , fuzzyQueryPrefixLength :: PrefixLength
1013+ , fuzzyQueryMaxExpansions :: MaxExpansions
1014+ , fuzzyQueryFuzziness :: Fuzziness
1015+ , fuzzyQueryBoost :: Maybe Boost
1016+ } deriving (Eq, Show)
1017+
1018+data FuzzyLikeFieldQuery =
1019+ FuzzyLikeFieldQuery
1020+ { fuzzyLikeField :: FieldName
1021+ -- anaphora is good for the soul.
1022+ , fuzzyLikeFieldText :: Text
1023+ , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
1024+ , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
1025+ , fuzzyLikeFieldFuzziness :: Fuzziness
1026+ , fuzzyLikeFieldPrefixLength :: PrefixLength
1027+ , fuzzyLikeFieldBoost :: Boost
1028+ , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
1029+ } deriving (Eq, Show)
1030+
1031+data FuzzyLikeThisQuery =
1032+ FuzzyLikeThisQuery
1033+ { fuzzyLikeFields :: [FieldName]
1034+ , fuzzyLikeText :: Text
1035+ , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
1036+ , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
1037+ , fuzzyLikeFuzziness :: Fuzziness
1038+ , fuzzyLikePrefixLength :: PrefixLength
1039+ , fuzzyLikeBoost :: Boost
1040+ , fuzzyLikeAnalyzer :: Maybe Analyzer
1041+ } deriving (Eq, Show)
1042+
1043+data FilteredQuery =
1044+ FilteredQuery
1045+ { filteredQuery :: Query
1046+ , filteredFilter :: Filter } deriving (Eq, Show)
1047+
1048+data DisMaxQuery =
1049+ DisMaxQuery { disMaxQueries :: [Query]
1050+ -- default 0.0
1051+ , disMaxTiebreaker :: Tiebreaker
1052+ , disMaxBoost :: Maybe Boost
1053+ } deriving (Eq, Show)
1054+
1055+data MatchQuery =
1056+ MatchQuery { matchQueryField :: FieldName
1057+ , matchQueryQueryString :: QueryString
1058+ , matchQueryOperator :: BooleanOperator
1059+ , matchQueryZeroTerms :: ZeroTermsQuery
1060+ , matchQueryCutoffFrequency :: Maybe CutoffFrequency
1061+ , matchQueryMatchType :: Maybe MatchQueryType
1062+ , matchQueryAnalyzer :: Maybe Analyzer
1063+ , matchQueryMaxExpansions :: Maybe MaxExpansions
1064+ , matchQueryLenient :: Maybe Lenient
1065+ , matchQueryBoost :: Maybe Boost } deriving (Eq, Show)
1066+
1067+{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
1068+ enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
1069+-}
1070+mkMatchQuery :: FieldName -> QueryString -> MatchQuery
1071+mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1072+
1073+data MatchQueryType =
1074+ MatchPhrase
1075+ | MatchPhrasePrefix deriving (Eq, Show)
1076+
1077+data MultiMatchQuery =
1078+ MultiMatchQuery { multiMatchQueryFields :: [FieldName]
1079+ , multiMatchQueryString :: QueryString
1080+ , multiMatchQueryOperator :: BooleanOperator
1081+ , multiMatchQueryZeroTerms :: ZeroTermsQuery
1082+ , multiMatchQueryTiebreaker :: Maybe Tiebreaker
1083+ , multiMatchQueryType :: Maybe MultiMatchQueryType
1084+ , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
1085+ , multiMatchQueryAnalyzer :: Maybe Analyzer
1086+ , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
1087+ , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Show)
1088+
1089+{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
1090+ enabling you to provide only the list of 'FieldName's and 'QueryString' to
1091+ make a 'MultiMatchQuery'.
1092+-}
1093+
1094+mkMultiMatchQuery :: [FieldName] -> QueryString -> MultiMatchQuery
1095+mkMultiMatchQuery matchFields query =
1096+ MultiMatchQuery matchFields query
1097+ Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1098+
1099+data MultiMatchQueryType =
1100+ MultiMatchBestFields
1101+ | MultiMatchMostFields
1102+ | MultiMatchCrossFields
1103+ | MultiMatchPhrase
1104+ | MultiMatchPhrasePrefix deriving (Eq, Show)
1105+
1106+data BoolQuery =
1107+ BoolQuery { boolQueryMustMatch :: [Query]
1108+ , boolQueryMustNotMatch :: [Query]
1109+ , boolQueryShouldMatch :: [Query]
1110+ , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
1111+ , boolQueryBoost :: Maybe Boost
1112+ , boolQueryDisableCoord :: Maybe DisableCoord
1113+ } deriving (Eq, Show)
1114+
1115+mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
1116+mkBoolQuery must mustNot should =
1117+ BoolQuery must mustNot should Nothing Nothing Nothing
1118+
1119+data BoostingQuery =
1120+ BoostingQuery { positiveQuery :: Query
1121+ , negativeQuery :: Query
1122+ , negativeBoost :: Boost } deriving (Eq, Show)
1123+
1124+data CommonTermsQuery =
1125+ CommonTermsQuery { commonField :: FieldName
1126+ , commonQuery :: QueryString
1127+ , commonCutoffFrequency :: CutoffFrequency
1128+ , commonLowFreqOperator :: BooleanOperator
1129+ , commonHighFreqOperator :: BooleanOperator
1130+ , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
1131+ , commonBoost :: Maybe Boost
1132+ , commonAnalyzer :: Maybe Analyzer
1133+ , commonDisableCoord :: Maybe DisableCoord
1134+ } deriving (Eq, Show)
1135+
1136+data CommonMinimumMatch =
1137+ CommonMinimumMatchHighLow MinimumMatchHighLow
1138+ | CommonMinimumMatch MinimumMatch
1139+ deriving (Eq, Show)
1140+
1141+data MinimumMatchHighLow =
1142+ MinimumMatchHighLow { lowFreq :: MinimumMatch
1143+ , highFreq :: MinimumMatch } deriving (Eq, Show)
1144+
1145+data Filter = AndFilter [Filter] Cache
1146+ | OrFilter [Filter] Cache
1147+ | NotFilter Filter Cache
1148+ | IdentityFilter
1149+ | BoolFilter BoolMatch
1150+ | ExistsFilter FieldName -- always cached
1151+ | GeoBoundingBoxFilter GeoBoundingBoxConstraint
1152+ | GeoDistanceFilter GeoPoint Distance DistanceType OptimizeBbox Cache
1153+ | GeoDistanceRangeFilter GeoPoint DistanceRange
1154+ | GeoPolygonFilter FieldName [LatLon]
1155+ | IdsFilter MappingName [DocId]
1156+ | LimitFilter Int
1157+ | MissingFilter FieldName Existence NullValue
1158+ | PrefixFilter FieldName PrefixValue Cache
1159+ | QueryFilter Query Cache
1160+ | RangeFilter FieldName RangeValue RangeExecution Cache
1161+ | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
1162+ | TermFilter Term Cache
1163+ deriving (Eq, Show)
1164+
1165+data ZeroTermsQuery = ZeroTermsNone
1166+ | ZeroTermsAll deriving (Eq, Show)
1167+
1168+data RangeExecution = RangeExecutionIndex
1169+ | RangeExecutionFielddata deriving (Eq, Show)
1170+
1171+newtype Regexp = Regexp Text deriving (Eq, Show)
1172+
1173+data RegexpFlags = AllRegexpFlags
1174+ | NoRegexpFlags
1175+ | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Show)
1176+
1177+data RegexpFlag = AnyString
1178+ | Automaton
1179+ | Complement
1180+ | Empty
1181+ | Intersection
1182+ | Interval deriving (Eq, Show)
1183+
1184+newtype LessThan = LessThan Double deriving (Eq, Show)
1185+newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
1186+newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
1187+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
1188+
1189+newtype LessThanD = LessThanD UTCTime deriving (Eq, Show)
1190+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show)
1191+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show)
1192+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show)
1193+
1194+data RangeValue = RangeDateLte LessThanEqD
1195+ | RangeDateLt LessThanD
1196+ | RangeDateGte GreaterThanEqD
1197+ | RangeDateGt GreaterThanD
1198+ | RangeDateGtLt GreaterThanD LessThanD
1199+ | RangeDateGteLte GreaterThanEqD LessThanEqD
1200+ | RangeDateGteLt GreaterThanEqD LessThanD
1201+ | RangeDateGtLte GreaterThanD LessThanEqD
1202+ | RangeDoubleLte LessThanEq
1203+ | RangeDoubleLt LessThan
1204+ | RangeDoubleGte GreaterThanEq
1205+ | RangeDoubleGt GreaterThan
1206+ | RangeDoubleGtLt GreaterThan LessThan
1207+ | RangeDoubleGteLte GreaterThanEq LessThanEq
1208+ | RangeDoubleGteLt GreaterThanEq LessThan
1209+ | RangeDoubleGtLte GreaterThan LessThanEq
1210+ deriving (Eq, Show)
1211+
1212+rangeValueToPair :: RangeValue -> [Pair]
1213+rangeValueToPair rv = case rv of
1214+ RangeDateLte (LessThanEqD t) -> ["lte" .= t]
1215+ RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
1216+ RangeDateLt (LessThanD t) -> ["lt" .= t]
1217+ RangeDateGt (GreaterThanD t) -> ["gt" .= t]
1218+ RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
1219+ RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
1220+ RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
1221+ RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
1222+ RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
1223+ RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
1224+ RangeDoubleLt (LessThan t) -> ["lt" .= t]
1225+ RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
1226+ RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
1227+ RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
1228+ RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
1229+ RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1230+
1231+data Term = Term { termField :: Text
1232+ , termValue :: Text } deriving (Eq, Show)
1233+
1234+data BoolMatch = MustMatch Term Cache
1235+ | MustNotMatch Term Cache
1236+ | ShouldMatch [Term] Cache deriving (Eq, Show)
1237+
1238+-- "memory" or "indexed"
1239+data GeoFilterType = GeoFilterMemory
1240+ | GeoFilterIndexed deriving (Eq, Show)
1241+
1242+data LatLon = LatLon { lat :: Double
1243+ , lon :: Double } deriving (Eq, Show)
1244+
1245+data GeoBoundingBox =
1246+ GeoBoundingBox { topLeft :: LatLon
1247+ , bottomRight :: LatLon } deriving (Eq, Show)
1248+
1249+data GeoBoundingBoxConstraint =
1250+ GeoBoundingBoxConstraint { geoBBField :: FieldName
1251+ , constraintBox :: GeoBoundingBox
1252+ , bbConstraintcache :: Cache
1253+ , geoType :: GeoFilterType
1254+ } deriving (Eq, Show)
1255+
1256+data GeoPoint =
1257+ GeoPoint { geoField :: FieldName
1258+ , latLon :: LatLon} deriving (Eq, Show)
1259+
1260+data DistanceUnit = Miles
1261+ | Yards
1262+ | Feet
1263+ | Inches
1264+ | Kilometers
1265+ | Meters
1266+ | Centimeters
1267+ | Millimeters
1268+ | NauticalMiles deriving (Eq, Show)
1269+
1270+data DistanceType = Arc
1271+ | SloppyArc -- doesn't exist <1.0
1272+ | Plane deriving (Eq, Show)
1273+
1274+data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1275+ | NoOptimizeBbox deriving (Eq, Show)
1276+
1277+data Distance =
1278+ Distance { coefficient :: Double
1279+ , unit :: DistanceUnit } deriving (Eq, Show)
1280+
1281+data DistanceRange =
1282+ DistanceRange { distanceFrom :: Distance
1283+ , distanceTo :: Distance } deriving (Eq, Show)
1284+
1285+data SearchResult a =
1286+ SearchResult { took :: Int
1287+ , timedOut :: Bool
1288+ , shards :: ShardResult
1289+ , searchHits :: SearchHits a
1290+ , aggregations :: Maybe AggregationResults
1291+ , scrollId :: Maybe ScrollId } deriving (Eq, Show)
1292+
1293+type ScrollId = Text -- Fixme: Newtype
1294+
1295+type Score = Maybe Double
1296+
1297+data SearchHits a =
1298+ SearchHits { hitsTotal :: Int
1299+ , maxScore :: Score
1300+ , hits :: [Hit a] } deriving (Eq, Show)
1301+
1302+
1303+instance Monoid (SearchHits a) where
1304+ mempty = SearchHits 0 Nothing mempty
1305+ mappend (SearchHits ta ma ha) (SearchHits tb mb hb) =
1306+ SearchHits (ta + tb) (max ma mb) (ha <> hb)
1307+
1308+
1309+data Hit a =
1310+ Hit { hitIndex :: IndexName
1311+ , hitType :: MappingName
1312+ , hitDocId :: DocId
1313+ , hitScore :: Score
1314+ , hitSource :: Maybe a
1315+ , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show)
1316+
1317+data ShardResult =
1318+ ShardResult { shardTotal :: Int
1319+ , shardsSuccessful :: Int
1320+ , shardsFailed :: Int } deriving (Eq, Show, Generic)
1321+
1322+type HitHighlight = M.Map Text [Text]
1323+
1324+showText :: Show a => a -> Text
1325+showText = T.pack . show
1326+
1327+type Aggregations = M.Map Text Aggregation
1328+
1329+emptyAggregations :: Aggregations
1330+emptyAggregations = M.empty
1331+
1332+mkAggregations :: Text -> Aggregation -> Aggregations
1333+mkAggregations name aggregation = M.insert name aggregation emptyAggregations
1334+
1335+data TermOrder = TermOrder{ termSortField :: Text
1336+ , termSortOrder :: SortOrder } deriving (Eq, Show)
1337+
1338+data TermInclusion = TermInclusion Text
1339+ | TermPattern Text Text deriving (Eq, Show)
1340+
1341+data CollectionMode = BreadthFirst
1342+ | DepthFirst deriving (Eq, Show)
1343+
1344+data ExecutionHint = Ordinals
1345+ | GlobalOrdinals
1346+ | GlobalOrdinalsHash
1347+ | GlobalOrdinalsLowCardinality
1348+ | Map deriving (Eq, Show)
1349+
1350+data TimeInterval = Weeks
1351+ | Days
1352+ | Hours
1353+ | Minutes
1354+ | Seconds deriving (Eq)
1355+
1356+data Interval = Year
1357+ | Quarter
1358+ | Month
1359+ | Week
1360+ | Day
1361+ | Hour
1362+ | Minute
1363+ | Second
1364+ | FractionalInterval Float TimeInterval deriving (Eq, Show)
1365+
1366+data Aggregation = TermsAgg TermsAggregation
1367+ | DateHistogramAgg DateHistogramAggregation
1368+ | ValueCountAgg ValueCountAggregation
1369+ | FilterAgg FilterAggregation deriving (Eq, Show)
1370+
1371+
1372+data TermsAggregation = TermsAggregation { term :: Either Text Text
1373+ , termInclude :: Maybe TermInclusion
1374+ , termExclude :: Maybe TermInclusion
1375+ , termOrder :: Maybe TermOrder
1376+ , termMinDocCount :: Maybe Int
1377+ , termSize :: Maybe Int
1378+ , termShardSize :: Maybe Int
1379+ , termCollectMode :: Maybe CollectionMode
1380+ , termExecutionHint :: Maybe ExecutionHint
1381+ , termAggs :: Maybe Aggregations
1382+ } deriving (Eq, Show)
1383+
1384+data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
1385+ , dateInterval :: Interval
1386+ , dateFormat :: Maybe Text
1387+ -- pre and post deprecated in 1.5
1388+ , datePreZone :: Maybe Text
1389+ , datePostZone :: Maybe Text
1390+ , datePreOffset :: Maybe Text
1391+ , datePostOffset :: Maybe Text
1392+ , dateAggs :: Maybe Aggregations
1393+ } deriving (Eq, Show)
1394+
1395+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1396+data ValueCountAggregation = FieldValueCount FieldName
1397+ | ScriptValueCount Script deriving (Eq, Show)
1398+
1399+-- | 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.
1400+data FilterAggregation = FilterAggregation { faFilter :: Filter
1401+ , faAggs :: Maybe Aggregations} deriving (Eq, Show)
1402+
1403+mkTermsAggregation :: Text -> TermsAggregation
1404+mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1405+
1406+mkTermsScriptAggregation :: Text -> TermsAggregation
1407+mkTermsScriptAggregation t = TermsAggregation (Right t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1408+
1409+mkDateHistogram :: FieldName -> Interval -> DateHistogramAggregation
1410+mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
1411+
1412+instance ToJSON TermOrder where
1413+ toJSON (TermOrder termSortField termSortOrder) = object [termSortField .= termSortOrder]
1414+
1415+instance ToJSON TermInclusion where
1416+ toJSON (TermInclusion x) = toJSON x
1417+ toJSON (TermPattern pattern flags) = omitNulls [ "pattern" .= pattern,
1418+ "flags" .= flags]
1419+
1420+instance ToJSON CollectionMode where
1421+ toJSON BreadthFirst = "breadth_first"
1422+ toJSON DepthFirst = "depth_first"
1423+
1424+instance ToJSON ExecutionHint where
1425+ toJSON Ordinals = "ordinals"
1426+ toJSON GlobalOrdinals = "global_ordinals"
1427+ toJSON GlobalOrdinalsHash = "global_ordinals_hash"
1428+ toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
1429+ toJSON Map = "map"
1430+
1431+instance ToJSON Interval where
1432+ toJSON Year = "year"
1433+ toJSON Quarter = "quarter"
1434+ toJSON Month = "month"
1435+ toJSON Week = "week"
1436+ toJSON Day = "day"
1437+ toJSON Hour = "hour"
1438+ toJSON Minute = "minute"
1439+ toJSON Second = "second"
1440+ toJSON (FractionalInterval fraction interval) = toJSON $ show fraction ++ show interval
1441+
1442+instance Show TimeInterval where
1443+ show Weeks = "w"
1444+ show Days = "d"
1445+ show Hours = "h"
1446+ show Minutes = "m"
1447+ show Seconds = "s"
1448+
1449+instance ToJSON Aggregation where
1450+ toJSON (TermsAgg (TermsAggregation term include exclude order minDocCount size shardSize collectMode executionHint termAggs)) =
1451+ omitNulls ["terms" .= omitNulls [ toJSON' term,
1452+ "include" .= include,
1453+ "exclude" .= exclude,
1454+ "order" .= order,
1455+ "min_doc_count" .= minDocCount,
1456+ "size" .= size,
1457+ "shard_size" .= shardSize,
1458+ "collect_mode" .= collectMode,
1459+ "execution_hint" .= executionHint
1460+ ],
1461+ "aggs" .= termAggs ]
1462+ where
1463+ toJSON' x = case x of { Left y -> "field" .= y; Right y -> "script" .= y }
1464+
1465+ toJSON (DateHistogramAgg (DateHistogramAggregation field interval format preZone postZone preOffset postOffset dateHistoAggs)) =
1466+ omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
1467+ "interval" .= interval,
1468+ "format" .= format,
1469+ "pre_zone" .= preZone,
1470+ "post_zone" .= postZone,
1471+ "pre_offset" .= preOffset,
1472+ "post_offset" .= postOffset
1473+ ],
1474+ "aggs" .= dateHistoAggs ]
1475+ toJSON (ValueCountAgg a) = object ["value_count" .= v]
1476+ where v = case a of
1477+ (FieldValueCount (FieldName n)) -> object ["field" .= n]
1478+ (ScriptValueCount (Script s)) -> object ["script" .= s]
1479+ toJSON (FilterAgg (FilterAggregation filt ags)) =
1480+ omitNulls [ "filter" .= filt
1481+ , "aggs" .= ags]
1482+
1483+type AggregationResults = M.Map Text Value
1484+
1485+class BucketAggregation a where
1486+ key :: a -> Text
1487+ docCount :: a -> Int
1488+ aggs :: a -> Maybe AggregationResults
1489+
1490+
1491+data Bucket a = Bucket { buckets :: [a]} deriving (Show)
1492+
1493+data TermsResult = TermsResult { termKey :: Text
1494+ , termsDocCount :: Int
1495+ , termsAggs :: Maybe AggregationResults } deriving (Show)
1496+
1497+data DateHistogramResult = DateHistogramResult { dateKey :: Int
1498+ , dateKeyStr :: Maybe Text
1499+ , dateDocCount :: Int
1500+ , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
1501+
1502+toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
1503+toTerms t a = M.lookup t a >>= deserialize
1504+ where deserialize = parseMaybe parseJSON
1505+
1506+toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
1507+toDateHistogram t a = M.lookup t a >>= deserialize
1508+ where deserialize = parseMaybe parseJSON
1509+
1510+instance BucketAggregation TermsResult where
1511+ key = termKey
1512+ docCount = termsDocCount
1513+ aggs = termsAggs
1514+
1515+instance BucketAggregation DateHistogramResult where
1516+ key = showText . dateKey
1517+ docCount = dateDocCount
1518+ aggs = dateHistogramAggs
1519+
1520+instance (FromJSON a, BucketAggregation a) => FromJSON (Bucket a) where
1521+ parseJSON (Object v) = Bucket <$>
1522+ v .: "buckets"
1523+ parseJSON _ = mempty
1524+
1525+instance FromJSON TermsResult where
1526+ parseJSON (Object v) = TermsResult <$>
1527+ v .: "key" <*>
1528+ v .: "doc_count" <*>
1529+ v .:? "aggregations"
1530+ parseJSON _ = mempty
1531+
1532+instance FromJSON DateHistogramResult where
1533+ parseJSON (Object v) = DateHistogramResult <$>
1534+ v .: "key" <*>
1535+ v .:? "key_as_string" <*>
1536+ v .: "doc_count" <*>
1537+ v .:? "aggregations"
1538+ parseJSON _ = mempty
1539+
1540+instance Monoid Filter where
1541+ mempty = IdentityFilter
1542+ mappend a b = AndFilter [a, b] defaultCache
1543+
1544+instance Seminearring Filter where
1545+ a <||> b = OrFilter [a, b] defaultCache
1546+
1547+instance ToJSON Filter where
1548+ toJSON (AndFilter filters cache) =
1549+ object ["and" .=
1550+ object [ "filters" .= fmap toJSON filters
1551+ , "_cache" .= cache]]
1552+
1553+ toJSON (OrFilter filters cache) =
1554+ object ["or" .=
1555+ object [ "filters" .= fmap toJSON filters
1556+ , "_cache" .= cache]]
1557+
1558+ toJSON (NotFilter notFilter cache) =
1559+ object ["not" .=
1560+ object ["filter" .= notFilter
1561+ , "_cache" .= cache]]
1562+
1563+ toJSON (IdentityFilter) =
1564+ object ["match_all" .= object []]
1565+
1566+ toJSON (TermFilter (Term termFilterField termFilterValue) cache) =
1567+ object ["term" .= object base]
1568+ where base = [termFilterField .= termFilterValue,
1569+ "_cache" .= cache]
1570+
1571+ toJSON (ExistsFilter (FieldName fieldName)) =
1572+ object ["exists" .= object
1573+ ["field" .= fieldName]]
1574+
1575+ toJSON (BoolFilter boolMatch) =
1576+ object ["bool" .= boolMatch]
1577+
1578+ toJSON (GeoBoundingBoxFilter bbConstraint) =
1579+ object ["geo_bounding_box" .= bbConstraint]
1580+
1581+ toJSON (GeoDistanceFilter (GeoPoint (FieldName distanceGeoField) geoDistLatLon)
1582+ distance distanceType optimizeBbox cache) =
1583+ object ["geo_distance" .=
1584+ object ["distance" .= distance
1585+ , "distance_type" .= distanceType
1586+ , "optimize_bbox" .= optimizeBbox
1587+ , distanceGeoField .= geoDistLatLon
1588+ , "_cache" .= cache]]
1589+
1590+ toJSON (GeoDistanceRangeFilter (GeoPoint (FieldName gddrField) drLatLon)
1591+ (DistanceRange geoDistRangeDistFrom drDistanceTo)) =
1592+ object ["geo_distance_range" .=
1593+ object ["from" .= geoDistRangeDistFrom
1594+ , "to" .= drDistanceTo
1595+ , gddrField .= drLatLon]]
1596+
1597+ toJSON (GeoPolygonFilter (FieldName geoPolygonFilterField) latLons) =
1598+ object ["geo_polygon" .=
1599+ object [geoPolygonFilterField .=
1600+ object ["points" .= fmap toJSON latLons]]]
1601+
1602+ toJSON (IdsFilter (MappingName mappingName) values) =
1603+ object ["ids" .=
1604+ object ["type" .= mappingName
1605+ , "values" .= fmap unpackId values]]
1606+
1607+ toJSON (LimitFilter limit) =
1608+ object ["limit" .= object ["value" .= limit]]
1609+
1610+ toJSON (MissingFilter (FieldName fieldName) (Existence existence) (NullValue nullValue)) =
1611+ object ["missing" .=
1612+ object ["field" .= fieldName
1613+ , "existence" .= existence
1614+ , "null_value" .= nullValue]]
1615+
1616+ toJSON (PrefixFilter (FieldName fieldName) fieldValue cache) =
1617+ object ["prefix" .=
1618+ object [fieldName .= fieldValue
1619+ , "_cache" .= cache]]
1620+
1621+ toJSON (QueryFilter query False) =
1622+ object ["query" .= toJSON query ]
1623+ toJSON (QueryFilter query True) =
1624+ object ["fquery" .=
1625+ object [ "query" .= toJSON query
1626+ , "_cache" .= True ]]
1627+
1628+ toJSON (RangeFilter (FieldName fieldName) rangeValue rangeExecution cache) =
1629+ object ["range" .=
1630+ object [ fieldName .= object (rangeValueToPair rangeValue)
1631+ , "execution" .= rangeExecution
1632+ , "_cache" .= cache]]
1633+
1634+ toJSON (RegexpFilter (FieldName fieldName)
1635+ (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
1636+ object ["regexp" .=
1637+ object [fieldName .=
1638+ object ["value" .= regexText
1639+ , "flags" .= flags]
1640+ , "_name" .= cacheName
1641+ , "_cache" .= cache
1642+ , "_cache_key" .= cacheKey]]
1643+
1644+instance ToJSON GeoPoint where
1645+ toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
1646+ object [ geoPointField .= geoPointLatLon ]
1647+
1648+
1649+instance ToJSON Query where
1650+ toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
1651+ object [ "term" .=
1652+ object [termQueryField .= object merged]]
1653+ where
1654+ base = [ "value" .= termQueryValue ]
1655+ boosted = maybe [] (return . ("boost" .=)) boost
1656+ merged = mappend base boosted
1657+
1658+ toJSON (TermsQuery terms) =
1659+ object [ "terms" .= object conjoined ]
1660+ where conjoined = [ getTermsField terms .=
1661+ fmap (toJSON . getTermValue) (toList terms)]
1662+ getTermsField ((Term f _ ) :| _) = f
1663+ getTermValue (Term _ v) = v
1664+ toJSON (IdsQuery idsQueryMappingName docIds) =
1665+ object [ "ids" .= object conjoined ]
1666+ where conjoined = [ "type" .= idsQueryMappingName
1667+ , "values" .= fmap toJSON docIds ]
1668+
1669+ toJSON (QueryQueryStringQuery qQueryStringQuery) =
1670+ object [ "query_string" .= qQueryStringQuery ]
1671+
1672+ toJSON (QueryMatchQuery matchQuery) =
1673+ object [ "match" .= matchQuery ]
1674+
1675+ toJSON (QueryMultiMatchQuery multiMatchQuery) =
1676+ toJSON multiMatchQuery
1677+
1678+ toJSON (QueryBoolQuery boolQuery) =
1679+ object [ "bool" .= boolQuery ]
1680+
1681+ toJSON (QueryBoostingQuery boostingQuery) =
1682+ object [ "boosting" .= boostingQuery ]
1683+
1684+ toJSON (QueryCommonTermsQuery commonTermsQuery) =
1685+ object [ "common" .= commonTermsQuery ]
1686+
1687+ toJSON (ConstantScoreFilter csFilter boost) =
1688+ object [ "constant_score" .= csFilter
1689+ , "boost" .= boost]
1690+
1691+ toJSON (ConstantScoreQuery query boost) =
1692+ object [ "constant_score" .= query
1693+ , "boost" .= boost]
1694+
1695+ toJSON (QueryDisMaxQuery disMaxQuery) =
1696+ object [ "dis_max" .= disMaxQuery ]
1697+
1698+ toJSON (QueryFilteredQuery qFilteredQuery) =
1699+ object [ "filtered" .= qFilteredQuery ]
1700+
1701+ toJSON (QueryFuzzyLikeThisQuery fuzzyQuery) =
1702+ object [ "fuzzy_like_this" .= fuzzyQuery ]
1703+
1704+ toJSON (QueryFuzzyLikeFieldQuery fuzzyFieldQuery) =
1705+ object [ "fuzzy_like_this_field" .= fuzzyFieldQuery ]
1706+
1707+ toJSON (QueryFuzzyQuery fuzzyQuery) =
1708+ object [ "fuzzy" .= fuzzyQuery ]
1709+
1710+ toJSON (QueryHasChildQuery childQuery) =
1711+ object [ "has_child" .= childQuery ]
1712+
1713+ toJSON (QueryHasParentQuery parentQuery) =
1714+ object [ "has_parent" .= parentQuery ]
1715+
1716+ toJSON (QueryIndicesQuery qIndicesQuery) =
1717+ object [ "indices" .= qIndicesQuery ]
1718+
1719+ toJSON (MatchAllQuery boost) =
1720+ object [ "match_all" .= omitNulls [ "boost" .= boost ] ]
1721+
1722+ toJSON (QueryMoreLikeThisQuery query) =
1723+ object [ "more_like_this" .= query ]
1724+
1725+ toJSON (QueryMoreLikeThisFieldQuery query) =
1726+ object [ "more_like_this_field" .= query ]
1727+
1728+ toJSON (QueryNestedQuery query) =
1729+ object [ "nested" .= query ]
1730+
1731+ toJSON (QueryPrefixQuery query) =
1732+ object [ "prefix" .= query ]
1733+
1734+ toJSON (QueryRangeQuery query) =
1735+ object [ "range" .= query ]
1736+
1737+ toJSON (QueryRegexpQuery query) =
1738+ object [ "regexp" .= query ]
1739+
1740+ toJSON (QuerySimpleQueryStringQuery query) =
1741+ object [ "simple_query_string" .= query ]
1742+
1743+
1744+omitNulls :: [(Text, Value)] -> Value
1745+omitNulls = object . filter notNull where
1746+ notNull (_, Null) = False
1747+ notNull (_, Array a) = (not . V.null) a
1748+ notNull _ = True
1749+
1750+
1751+instance ToJSON SimpleQueryStringQuery where
1752+ toJSON SimpleQueryStringQuery {..} =
1753+ omitNulls (base ++ maybeAdd)
1754+ where base = [ "query" .= simpleQueryStringQuery ]
1755+ maybeAdd = [ "fields" .= simpleQueryStringField
1756+ , "default_operator" .= simpleQueryStringOperator
1757+ , "analyzer" .= simpleQueryStringAnalyzer
1758+ , "flags" .= simpleQueryStringFlags
1759+ , "lowercase_expanded_terms" .= simpleQueryStringLowercaseExpanded
1760+ , "locale" .= simpleQueryStringLocale ]
1761+
1762+
1763+instance ToJSON FieldOrFields where
1764+ toJSON (FofField fieldName) =
1765+ toJSON fieldName
1766+ toJSON (FofFields fieldNames) =
1767+ toJSON fieldNames
1768+
1769+instance ToJSON SimpleQueryFlag where
1770+ toJSON SimpleQueryAll = "ALL"
1771+ toJSON SimpleQueryNone = "NONE"
1772+ toJSON SimpleQueryAnd = "AND"
1773+ toJSON SimpleQueryOr = "OR"
1774+ toJSON SimpleQueryPrefix = "PREFIX"
1775+ toJSON SimpleQueryPhrase = "PHRASE"
1776+ toJSON SimpleQueryPrecedence = "PRECEDENCE"
1777+ toJSON SimpleQueryEscape = "ESCAPE"
1778+ toJSON SimpleQueryWhitespace = "WHITESPACE"
1779+ toJSON SimpleQueryFuzzy = "FUZZY"
1780+ toJSON SimpleQueryNear = "NEAR"
1781+ toJSON SimpleQuerySlop = "SLOP"
1782+
1783+
1784+instance ToJSON RegexpQuery where
1785+ toJSON (RegexpQuery (FieldName rqQueryField)
1786+ (Regexp regexpQueryQuery) rqQueryFlags
1787+ rqQueryBoost) =
1788+ object [ rqQueryField .= omitNulls base ]
1789+ where base = [ "value" .= regexpQueryQuery
1790+ , "flags" .= rqQueryFlags
1791+ , "boost" .= rqQueryBoost ]
1792+
1793+
1794+instance ToJSON QueryStringQuery where
1795+ toJSON (QueryStringQuery qsQueryString
1796+ qsDefaultField qsOperator
1797+ qsAnalyzer qsAllowWildcard
1798+ qsLowercaseExpanded qsEnablePositionIncrements
1799+ qsFuzzyMaxExpansions qsFuzziness
1800+ qsFuzzyPrefixLength qsPhraseSlop
1801+ qsBoost qsAnalyzeWildcard
1802+ qsGeneratePhraseQueries qsMinimumShouldMatch
1803+ qsLenient qsLocale) =
1804+ omitNulls base
1805+ where
1806+ base = [ "query" .= qsQueryString
1807+ , "default_field" .= qsDefaultField
1808+ , "default_operator" .= qsOperator
1809+ , "analyzer" .= qsAnalyzer
1810+ , "allow_leading_wildcard" .= qsAllowWildcard
1811+ , "lowercase_expanded_terms" .= qsLowercaseExpanded
1812+ , "enable_position_increments" .= qsEnablePositionIncrements
1813+ , "fuzzy_max_expansions" .= qsFuzzyMaxExpansions
1814+ , "fuzziness" .= qsFuzziness
1815+ , "fuzzy_prefix_length" .= qsFuzzyPrefixLength
1816+ , "phrase_slop" .= qsPhraseSlop
1817+ , "boost" .= qsBoost
1818+ , "analyze_wildcard" .= qsAnalyzeWildcard
1819+ , "auto_generate_phrase_queries" .= qsGeneratePhraseQueries
1820+ , "minimum_should_match" .= qsMinimumShouldMatch
1821+ , "lenient" .= qsLenient
1822+ , "locale" .= qsLocale ]
1823+
1824+
1825+instance ToJSON RangeQuery where
1826+ toJSON (RangeQuery (FieldName fieldName) range boost) =
1827+ object [ fieldName .= conjoined ]
1828+ where conjoined = [ "boost" .= boost ] ++ (rangeValueToPair range)
1829+
1830+
1831+instance ToJSON PrefixQuery where
1832+ toJSON (PrefixQuery (FieldName fieldName) queryValue boost) =
1833+ object [ fieldName .= omitNulls base ]
1834+ where base = [ "value" .= queryValue
1835+ , "boost" .= boost ]
1836+
1837+
1838+instance ToJSON NestedQuery where
1839+ toJSON (NestedQuery nqPath nqScoreType nqQuery) =
1840+ object [ "path" .= nqPath
1841+ , "score_mode" .= nqScoreType
1842+ , "query" .= nqQuery ]
1843+
1844+
1845+instance ToJSON MoreLikeThisFieldQuery where
1846+ toJSON (MoreLikeThisFieldQuery text (FieldName fieldName)
1847+ percent mtf mqt stopwords mindf maxdf
1848+ minwl maxwl boostTerms boost analyzer) =
1849+ object [ fieldName .= omitNulls base ]
1850+ where base = [ "like_text" .= text
1851+ , "percent_terms_to_match" .= percent
1852+ , "min_term_freq" .= mtf
1853+ , "max_query_terms" .= mqt
1854+ , "stop_words" .= stopwords
1855+ , "min_doc_freq" .= mindf
1856+ , "max_doc_freq" .= maxdf
1857+ , "min_word_length" .= minwl
1858+ , "max_word_length" .= maxwl
1859+ , "boost_terms" .= boostTerms
1860+ , "boost" .= boost
1861+ , "analyzer" .= analyzer ]
1862+
1863+
1864+instance ToJSON MoreLikeThisQuery where
1865+ toJSON (MoreLikeThisQuery text fields percent
1866+ mtf mqt stopwords mindf maxdf
1867+ minwl maxwl boostTerms boost analyzer) =
1868+ omitNulls base
1869+ where base = [ "like_text" .= text
1870+ , "fields" .= fields
1871+ , "percent_terms_to_match" .= percent
1872+ , "min_term_freq" .= mtf
1873+ , "max_query_terms" .= mqt
1874+ , "stop_words" .= stopwords
1875+ , "min_doc_freq" .= mindf
1876+ , "max_doc_freq" .= maxdf
1877+ , "min_word_length" .= minwl
1878+ , "max_word_length" .= maxwl
1879+ , "boost_terms" .= boostTerms
1880+ , "boost" .= boost
1881+ , "analyzer" .= analyzer ]
1882+
1883+
1884+instance ToJSON IndicesQuery where
1885+ toJSON (IndicesQuery indices query noMatch) =
1886+ omitNulls [ "indices" .= indices
1887+ , "no_match_query" .= noMatch
1888+ , "query" .= query ]
1889+
1890+
1891+instance ToJSON HasParentQuery where
1892+ toJSON (HasParentQuery queryType query scoreType) =
1893+ omitNulls [ "parent_type" .= queryType
1894+ , "score_type" .= scoreType
1895+ , "query" .= query ]
1896+
1897+
1898+instance ToJSON HasChildQuery where
1899+ toJSON (HasChildQuery queryType query scoreType) =
1900+ omitNulls [ "query" .= query
1901+ , "score_type" .= scoreType
1902+ , "type" .= queryType ]
1903+
1904+
1905+instance ToJSON FuzzyQuery where
1906+ toJSON (FuzzyQuery (FieldName fieldName) queryText
1907+ prefixLength maxEx fuzziness boost) =
1908+ object [ fieldName .= omitNulls base ]
1909+ where base = [ "value" .= queryText
1910+ , "fuzziness" .= fuzziness
1911+ , "prefix_length" .= prefixLength
1912+ , "boost" .= boost
1913+ , "max_expansions" .= maxEx ]
1914+
1915+
1916+instance ToJSON FuzzyLikeFieldQuery where
1917+ toJSON (FuzzyLikeFieldQuery (FieldName fieldName)
1918+ fieldText maxTerms ignoreFreq fuzziness prefixLength
1919+ boost analyzer) =
1920+ object [ fieldName .=
1921+ omitNulls [ "like_text" .= fieldText
1922+ , "max_query_terms" .= maxTerms
1923+ , "ignore_tf" .= ignoreFreq
1924+ , "fuzziness" .= fuzziness
1925+ , "prefix_length" .= prefixLength
1926+ , "analyzer" .= analyzer
1927+ , "boost" .= boost ]]
1928+
1929+
1930+instance ToJSON FuzzyLikeThisQuery where
1931+ toJSON (FuzzyLikeThisQuery fields text maxTerms
1932+ ignoreFreq fuzziness prefixLength boost analyzer) =
1933+ omitNulls base
1934+ where base = [ "fields" .= fields
1935+ , "like_text" .= text
1936+ , "max_query_terms" .= maxTerms
1937+ , "ignore_tf" .= ignoreFreq
1938+ , "fuzziness" .= fuzziness
1939+ , "prefix_length" .= prefixLength
1940+ , "analyzer" .= analyzer
1941+ , "boost" .= boost ]
1942+
1943+
1944+instance ToJSON FilteredQuery where
1945+ toJSON (FilteredQuery query fFilter) =
1946+ object [ "query" .= query
1947+ , "filter" .= fFilter ]
1948+
1949+
1950+instance ToJSON DisMaxQuery where
1951+ toJSON (DisMaxQuery queries tiebreaker boost) =
1952+ omitNulls base
1953+ where base = [ "queries" .= queries
1954+ , "boost" .= boost
1955+ , "tie_breaker" .= tiebreaker ]
1956+
1957+
1958+instance ToJSON CommonTermsQuery where
1959+ toJSON (CommonTermsQuery (FieldName fieldName)
1960+ (QueryString query) cf lfo hfo msm
1961+ boost analyzer disableCoord) =
1962+ object [fieldName .= omitNulls base ]
1963+ where base = [ "query" .= query
1964+ , "cutoff_frequency" .= cf
1965+ , "low_freq_operator" .= lfo
1966+ , "minimum_should_match" .= msm
1967+ , "boost" .= boost
1968+ , "analyzer" .= analyzer
1969+ , "disable_coord" .= disableCoord
1970+ , "high_freq_operator" .= hfo ]
1971+
1972+
1973+instance ToJSON CommonMinimumMatch where
1974+ toJSON (CommonMinimumMatch mm) = toJSON mm
1975+ toJSON (CommonMinimumMatchHighLow (MinimumMatchHighLow lowF highF)) =
1976+ object [ "low_freq" .= lowF
1977+ , "high_freq" .= highF ]
1978+
1979+instance ToJSON BoostingQuery where
1980+ toJSON (BoostingQuery bqPositiveQuery bqNegativeQuery bqNegativeBoost) =
1981+ object [ "positive" .= bqPositiveQuery
1982+ , "negative" .= bqNegativeQuery
1983+ , "negative_boost" .= bqNegativeBoost ]
1984+
1985+
1986+instance ToJSON BoolQuery where
1987+ toJSON (BoolQuery mustM notM shouldM bqMin boost disableCoord) =
1988+ omitNulls base
1989+ where base = [ "must" .= mustM
1990+ , "must_not" .= notM
1991+ , "should" .= shouldM
1992+ , "minimum_should_match" .= bqMin
1993+ , "boost" .= boost
1994+ , "disable_coord" .= disableCoord ]
1995+
1996+
1997+instance ToJSON MatchQuery where
1998+ toJSON (MatchQuery (FieldName fieldName)
1999+ (QueryString mqQueryString) booleanOperator
2000+ zeroTermsQuery cutoffFrequency matchQueryType
2001+ analyzer maxExpansions lenient boost) =
2002+ object [ fieldName .= omitNulls base ]
2003+ where base = [ "query" .= mqQueryString
2004+ , "operator" .= booleanOperator
2005+ , "zero_terms_query" .= zeroTermsQuery
2006+ , "cutoff_frequency" .= cutoffFrequency
2007+ , "type" .= matchQueryType
2008+ , "analyzer" .= analyzer
2009+ , "max_expansions" .= maxExpansions
2010+ , "lenient" .= lenient
2011+ , "boost" .= boost ]
2012+
2013+
2014+instance ToJSON MultiMatchQuery where
2015+ toJSON (MultiMatchQuery fields (QueryString query) boolOp
2016+ ztQ tb mmqt cf analyzer maxEx lenient) =
2017+ object ["multi_match" .= omitNulls base]
2018+ where base = [ "fields" .= fmap toJSON fields
2019+ , "query" .= query
2020+ , "operator" .= boolOp
2021+ , "zero_terms_query" .= ztQ
2022+ , "tiebreaker" .= tb
2023+ , "type" .= mmqt
2024+ , "cutoff_frequency" .= cf
2025+ , "analyzer" .= analyzer
2026+ , "max_expansions" .= maxEx
2027+ , "lenient" .= lenient ]
2028+
2029+
2030+instance ToJSON MultiMatchQueryType where
2031+ toJSON MultiMatchBestFields = "best_fields"
2032+ toJSON MultiMatchMostFields = "most_fields"
2033+ toJSON MultiMatchCrossFields = "cross_fields"
2034+ toJSON MultiMatchPhrase = "phrase"
2035+ toJSON MultiMatchPhrasePrefix = "phrase_prefix"
2036+
2037+instance ToJSON BooleanOperator where
2038+ toJSON And = String "and"
2039+ toJSON Or = String "or"
2040+
2041+instance ToJSON ZeroTermsQuery where
2042+ toJSON ZeroTermsNone = String "none"
2043+ toJSON ZeroTermsAll = String "all"
2044+
2045+instance ToJSON MatchQueryType where
2046+ toJSON MatchPhrase = "phrase"
2047+ toJSON MatchPhrasePrefix = "phrase_prefix"
2048+
2049+instance ToJSON FieldName where
2050+ toJSON (FieldName fieldName) = String fieldName
2051+
2052+instance ToJSON ReplicaCount
2053+instance ToJSON ShardCount
2054+instance ToJSON CutoffFrequency
2055+instance ToJSON Analyzer
2056+instance ToJSON MaxExpansions
2057+instance ToJSON Lenient
2058+instance ToJSON Boost
2059+instance ToJSON Version
2060+instance ToJSON Tiebreaker
2061+instance ToJSON MinimumMatch
2062+instance ToJSON DisableCoord
2063+instance ToJSON PrefixLength
2064+instance ToJSON Fuzziness
2065+instance ToJSON IgnoreTermFrequency
2066+instance ToJSON MaxQueryTerms
2067+instance ToJSON TypeName
2068+instance ToJSON IndexName
2069+instance ToJSON TemplateName
2070+instance ToJSON TemplatePattern
2071+instance ToJSON BoostTerms
2072+instance ToJSON MaxWordLength
2073+instance ToJSON MinWordLength
2074+instance ToJSON MaxDocFrequency
2075+instance ToJSON MinDocFrequency
2076+instance ToJSON PhraseSlop
2077+instance ToJSON StopWord
2078+instance ToJSON QueryPath
2079+instance ToJSON MinimumTermFrequency
2080+instance ToJSON PercentMatch
2081+instance ToJSON MappingName
2082+instance ToJSON DocId
2083+instance ToJSON QueryString
2084+instance ToJSON AllowLeadingWildcard
2085+instance ToJSON LowercaseExpanded
2086+instance ToJSON AnalyzeWildcard
2087+instance ToJSON GeneratePhraseQueries
2088+instance ToJSON Locale
2089+instance ToJSON EnablePositionIncrements
2090+instance FromJSON Version
2091+instance FromJSON IndexName
2092+instance FromJSON MappingName
2093+instance FromJSON DocId
2094+
2095+
2096+instance FromJSON Status where
2097+ parseJSON (Object v) = Status <$>
2098+ v .:? "ok" <*>
2099+ v .: "status" <*>
2100+ v .: "name" <*>
2101+ v .: "version" <*>
2102+ v .: "tagline"
2103+ parseJSON _ = empty
2104+
2105+
2106+instance ToJSON IndexSettings where
2107+ toJSON (IndexSettings s r) = object ["settings" .=
2108+ object ["index" .=
2109+ object ["number_of_shards" .= s, "number_of_replicas" .= r]
2110+ ]
2111+ ]
2112+
2113+instance ToJSON IndexTemplate where
2114+ toJSON (IndexTemplate p s m) = merge
2115+ (object [ "template" .= p
2116+ , "mappings" .= foldl' merge (object []) m
2117+ ])
2118+ (toJSON s)
2119+ where
2120+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
2121+ merge o Null = o
2122+ merge _ _ = undefined
2123+
2124+instance (FromJSON a) => FromJSON (EsResult a) where
2125+ parseJSON jsonVal@(Object v) = do
2126+ found <- v .:? "found" .!= False
2127+ fr <- if found
2128+ then parseJSON jsonVal
2129+ else return Nothing
2130+ EsResult <$> v .: "_index" <*>
2131+ v .: "_type" <*>
2132+ v .: "_id" <*>
2133+ pure fr
2134+ parseJSON _ = empty
2135+
2136+instance (FromJSON a) => FromJSON (EsResultFound a) where
2137+ parseJSON (Object v) = EsResultFound <$>
2138+ v .: "_version" <*>
2139+ v .: "_source"
2140+ parseJSON _ = empty
2141+
2142+instance FromJSON EsError where
2143+ parseJSON (Object v) = EsError <$>
2144+ v .: "status" <*>
2145+ v .: "error"
2146+ parseJSON _ = empty
2147+
2148+instance ToJSON Search where
2149+ toJSON (Search query sFilter sort searchAggs highlight sTrackSortScores sFrom sSize _ sFields sSource) =
2150+ omitNulls [ "query" .= query
2151+ , "filter" .= sFilter
2152+ , "sort" .= sort
2153+ , "aggregations" .= searchAggs
2154+ , "highlight" .= highlight
2155+ , "from" .= sFrom
2156+ , "size" .= sSize
2157+ , "track_scores" .= sTrackSortScores
2158+ , "fields" .= sFields
2159+ , "_source" .= sSource]
2160+
4892161
4902162instance ToJSON Source where
4912163 toJSON NoSource = toJSON False
4922164 toJSON (SourcePatterns patterns) = toJSON patterns
493− toJSON (SourceIncludeExclude incl excl) = object [ "includes" .= incl, "excludes" .= excl ]
494−
495−data PatternOrPatterns = PopPattern Pattern
496− | PopPatterns [Pattern] deriving (Eq, Read, Show)
2165+ toJSON (SourceIncludeExclude incl excl) = object [ "include" .= incl, "exclude" .= excl ]
4972166
4982167instance ToJSON PatternOrPatterns where
4992168 toJSON (PopPattern pattern) = toJSON pattern
5002169 toJSON (PopPatterns patterns) = toJSON patterns
5012170
502−data Include = Include [Pattern] deriving (Eq, Read, Show)
503−data Exclude = Exclude [Pattern] deriving (Eq, Read, Show)
504−
5052171instance ToJSON Include where
5062172 toJSON (Include patterns) = toJSON patterns
5072173
5082174instance ToJSON Exclude where
5092175 toJSON (Exclude patterns) = toJSON patterns
5102176
511−newtype Pattern = Pattern Text deriving (Eq, Read, Show)
512−
5132177instance ToJSON Pattern where
5142178 toJSON (Pattern pattern) = toJSON pattern
5152179
516−data SearchResult a =
517− SearchResult { took :: Int
518− , timedOut :: Bool
519− , shards :: ShardResult
520− , searchHits :: SearchHits a
521− , 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)
2180+
2181+instance ToJSON FieldHighlight where
2182+ toJSON (FieldHighlight (FieldName fName) (Just fSettings)) =
2183+ object [ fName .= fSettings ]
2184+ toJSON (FieldHighlight (FieldName fName) Nothing) =
2185+ object [ fName .= emptyObject ]
2186+
2187+instance ToJSON Highlights where
2188+ toJSON (Highlights global fields) =
2189+ omitNulls (("fields" .= fields)
2190+ : highlightSettingsPairs global)
2191+
2192+instance ToJSON HighlightSettings where
2193+ toJSON hs = omitNulls (highlightSettingsPairs (Just hs))
2194+
2195+highlightSettingsPairs :: Maybe HighlightSettings -> [Pair]
2196+highlightSettingsPairs Nothing = []
2197+highlightSettingsPairs (Just (Plain plh)) = plainHighPairs (Just plh)
2198+highlightSettingsPairs (Just (Postings ph)) = postHighPairs (Just ph)
2199+highlightSettingsPairs (Just (FastVector fvh)) = fastVectorHighPairs (Just fvh)
2200+
2201+
2202+plainHighPairs :: Maybe PlainHighlight -> [Pair]
2203+plainHighPairs Nothing = []
2204+plainHighPairs (Just (PlainHighlight plCom plNonPost)) =
2205+ [ "type" .= String "plain"]
2206+ ++ commonHighlightPairs plCom
2207+ ++ nonPostingsToPairs plNonPost
2208+
2209+postHighPairs :: Maybe PostingsHighlight -> [Pair]
2210+postHighPairs Nothing = []
2211+postHighPairs (Just (PostingsHighlight pCom)) =
2212+ ("type" .= String "postings")
2213+ : commonHighlightPairs pCom
2214+
2215+fastVectorHighPairs :: Maybe FastVectorHighlight -> [Pair]
2216+fastVectorHighPairs Nothing = []
2217+fastVectorHighPairs (Just
2218+ (FastVectorHighlight fvCom fvNonPostSettings fvBoundChars
2219+ fvBoundMaxScan fvFragOff fvMatchedFields
2220+ fvPhraseLim)) =
2221+ [ "type" .= String "fvh"
2222+ , "boundary_chars" .= fvBoundChars
2223+ , "boundary_max_scan" .= fvBoundMaxScan
2224+ , "fragment_offset" .= fvFragOff
2225+ , "matched_fields" .= fvMatchedFields
2226+ , "phraseLimit" .= fvPhraseLim]
2227+ ++ commonHighlightPairs fvCom
2228+ ++ nonPostingsToPairs fvNonPostSettings
2229+
2230+commonHighlightPairs :: Maybe CommonHighlight -> [Pair]
2231+commonHighlightPairs Nothing = []
2232+commonHighlightPairs (Just (CommonHighlight chScore chForceSource chTag chEncoder
2233+ chNoMatchSize chHighlightQuery
2234+ chRequireFieldMatch)) =
2235+ [ "order" .= chScore
2236+ , "force_source" .= chForceSource
2237+ , "encoder" .= chEncoder
2238+ , "no_match_size" .= chNoMatchSize
2239+ , "highlight_query" .= chHighlightQuery
2240+ , "require_fieldMatch" .= chRequireFieldMatch]
2241+ ++ highlightTagToPairs chTag
2242+
2243+
2244+nonPostingsToPairs :: Maybe NonPostings -> [Pair]
2245+nonPostingsToPairs Nothing = []
2246+nonPostingsToPairs (Just (NonPostings npFragSize npNumOfFrags)) =
2247+ [ "fragment_size" .= npFragSize
2248+ , "number_of_fragments" .= npNumOfFrags]
2249+
2250+
2251+
2252+instance ToJSON HighlightEncoder where
2253+ toJSON DefaultEncoder = String "default"
2254+ toJSON HTMLEncoder = String "html"
2255+
2256+highlightTagToPairs :: Maybe HighlightTag -> [Pair]
2257+highlightTagToPairs (Just (TagSchema _)) = [ "scheme" .= String "default"]
2258+highlightTagToPairs (Just (CustomTags (pre, post))) = [ "pre_tags" .= pre
2259+ , "post_tags" .= post]
2260+highlightTagToPairs Nothing = []
2261+
2262+instance ToJSON SortSpec where
2263+ toJSON (DefaultSortSpec
2264+ (DefaultSort (FieldName dsSortFieldName) dsSortOrder dsIgnoreUnmapped
2265+ dsSortMode dsMissingSort dsNestedFilter)) =
2266+ object [dsSortFieldName .= omitNulls base] where
2267+ base = [ "order" .= dsSortOrder
2268+ , "ignore_unmapped" .= dsIgnoreUnmapped
2269+ , "mode" .= dsSortMode
2270+ , "missing" .= dsMissingSort
2271+ , "nested_filter" .= dsNestedFilter ]
2272+
2273+ toJSON (GeoDistanceSortSpec gdsSortOrder (GeoPoint (FieldName field) gdsLatLon) units) =
2274+ object [ "unit" .= units
2275+ , field .= gdsLatLon
2276+ , "order" .= gdsSortOrder ]
2277+
2278+
2279+instance ToJSON SortOrder where
2280+ toJSON Ascending = String "asc"
2281+ toJSON Descending = String "desc"
2282+
2283+
2284+instance ToJSON SortMode where
2285+ toJSON SortMin = String "min"
2286+ toJSON SortMax = String "max"
2287+ toJSON SortSum = String "sum"
2288+ toJSON SortAvg = String "avg"
2289+
2290+
2291+instance ToJSON Missing where
2292+ toJSON LastMissing = String "_last"
2293+ toJSON FirstMissing = String "_first"
2294+ toJSON (CustomMissing txt) = String txt
2295+
2296+
2297+instance ToJSON ScoreType where
2298+ toJSON ScoreTypeMax = "max"
2299+ toJSON ScoreTypeAvg = "avg"
2300+ toJSON ScoreTypeSum = "sum"
2301+ toJSON ScoreTypeNone = "none"
2302+
2303+
2304+instance ToJSON Distance where
2305+ toJSON (Distance dCoefficient dUnit) =
2306+ String boltedTogether where
2307+ coefText = showText dCoefficient
2308+ (String unitText) = toJSON dUnit
2309+ boltedTogether = mappend coefText unitText
2310+
2311+
2312+instance ToJSON DistanceUnit where
2313+ toJSON Miles = String "mi"
2314+ toJSON Yards = String "yd"
2315+ toJSON Feet = String "ft"
2316+ toJSON Inches = String "in"
2317+ toJSON Kilometers = String "km"
2318+ toJSON Meters = String "m"
2319+ toJSON Centimeters = String "cm"
2320+ toJSON Millimeters = String "mm"
2321+ toJSON NauticalMiles = String "nmi"
2322+
2323+
2324+instance ToJSON DistanceType where
2325+ toJSON Arc = String "arc"
2326+ toJSON SloppyArc = String "sloppy_arc"
2327+ toJSON Plane = String "plane"
2328+
2329+
2330+instance ToJSON OptimizeBbox where
2331+ toJSON NoOptimizeBbox = String "none"
2332+ toJSON (OptimizeGeoFilterType gft) = toJSON gft
2333+
2334+
2335+instance ToJSON GeoBoundingBoxConstraint where
2336+ toJSON (GeoBoundingBoxConstraint
2337+ (FieldName gbbcGeoBBField) gbbcConstraintBox cache type') =
2338+ object [gbbcGeoBBField .= gbbcConstraintBox
2339+ , "_cache" .= cache
2340+ , "type" .= type']
2341+
2342+
2343+instance ToJSON GeoFilterType where
2344+ toJSON GeoFilterMemory = String "memory"
2345+ toJSON GeoFilterIndexed = String "indexed"
2346+
2347+
2348+instance ToJSON GeoBoundingBox where
2349+ toJSON (GeoBoundingBox gbbTopLeft gbbBottomRight) =
2350+ object ["top_left" .= gbbTopLeft
2351+ , "bottom_right" .= gbbBottomRight]
2352+
2353+
2354+instance ToJSON LatLon where
2355+ toJSON (LatLon lLat lLon) =
2356+ object ["lat" .= lLat
2357+ , "lon" .= lLon]
2358+
2359+
2360+-- index for smaller ranges, fielddata for longer ranges
2361+instance ToJSON RangeExecution where
2362+ toJSON RangeExecutionIndex = "index"
2363+ toJSON RangeExecutionFielddata = "fielddata"
2364+
2365+
2366+instance ToJSON RegexpFlags where
2367+ toJSON AllRegexpFlags = String "ALL"
2368+ toJSON NoRegexpFlags = String "NONE"
2369+ toJSON (SomeRegexpFlags (h :| fs)) = String $ T.intercalate "|" flagStrs
2370+ where flagStrs = map flagStr . nub $ h:fs
2371+ flagStr AnyString = "ANYSTRING"
2372+ flagStr Automaton = "AUTOMATON"
2373+ flagStr Complement = "COMPLEMENT"
2374+ flagStr Empty = "EMPTY"
2375+ flagStr Intersection = "INTERSECTION"
2376+ flagStr Interval = "INTERVAL"
2377+
2378+instance ToJSON Term where
2379+ toJSON (Term field value) = object ["term" .= object
2380+ [field .= value]]
2381+
2382+
2383+instance ToJSON BoolMatch where
2384+ toJSON (MustMatch term cache) = object ["must" .= term,
2385+ "_cache" .= cache]
2386+ toJSON (MustNotMatch term cache) = object ["must_not" .= term,
2387+ "_cache" .= cache]
2388+ toJSON (ShouldMatch terms cache) = object ["should" .= fmap toJSON terms,
2389+ "_cache" .= cache]
2390+
5282391
5292392instance (FromJSON a) => FromJSON (SearchResult a) where
5302393 parseJSON (Object v) = SearchResult <$>
@@ -533,10 +2396,52 @@ instance (FromJSON a) => FromJSON (SearchResult a) where
5332396 v .: "_shards" <*>
5342397 v .: "hits" <*>
5352398 v .:? "aggregations" <*>
536− v .:? "_scroll_id" <*>
537− v .:? "suggest"
2399+ v .:? "_scroll_id"
2400+ parseJSON _ = empty
2401+
2402+instance (FromJSON a) => FromJSON (SearchHits a) where
2403+ parseJSON (Object v) = SearchHits <$>
2404+ v .: "total" <*>
2405+ v .: "max_score" <*>
2406+ v .: "hits"
5382407 parseJSON _ = empty
5392408
540−newtype ScrollId =
541− ScrollId Text
542− deriving (Eq, Show, Ord, ToJSON, FromJSON)
2409+instance (FromJSON a) => FromJSON (Hit a) where
2410+ parseJSON (Object v) = Hit <$>
2411+ v .: "_index" <*>
2412+ v .: "_type" <*>
2413+ v .: "_id" <*>
2414+ v .: "_score" <*>
2415+ v .:? "_source" <*>
2416+ v .:? "highlight"
2417+ parseJSON _ = empty
2418+
2419+instance FromJSON ShardResult where
2420+ parseJSON (Object v) = ShardResult <$>
2421+ v .: "total" <*>
2422+ v .: "successful" <*>
2423+ v .: "failed"
2424+ parseJSON _ = empty
2425+
2426+
2427+instance FromJSON DocVersion where
2428+ parseJSON v = do
2429+ i <- parseJSON v
2430+ maybe (fail "DocVersion out of range") return $ mkDocVersion i
2431+
2432+instance Bounded DocVersion where
2433+ minBound = DocVersion 1
2434+ maxBound = DocVersion 9200000000000000000 -- 9.2e+18
2435+
2436+instance Enum DocVersion where
2437+ succ x
2438+ | x /= maxBound = DocVersion (succ $ docVersionNumber x)
2439+ | otherwise = succError "DocVersion"
2440+ pred x
2441+ | x /= minBound = DocVersion (pred $ docVersionNumber x)
2442+ | otherwise = predError "DocVersion"
2443+ toEnum i =
2444+ fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
2445+ fromEnum = docVersionNumber
2446+ enumFrom = boundedEnumFrom
2447+ enumFromThen = boundedEnumFromThen