git.iko.soy/typeable/bloodhound
on master help sign in
Merged
commitcfc828afba6c6f8e9dd5e72b3ee6eff8918d6a03 authorChris Allen <cma@bitemyapp.com> dateSat 7 Apr 2018, 16:25 (9 years ago) parent80984f2e66814c
72 files changed+15494−14282
.gitignore+1 .hlint.yaml+66 .travis.yml+16−17 Makefile+43−10 bloodhound.cabal+52−17 changelog.md+14 examples/Tweet.hs+1−4 examples/package.yaml−1 src/Bloodhound/Import.hs+81 src/Database/Bloodhound/Common/Script.hs+220 src/Database/V1/Bloodhound/Client.hs+13−9 src/Database/V1/Bloodhound/Internal/Aggregation.hs+416 src/Database/V1/Bloodhound/Internal/Client.hs+2341 src/Database/V1/Bloodhound/Internal/Highlight.hs+144 src/Database/V1/Bloodhound/Internal/Newtypes.hs+211 src/Database/V1/Bloodhound/Internal/Query.hs+1689 src/Database/V1/Bloodhound/Internal/Sort.hs+106 src/Database/V1/Bloodhound/Internal/StringlyTyped.hs+26 src/Database/V1/Bloodhound/Internal/Suggest.hs+252 src/Database/V1/Bloodhound/Types.hs+33−5161 src/Database/V1/Bloodhound/Types/Internal.hs−57 src/Database/V5/Bloodhound/Client.hs+44−18 src/Database/V5/Bloodhound/Internal/Aggregation.hs+467 src/Database/V5/Bloodhound/Internal/Analysis.hs+312 src/Database/V5/Bloodhound/Internal/Client.hs+2408 src/Database/V5/Bloodhound/Internal/Highlight.hs+164 src/Database/V5/Bloodhound/Internal/Newtypes.hs+234 src/Database/V5/Bloodhound/Internal/Query.hs+1597 src/Database/V5/Bloodhound/Internal/Sort.hs+107 src/Database/V5/Bloodhound/Internal/StringlyTyped.hs+51 src/Database/V5/Bloodhound/Internal/Suggest.hs+265 src/Database/V5/Bloodhound/Types.hs+95−5272 src/Database/V5/Bloodhound/Types/Internal.hs−57 stack-7.10.yaml+3 stack-7.8.yaml+3 stack-8.0.yaml+1 stack-8.2.yaml+1 tests/V1/Test/Aggregation.hs+132 tests/V1/Test/ApproxEq.hs+63 tests/V1/Test/BulkAPI.hs+77 tests/V1/Test/Common.hs+289 tests/V1/Test/Documents.hs+52 tests/V1/Test/Generators.hs+432 tests/V1/Test/Highlights.hs+32 tests/V1/Test/Import.hs+58 tests/V1/Test/Indices.hs+78 tests/V1/Test/JSON.hs+202 tests/V1/Test/Query.hs+115 tests/V1/Test/Snapshots.hs+201 tests/V1/Test/Sorting.hs+22 tests/V1/Test/SourceFiltering.hs+38 tests/V1/Test/Suggest.hs+22 tests/V1/Test/Templates.hs+26 tests/V1/tests.hs+37−1886 tests/V5/Test/Aggregation.hs+147 tests/V5/Test/ApproxEq.hs+63 tests/V5/Test/BulkAPI.hs+95 tests/V5/Test/Common.hs+292 tests/V5/Test/Documents.hs+51 tests/V5/Test/Generators.hs+536 tests/V5/Test/Highlights.hs+32 tests/V5/Test/Import.hs+58 tests/V5/Test/Indices.hs+152 tests/V5/Test/JSON.hs+213 tests/V5/Test/Query.hs+115 tests/V5/Test/Script.hs+36 tests/V5/Test/Snapshots.hs+201 tests/V5/Test/Sorting.hs+22 tests/V5/Test/SourceFiltering.hs+38 tests/V5/Test/Suggest.hs+23 tests/V5/Test/Templates.hs+26 tests/V5/tests.hs+41−1773
.gitignore+1
@@ -18,3 +18,4 @@ bloodhound.iml
1818.hgignore
1919examples/bloodhound-examples.cabal
2020/.ghc.environment.*
21+.hspec-failures
.hlint.yamlnew file+66
@@ -0,0 +1,66 @@
1+# HLint configuration file
2+# https://github.com/ndmitchell/hlint
3+##########################
4+
5+# This file contains a template configuration file, which is typically
6+# placed as .hlint.yaml in the root of your project
7+
8+
9+# Specify additional command line arguments
10+#
11+# - arguments: [--color, --cpp-simple, -XQuasiQuotes]
12+
13+
14+# Control which extensions/flags/modules/functions can be used
15+#
16+# - extensions:
17+# - default: false # all extension are banned by default
18+# - name: [PatternGuards, ViewPatterns] # only these listed extensions can be used
19+# - {name: CPP, within: CrossPlatform} # CPP can only be used in a given module
20+#
21+# - flags:
22+# - {name: -w, within: []} # -w is allowed nowhere
23+#
24+# - modules:
25+# - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set'
26+# - {name: Control.Arrow, within: []} # Certain modules are banned entirely
27+#
28+# - functions:
29+# - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules
30+
31+
32+# Add custom hints for this project
33+#
34+# Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar"
35+# - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x}
36+
37+
38+# Turn on hints that are off by default
39+#
40+# Ban "module X(module X) where", to require a real export list
41+# - warn: {name: Use explicit module export list}
42+#
43+# Replace a $ b $ c with a . b $ c
44+# - group: {name: dollar, enabled: true}
45+#
46+# Generalise map to fmap, ++ to <>
47+# - group: {name: generalise, enabled: true}
48+
49+
50+# Ignore some builtin hints
51+- ignore: {name: "Use <$>"}
52+- ignore: {name: "Use lambda-case"}
53+# When we don't use camelCase it's to match ES.
54+# Possibly this is a mistake.
55+- ignore: {name: "Use camelCase"}
56+- ignore: {name: "Eta reduce"}
57+# - ignore: {name: Use let}
58+# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
59+
60+
61+# Define some custom infix operators
62+# - fixity: infixr 3 ~^#^~
63+
64+
65+# To generate a suitable file for HLint do:
66+# $ hlint --default > .hlint.yaml
.travis.yml+16−17
@@ -8,21 +8,21 @@ addons:
88 - oracle-java8-installer
99
1010env:
11− global:
12− - JAVA_HOME=/usr/lib/jvm/java-8-oracle
13− matrix:
14− - GHCVER=7.8 ESVER=1.3.6 STACK_YAML=stack-7.8.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
15− - GHCVER=7.8 ESVER=1.4.1 STACK_YAML=stack-7.8.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
16− - GHCVER=7.10 ESVER=1.5.2 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
17− - GHCVER=7.10 ESVER=1.6.0 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
18− - GHCVER=7.10 ESVER=1.7.6 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
19− - GHCVER=8.0 ESVER=1.7.6 STACK_YAML=stack-8.0.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
20− - GHCVER=8.0 ESVER=5.0.2 STACK_YAML=stack-8.0.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
21− - GHCVER=8.2 ESVER=1.7.6 STACK_YAML=stack.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
22− - GHCVER=8.2 ESVER=5.0.2 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
23− - GHCVER=8.2 ESVER=5.5.0 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
24− allow_failures:
25− - GHCVER=8.2 ESVER=6.1.3 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
11+ global:
12+ - JAVA_HOME=/usr/lib/jvm/java-8-oracle
13+ matrix:
14+ - GHCVER=7.8 ESVER=1.3.6 STACK_YAML=stack-7.8.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
15+ - GHCVER=7.8 ESVER=1.4.1 STACK_YAML=stack-7.8.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
16+ - GHCVER=7.10 ESVER=1.5.2 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
17+ - GHCVER=7.10 ESVER=1.6.0 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
18+ - GHCVER=7.10 ESVER=1.7.6 STACK_YAML=stack-7.10.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
19+ - GHCVER=8.0 ESVER=1.7.6 STACK_YAML=stack-8.0.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
20+ - GHCVER=8.0 ESVER=5.0.2 STACK_YAML=stack-8.0.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
21+ - GHCVER=8.2 ESVER=1.7.6 STACK_YAML=stack.yaml ESFLAG=ES1 DLINK=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch
22+ - GHCVER=8.2 ESVER=5.0.2 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
23+ - GHCVER=8.2 ESVER=5.5.0 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
24+# allow_failures:
25+# - GHCVER=8.2 ESVER=6.1.3 STACK_YAML=stack.yaml ESFLAG=ES5 DLINK=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch ES_JAVA_OPTS="-Xms500m -Xmx750m"
2626
2727install:
2828 # stack
@@ -30,7 +30,6 @@ install:
3030 - travis_retry curl -L https://github.com/commercialhaskell/stack/releases/download/v1.6.1/stack-1.6.1-linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
3131 - export PATH=~/.local/bin:$PATH
3232 - stack --no-terminal --version
33−
3433 # elasticsearch
3534 - wget --no-check-certificate $DLINK-$ESVER.tar.gz
3635 - tar xzf elasticsearch-$ESVER.tar.gz
@@ -43,7 +42,7 @@ script:
4342 - stack update --no-terminal
4443 - stack build -j2 --fast --no-terminal
4544 - travis_wait 45 sleep 1800 &
46− - stack test --fast --no-terminal bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:$ESFLAG
45+ - stack test --fast --no-terminal bloodhound:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:$ESFLAG
4746
4847cache:
4948 timeout: 2000
Makefile+43−10
@@ -1,15 +1,45 @@
1+stack = STACK_YAML='stack.yaml' stack
2+
13build:
24 stack build
35
6+build-validate:
7+ stack build --fast --ghc-options '-Wall -Werror'
8+
9+ghci:
10+ stack ghci
11+
12+test: echo-warn
13+ stack test
14+
15+test-rerun: echo-warn
16+ stack test --test-arguments "-r"
17+
18+test-ghci:
19+ stack ghci bloodhound:test:bloodhound-tests
20+
21+ghcid:
22+ ghcid -c "$(stack) ghci bloodhound:lib --test --ghci-options='-fobject-code -fno-warn-unused-do-bind' --main-is bloodhound:test:bloodhound-tests"
23+
24+ghcid-validate:
25+ ghcid -c "$(stack) ghci bloodhound:lib --test --ghci-options='-Werror -fobject-code -fno-warn-unused-do-bind' --main-is bloodhound:test:bloodhound-tests"
26+
27+weeder:
28+ weeder . --build
29+
30+# hlint --default > .hlint.yaml
31+hlint:
32+ hlint .
33+
34+hlint-watch:
35+ sos src/ -c "hlint ." -p "src/(.*)\.hs"
36+
437mod-build:
538 stack build --ghc-options '+RTS -A128M -RTS'
639
740echo-warn:
841 echo "Make certain you have an elasticsearch instance on localhost:9200 !"
942
10−test: echo-warn
11− stack test
12−
13437.8-build:
1444 STACK_YAML="stack-7.8.yaml" stack build
1545
@@ -23,16 +53,22 @@ test: echo-warn
2353 STACK_YAML="stack-7.10.yaml" stack test
2454
25557.10-test-ES1:
26− STACK_YAML="stack-7.10.yaml" stack test --fast bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES1
56+ STACK_YAML="stack-7.10.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES1
2757
28587.10-test-ES5:
29− STACK_YAML="stack-7.10.yaml" stack test --fast bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES5
59+ STACK_YAML="stack-7.10.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES5
3060
31618.0-test-ES1:
32− STACK_YAML="stack.yaml" stack test --fast bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES1
62+ STACK_YAML="stack-8.0.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES1
3363
34648.0-test-ES5:
35− STACK_YAML="stack-8.0.yaml" stack test --fast bloodhound:tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES5
65+ STACK_YAML="stack-8.0.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES5
66+
67+8.2-test-ES1:
68+ STACK_YAML="stack.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES1
69+
70+8.2-test-ES5:
71+ STACK_YAML="stack.yaml" stack test --fast bloodhound:test:bloodhound-tests --test-arguments="--qc-max-success 500" --flag bloodhound:ES5
3672
37738.0-build:
3874 STACK_YAML="stack-8.0.yaml" stack build
@@ -44,8 +80,5 @@ module-touch:
4480 touch src/Database/V1/Bloodhound/Types.hs
4581 touch src/Database/V5/Bloodhound/Types.hs
4682
47−ghci:
48− stack ghci
49−
5083upload:
5184 stack upload --no-signature .
bloodhound.cabal+52−17
@@ -7,7 +7,7 @@ license: BSD3
77license-file: LICENSE
88author: Chris Allen
99maintainer: cma@bitemyapp.com
10−copyright: 2015, Chris Allen
10+copyright: 2018 Chris Allen
1111category: Database, Search
1212build-type: Simple
1313cabal-version: >=1.10
@@ -33,16 +33,35 @@ Flag ES5
3333
3434library
3535 ghc-options: -Wall
36− exposed-modules: Database.V1.Bloodhound
37− Database.V1.Bloodhound.Client
38− Database.V1.Bloodhound.Types
39− Database.V1.Bloodhound.Types.Class
40− Database.V1.Bloodhound.Types.Internal
41− Database.V5.Bloodhound
36+ exposed-modules: Database.V5.Bloodhound
4237 Database.V5.Bloodhound.Client
4338 Database.V5.Bloodhound.Types
4439 Database.V5.Bloodhound.Types.Class
45− Database.V5.Bloodhound.Types.Internal
40+ Database.V5.Bloodhound.Internal.Aggregation
41+ Database.V5.Bloodhound.Internal.Analysis
42+ Database.V5.Bloodhound.Internal.Client
43+ Database.V5.Bloodhound.Internal.Highlight
44+ Database.V5.Bloodhound.Internal.Newtypes
45+ Database.V5.Bloodhound.Internal.Query
46+ Database.V5.Bloodhound.Internal.Sort
47+ Database.V5.Bloodhound.Internal.StringlyTyped
48+ Database.V5.Bloodhound.Internal.Suggest
49+
50+ Database.V1.Bloodhound
51+ Database.V1.Bloodhound.Client
52+ Database.V1.Bloodhound.Types
53+ Database.V1.Bloodhound.Types.Class
54+ Database.V1.Bloodhound.Internal.Aggregation
55+ Database.V1.Bloodhound.Internal.Client
56+ Database.V1.Bloodhound.Internal.Highlight
57+ Database.V1.Bloodhound.Internal.Newtypes
58+ Database.V1.Bloodhound.Internal.Query
59+ Database.V1.Bloodhound.Internal.Sort
60+ Database.V1.Bloodhound.Internal.StringlyTyped
61+ Database.V1.Bloodhound.Internal.Suggest
62+
63+ other-modules: Bloodhound.Import
64+ Database.Bloodhound.Common.Script
4665 hs-source-dirs: src
4766 build-depends: base >= 4.3 && <5,
4867 bytestring >= 0.10.0 && <0.11,
@@ -58,15 +77,13 @@ library
5877 http-types >= 0.8 && <0.13,
5978 vector >= 0.10.9 && <0.13,
6079 scientific >= 0.3.0.0 && <0.4.0.0,
61− exceptions,
62− data-default-class,
6380 blaze-builder,
64− unordered-containers,
65− mtl-compat,
66− hashable
81+ exceptions,
82+ hashable,
83+ unordered-containers
6784 default-language: Haskell2010
6885
69−test-suite tests
86+test-suite bloodhound-tests
7087 ghc-options: -Wall -fno-warn-orphans
7188 type: exitcode-stdio-1.0
7289 main-is: tests.hs
@@ -74,7 +91,22 @@ test-suite tests
7491 hs-source-dirs: tests/V1
7592 else
7693 hs-source-dirs: tests/V5
77−
94+ other-modules: Test.Aggregation
95+ Test.ApproxEq
96+ Test.BulkAPI
97+ Test.Common
98+ Test.Documents
99+ Test.Generators
100+ Test.Highlights
101+ Test.Import
102+ Test.Indices
103+ Test.JSON
104+ Test.Query
105+ Test.Snapshots
106+ Test.Sorting
107+ Test.SourceFiltering
108+ Test.Suggest
109+ Test.Templates
78110 build-depends: base,
79111 bloodhound,
80112 bytestring,
@@ -90,11 +122,14 @@ test-suite tests
90122 vector,
91123 unordered-containers >= 0.2.5.0 && <0.3,
92124 mtl,
125+ pretty-simple,
126+ quickcheck-arbitrary-template,
93127 quickcheck-properties,
94− generics-sop >=0.2 && <0.4,
95128 errors,
96129 exceptions,
97130 temporary,
98131 unix-compat,
99− network-uri
132+ network-uri,
133+ microlens,
134+ microlens-aeson
100135 default-language: Haskell2010
changelog.md+14
@@ -1,3 +1,17 @@
1+0.16.0.0
2+========
3+- @bitemyapp
4+ - Reorganized modules internally, ripped out Generic,
5+ rewrote part of the test suite
6+- @andrewthad
7+ - Added support for autogenerated elasticsearch ids in the bulk API
8+ - Added support for token filters
9+ - Added action for searching multiple indices
10+- @lunaris
11+ - Added support for scripts fields and function score queries
12+- @bermanjosh
13+ - Added support for direct generators
14+
1150.15.0.2
216========
317- @michaelxavier
examples/Tweet.hs+1−4
@@ -5,7 +5,6 @@ module Main
55 ) where
66
77
8−-------------------------------------------------------------------------------
98import Control.Monad.IO.Class (liftIO)
109import Data.Aeson (FromJSON (..), defaultOptions,
1110 genericParseJSON, genericToJSON,
@@ -18,7 +17,6 @@ import qualified Data.Vector as V
1817import Database.V5.Bloodhound
1918import GHC.Generics (Generic)
2019import Network.HTTP.Client (defaultManagerSettings)
21−-------------------------------------------------------------------------------
2220
2321
2422data TweetMapping = TweetMapping deriving (Eq, Show)
@@ -29,7 +27,7 @@ instance ToJSON TweetMapping where
2927 [ "properties" .=
3028 object ["location" .= object ["type" .= ("geo_point" :: Text)]]
3129 ]
32−-------------------------------------------------------------------------------
30+
3331data Tweet = Tweet
3432 { user :: Text
3533 , postDate :: UTCTime
@@ -39,7 +37,6 @@ data Tweet = Tweet
3937 } deriving (Eq, Generic, Show)
4038
4139
42−-------------------------------------------------------------------------------
4340exampleTweet :: Tweet
4441exampleTweet =
4542 Tweet
examples/package.yaml−1
@@ -17,7 +17,6 @@ dependencies:
1717- http-client
1818- vector
1919- semigroups
20−- transformers
2120ghc-options:
2221- -Wall
2322- -threaded
src/Bloodhound/Import.hsnew file+81
@@ -0,0 +1,81 @@
1+module Bloodhound.Import
2+ ( module X
3+ , LByteString
4+ , Method
5+ , omitNulls
6+ , parseNEJSON
7+ , parseReadText
8+ , readMay
9+ , showText
10+ , deleteSeveral
11+ , oPath
12+ ) where
13+
14+import Control.Applicative as X (Alternative (..), optional)
15+import Control.Exception as X (Exception)
16+import Control.Monad as X (MonadPlus (..), forM, (<=<))
17+import Control.Monad.Catch as X (MonadCatch, MonadMask,
18+ MonadThrow)
19+import Control.Monad.Except as X (MonadError)
20+import Control.Monad.Fix as X (MonadFix)
21+import Control.Monad.IO.Class as X (MonadIO (..))
22+import Control.Monad.Reader as X (MonadReader (..),
23+ MonadTrans (..), ReaderT (..))
24+import Control.Monad.State as X (MonadState)
25+import Control.Monad.Writer as X (MonadWriter)
26+import Data.Aeson as X
27+import Data.Aeson.Types as X (Pair, Parser, emptyObject,
28+ parseEither, parseMaybe,
29+ typeMismatch)
30+import Data.Bifunctor as X (first)
31+import Data.Char as X (isNumber)
32+import Data.Hashable as X (Hashable)
33+import qualified Data.HashMap.Strict as HM
34+import Data.List as X (foldl', intercalate, nub)
35+import Data.List.NonEmpty as X (NonEmpty (..), toList)
36+import Data.Maybe as X (catMaybes, fromMaybe,
37+ isNothing, maybeToList)
38+import Data.Scientific as X (Scientific)
39+import Data.Semigroup as X (Semigroup (..))
40+import Data.Text as X (Text)
41+import Data.Time.Calendar as X (Day (..), showGregorian)
42+import Data.Time.Clock as X (NominalDiffTime, UTCTime)
43+import Data.Time.Clock.POSIX as X
44+
45+import qualified Data.ByteString.Lazy as BL
46+import qualified Data.Text as T
47+import qualified Data.Traversable as DT
48+import qualified Data.Vector as V
49+import qualified Network.HTTP.Types.Method as NHTM
50+
51+type LByteString = BL.ByteString
52+
53+type Method = NHTM.Method
54+
55+readMay :: Read a => String -> Maybe a
56+readMay s = case reads s of
57+ (a, ""):_ -> Just a
58+ _ -> Nothing
59+
60+parseReadText :: Read a => Text -> Parser a
61+parseReadText = maybe mzero return . readMay . T.unpack
62+
63+showText :: Show a => a -> Text
64+showText = T.pack . show
65+
66+omitNulls :: [(Text, Value)] -> Value
67+omitNulls = object . filter notNull where
68+ notNull (_, Null) = False
69+ notNull (_, Array a) = (not . V.null) a
70+ notNull _ = True
71+
72+parseNEJSON :: (FromJSON a) => [Value] -> Parser (NonEmpty a)
73+parseNEJSON [] = fail "Expected non-empty list"
74+parseNEJSON (x:xs) = DT.mapM parseJSON (x :| xs)
75+
76+deleteSeveral :: (Eq k, Hashable k) => [k] -> HM.HashMap k v -> HM.HashMap k v
77+deleteSeveral ks hm = foldr HM.delete hm ks
78+
79+oPath :: ToJSON a => NonEmpty Text -> a -> Value
80+oPath (k :| []) v = object [k .= v]
81+oPath (k:| (h:t)) v = object [k .= oPath (h :| t) v]
src/Database/Bloodhound/Common/Script.hsnew file+220
@@ -0,0 +1,220 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.Bloodhound.Common.Script where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.HashMap.Strict as HM
9+
10+import Database.V5.Bloodhound.Internal.Newtypes
11+
12+newtype ScriptFields =
13+ ScriptFields (HM.HashMap ScriptFieldName ScriptFieldValue)
14+ deriving (Eq, Show)
15+
16+type ScriptFieldName = Text
17+type ScriptFieldValue = Value
18+
19+data Script =
20+ Script { scriptLanguage :: Maybe ScriptLanguage
21+ , scriptInline :: Maybe ScriptInline
22+ , scriptStored :: Maybe ScriptId
23+ , scriptParams :: Maybe ScriptParams
24+ } deriving (Eq, Show)
25+
26+newtype ScriptLanguage =
27+ ScriptLanguage Text deriving (Eq, Show, FromJSON, ToJSON)
28+
29+newtype ScriptInline =
30+ ScriptInline Text deriving (Eq, Show, FromJSON, ToJSON)
31+
32+newtype ScriptId =
33+ ScriptId Text deriving (Eq, Show, FromJSON, ToJSON)
34+
35+newtype ScriptParams =
36+ ScriptParams (HM.HashMap ScriptParamName ScriptParamValue)
37+ deriving (Eq, Show)
38+
39+type ScriptParamName = Text
40+type ScriptParamValue = Value
41+
42+data BoostMode =
43+ BoostModeMultiply
44+ | BoostModeReplace
45+ | BoostModeSum
46+ | BoostModeAvg
47+ | BoostModeMax
48+ | BoostModeMin deriving (Eq, Show)
49+
50+data ScoreMode =
51+ ScoreModeMultiply
52+ | ScoreModeSum
53+ | ScoreModeAvg
54+ | ScoreModeFirst
55+ | ScoreModeMax
56+ | ScoreModeMin deriving (Eq, Show)
57+
58+data FunctionScoreFunction =
59+ FunctionScoreFunctionScript Script
60+ | FunctionScoreFunctionRandom Seed
61+ | FunctionScoreFunctionFieldValueFactor FieldValueFactor
62+ deriving (Eq, Show)
63+
64+newtype Weight =
65+ Weight Float deriving (Eq, Show, FromJSON, ToJSON)
66+
67+newtype Seed =
68+ Seed Float deriving (Eq, Show, FromJSON, ToJSON)
69+
70+data FieldValueFactor =
71+ FieldValueFactor { fieldValueFactorField :: FieldName
72+ , fieldValueFactor :: Maybe Factor
73+ , fieldValueFactorModifier :: Maybe FactorModifier
74+ , fieldValueFactorMissing :: Maybe FactorMissingFieldValue
75+ } deriving (Eq, Show)
76+
77+newtype Factor =
78+ Factor Float deriving (Eq, Show, FromJSON, ToJSON)
79+
80+data FactorModifier =
81+ FactorModifierNone
82+ | FactorModifierLog
83+ | FactorModifierLog1p
84+ | FactorModifierLog2p
85+ | FactorModifierLn
86+ | FactorModifierLn1p
87+ | FactorModifierLn2p
88+ | FactorModifierSquare
89+ | FactorModifierSqrt
90+ | FactorModifierReciprocal deriving (Eq, Show)
91+
92+newtype FactorMissingFieldValue =
93+ FactorMissingFieldValue Float deriving (Eq, Show, FromJSON, ToJSON)
94+
95+instance ToJSON BoostMode where
96+ toJSON BoostModeMultiply = "multiply"
97+ toJSON BoostModeReplace = "replace"
98+ toJSON BoostModeSum = "sum"
99+ toJSON BoostModeAvg = "avg"
100+ toJSON BoostModeMax = "max"
101+ toJSON BoostModeMin = "min"
102+
103+instance FromJSON BoostMode where
104+ parseJSON = withText "BoostMode" parse
105+ where parse "multiply" = pure BoostModeMultiply
106+ parse "replace" = pure BoostModeReplace
107+ parse "sum" = pure BoostModeSum
108+ parse "avg" = pure BoostModeAvg
109+ parse "max" = pure BoostModeMax
110+ parse "min" = pure BoostModeMin
111+ parse bm = fail ("Unexpected BoostMode: " <> show bm)
112+
113+instance ToJSON ScoreMode where
114+ toJSON ScoreModeMultiply = "multiply"
115+ toJSON ScoreModeSum = "sum"
116+ toJSON ScoreModeFirst = "first"
117+ toJSON ScoreModeAvg = "avg"
118+ toJSON ScoreModeMax = "max"
119+ toJSON ScoreModeMin = "min"
120+
121+instance FromJSON ScoreMode where
122+ parseJSON = withText "ScoreMode" parse
123+ where parse "multiply" = pure ScoreModeMultiply
124+ parse "sum" = pure ScoreModeSum
125+ parse "first" = pure ScoreModeFirst
126+ parse "avg" = pure ScoreModeAvg
127+ parse "max" = pure ScoreModeMax
128+ parse "min" = pure ScoreModeMin
129+ parse sm = fail ("Unexpected ScoreMode: " <> show sm)
130+
131+functionScoreFunctionPair :: FunctionScoreFunction -> (Text, Value)
132+functionScoreFunctionPair (FunctionScoreFunctionScript functionScoreScript) =
133+ ("script_score", toJSON functionScoreScript)
134+functionScoreFunctionPair (FunctionScoreFunctionRandom seed) =
135+ ("random_score", omitNulls [ "seed" .= seed ])
136+functionScoreFunctionPair (FunctionScoreFunctionFieldValueFactor fvf) =
137+ ("field_value_factor", toJSON fvf)
138+
139+parseFunctionScoreFunction :: Object -> Parser FunctionScoreFunction
140+parseFunctionScoreFunction o =
141+ singleScript `taggedWith` "script_score"
142+ <|> singleRandom `taggedWith` "random_score"
143+ <|> singleFieldValueFactor `taggedWith` "field_value_factor"
144+ where taggedWith parser k = parser =<< o .: k
145+ singleScript = pure . FunctionScoreFunctionScript
146+ singleRandom o' = FunctionScoreFunctionRandom <$> o' .: "seed"
147+ singleFieldValueFactor = pure . FunctionScoreFunctionFieldValueFactor
148+
149+instance ToJSON ScriptFields where
150+ toJSON (ScriptFields x) = Object x
151+
152+instance FromJSON ScriptFields where
153+ parseJSON (Object o) = pure (ScriptFields o)
154+ parseJSON _ = fail "error parsing ScriptFields"
155+
156+instance ToJSON Script where
157+ toJSON (Script lang inline stored params) =
158+ object [ "script" .= omitNulls base ]
159+ where base = [ "lang" .= lang
160+ , "inline" .= inline
161+ , "stored" .= stored
162+ , "params" .= params ]
163+
164+instance FromJSON Script where
165+ parseJSON = withObject "Script" parse
166+ where parse o = o .: "script" >>= \o' ->
167+ Script
168+ <$> o' .:? "lang"
169+ <*> o' .:? "inline"
170+ <*> o' .:? "stored"
171+ <*> o' .:? "params"
172+
173+instance ToJSON ScriptParams where
174+ toJSON (ScriptParams x) = Object x
175+
176+instance FromJSON ScriptParams where
177+ parseJSON (Object o) = pure (ScriptParams o)
178+ parseJSON _ = fail "error parsing ScriptParams"
179+
180+instance ToJSON FieldValueFactor where
181+ toJSON (FieldValueFactor field factor modifier missing) =
182+ omitNulls base
183+ where base = [ "field" .= field
184+ , "factor" .= factor
185+ , "modifier" .= modifier
186+ , "missing" .= missing ]
187+
188+instance FromJSON FieldValueFactor where
189+ parseJSON = withObject "FieldValueFactor" parse
190+ where parse o = FieldValueFactor
191+ <$> o .: "field"
192+ <*> o .:? "factor"
193+ <*> o .:? "modifier"
194+ <*> o .:? "missing"
195+
196+instance ToJSON FactorModifier where
197+ toJSON FactorModifierNone = "none"
198+ toJSON FactorModifierLog = "log"
199+ toJSON FactorModifierLog1p = "log1p"
200+ toJSON FactorModifierLog2p = "log2p"
201+ toJSON FactorModifierLn = "ln"
202+ toJSON FactorModifierLn1p = "ln1p"
203+ toJSON FactorModifierLn2p = "ln2p"
204+ toJSON FactorModifierSquare = "square"
205+ toJSON FactorModifierSqrt = "sqrt"
206+ toJSON FactorModifierReciprocal = "reciprocal"
207+
208+instance FromJSON FactorModifier where
209+ parseJSON = withText "FactorModifier" parse
210+ where parse "none" = pure FactorModifierNone
211+ parse "log" = pure FactorModifierLog
212+ parse "log1p" = pure FactorModifierLog1p
213+ parse "log2p" = pure FactorModifierLog2p
214+ parse "ln" = pure FactorModifierLn
215+ parse "ln1p" = pure FactorModifierLn1p
216+ parse "ln2p" = pure FactorModifierLn2p
217+ parse "square" = pure FactorModifierSquare
218+ parse "sqrt" = pure FactorModifierSqrt
219+ parse "reciprocal" = pure FactorModifierReciprocal
220+ parse fm = fail ("Unexpected FactorModifier: " <> show fm)
src/Database/V1/Bloodhound/Client.hs+13−9
@@ -223,7 +223,8 @@ getStatus :: MonadBH m => m (Maybe Status)
223223getStatus = do
224224 response <- get =<< url
225225 return $ decode (responseBody response)
226− where url = joinPath []
226+ where
227+ url = joinPath []
227228
228229-- | 'getSnapshotRepos' gets the definitions of a subset of the
229230-- defined snapshot repos.
@@ -252,7 +253,7 @@ instance FromJSON GSRs where
252253 parseJSON = withObject "Collection of GenericSnapshotRepo" parse
253254 where
254255 parse = fmap GSRs . mapM (uncurry go) . HM.toList
255− go rawName = withObject "GenericSnapshotRepo" $ \o -> do
256+ go rawName = withObject "GenericSnapshotRepo" $ \o ->
256257 GenericSnapshotRepo (SnapshotRepoName rawName) <$> o .: "type"
257258 <*> o .: "settings"
258259
@@ -454,16 +455,18 @@ deleteIndex (IndexName indexName) =
454455updateIndexSettings :: MonadBH m => NonEmpty UpdatableIndexSetting -> IndexName -> m Reply
455456updateIndexSettings updates (IndexName indexName) =
456457 bindM2 put url (return body)
457− where url = joinPath [indexName, "_settings"]
458− body = Just (encode jsonBody)
459− jsonBody = Object (deepMerge [u | Object u <- toJSON <$> toList updates])
458+ where
459+ url = joinPath [indexName, "_settings"]
460+ body = Just (encode jsonBody)
461+ jsonBody = Object (deepMerge [u | Object u <- toJSON <$> toList updates])
460462
461463
462464getIndexSettings :: (MonadBH m, MonadThrow m) => IndexName
463465 -> m (Either EsError IndexSettingsSummary)
464−getIndexSettings (IndexName indexName) = do
466+getIndexSettings (IndexName indexName) =
465467 parseEsResponse =<< get =<< url
466− where url = joinPath [indexName, "_settings"]
468+ where
469+ url = joinPath [indexName, "_settings"]
467470
468471
469472-- | 'optimizeIndex' will optimize a single index, list of indexes or
@@ -586,7 +589,7 @@ listIndices =
586589 url = joinPath ["_cat/indices?format=json"]
587590 parse body = maybe (throwM (EsProtocolException body)) return $ do
588591 vals <- decode body
589− forM vals $ \val -> do
592+ forM vals $ \val ->
590593 case val of
591594 Object obj -> do
592595 indexVal <- HM.lookup "index" obj
@@ -718,7 +721,8 @@ encodeBulkOperations stream = collapsed where
718721 collapsed = toLazyByteString $ mappend mashedTaters (byteString "\n")
719722
720723mash :: Builder -> V.Vector L.ByteString -> Builder
721−mash = V.foldl' (\b x -> b `mappend` (byteString "\n") `mappend` (lazyByteString x))
724+mash =
725+ V.foldl' (\b x -> b <> byteString "\n" <> lazyByteString x)
722726
723727mkBulkStreamValue :: Text -> Text -> Text -> Text -> Value
724728mkBulkStreamValue operation indexName mappingName docId =
src/Database/V1/Bloodhound/Internal/Aggregation.hsnew file+416
@@ -0,0 +1,416 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE RecordWildCards #-}
3+
4+module Database.V1.Bloodhound.Internal.Aggregation where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.HashMap.Strict as HM
9+import qualified Data.Map.Strict as M
10+import qualified Data.Text as T
11+
12+import Database.V1.Bloodhound.Internal.Client
13+import Database.V1.Bloodhound.Internal.Highlight (HitHighlight)
14+import Database.V1.Bloodhound.Internal.Newtypes
15+import Database.V1.Bloodhound.Internal.Query
16+import Database.V1.Bloodhound.Internal.Sort
17+
18+
19+type Aggregations = M.Map Text Aggregation
20+
21+emptyAggregations :: Aggregations
22+emptyAggregations = M.empty
23+
24+mkAggregations :: Text -> Aggregation -> Aggregations
25+mkAggregations name aggregation = M.insert name aggregation emptyAggregations
26+
27+data Aggregation = TermsAgg TermsAggregation
28+ | CardinalityAgg CardinalityAggregation
29+ | DateHistogramAgg DateHistogramAggregation
30+ | ValueCountAgg ValueCountAggregation
31+ | FilterAgg FilterAggregation
32+ | DateRangeAgg DateRangeAggregation
33+ | MissingAgg MissingAggregation
34+ | TopHitsAgg TopHitsAggregation
35+ deriving (Eq, Show)
36+
37+
38+instance ToJSON Aggregation where
39+ toJSON (TermsAgg (TermsAggregation term include exclude order minDocCount size shardSize collectMode executionHint termAggs)) =
40+ omitNulls ["terms" .= omitNulls [ toJSON' term,
41+ "include" .= include,
42+ "exclude" .= exclude,
43+ "order" .= order,
44+ "min_doc_count" .= minDocCount,
45+ "size" .= size,
46+ "shard_size" .= shardSize,
47+ "collect_mode" .= collectMode,
48+ "execution_hint" .= executionHint
49+ ],
50+ "aggs" .= termAggs ]
51+ where
52+ toJSON' x = case x of { Left y -> "field" .= y; Right y -> "script" .= y }
53+
54+ toJSON (CardinalityAgg (CardinalityAggregation field precisionThreshold)) =
55+ object ["cardinality" .= omitNulls [ "field" .= field,
56+ "precisionThreshold" .= precisionThreshold
57+ ]
58+ ]
59+
60+ toJSON (DateHistogramAgg (DateHistogramAggregation field interval format preZone postZone preOffset postOffset dateHistoAggs)) =
61+ omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
62+ "interval" .= interval,
63+ "format" .= format,
64+ "pre_zone" .= preZone,
65+ "post_zone" .= postZone,
66+ "pre_offset" .= preOffset,
67+ "post_offset" .= postOffset
68+ ],
69+ "aggs" .= dateHistoAggs ]
70+ toJSON (ValueCountAgg a) = object ["value_count" .= v]
71+ where v = case a of
72+ (FieldValueCount (FieldName n)) -> object ["field" .= n]
73+ (ScriptValueCount (Script s)) -> object ["script" .= s]
74+ toJSON (FilterAgg (FilterAggregation filt ags)) =
75+ omitNulls [ "filter" .= filt
76+ , "aggs" .= ags]
77+ toJSON (DateRangeAgg a) = object [ "date_range" .= a
78+ ]
79+ toJSON (MissingAgg (MissingAggregation{..})) =
80+ object ["missing" .= object ["field" .= maField]]
81+
82+ toJSON (TopHitsAgg (TopHitsAggregation mfrom msize msort)) =
83+ omitNulls ["top_hits" .= omitNulls [ "size" .= msize
84+ , "from" .= mfrom
85+ , "sort" .= msort
86+ ]
87+ ]
88+
89+data TopHitsAggregation = TopHitsAggregation
90+ { taFrom :: Maybe From
91+ , taSize :: Maybe Size
92+ , taSort :: Maybe Sort
93+ } deriving (Eq, Show)
94+
95+data MissingAggregation = MissingAggregation
96+ { maField :: Text
97+ } deriving (Eq, Show)
98+
99+data TermsAggregation = TermsAggregation { term :: Either Text Text
100+ , termInclude :: Maybe TermInclusion
101+ , termExclude :: Maybe TermInclusion
102+ , termOrder :: Maybe TermOrder
103+ , termMinDocCount :: Maybe Int
104+ , termSize :: Maybe Int
105+ , termShardSize :: Maybe Int
106+ , termCollectMode :: Maybe CollectionMode
107+ , termExecutionHint :: Maybe ExecutionHint
108+ , termAggs :: Maybe Aggregations
109+ } deriving (Eq, Show)
110+
111+data CardinalityAggregation = CardinalityAggregation { cardinalityField :: FieldName,
112+ precisionThreshold :: Maybe Int
113+ } deriving (Eq, Show)
114+
115+data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
116+ , dateInterval :: Interval
117+ , dateFormat :: Maybe Text
118+ -- pre and post deprecated in 1.5
119+ , datePreZone :: Maybe Text
120+ , datePostZone :: Maybe Text
121+ , datePreOffset :: Maybe Text
122+ , datePostOffset :: Maybe Text
123+ , dateAggs :: Maybe Aggregations
124+ } deriving (Eq, Show)
125+
126+
127+data DateRangeAggregation = DateRangeAggregation { draField :: FieldName
128+ , draFormat :: Maybe Text
129+ , draRanges :: NonEmpty DateRangeAggRange
130+ } deriving (Eq, Show)
131+
132+data DateRangeAggRange = DateRangeFrom DateMathExpr
133+ | DateRangeTo DateMathExpr
134+ | DateRangeFromAndTo DateMathExpr DateMathExpr deriving (Eq, Show)
135+
136+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
137+data ValueCountAggregation = FieldValueCount FieldName
138+ | ScriptValueCount Script deriving (Eq, Show)
139+
140+-- | 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.
141+data FilterAggregation = FilterAggregation { faFilter :: Filter
142+ , faAggs :: Maybe Aggregations} deriving (Eq, Show)
143+
144+mkTermsAggregation :: Text -> TermsAggregation
145+mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
146+
147+mkTermsScriptAggregation :: Text -> TermsAggregation
148+mkTermsScriptAggregation t = TermsAggregation (Right t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
149+
150+mkDateHistogram :: FieldName -> Interval -> DateHistogramAggregation
151+mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
152+
153+mkCardinalityAggregation :: FieldName -> CardinalityAggregation
154+mkCardinalityAggregation t = CardinalityAggregation t Nothing
155+
156+data TermInclusion = TermInclusion Text
157+ | TermPattern Text Text deriving (Eq, Show)
158+
159+instance ToJSON TermInclusion where
160+ toJSON (TermInclusion x) = toJSON x
161+ toJSON (TermPattern pattern flags) =
162+ omitNulls [ "pattern" .= pattern
163+ , "flags" .= flags]
164+
165+data TermOrder = TermOrder
166+ { termSortField :: Text
167+ , termSortOrder :: SortOrder } deriving (Eq, Show)
168+
169+instance ToJSON TermOrder where
170+ toJSON (TermOrder termSortField termSortOrder) =
171+ object [termSortField .= termSortOrder]
172+
173+
174+data ExecutionHint = Ordinals
175+ | GlobalOrdinals
176+ | GlobalOrdinalsHash
177+ | GlobalOrdinalsLowCardinality
178+ | Map deriving (Eq, Show)
179+
180+instance ToJSON ExecutionHint where
181+ toJSON Ordinals = "ordinals"
182+ toJSON GlobalOrdinals = "global_ordinals"
183+ toJSON GlobalOrdinalsHash = "global_ordinals_hash"
184+ toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
185+ toJSON Map = "map"
186+
187+
188+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
189+data DateMathExpr =
190+ DateMathExpr DateMathAnchor [DateMathModifier]
191+ deriving (Eq, Show)
192+
193+instance ToJSON DateMathExpr where
194+ toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
195+ where fmtA DMNow = "now"
196+ fmtA (DMDate date) = (T.pack $ showGregorian date) <> "||"
197+ fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
198+ fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
199+ fmtMod (RoundDownTo u) = "/" <> fmtU u
200+ fmtU DMYear = "y"
201+ fmtU DMMonth = "M"
202+ fmtU DMWeek = "w"
203+ fmtU DMDay = "d"
204+ fmtU DMHour = "h"
205+ fmtU DMMinute = "m"
206+ fmtU DMSecond = "s"
207+
208+-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
209+data DateMathAnchor =
210+ DMNow
211+ | DMDate Day
212+ deriving (Eq, Show)
213+
214+data DateMathModifier =
215+ AddTime Int DateMathUnit
216+ | SubtractTime Int DateMathUnit
217+ | RoundDownTo DateMathUnit
218+ deriving (Eq, Show)
219+
220+data DateMathUnit =
221+ DMYear
222+ | DMMonth
223+ | DMWeek
224+ | DMDay
225+ | DMHour
226+ | DMMinute
227+ | DMSecond
228+ deriving (Eq, Show)
229+
230+data CollectionMode = BreadthFirst
231+ | DepthFirst deriving (Eq, Show)
232+
233+type AggregationResults = M.Map Text Value
234+
235+class BucketAggregation a where
236+ key :: a -> BucketValue
237+ docCount :: a -> Int
238+ aggs :: a -> Maybe AggregationResults
239+
240+data BucketValue = TextValue Text
241+ | ScientificValue Scientific
242+ | BoolValue Bool deriving (Show)
243+
244+data Bucket a = Bucket { buckets :: [a]} deriving (Show)
245+
246+data TermsResult = TermsResult { termKey :: BucketValue
247+ , termsDocCount :: Int
248+ , termsAggs :: Maybe AggregationResults } deriving (Show)
249+
250+data DateHistogramResult = DateHistogramResult { dateKey :: Int
251+ , dateKeyStr :: Maybe Text
252+ , dateDocCount :: Int
253+ , dateHistogramAggs :: Maybe AggregationResults } deriving (Show)
254+
255+data DateRangeResult = DateRangeResult { dateRangeKey :: Text
256+ , dateRangeFrom :: Maybe UTCTime
257+ , dateRangeFromAsString :: Maybe Text
258+ , dateRangeTo :: Maybe UTCTime
259+ , dateRangeToAsString :: Maybe Text
260+ , dateRangeDocCount :: Int
261+ , dateRangeAggs :: Maybe AggregationResults } deriving (Show, Eq)
262+
263+toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
264+toTerms = toAggResult
265+
266+toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
267+toDateHistogram = toAggResult
268+
269+toMissing :: Text -> AggregationResults -> Maybe MissingResult
270+toMissing = toAggResult
271+
272+toTopHits :: (FromJSON a) => Text -> AggregationResults -> Maybe (TopHitResult a)
273+toTopHits = toAggResult
274+
275+toAggResult :: (FromJSON a) => Text -> AggregationResults -> Maybe a
276+toAggResult t a = M.lookup t a >>= deserialize
277+ where deserialize = parseMaybe parseJSON
278+
279+instance BucketAggregation TermsResult where
280+ key = termKey
281+ docCount = termsDocCount
282+ aggs = termsAggs
283+
284+instance BucketAggregation DateHistogramResult where
285+ key = TextValue . showText . dateKey
286+ docCount = dateDocCount
287+ aggs = dateHistogramAggs
288+
289+instance BucketAggregation DateRangeResult where
290+ key = TextValue . dateRangeKey
291+ docCount = dateRangeDocCount
292+ aggs = dateRangeAggs
293+
294+instance (FromJSON a) => FromJSON (Bucket a) where
295+ parseJSON (Object v) = Bucket <$>
296+ v .: "buckets"
297+ parseJSON _ = mempty
298+
299+instance FromJSON BucketValue where
300+ parseJSON (String t) = return $ TextValue t
301+ parseJSON (Number s) = return $ ScientificValue s
302+ parseJSON (Bool b) = return $ BoolValue b
303+ parseJSON _ = mempty
304+
305+instance FromJSON MissingResult where
306+ parseJSON = withObject "MissingResult" parse
307+ where parse v = MissingResult <$> v .: "doc_count"
308+
309+instance FromJSON TermsResult where
310+ parseJSON (Object v) = TermsResult <$>
311+ v .: "key" <*>
312+ v .: "doc_count" <*>
313+ (pure $ getNamedSubAgg v ["key", "doc_count"])
314+ parseJSON _ = mempty
315+
316+instance FromJSON DateHistogramResult where
317+ parseJSON (Object v) = DateHistogramResult <$>
318+ v .: "key" <*>
319+ v .:? "key_as_string" <*>
320+ v .: "doc_count" <*>
321+ (pure $ getNamedSubAgg v [ "key"
322+ , "doc_count"
323+ , "key_as_string"
324+ ]
325+ )
326+ parseJSON _ = mempty
327+
328+instance FromJSON DateRangeResult where
329+ parseJSON = withObject "DateRangeResult" parse
330+ where parse v = DateRangeResult <$>
331+ v .: "key" <*>
332+ (fmap posixMS <$> v .:? "from") <*>
333+ v .:? "from_as_string" <*>
334+ (fmap posixMS <$> v .:? "to") <*>
335+ v .:? "to_as_string" <*>
336+ v .: "doc_count" <*>
337+ (pure $ getNamedSubAgg v [ "key"
338+ , "from"
339+ , "from_as_string"
340+ , "to"
341+ , "to_as_string"
342+ , "doc_count"
343+ ]
344+ )
345+
346+instance (FromJSON a) => FromJSON (TopHitResult a) where
347+ parseJSON (Object v) = TopHitResult <$>
348+ v .: "hits"
349+ parseJSON _ = fail "Failure in FromJSON (TopHitResult a)"
350+
351+data MissingResult = MissingResult { missingDocCount :: Int } deriving (Show)
352+
353+data TopHitResult a = TopHitResult { tarHits :: (SearchHits a)
354+ } deriving Show
355+
356+data SearchHits a =
357+ SearchHits { hitsTotal :: Int
358+ , maxScore :: Score
359+ , hits :: [Hit a] } deriving (Eq, Show)
360+
361+instance Semigroup (SearchHits a) where
362+ (SearchHits ta ma ha) <> (SearchHits tb mb hb) = SearchHits (ta + tb) (max ma mb) (ha <> hb)
363+
364+instance Monoid (SearchHits a) where
365+ mempty = SearchHits 0 Nothing mempty
366+ mappend = (<>)
367+
368+data Hit a =
369+ Hit { hitIndex :: IndexName
370+ , hitType :: MappingName
371+ , hitDocId :: DocId
372+ , hitScore :: Score
373+ , hitSource :: Maybe a
374+ , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show)
375+
376+-- Try to get an AggregationResults when we don't know the
377+-- field name. We filter out the known keys to try to minimize the noise.
378+getNamedSubAgg :: Object -> [Text] -> Maybe AggregationResults
379+getNamedSubAgg o knownKeys = maggRes
380+ where unknownKeys = HM.filterWithKey (\k _ -> k `notElem` knownKeys) o
381+ maggRes
382+ | HM.null unknownKeys = Nothing
383+ | otherwise = Just . M.fromList $ HM.toList unknownKeys
384+
385+instance ToJSON CollectionMode where
386+ toJSON BreadthFirst = "breadth_first"
387+ toJSON DepthFirst = "depth_first"
388+
389+instance ToJSON DateRangeAggregation where
390+ toJSON DateRangeAggregation {..} =
391+ omitNulls [ "field" .= draField
392+ , "format" .= draFormat
393+ , "ranges" .= toList draRanges
394+ ]
395+
396+instance (FromJSON a) => FromJSON (SearchHits a) where
397+ parseJSON (Object v) = SearchHits <$>
398+ v .: "total" <*>
399+ v .: "max_score" <*>
400+ v .: "hits"
401+ parseJSON _ = empty
402+
403+instance ToJSON DateRangeAggRange where
404+ toJSON (DateRangeFrom e) = object [ "from" .= e ]
405+ toJSON (DateRangeTo e) = object [ "to" .= e ]
406+ toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
407+
408+instance (FromJSON a) => FromJSON (Hit a) where
409+ parseJSON (Object v) = Hit <$>
410+ v .: "_index" <*>
411+ v .: "_type" <*>
412+ v .: "_id" <*>
413+ v .: "_score" <*>
414+ v .:? "_source" <*>
415+ v .:? "highlight"
416+ parseJSON _ = empty
src/Database/V1/Bloodhound/Internal/Client.hsnew file+2341
@@ -0,0 +1,2341 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE RecordWildCards #-}
6+{-# LANGUAGE UndecidableInstances #-}
7+
8+module Database.V1.Bloodhound.Internal.Client where
9+
10+import Bloodhound.Import
11+
12+import Control.Applicative as A
13+import qualified Data.HashMap.Strict as HM
14+import Data.Text (Text)
15+import qualified Data.Text as T
16+import qualified Data.Traversable as DT
17+import qualified Data.Vector as V
18+import qualified Data.Version as Vers
19+import GHC.Enum
20+import Network.HTTP.Client
21+import qualified Text.ParserCombinators.ReadP as RP
22+import Text.Read (Read (..))
23+import qualified Text.Read as TR
24+
25+import Database.V1.Bloodhound.Internal.Newtypes
26+import Database.V1.Bloodhound.Internal.Query
27+import Database.V1.Bloodhound.Internal.StringlyTyped
28+
29+
30+{-| Common environment for Elasticsearch calls. Connections will be
31+ pipelined according to the provided HTTP connection manager.
32+-}
33+data BHEnv = BHEnv { bhServer :: Server
34+ , bhManager :: Manager
35+ , bhRequestHook :: Request -> IO Request
36+ -- ^ Low-level hook that is run before every request is sent. Used to implement custom authentication strategies. Defaults to 'return' with 'mkBHEnv'.
37+ }
38+
39+instance (Functor m, Applicative m, MonadIO m) => MonadBH (ReaderT BHEnv m) where
40+ getBHEnv = ask
41+
42+{-| 'Server' is used with the client functions to point at the ES instance
43+-}
44+newtype Server = Server Text deriving (Eq, Show, FromJSON)
45+
46+{-| All API calls to Elasticsearch operate within
47+ MonadBH
48+ . The idea is that it can be easily embedded in your
49+ own monad transformer stack. A default instance for a ReaderT and
50+ alias 'BH' is provided for the simple case.
51+-}
52+class (Functor m, A.Applicative m, MonadIO m) => MonadBH m where
53+ getBHEnv :: m BHEnv
54+
55+-- $setup
56+-- >>> :set -XOverloadedStrings
57+-- >>> import Data.Aeson
58+-- >>> import Database.V1.Bloodhound
59+-- >>> let testServer = (Server "http://localhost:9200")
60+-- >>> let testIndex = IndexName "twitter"
61+-- >>> let testMapping = MappingName "tweet"
62+-- >>> let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
63+
64+-- defaultIndexSettings is exported by Database.Bloodhound as well
65+-- no trailing slashes in servers, library handles building the path.
66+
67+-- | Create a 'BHEnv' with all optional fields defaulted. HTTP hook
68+-- will be a noop. You can use the exported fields to customize it further, e.g.:
69+--
70+-- >> (mkBHEnv myServer myManager) { bhRequestHook = customHook }
71+mkBHEnv :: Server -> Manager -> BHEnv
72+mkBHEnv s m = BHEnv s m return
73+
74+newtype BH m a = BH {
75+ unBH :: ReaderT BHEnv m a
76+ } deriving ( Functor
77+ , A.Applicative
78+ , Monad
79+ , MonadIO
80+ , MonadState s
81+ , MonadWriter w
82+ , MonadError e
83+ , Alternative
84+ , MonadPlus
85+ , MonadFix
86+ , MonadThrow
87+ , MonadCatch
88+ , MonadMask)
89+
90+instance MonadTrans BH where
91+ lift = BH . lift
92+
93+instance (MonadReader r m) => MonadReader r (BH m) where
94+ ask = lift ask
95+ local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
96+ local f (m r)
97+
98+instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
99+ getBHEnv = BH getBHEnv
100+
101+runBH :: BHEnv -> BH m a -> m a
102+runBH e f = runReaderT (unBH f) e
103+
104+{-| 'Version' is embedded in 'Status' -}
105+data Version = Version { number :: VersionNumber
106+ , build_hash :: BuildHash
107+ , build_timestamp :: UTCTime
108+ , build_snapshot :: Bool
109+ , lucene_version :: VersionNumber } deriving (Eq, Show)
110+
111+instance ToJSON Version where
112+ toJSON Version {..} = object ["number" .= number
113+ ,"build_hash" .= build_hash
114+ ,"build_timestamp" .= build_timestamp
115+ ,"build_snapshot" .= build_snapshot
116+ ,"lucene_version" .= lucene_version]
117+
118+instance FromJSON Version where
119+ parseJSON = withObject "Version" parse
120+ where parse o = Version
121+ <$> o .: "number"
122+ <*> o .: "build_hash"
123+ <*> o .: "build_timestamp"
124+ <*> o .: "build_snapshot"
125+ <*> o .: "lucene_version"
126+
127+-- | Traditional software versioning number
128+newtype VersionNumber = VersionNumber { versionNumber :: Vers.Version
129+ } deriving (Eq, Show, Ord)
130+
131+{-| 'Status' is a data type for describing the JSON body returned by
132+ Elasticsearch when you query its status. This was deprecated in 1.2.0.
133+
134+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
135+-}
136+
137+data Status = Status { ok :: Maybe Bool
138+ , status :: Int
139+ , name :: Text
140+ , version :: Version
141+ , tagline :: Text } deriving (Eq, Show)
142+
143+instance FromJSON Status where
144+ parseJSON (Object v) = Status <$>
145+ v .:? "ok" <*>
146+ (v .:? "status" .!= 200) <*>
147+ v .: "name" <*>
148+ v .: "version" <*>
149+ v .: "tagline"
150+ parseJSON _ = empty
151+
152+{-| 'IndexSettings' is used to configure the shards and replicas when you create
153+ an Elasticsearch Index.
154+
155+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
156+-}
157+
158+data IndexSettings =
159+ IndexSettings { indexShards :: ShardCount
160+ , indexReplicas :: ReplicaCount } deriving (Eq, Show)
161+
162+{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
163+defaultIndexSettings :: IndexSettings
164+defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
165+
166+
167+{-| 'IndexOptimizationSettings' is used to configure index optimization. See
168+ <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-optimize.html>
169+ for more info.
170+-}
171+data IndexOptimizationSettings =
172+ IndexOptimizationSettings { maxNumSegments :: Maybe Int
173+ -- ^ 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.
174+ , onlyExpungeDeletes :: Bool
175+ -- ^ 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.
176+ , flushAfterOptimize :: Bool
177+ -- ^ Should a flush be performed after the optimize.
178+ } deriving (Eq, Show)
179+
180+
181+{-| 'defaultIndexOptimizationSettings' implements the default settings that
182+ ElasticSearch uses for index optimization. 'maxNumSegments' is Nothing,
183+ 'onlyExpungeDeletes' is False, and flushAfterOptimize is True.
184+-}
185+defaultIndexOptimizationSettings :: IndexOptimizationSettings
186+defaultIndexOptimizationSettings = IndexOptimizationSettings Nothing False True
187+
188+{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
189+
190+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
191+-}
192+data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
193+ -- ^ The number of replicas each shard has.
194+ | AutoExpandReplicas ReplicaBounds
195+ | BlocksReadOnly Bool
196+ -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
197+ | BlocksRead Bool
198+ -- ^ Set to True to disable read operations against the index.
199+ | BlocksWrite Bool
200+ -- ^ Set to True to disable write operations against the index.
201+ | BlocksMetaData Bool
202+ -- ^ Set to True to disable metadata operations against the index.
203+ | RefreshInterval NominalDiffTime
204+ -- ^ The async refresh interval of a shard
205+ | IndexConcurrency Int
206+ | FailOnMergeFailure Bool
207+ | TranslogFlushThresholdOps Int
208+ -- ^ When to flush on operations.
209+ | TranslogFlushThresholdSize Bytes
210+ -- ^ When to flush based on translog (bytes) size.
211+ | TranslogFlushThresholdPeriod NominalDiffTime
212+ -- ^ When to flush based on a period of not flushing.
213+ | TranslogDisableFlush Bool
214+ -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
215+ | CacheFilterMaxSize (Maybe Bytes)
216+ -- ^ The maximum size of filter cache (per segment in shard).
217+ | CacheFilterExpire (Maybe NominalDiffTime)
218+ -- ^ The expire after access time for filter cache.
219+ | GatewaySnapshotInterval NominalDiffTime
220+ -- ^ The gateway snapshot interval (only applies to shared gateways).
221+ | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
222+ -- ^ A node matching any rule will be allowed to host shards from the index.
223+ | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
224+ -- ^ A node matching any rule will NOT be allowed to host shards from the index.
225+ | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
226+ -- ^ Only nodes matching all rules will be allowed to host shards from the index.
227+ | RoutingAllocationEnable AllocationPolicy
228+ -- ^ Enables shard allocation for a specific index.
229+ | RoutingAllocationShardsPerNode ShardCount
230+ -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
231+ | RecoveryInitialShards InitialShardCount
232+ -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
233+ | GCDeletes NominalDiffTime
234+ | TTLDisablePurge Bool
235+ -- ^ Disables temporarily the purge of expired docs.
236+ | TranslogFSType FSType
237+ | IndexCompoundFormat CompoundFormat
238+ | IndexCompoundOnFlush Bool
239+ | WarmerEnabled Bool
240+ deriving (Eq, Show)
241+
242+data AllocationPolicy = AllocAll
243+ -- ^ Allows shard allocation for all shards.
244+ | AllocPrimaries
245+ -- ^ Allows shard allocation only for primary shards.
246+ | AllocNewPrimaries
247+ -- ^ Allows shard allocation only for primary shards for new indices.
248+ | AllocNone
249+ -- ^ No shard allocation is allowed
250+ deriving (Eq, Show)
251+
252+data ReplicaBounds = ReplicasBounded Int Int
253+ | ReplicasLowerBounded Int
254+ | ReplicasUnbounded
255+ deriving (Eq, Show)
256+
257+-- | A measure of bytes used for various configurations. You may want
258+-- to use smart constructors like 'gigabytes' for larger values.
259+--
260+-- >>> gigabytes 9
261+-- Bytes 9000000000
262+--
263+-- >>> megabytes 9
264+-- Bytes 9000000
265+--
266+-- >>> kilobytes 9
267+-- Bytes 9000
268+newtype Bytes = Bytes Int deriving (Eq, Show, Ord, ToJSON, FromJSON)
269+
270+gigabytes :: Int -> Bytes
271+gigabytes n = megabytes (1000 * n)
272+
273+
274+megabytes :: Int -> Bytes
275+megabytes n = kilobytes (1000 * n)
276+
277+
278+kilobytes :: Int -> Bytes
279+kilobytes n = Bytes (1000 * n)
280+
281+data Interval = Year
282+ | Quarter
283+ | Month
284+ | Week
285+ | Day
286+ | Hour
287+ | Minute
288+ | Second
289+ | FractionalInterval Float TimeInterval deriving (Eq, Show)
290+
291+data TimeInterval = Weeks
292+ | Days
293+ | Hours
294+ | Minutes
295+ | Seconds deriving Eq
296+
297+instance Show TimeInterval where
298+ show Weeks = "w"
299+ show Days = "d"
300+ show Hours = "h"
301+ show Minutes = "m"
302+ show Seconds = "s"
303+
304+instance Read TimeInterval where
305+ readPrec = f =<< TR.get
306+ where
307+ f 'w' = return Weeks
308+ f 'd' = return Days
309+ f 'h' = return Hours
310+ f 'm' = return Minutes
311+ f 's' = return Seconds
312+ f _ = fail "TimeInterval expected one of w, d, h, m, s"
313+
314+-- | Typically a 7 character hex string.
315+newtype BuildHash = BuildHash { buildHash :: Text }
316+ deriving (Eq, Ord, Show, FromJSON, ToJSON)
317+
318+data NodeAttrFilter = NodeAttrFilter
319+ { nodeAttrFilterName :: NodeAttrName
320+ , nodeAttrFilterValues :: NonEmpty Text }
321+ deriving (Eq, Ord, Show)
322+
323+newtype NodeAttrName = NodeAttrName Text deriving (Eq, Ord, Show)
324+
325+data InitialShardCount = QuorumShards
326+ | QuorumMinus1Shards
327+ | FullShards
328+ | FullMinus1Shards
329+ | ExplicitShards Int
330+ deriving (Eq, Show)
331+
332+instance FromJSON InitialShardCount where
333+ parseJSON v = withText "InitialShardCount" parseText v
334+ <|> ExplicitShards <$> parseJSON v
335+ where parseText "quorum" = pure QuorumShards
336+ parseText "quorum-1" = pure QuorumMinus1Shards
337+ parseText "full" = pure FullShards
338+ parseText "full-1" = pure FullMinus1Shards
339+ parseText _ = mzero
340+
341+instance ToJSON InitialShardCount where
342+ toJSON QuorumShards = String "quorum"
343+ toJSON QuorumMinus1Shards = String "quorum-1"
344+ toJSON FullShards = String "full"
345+ toJSON FullMinus1Shards = String "full-1"
346+ toJSON (ExplicitShards x) = toJSON x
347+
348+data FSType = FSSimple
349+ | FSBuffered deriving (Eq, Show)
350+
351+instance ToJSON FSType where
352+ toJSON FSSimple = "simple"
353+ toJSON FSBuffered = "buffered"
354+
355+instance FromJSON FSType where
356+ parseJSON = withText "FSType" parse
357+ where parse "simple" = pure FSSimple
358+ parse "buffered" = pure FSBuffered
359+ parse t = fail ("Invalid FSType: " <> show t)
360+
361+data CompoundFormat = CompoundFileFormat Bool
362+ | MergeSegmentVsTotalIndex Double
363+ -- ^ percentage between 0 and 1 where 0 is false, 1 is true
364+ deriving (Eq, Show)
365+
366+attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
367+attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
368+ | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
369+
370+parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
371+parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
372+ where parse o = case HM.toList o of
373+ [] -> fail "Expected non-empty list of NodeAttrFilters"
374+ x:xs -> DT.mapM (uncurry parse') (x :| xs)
375+ parse' n = withText "Text" $ \t ->
376+ case T.splitOn "," t of
377+ fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
378+ [] -> fail "Expected non-empty list of filter values"
379+
380+newtype NominalDiffTimeJSON = NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
381+
382+data IndexSettingsSummary = IndexSettingsSummary { sSummaryIndexName :: IndexName
383+ , sSummaryFixedSettings :: IndexSettings
384+ , sSummaryUpdateable :: [UpdatableIndexSetting]}
385+ deriving (Eq, Show)
386+
387+parseSettings :: Object -> Parser [UpdatableIndexSetting]
388+parseSettings o = do
389+ o' <- o .: "index"
390+ -- slice the index object into singleton hashmaps and try to parse each
391+ parses <- forM (HM.toList o') $ \(k, v) -> do
392+ -- blocks are now nested into the "index" key, which is not how they're serialized
393+ let atRoot = Object (HM.singleton k v)
394+ let atIndex = Object (HM.singleton "index" atRoot)
395+ optional (parseJSON atRoot <|> parseJSON atIndex)
396+ return (catMaybes parses)
397+
398+{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
399+
400+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
401+-}
402+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show)
403+
404+data FieldType = GeoPointType
405+ | GeoShapeType
406+ | FloatType
407+ | IntegerType
408+ | LongType
409+ | ShortType
410+ | ByteType deriving (Eq, Show)
411+
412+data FieldDefinition =
413+ FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
414+
415+{-| An 'IndexTemplate' defines a template that will automatically be
416+ applied to new indices created. The templates include both
417+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
418+ controls if the template will be applied to the index created.
419+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
420+
421+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
422+-}
423+data IndexTemplate =
424+ IndexTemplate { templatePattern :: TemplatePattern
425+ , templateSettings :: Maybe IndexSettings
426+ , templateMappings :: [Value]
427+ }
428+
429+data MappingField =
430+ MappingField { mappingFieldName :: FieldName
431+ , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
432+
433+{-| Support for type reification of 'Mapping's is currently incomplete, for
434+ now the mapping API verbiage expects a 'ToJSON'able blob.
435+
436+ Indexes have mappings, mappings are schemas for the documents contained in the
437+ index. I'd recommend having only one mapping per index, always having a mapping,
438+ and keeping different kinds of documents separated if possible.
439+-}
440+data Mapping = Mapping { typeName :: TypeName
441+ , mappingFields :: [MappingField] } deriving (Eq, Show)
442+
443+{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
444+ operation index, create, delete, and update. 'BulkIndex' behaves like an
445+ "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
446+
447+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk>
448+-}
449+data BulkOperation =
450+ BulkIndex IndexName MappingName DocId Value
451+ | BulkCreate IndexName MappingName DocId Value
452+ | BulkDelete IndexName MappingName DocId
453+ | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Show)
454+
455+{-| 'EsResult' describes the standard wrapper JSON document that you see in
456+ successful Elasticsearch lookups or lookups that couldn't find the document.
457+-}
458+data EsResult a = EsResult { _index :: Text
459+ , _type :: Text
460+ , _id :: Text
461+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show)
462+
463+instance (FromJSON a) => FromJSON (EsResult a) where
464+ parseJSON jsonVal@(Object v) = do
465+ found <- v .:? "found" .!= False
466+ fr <- if found
467+ then parseJSON jsonVal
468+ else return Nothing
469+ EsResult <$> v .: "_index" <*>
470+ v .: "_type" <*>
471+ v .: "_id" <*>
472+ pure fr
473+ parseJSON _ = empty
474+
475+{-| 'EsResultFound' contains the document and its metadata inside of an
476+ 'EsResult' when the document was successfully found.
477+-}
478+data EsResultFound a = EsResultFound { _version :: DocVersion
479+ , _source :: a } deriving (Eq, Show)
480+
481+instance (FromJSON a) => FromJSON (EsResultFound a) where
482+ parseJSON (Object v) = EsResultFound <$>
483+ v .: "_version" <*>
484+ v .: "_source"
485+ parseJSON _ = empty
486+
487+{-| 'EsError' is the generic type that will be returned when there was a
488+ problem. If you can't parse the expected response, its a good idea to
489+ try parsing this.
490+-}
491+data EsError = EsError { errorStatus :: Int
492+ , errorMessage :: Text } deriving (Eq, Show)
493+
494+{-| 'EsProtocolException' will be thrown if Bloodhound cannot parse a response
495+returned by the ElasticSearch server. If you encounter this error, please
496+verify that your domain data types and FromJSON instances are working properly
497+(for example, the 'a' of '[Hit a]' in 'SearchResult.searchHits.hits'). If you're
498+sure that your mappings are correct, then this error may be an indication of an
499+incompatibility between Bloodhound and ElasticSearch. Please open a bug report
500+and be sure to include the exception body.
501+-}
502+data EsProtocolException = EsProtocolException { esProtoExBody :: LByteString }
503+ deriving (Eq, Show)
504+
505+instance Exception EsProtocolException
506+
507+data IndexAlias = IndexAlias { srcIndex :: IndexName
508+ , indexAlias :: IndexAliasName } deriving (Eq, Show)
509+
510+{-| 'DocVersion' is an integer version number for a document between 1
511+and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
512+-}
513+newtype DocVersion = DocVersion {
514+ docVersionNumber :: Int
515+ } deriving (Eq, Show, Ord, ToJSON)
516+
517+instance FromJSON DocVersion where
518+ parseJSON v = do
519+ i <- parseJSON v
520+ maybe (fail "DocVersion out of range") return $ mkDocVersion i
521+
522+-- | Smart constructor for in-range doc version
523+mkDocVersion :: Int -> Maybe DocVersion
524+mkDocVersion i
525+ | i >= (docVersionNumber minBound) && i <= (docVersionNumber maxBound) =
526+ Just $ DocVersion i
527+ | otherwise = Nothing
528+
529+data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
530+ | RemoveAlias IndexAlias deriving (Show, Eq)
531+
532+data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
533+ , aliasCreateFilter :: Maybe Filter}
534+ deriving (Show, Eq)
535+
536+data AliasRouting = AllAliasRouting RoutingValue
537+ | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
538+ deriving (Show, Eq)
539+
540+newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Show, Eq)
541+
542+newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Show, Eq, ToJSON, FromJSON)
543+
544+newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Show, Eq, ToJSON, FromJSON)
545+
546+newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Show, Eq)
547+
548+{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
549+data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
550+ , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Show, Eq)
551+
552+{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
553+own version numbers instead of ones from ES.
554+-}
555+newtype ExternalDocVersion = ExternalDocVersion DocVersion
556+ deriving (Eq, Show, Ord, Bounded, Enum, ToJSON)
557+
558+{-| 'VersionControl' is specified when indexing documents as a
559+optimistic concurrency control.
560+-}
561+data VersionControl = NoVersionControl
562+ -- ^ Don't send a version. This is a pure overwrite.
563+ | InternalVersion DocVersion
564+ -- ^ Use the default ES versioning scheme. Only
565+ -- index the document if the version is the same
566+ -- as the one specified. Only applicable to
567+ -- updates, as you should be getting Version from
568+ -- a search result.
569+ | ExternalGT ExternalDocVersion
570+ -- ^ Use your own version numbering. Only index
571+ -- the document if the version is strictly higher
572+ -- OR the document doesn't exist. The given
573+ -- version will be used as the new version number
574+ -- for the stored document. N.B. All updates must
575+ -- increment this number, meaning there is some
576+ -- global, external ordering of updates.
577+ | ExternalGTE ExternalDocVersion
578+ -- ^ Use your own version numbering. Only index
579+ -- the document if the version is equal or higher
580+ -- than the stored version. Will succeed if there
581+ -- is no existing document. The given version will
582+ -- be used as the new version number for the
583+ -- stored document. Use with care, as this could
584+ -- result in data loss.
585+ | ForceVersion ExternalDocVersion
586+ -- ^ The document will always be indexed and the
587+ -- given version will be the new version. This is
588+ -- typically used for correcting errors. Use with
589+ -- care, as this could result in data loss.
590+ deriving (Show, Eq, Ord)
591+
592+{-| 'DocumentParent' is used to specify a parent document.
593+-}
594+newtype DocumentParent = DocumentParent DocId
595+ deriving (Eq, Show)
596+
597+{-| 'IndexDocumentSettings' are special settings supplied when indexing
598+a document. For the best backwards compatiblity when new fields are
599+added, you should probably prefer to start with 'defaultIndexDocumentSettings'
600+-}
601+data IndexDocumentSettings =
602+ IndexDocumentSettings { idsVersionControl :: VersionControl
603+ , idsParent :: Maybe DocumentParent
604+ } deriving (Eq, Show)
605+
606+{-| Reasonable default settings. Chooses no version control and no parent.
607+-}
608+defaultIndexDocumentSettings :: IndexDocumentSettings
609+defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
610+
611+{-| 'IndexSelection' is used for APIs which take a single index, a list of
612+ indexes, or the special @_all@ index.
613+-}
614+--TODO: this does not fully support <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/multi-index.html multi-index syntax>. It wouldn't be too hard to implement but you'd have to add the optional parameters (ignore_unavailable, allow_no_indices, expand_wildcards) to any APIs using it. Also would be a breaking API.
615+data IndexSelection = IndexList (NonEmpty IndexName)
616+ | AllIndexes deriving (Eq, Show)
617+
618+{-| 'NodeSelection' is used for most cluster APIs. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes here> for more details.
619+-}
620+data NodeSelection = LocalNode
621+ -- ^ Whatever node receives this request
622+ | NodeList (NonEmpty NodeSelector)
623+ | AllNodes deriving (Eq, Show)
624+
625+
626+-- | An exact match or pattern to identify a node. Note that All of
627+-- these options support wildcarding, so your node name, server, attr
628+-- name can all contain * characters to be a fuzzy match.
629+data NodeSelector = NodeByName NodeName
630+ | NodeByFullNodeId FullNodeId
631+ | NodeByHost Server
632+ -- ^ e.g. 10.0.0.1 or even 10.0.0.*
633+ | NodeByAttribute NodeAttrName Text
634+ -- ^ NodeAttrName can be a pattern, e.g. rack*. The value can too.
635+ deriving (Eq, Show)
636+
637+-- | A human-readable node name that is supplied by the user in the
638+-- node config or automatically generated by ElasticSearch.
639+newtype NodeName = NodeName { nodeName :: Text }
640+ deriving (Eq, Ord, Show, FromJSON)
641+
642+-- | Unique, automatically-generated name assigned to nodes that are
643+-- usually returned in node-oriented APIs.
644+newtype FullNodeId = FullNodeId { fullNodeId :: Text }
645+ deriving (Eq, Ord, Show, FromJSON)
646+
647+-- | Username type used for HTTP Basic authentication. See 'basicAuthHook'.
648+newtype EsUsername = EsUsername { esUsername :: Text } deriving (Show, Eq)
649+
650+-- | Password type used for HTTP Basic authentication. See 'basicAuthHook'.
651+newtype EsPassword = EsPassword { esPassword :: Text } deriving (Show, Eq)
652+
653+data SnapshotRepoSelection = SnapshotRepoList (NonEmpty SnapshotRepoPattern)
654+ | AllSnapshotRepos deriving (Eq, Show)
655+
656+
657+-- | Either specifies an exact repo name or one with globs in it,
658+-- e.g. @RepoPattern "foo*"@ __NOTE__: Patterns are not supported on ES < 1.7
659+data SnapshotRepoPattern = ExactRepo SnapshotRepoName
660+ | RepoPattern Text
661+ deriving (Eq, Show)
662+
663+-- | The unique name of a snapshot repository.
664+newtype SnapshotRepoName = SnapshotRepoName { snapshotRepoName :: Text }
665+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
666+
667+
668+-- | A generic representation of a snapshot repo. This is what gets
669+-- sent to and parsed from the server. For repo types enabled by
670+-- plugins that aren't exported by this library, consider making a
671+-- custom type which implements 'SnapshotRepo'. If it is a common repo
672+-- type, consider submitting a pull request to have it included in the
673+-- library proper
674+data GenericSnapshotRepo = GenericSnapshotRepo {
675+ gSnapshotRepoName :: SnapshotRepoName
676+ , gSnapshotRepoType :: SnapshotRepoType
677+ , gSnapshotRepoSettings :: GenericSnapshotRepoSettings
678+ } deriving (Eq, Show)
679+
680+-- | Law: fromGSnapshotRepo (toGSnapshotRepo r) == Right r
681+class SnapshotRepo r where
682+ toGSnapshotRepo :: r -> GenericSnapshotRepo
683+ fromGSnapshotRepo :: GenericSnapshotRepo -> Either SnapshotRepoConversionError r
684+
685+data SnapshotRepoConversionError = RepoTypeMismatch SnapshotRepoType SnapshotRepoType
686+ -- ^ Expected type and actual type
687+ | OtherRepoConversionError Text
688+ deriving (Show, Eq)
689+
690+instance SnapshotRepo GenericSnapshotRepo where
691+ toGSnapshotRepo = id
692+ fromGSnapshotRepo = Right
693+
694+
695+newtype SnapshotRepoType = SnapshotRepoType { snapshotRepoType :: Text }
696+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
697+
698+
699+-- | Opaque representation of snapshot repo settings. Instances of
700+-- 'SnapshotRepo' will produce this.
701+newtype GenericSnapshotRepoSettings = GenericSnapshotRepoSettings { gSnapshotRepoSettingsObject :: Object }
702+ deriving (Eq, Show, ToJSON)
703+
704+
705+ -- Regardless of whether you send strongly typed json, my version of
706+ -- ES sends back stringly typed json in the settings, e.g. booleans
707+ -- as strings, so we'll try to convert them.
708+instance FromJSON GenericSnapshotRepoSettings where
709+ parseJSON = fmap (GenericSnapshotRepoSettings . fmap unStringlyTypeJSON). parseJSON
710+
711+-- | The result of running 'verifySnapshotRepo'.
712+newtype SnapshotVerification = SnapshotVerification { snapshotNodeVerifications :: [SnapshotNodeVerification] }
713+ deriving (Eq, Show)
714+
715+
716+instance FromJSON SnapshotVerification where
717+ parseJSON = withObject "SnapshotVerification" parse
718+ where
719+ parse o = do
720+ o2 <- o .: "nodes"
721+ SnapshotVerification <$> mapM (uncurry parse') (HM.toList o2)
722+ parse' rawFullId = withObject "SnapshotNodeVerification" $ \o ->
723+ SnapshotNodeVerification (FullNodeId rawFullId) <$> o .: "name"
724+
725+
726+-- | A node that has verified a snapshot
727+data SnapshotNodeVerification = SnapshotNodeVerification {
728+ snvFullId :: FullNodeId
729+ , snvNodeName :: NodeName
730+ } deriving (Eq, Show)
731+
732+newtype ClusterName = ClusterName { clusterName :: Text }
733+ deriving (Eq, Ord, Show, FromJSON)
734+
735+data NodesInfo = NodesInfo {
736+ nodesInfo :: [NodeInfo]
737+ , nodesClusterName :: ClusterName
738+ } deriving (Eq, Show)
739+
740+instance FromJSON NodesInfo where
741+ parseJSON = withObject "NodesInfo" parse
742+ where
743+ parse o = do
744+ nodes <- o .: "nodes"
745+ infos <- forM (HM.toList nodes) $ \(fullNID, v) -> do
746+ node <- parseJSON v
747+ parseNodeInfo (FullNodeId fullNID) node
748+ cn <- o .: "cluster_name"
749+ return (NodesInfo infos cn)
750+
751+data NodesStats = NodesStats {
752+ nodesStats :: [NodeStats]
753+ , nodesStatsClusterName :: ClusterName
754+ } deriving (Eq, Show)
755+
756+data NodeStats = NodeStats {
757+ nodeStatsName :: NodeName
758+ , nodeStatsFullId :: FullNodeId
759+ , nodeStatsBreakersStats :: Maybe NodeBreakersStats
760+ , nodeStatsHTTP :: NodeHTTPStats
761+ , nodeStatsTransport :: NodeTransportStats
762+ , nodeStatsFS :: NodeFSStats
763+ , nodeStatsNetwork :: NodeNetworkStats
764+ , nodeStatsThreadPool :: NodeThreadPoolsStats
765+ , nodeStatsJVM :: NodeJVMStats
766+ , nodeStatsProcess :: NodeProcessStats
767+ , nodeStatsOS :: NodeOSStats
768+ , nodeStatsIndices :: NodeIndicesStats
769+ } deriving (Eq, Show)
770+
771+data NodeBreakersStats = NodeBreakersStats {
772+ nodeStatsParentBreaker :: NodeBreakerStats
773+ , nodeStatsRequestBreaker :: NodeBreakerStats
774+ , nodeStatsFieldDataBreaker :: NodeBreakerStats
775+ } deriving (Eq, Show)
776+
777+data NodeBreakerStats = NodeBreakerStats {
778+ nodeBreakersTripped :: Int
779+ , nodeBreakersOverhead :: Double
780+ , nodeBreakersEstSize :: Bytes
781+ , nodeBreakersLimitSize :: Bytes
782+ } deriving (Eq, Show)
783+
784+data NodeHTTPStats = NodeHTTPStats {
785+ nodeHTTPTotalOpened :: Int
786+ , nodeHTTPCurrentOpen :: Int
787+ } deriving (Eq, Show)
788+
789+data NodeTransportStats = NodeTransportStats {
790+ nodeTransportTXSize :: Bytes
791+ , nodeTransportCount :: Int
792+ , nodeTransportRXSize :: Bytes
793+ , nodeTransportRXCount :: Int
794+ , nodeTransportServerOpen :: Int
795+ } deriving (Eq, Show)
796+
797+data NodeFSStats = NodeFSStats {
798+ nodeFSDataPaths :: [NodeDataPathStats]
799+ , nodeFSTotal :: NodeFSTotalStats
800+ , nodeFSTimestamp :: UTCTime
801+ } deriving (Eq, Show)
802+
803+data NodeDataPathStats = NodeDataPathStats {
804+ nodeDataPathDiskServiceTime :: Maybe Double
805+ , nodeDataPathDiskQueue :: Maybe Double
806+ , nodeDataPathIOSize :: Maybe Bytes
807+ , nodeDataPathWriteSize :: Maybe Bytes
808+ , nodeDataPathReadSize :: Maybe Bytes
809+ , nodeDataPathIOOps :: Maybe Int
810+ , nodeDataPathWrites :: Maybe Int
811+ , nodeDataPathReads :: Maybe Int
812+ , nodeDataPathAvailable :: Bytes
813+ , nodeDataPathFree :: Bytes
814+ , nodeDataPathTotal :: Bytes
815+ , nodeDataPathType :: Maybe Text
816+ , nodeDataPathDevice :: Text
817+ , nodeDataPathMount :: Text
818+ , nodeDataPathPath :: Text
819+ } deriving (Eq, Show)
820+
821+data NodeFSTotalStats = NodeFSTotalStats {
822+ nodeFSTotalDiskServiceTime :: Maybe Double
823+ , nodeFSTotalDiskQueue :: Maybe Double
824+ , nodeFSTotalIOSize :: Maybe Bytes
825+ , nodeFSTotalWriteSize :: Maybe Bytes
826+ , nodeFSTotalReadSize :: Maybe Bytes
827+ , nodeFSTotalIOOps :: Maybe Int
828+ , nodeFSTotalWrites :: Maybe Int
829+ , nodeFSTotalReads :: Maybe Int
830+ , nodeFSTotalAvailable :: Bytes
831+ , nodeFSTotalFree :: Bytes
832+ , nodeFSTotalTotal :: Bytes
833+ } deriving (Eq, Show)
834+
835+data NodeNetworkStats = NodeNetworkStats {
836+ nodeNetTCPOutRSTs :: Int
837+ , nodeNetTCPInErrs :: Int
838+ , nodeNetTCPAttemptFails :: Int
839+ , nodeNetTCPEstabResets :: Int
840+ , nodeNetTCPRetransSegs :: Int
841+ , nodeNetTCPOutSegs :: Int
842+ , nodeNetTCPInSegs :: Int
843+ , nodeNetTCPCurrEstab :: Int
844+ , nodeNetTCPPassiveOpens :: Int
845+ , nodeNetTCPActiveOpens :: Int
846+ } deriving (Eq, Show)
847+
848+data NodeThreadPoolsStats = NodeThreadPoolsStats {
849+ nodeThreadPoolsStatsSnapshot :: NodeThreadPoolStats
850+ , nodeThreadPoolsStatsBulk :: NodeThreadPoolStats
851+ , nodeThreadPoolsStatsMerge :: NodeThreadPoolStats
852+ , nodeThreadPoolsStatsGet :: NodeThreadPoolStats
853+ , nodeThreadPoolsStatsManagement :: NodeThreadPoolStats
854+ , nodeThreadPoolsStatsFetchShardStore :: Maybe NodeThreadPoolStats
855+ , nodeThreadPoolsStatsOptimize :: NodeThreadPoolStats
856+ , nodeThreadPoolsStatsFlush :: NodeThreadPoolStats
857+ , nodeThreadPoolsStatsSearch :: NodeThreadPoolStats
858+ , nodeThreadPoolsStatsWarmer :: NodeThreadPoolStats
859+ , nodeThreadPoolsStatsGeneric :: NodeThreadPoolStats
860+ , nodeThreadPoolsStatsSuggest :: NodeThreadPoolStats
861+ , nodeThreadPoolsStatsRefresh :: NodeThreadPoolStats
862+ , nodeThreadPoolsStatsIndex :: NodeThreadPoolStats
863+ , nodeThreadPoolsStatsListener :: Maybe NodeThreadPoolStats
864+ , nodeThreadPoolsStatsFetchShardStarted :: Maybe NodeThreadPoolStats
865+ , nodeThreadPoolsStatsPercolate :: NodeThreadPoolStats
866+ } deriving (Eq, Show)
867+
868+data NodeThreadPoolStats = NodeThreadPoolStats {
869+ nodeThreadPoolCompleted :: Int
870+ , nodeThreadPoolLargest :: Int
871+ , nodeThreadPoolRejected :: Int
872+ , nodeThreadPoolActive :: Int
873+ , nodeThreadPoolQueue :: Int
874+ , nodeThreadPoolThreads :: Int
875+ } deriving (Eq, Show)
876+
877+data NodeJVMStats = NodeJVMStats {
878+ nodeJVMStatsMappedBufferPool :: JVMBufferPoolStats
879+ , nodeJVMStatsDirectBufferPool :: JVMBufferPoolStats
880+ , nodeJVMStatsGCOldCollector :: JVMGCStats
881+ , nodeJVMStatsGCYoungCollector :: JVMGCStats
882+ , nodeJVMStatsPeakThreadsCount :: Int
883+ , nodeJVMStatsThreadsCount :: Int
884+ , nodeJVMStatsOldPool :: JVMPoolStats
885+ , nodeJVMStatsSurvivorPool :: JVMPoolStats
886+ , nodeJVMStatsYoungPool :: JVMPoolStats
887+ , nodeJVMStatsNonHeapCommitted :: Bytes
888+ , nodeJVMStatsNonHeapUsed :: Bytes
889+ , nodeJVMStatsHeapMax :: Bytes
890+ , nodeJVMStatsHeapCommitted :: Bytes
891+ , nodeJVMStatsHeapUsedPercent :: Int
892+ , nodeJVMStatsHeapUsed :: Bytes
893+ , nodeJVMStatsUptime :: NominalDiffTime
894+ , nodeJVMStatsTimestamp :: UTCTime
895+ } deriving (Eq, Show)
896+
897+data JVMBufferPoolStats = JVMBufferPoolStats {
898+ jvmBufferPoolStatsTotalCapacity :: Bytes
899+ , jvmBufferPoolStatsUsed :: Bytes
900+ , jvmBufferPoolStatsCount :: Int
901+ } deriving (Eq, Show)
902+
903+data JVMGCStats = JVMGCStats {
904+ jvmGCStatsCollectionTime :: NominalDiffTime
905+ , jvmGCStatsCollectionCount :: Int
906+ } deriving (Eq, Show)
907+
908+data JVMPoolStats = JVMPoolStats {
909+ jvmPoolStatsPeakMax :: Bytes
910+ , jvmPoolStatsPeakUsed :: Bytes
911+ , jvmPoolStatsMax :: Bytes
912+ , jvmPoolStatsUsed :: Bytes
913+ } deriving (Eq, Show)
914+
915+data NodeProcessStats = NodeProcessStats {
916+ nodeProcessMemTotalVirtual :: Bytes
917+ , nodeProcessMemShare :: Bytes
918+ , nodeProcessMemResident :: Bytes
919+ , nodeProcessCPUTotal :: NominalDiffTime
920+ , nodeProcessCPUUser :: NominalDiffTime
921+ , nodeProcessCPUSys :: NominalDiffTime
922+ , nodeProcessCPUPercent :: Int
923+ , nodeProcessOpenFDs :: Int
924+ , nodeProcessTimestamp :: UTCTime
925+ } deriving (Eq, Show)
926+
927+data NodeOSStats = NodeOSStats {
928+ nodeOSSwapFree :: Bytes
929+ , nodeOSSwapUsed :: Bytes
930+ , nodeOSMemActualUsed :: Bytes
931+ , nodeOSMemActualFree :: Bytes
932+ , nodeOSMemUsedPercent :: Int
933+ , nodeOSMemFreePercent :: Int
934+ , nodeOSMemUsed :: Bytes
935+ , nodeOSMemFree :: Bytes
936+ , nodeOSCPUStolen :: Int
937+ , nodeOSCPUUsage :: Int
938+ , nodeOSCPUIdle :: Int
939+ , nodeOSCPUUser :: Int
940+ , nodeOSCPUSys :: Int
941+ , nodeOSLoad :: Maybe LoadAvgs
942+ , nodeOSUptime :: NominalDiffTime
943+ , nodeOSTimestamp :: UTCTime
944+ } deriving (Eq, Show)
945+
946+data LoadAvgs = LoadAvgs {
947+ loadAvg1Min :: Double
948+ , loadAvg5Min :: Double
949+ , loadAvg15Min :: Double
950+ } deriving (Eq, Show)
951+
952+data NodeIndicesStats = NodeIndicesStats {
953+ nodeIndicesStatsRecoveryThrottleTime :: Maybe NominalDiffTime
954+ , nodeIndicesStatsRecoveryCurrentAsTarget :: Maybe Int
955+ , nodeIndicesStatsRecoveryCurrentAsSource :: Maybe Int
956+ , nodeIndicesStatsQueryCacheMisses :: Maybe Int
957+ , nodeIndicesStatsQueryCacheHits :: Maybe Int
958+ , nodeIndicesStatsQueryCacheEvictions :: Maybe Int
959+ , nodeIndicesStatsQueryCacheSize :: Maybe Bytes
960+ , nodeIndicesStatsSuggestCurrent :: Int
961+ , nodeIndicesStatsSuggestTime :: NominalDiffTime
962+ , nodeIndicesStatsSuggestTotal :: Int
963+ , nodeIndicesStatsTranslogSize :: Bytes
964+ , nodeIndicesStatsTranslogOps :: Int
965+ , nodeIndicesStatsSegFixedBitSetMemory :: Maybe Bytes
966+ , nodeIndicesStatsSegVersionMapMemory :: Bytes
967+ , nodeIndicesStatsSegIndexWriterMaxMemory :: Maybe Bytes
968+ , nodeIndicesStatsSegIndexWriterMemory :: Bytes
969+ , nodeIndicesStatsSegMemory :: Bytes
970+ , nodeIndicesStatsSegCount :: Int
971+ , nodeIndicesStatsCompletionSize :: Bytes
972+ , nodeIndicesStatsPercolateQueries :: Int
973+ , nodeIndicesStatsPercolateMemory :: Bytes
974+ , nodeIndicesStatsPercolateCurrent :: Int
975+ , nodeIndicesStatsPercolateTime :: NominalDiffTime
976+ , nodeIndicesStatsPercolateTotal :: Int
977+ , nodeIndicesStatsFieldDataEvictions :: Int
978+ , nodeIndicesStatsFieldDataMemory :: Bytes
979+ , nodeIndicesStatsIDCacheMemory :: Bytes
980+ , nodeIndicesStatsFilterCacheEvictions :: Int
981+ , nodeIndicesStatsFilterCacheMemory :: Bytes
982+ , nodeIndicesStatsWarmerTotalTime :: NominalDiffTime
983+ , nodeIndicesStatsWarmerTotal :: Int
984+ , nodeIndicesStatsWarmerCurrent :: Int
985+ , nodeIndicesStatsFlushTotalTime :: NominalDiffTime
986+ , nodeIndicesStatsFlushTotal :: Int
987+ , nodeIndicesStatsRefreshTotalTime :: NominalDiffTime
988+ , nodeIndicesStatsRefreshTotal :: Int
989+ , nodeIndicesStatsMergesTotalSize :: Bytes
990+ , nodeIndicesStatsMergesTotalDocs :: Int
991+ , nodeIndicesStatsMergesTotalTime :: NominalDiffTime
992+ , nodeIndicesStatsMergesTotal :: Int
993+ , nodeIndicesStatsMergesCurrentSize :: Bytes
994+ , nodeIndicesStatsMergesCurrentDocs :: Int
995+ , nodeIndicesStatsMergesCurrent :: Int
996+ , nodeIndicesStatsSearchFetchCurrent :: Int
997+ , nodeIndicesStatsSearchFetchTime :: NominalDiffTime
998+ , nodeIndicesStatsSearchFetchTotal :: Int
999+ , nodeIndicesStatsSearchQueryCurrent :: Int
1000+ , nodeIndicesStatsSearchQueryTime :: NominalDiffTime
1001+ , nodeIndicesStatsSearchQueryTotal :: Int
1002+ , nodeIndicesStatsSearchOpenContexts :: Int
1003+ , nodeIndicesStatsGetCurrent :: Int
1004+ , nodeIndicesStatsGetMissingTime :: NominalDiffTime
1005+ , nodeIndicesStatsGetMissingTotal :: Int
1006+ , nodeIndicesStatsGetExistsTime :: NominalDiffTime
1007+ , nodeIndicesStatsGetExistsTotal :: Int
1008+ , nodeIndicesStatsGetTime :: NominalDiffTime
1009+ , nodeIndicesStatsGetTotal :: Int
1010+ , nodeIndicesStatsIndexingThrottleTime :: Maybe NominalDiffTime
1011+ , nodeIndicesStatsIndexingIsThrottled :: Maybe Bool
1012+ , nodeIndicesStatsIndexingNoopUpdateTotal :: Maybe Int
1013+ , nodeIndicesStatsIndexingDeleteCurrent :: Int
1014+ , nodeIndicesStatsIndexingDeleteTime :: NominalDiffTime
1015+ , nodeIndicesStatsIndexingDeleteTotal :: Int
1016+ , nodeIndicesStatsIndexingIndexCurrent :: Int
1017+ , nodeIndicesStatsIndexingIndexTime :: NominalDiffTime
1018+ , nodeIndicesStatsIndexingTotal :: Int
1019+ , nodeIndicesStatsStoreThrottleTime :: NominalDiffTime
1020+ , nodeIndicesStatsStoreSize :: Bytes
1021+ , nodeIndicesStatsDocsDeleted :: Int
1022+ , nodeIndicesStatsDocsCount :: Int
1023+ } deriving (Eq, Show)
1024+
1025+data NodeInfo = NodeInfo {
1026+ nodeInfoHTTPAddress :: EsAddress
1027+ , nodeInfoBuild :: BuildHash
1028+ , nodeInfoESVersion :: VersionNumber
1029+ , nodeInfoIP :: Server
1030+ , nodeInfoHost :: Server
1031+ , nodeInfoTransportAddress :: EsAddress
1032+ , nodeInfoName :: NodeName
1033+ , nodeInfoFullId :: FullNodeId
1034+ , nodeInfoPlugins :: [NodePluginInfo]
1035+ , nodeInfoHTTP :: NodeHTTPInfo
1036+ , nodeInfoTransport :: NodeTransportInfo
1037+ , nodeInfoNetwork :: NodeNetworkInfo
1038+ , nodeInfoThreadPool :: NodeThreadPoolsInfo
1039+ , nodeInfoJVM :: NodeJVMInfo
1040+ , nodeInfoProcess :: NodeProcessInfo
1041+ , nodeInfoOS :: NodeOSInfo
1042+ , nodeInfoSettings :: Object
1043+ -- ^ The members of the settings objects are not consistent,
1044+ -- dependent on plugins, etc.
1045+ } deriving (Eq, Show)
1046+
1047+data NodePluginInfo = NodePluginInfo {
1048+ nodePluginSite :: Bool
1049+ -- ^ Is this a site plugin?
1050+ , nodePluginJVM :: Bool
1051+ -- ^ Is this plugin running on the JVM
1052+ , nodePluginDescription :: Text
1053+ , nodePluginVersion :: MaybeNA VersionNumber
1054+ , nodePluginName :: PluginName
1055+ } deriving (Eq, Show)
1056+
1057+data NodeHTTPInfo = NodeHTTPInfo {
1058+ nodeHTTPMaxContentLength :: Bytes
1059+ , nodeHTTPTransportAddress :: BoundTransportAddress
1060+ } deriving (Eq, Show)
1061+
1062+data NodeTransportInfo = NodeTransportInfo {
1063+ nodeTransportProfiles :: [BoundTransportAddress]
1064+ , nodeTransportAddress :: BoundTransportAddress
1065+ } deriving (Eq, Show)
1066+
1067+data BoundTransportAddress = BoundTransportAddress {
1068+ publishAddress :: EsAddress
1069+ , boundAddress :: EsAddress
1070+ } deriving (Eq, Show)
1071+
1072+data NodeNetworkInfo = NodeNetworkInfo {
1073+ nodeNetworkPrimaryInterface :: NodeNetworkInterface
1074+ , nodeNetworkRefreshInterval :: NominalDiffTime
1075+ } deriving (Eq, Show)
1076+
1077+newtype MacAddress = MacAddress { macAddress :: Text }
1078+ deriving (Eq, Ord, Show, FromJSON)
1079+
1080+newtype NetworkInterfaceName = NetworkInterfaceName { networkInterfaceName :: Text }
1081+ deriving (Eq, Ord, Show, FromJSON)
1082+
1083+data NodeNetworkInterface = NodeNetworkInterface {
1084+ nodeNetIfaceMacAddress :: MacAddress
1085+ , nodeNetIfaceName :: NetworkInterfaceName
1086+ , nodeNetIfaceAddress :: Server
1087+ } deriving (Eq, Show)
1088+
1089+data NodeThreadPoolsInfo = NodeThreadPoolsInfo {
1090+ nodeThreadPoolsRefresh :: NodeThreadPoolInfo
1091+ , nodeThreadPoolsManagement :: NodeThreadPoolInfo
1092+ , nodeThreadPoolsPercolate :: NodeThreadPoolInfo
1093+ , nodeThreadPoolsListener :: Maybe NodeThreadPoolInfo
1094+ , nodeThreadPoolsFetchShardStarted :: Maybe NodeThreadPoolInfo
1095+ , nodeThreadPoolsSearch :: NodeThreadPoolInfo
1096+ , nodeThreadPoolsFlush :: NodeThreadPoolInfo
1097+ , nodeThreadPoolsWarmer :: NodeThreadPoolInfo
1098+ , nodeThreadPoolsOptimize :: NodeThreadPoolInfo
1099+ , nodeThreadPoolsBulk :: NodeThreadPoolInfo
1100+ , nodeThreadPoolsSuggest :: NodeThreadPoolInfo
1101+ , nodeThreadPoolsMerge :: NodeThreadPoolInfo
1102+ , nodeThreadPoolsSnapshot :: NodeThreadPoolInfo
1103+ , nodeThreadPoolsGet :: NodeThreadPoolInfo
1104+ , nodeThreadPoolsFetchShardStore :: Maybe NodeThreadPoolInfo
1105+ , nodeThreadPoolsIndex :: NodeThreadPoolInfo
1106+ , nodeThreadPoolsGeneric :: NodeThreadPoolInfo
1107+ } deriving (Eq, Show)
1108+
1109+data NodeThreadPoolInfo = NodeThreadPoolInfo {
1110+ nodeThreadPoolQueueSize :: ThreadPoolSize
1111+ , nodeThreadPoolKeepalive :: Maybe NominalDiffTime
1112+ , nodeThreadPoolMin :: Maybe Int
1113+ , nodeThreadPoolMax :: Maybe Int
1114+ , nodeThreadPoolType :: ThreadPoolType
1115+ } deriving (Eq, Show)
1116+
1117+data ThreadPoolSize = ThreadPoolBounded Int
1118+ | ThreadPoolUnbounded
1119+ deriving (Eq, Show)
1120+
1121+data ThreadPoolType = ThreadPoolScaling
1122+ | ThreadPoolFixed
1123+ | ThreadPoolCached
1124+ deriving (Eq, Show)
1125+
1126+data NodeJVMInfo = NodeJVMInfo {
1127+ nodeJVMInfoMemoryPools :: [JVMMemoryPool]
1128+ , nodeJVMInfoMemoryPoolsGCCollectors :: [JVMGCCollector]
1129+ , nodeJVMInfoMemoryInfo :: JVMMemoryInfo
1130+ , nodeJVMInfoStartTime :: UTCTime
1131+ , nodeJVMInfoVMVendor :: Text
1132+ , nodeJVMVMVersion :: VersionNumber
1133+ -- ^ JVM doesn't seme to follow normal version conventions
1134+ , nodeJVMVMName :: Text
1135+ , nodeJVMVersion :: VersionNumber
1136+ , nodeJVMPID :: PID
1137+ } deriving (Eq, Show)
1138+
1139+-- | Handles quirks in the way JVM versions are rendered (1.7.0_101 -> 1.7.0.101)
1140+newtype JVMVersion = JVMVersion { unJVMVersion :: VersionNumber }
1141+
1142+data JVMMemoryInfo = JVMMemoryInfo {
1143+ jvmMemoryInfoDirectMax :: Bytes
1144+ , jvmMemoryInfoNonHeapMax :: Bytes
1145+ , jvmMemoryInfoNonHeapInit :: Bytes
1146+ , jvmMemoryInfoHeapMax :: Bytes
1147+ , jvmMemoryInfoHeapInit :: Bytes
1148+ } deriving (Eq, Show)
1149+
1150+newtype JVMMemoryPool = JVMMemoryPool {
1151+ jvmMemoryPool :: Text
1152+ } deriving (Eq, Show, FromJSON)
1153+
1154+newtype JVMGCCollector = JVMGCCollector {
1155+ jvmGCCollector :: Text
1156+ } deriving (Eq, Show, FromJSON)
1157+
1158+newtype PID = PID {
1159+ pid :: Int
1160+ } deriving (Eq, Show, FromJSON)
1161+
1162+data NodeOSInfo = NodeOSInfo {
1163+ nodeOSSwap :: Bytes
1164+ , nodeOSMem :: Bytes
1165+ , nodeOSCPUInfo :: CPUInfo
1166+ , nodeOSAvailableProcessors :: Int
1167+ , nodeOSRefreshInterval :: NominalDiffTime
1168+ } deriving (Eq, Show)
1169+
1170+data CPUInfo = CPUInfo {
1171+ cpuCacheSize :: Bytes
1172+ , cpuCoresPerSocket :: Int
1173+ , cpuTotalSockets :: Int
1174+ , cpuTotalCores :: Int
1175+ , cpuMHZ :: Int
1176+ , cpuModel :: Text
1177+ , cpuVendor :: Text
1178+ } deriving (Eq, Show)
1179+
1180+data NodeProcessInfo = NodeProcessInfo {
1181+ nodeProcessMLockAll :: Bool
1182+ -- ^ See <https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
1183+ , nodeProcessMaxFileDescriptors :: Int
1184+ , nodeProcessId :: PID
1185+ , nodeProcessRefreshInterval :: NominalDiffTime
1186+ } deriving (Eq, Show)
1187+
1188+-- | A quirky address format used throughout ElasticSearch. An example
1189+-- would be inet[/1.1.1.1:9200]. inet may be a placeholder for a
1190+-- <https://en.wikipedia.org/wiki/Fully_qualified_domain_name FQDN>.
1191+newtype EsAddress = EsAddress { esAddress :: Text }
1192+ deriving (Eq, Ord, Show, FromJSON)
1193+
1194+newtype PluginName = PluginName { pluginName :: Text }
1195+ deriving (Eq, Ord, Show, FromJSON)
1196+
1197+data ShardResult =
1198+ ShardResult { shardTotal :: Int
1199+ , shardsSuccessful :: Int
1200+ , shardsFailed :: Int } deriving (Eq, Show)
1201+
1202+data SnapshotState = SnapshotInit
1203+ | SnapshotStarted
1204+ | SnapshotSuccess
1205+ | SnapshotFailed
1206+ | SnapshotAborted
1207+ | SnapshotMissing
1208+ | SnapshotWaiting
1209+ deriving (Show, Eq)
1210+
1211+instance FromJSON SnapshotState where
1212+ parseJSON = withText "SnapshotState" parse
1213+ where
1214+ parse "INIT" = return SnapshotInit
1215+ parse "STARTED" = return SnapshotStarted
1216+ parse "SUCCESS" = return SnapshotSuccess
1217+ parse "FAILED" = return SnapshotFailed
1218+ parse "ABORTED" = return SnapshotAborted
1219+ parse "MISSING" = return SnapshotMissing
1220+ parse "WAITING" = return SnapshotWaiting
1221+ parse t = fail ("Invalid snapshot state " <> T.unpack t)
1222+
1223+data SnapshotRestoreSettings = SnapshotRestoreSettings {
1224+ snapRestoreWaitForCompletion :: Bool
1225+ -- ^ Should the API call return immediately after initializing
1226+ -- the restore or wait until completed? Note that if this is
1227+ -- enabled, it could wait a long time, so you should adjust your
1228+ -- 'ManagerSettings' accordingly to set long timeouts or
1229+ -- explicitly handle timeouts.
1230+ , snapRestoreIndices :: Maybe IndexSelection
1231+ -- ^ Nothing will restore all indices in the snapshot. Just [] is
1232+ -- permissable and will essentially be a no-op restore.
1233+ , snapRestoreIgnoreUnavailable :: Bool
1234+ -- ^ If set to True, any indices that do not exist will be ignored
1235+ -- during snapshot rather than failing the restore.
1236+ , snapRestoreIncludeGlobalState :: Bool
1237+ -- ^ If set to false, will ignore any global state in the snapshot
1238+ -- and will not restore it.
1239+ , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
1240+ -- ^ A regex pattern for matching indices. Used with
1241+ -- 'snapRestoreRenameReplacement', the restore can reference the
1242+ -- matched index and create a new index name upon restore.
1243+ , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
1244+ -- ^ Expression of how index renames should be constructed.
1245+ , snapRestorePartial :: Bool
1246+ -- ^ If some indices fail to restore, should the process proceed?
1247+ , snapRestoreIncludeAliases :: Bool
1248+ -- ^ Should the restore also restore the aliases captured in the
1249+ -- snapshot.
1250+ , snapRestoreIndexSettingsOverrides :: Maybe RestoreIndexSettings
1251+ -- ^ Settings to apply during the restore process. __NOTE:__ This
1252+ -- option is not supported in ES < 1.5 and should be set to
1253+ -- Nothing in that case.
1254+ , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
1255+ -- ^ This type could be more rich but it isn't clear which
1256+ -- settings are allowed to be ignored during restore, so we're
1257+ -- going with including this feature in a basic form rather than
1258+ -- omitting it. One example here would be
1259+ -- "index.refresh_interval". Any setting specified here will
1260+ -- revert back to the server default during the restore process.
1261+ } deriving (Eq, Show)
1262+
1263+-- | Regex-stype pattern, e.g. "index_(.+)" to match index names
1264+newtype RestoreRenamePattern = RestoreRenamePattern { rrPattern :: Text }
1265+ deriving (Show, Eq, Ord, ToJSON)
1266+
1267+
1268+-- | A single token in a index renaming scheme for a restore. These
1269+-- are concatenated into a string before being sent to
1270+-- ElasticSearch. Check out these Java
1271+-- <https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html docs> to find out more if you're into that sort of thing.
1272+data RestoreRenameToken = RRTLit Text
1273+ -- ^ Just a literal string of characters
1274+ | RRSubWholeMatch
1275+ -- ^ Equivalent to $0. The entire matched pattern, not any subgroup
1276+ | RRSubGroup RRGroupRefNum
1277+ -- ^ A specific reference to a group number
1278+ deriving (Show, Eq)
1279+
1280+
1281+-- | A group number for regex matching. Only values from 1-9 are
1282+-- supported. Construct with 'mkRRGroupRefNum'
1283+newtype RRGroupRefNum = RRGroupRefNum { rrGroupRefNum :: Int }
1284+ deriving (Show, Eq, Ord)
1285+
1286+instance Bounded RRGroupRefNum where
1287+ minBound = RRGroupRefNum 1
1288+ maxBound = RRGroupRefNum 9
1289+
1290+
1291+-- | Only allows valid group number references (1-9).
1292+mkRRGroupRefNum :: Int -> Maybe RRGroupRefNum
1293+mkRRGroupRefNum i
1294+ | i >= (rrGroupRefNum minBound) && i <= (rrGroupRefNum maxBound) =
1295+ Just $ RRGroupRefNum i
1296+ | otherwise = Nothing
1297+
1298+-- | Index settings that can be overridden. The docs only mention you
1299+-- can update number of replicas, but there may be more. You
1300+-- definitely cannot override shard count.
1301+data RestoreIndexSettings = RestoreIndexSettings {
1302+ restoreOverrideReplicas :: Maybe ReplicaCount
1303+ } deriving (Show, Eq)
1304+
1305+instance ToJSON RestoreIndexSettings where
1306+ toJSON RestoreIndexSettings {..} = object prs
1307+ where
1308+ prs = catMaybes [("index.number_of_replicas" .=) <$> restoreOverrideReplicas]
1309+
1310+data SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings {
1311+ repoUpdateVerify :: Bool
1312+ -- ^ After creation/update, synchronously check that nodes can
1313+ -- write to this repo. Defaults to True. You may use False if you
1314+ -- need a faster response and plan on verifying manually later
1315+ -- with 'verifySnapshotRepo'.
1316+ } deriving (Eq, Show)
1317+
1318+-- | Reasonable defaults for repo creation/update
1319+--
1320+-- * repoUpdateVerify True
1321+defaultSnapshotRepoUpdateSettings :: SnapshotRepoUpdateSettings
1322+defaultSnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings True
1323+
1324+-- | A filesystem-based snapshot repo that ships with
1325+-- ElasticSearch. This is an instance of 'SnapshotRepo' so it can be
1326+-- used with 'updateSnapshotRepo'
1327+data FsSnapshotRepo = FsSnapshotRepo {
1328+ fsrName :: SnapshotRepoName
1329+ , fsrLocation :: FilePath
1330+ , fsrCompressMetadata :: Bool
1331+ , fsrChunkSize :: Maybe Bytes
1332+ -- ^ Size by which to split large files during snapshotting.
1333+ , fsrMaxRestoreBytesPerSec :: Maybe Bytes
1334+ -- ^ Throttle node restore rate. If not supplied, defaults to 40mb/sec
1335+ , fsrMaxSnapshotBytesPerSec :: Maybe Bytes
1336+ -- ^ Throttle node snapshot rate. If not supplied, defaults to 40mb/sec
1337+ } deriving (Eq, Show)
1338+
1339+instance SnapshotRepo FsSnapshotRepo where
1340+ toGSnapshotRepo FsSnapshotRepo {..} =
1341+ GenericSnapshotRepo fsrName fsRepoType (GenericSnapshotRepoSettings settings)
1342+ where
1343+ Object settings = object $ [ "location" .= fsrLocation
1344+ , "compress" .= fsrCompressMetadata
1345+ ] ++ optionalPairs
1346+ optionalPairs = catMaybes [ ("chunk_size" .=) <$> fsrChunkSize
1347+ , ("max_restore_bytes_per_sec" .=) <$> fsrMaxRestoreBytesPerSec
1348+ , ("max_snapshot_bytes_per_sec" .=) <$> fsrMaxSnapshotBytesPerSec
1349+ ]
1350+ fromGSnapshotRepo GenericSnapshotRepo {..}
1351+ | gSnapshotRepoType == fsRepoType = do
1352+ let o = gSnapshotRepoSettingsObject gSnapshotRepoSettings
1353+ parseRepo $ do
1354+ FsSnapshotRepo gSnapshotRepoName <$> o .: "location"
1355+ <*> o .:? "compress" .!= False
1356+ <*> o .:? "chunk_size"
1357+ <*> o .:? "max_restore_bytes_per_sec"
1358+ <*> o .:? "max_snapshot_bytes_per_sec"
1359+ | otherwise = Left (RepoTypeMismatch fsRepoType gSnapshotRepoType)
1360+
1361+data SnapshotCreateSettings = SnapshotCreateSettings {
1362+ snapWaitForCompletion :: Bool
1363+ -- ^ Should the API call return immediately after initializing
1364+ -- the snapshot or wait until completed? Note that if this is
1365+ -- enabled it could wait a long time, so you should adjust your
1366+ -- 'ManagerSettings' accordingly to set long timeouts or
1367+ -- explicitly handle timeouts.
1368+ , snapIndices :: Maybe IndexSelection
1369+ -- ^ Nothing will snapshot all indices. Just [] is permissable and
1370+ -- will essentially be a no-op snapshot.
1371+ , snapIgnoreUnavailable :: Bool
1372+ -- ^ If set to True, any matched indices that don't exist will be
1373+ -- ignored. Otherwise it will be an error and fail.
1374+ , snapIncludeGlobalState :: Bool
1375+ , snapPartial :: Bool
1376+ -- ^ If some indices failed to snapshot (e.g. if not all primary
1377+ -- shards are available), should the process proceed?
1378+ } deriving (Eq, Show)
1379+
1380+-- | Reasonable defaults for snapshot creation
1381+--
1382+-- * snapWaitForCompletion False
1383+-- * snapIndices Nothing
1384+-- * snapIgnoreUnavailable False
1385+-- * snapIncludeGlobalState True
1386+-- * snapPartial False
1387+defaultSnapshotCreateSettings :: SnapshotCreateSettings
1388+defaultSnapshotCreateSettings = SnapshotCreateSettings {
1389+ snapWaitForCompletion = False
1390+ , snapIndices = Nothing
1391+ , snapIgnoreUnavailable = False
1392+ , snapIncludeGlobalState = True
1393+ , snapPartial = False
1394+ }
1395+
1396+
1397+data SnapshotSelection = SnapshotList (NonEmpty SnapshotPattern)
1398+ | AllSnapshots deriving (Eq, Show)
1399+
1400+
1401+-- | Either specifies an exact snapshot name or one with globs in it,
1402+-- e.g. @SnapPattern "foo*"@ __NOTE__: Patterns are not supported on
1403+-- ES < 1.7
1404+data SnapshotPattern = ExactSnap SnapshotName
1405+ | SnapPattern Text
1406+ deriving (Eq, Show)
1407+
1408+
1409+-- | General information about the state of a snapshot. Has some
1410+-- redundancies with 'SnapshotStatus'
1411+data SnapshotInfo = SnapshotInfo {
1412+ snapInfoShards :: ShardResult
1413+ , snapInfoFailures :: [SnapshotShardFailure]
1414+ , snapInfoDuration :: NominalDiffTime
1415+ , snapInfoEndTime :: UTCTime
1416+ , snapInfoStartTime :: UTCTime
1417+ , snapInfoState :: SnapshotState
1418+ , snapInfoIndices :: [IndexName]
1419+ , snapInfoName :: SnapshotName
1420+ } deriving (Eq, Show)
1421+
1422+
1423+instance FromJSON SnapshotInfo where
1424+ parseJSON = withObject "SnapshotInfo" parse
1425+ where
1426+ parse o = SnapshotInfo <$> o .: "shards"
1427+ <*> o .: "failures"
1428+ <*> (unMS <$> o .: "duration_in_millis")
1429+ <*> (posixMS <$> o .: "end_time_in_millis")
1430+ <*> (posixMS <$> o .: "start_time_in_millis")
1431+ <*> o .: "state"
1432+ <*> o .: "indices"
1433+ <*> o .: "snapshot"
1434+
1435+data SnapshotShardFailure = SnapshotShardFailure {
1436+ snapShardFailureIndex :: IndexName
1437+ , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
1438+ , snapShardFailureReason :: Text
1439+ , snapShardFailureShardId :: ShardId
1440+ } deriving (Eq, Show)
1441+
1442+
1443+instance FromJSON SnapshotShardFailure where
1444+ parseJSON = withObject "SnapshotShardFailure" parse
1445+ where
1446+ parse o = SnapshotShardFailure <$> o .: "index"
1447+ <*> o .:? "node_id"
1448+ <*> o .: "reason"
1449+ <*> o .: "shard_id"
1450+
1451+parseRepo :: Parser a -> Either SnapshotRepoConversionError a
1452+parseRepo parser = case parseEither (const parser) () of
1453+ Left e -> Left (OtherRepoConversionError (T.pack e))
1454+ Right a -> Right a
1455+
1456+parseNodeStats :: FullNodeId -> Object -> Parser NodeStats
1457+parseNodeStats fnid o = do
1458+ NodeStats <$> o .: "name"
1459+ <*> pure fnid
1460+ <*> o .:? "breakers"
1461+ <*> o .: "http"
1462+ <*> o .: "transport"
1463+ <*> o .: "fs"
1464+ <*> o .: "network"
1465+ <*> o .: "thread_pool"
1466+ <*> o .: "jvm"
1467+ <*> o .: "process"
1468+ <*> o .: "os"
1469+ <*> o .: "indices"
1470+
1471+parseNodeInfo :: FullNodeId -> Object -> Parser NodeInfo
1472+parseNodeInfo nid o =
1473+ NodeInfo <$> o .: "http_address"
1474+ <*> o .: "build"
1475+ <*> o .: "version"
1476+ <*> o .: "ip"
1477+ <*> o .: "host"
1478+ <*> o .: "transport_address"
1479+ <*> o .: "name"
1480+ <*> pure nid
1481+ <*> o .: "plugins"
1482+ <*> o .: "http"
1483+ <*> o .: "transport"
1484+ <*> o .: "network"
1485+ <*> o .: "thread_pool"
1486+ <*> o .: "jvm"
1487+ <*> o .: "process"
1488+ <*> o .: "os"
1489+ <*> o .: "settings"
1490+
1491+-- | Reasonable defaults for snapshot restores
1492+--
1493+-- * snapRestoreWaitForCompletion False
1494+-- * snapRestoreIndices Nothing
1495+-- * snapRestoreIgnoreUnavailable False
1496+-- * snapRestoreIncludeGlobalState True
1497+-- * snapRestoreRenamePattern Nothing
1498+-- * snapRestoreRenameReplacement Nothing
1499+-- * snapRestorePartial False
1500+-- * snapRestoreIncludeAliases True
1501+-- * snapRestoreIndexSettingsOverrides Nothing
1502+-- * snapRestoreIgnoreIndexSettings Nothing
1503+defaultSnapshotRestoreSettings :: SnapshotRestoreSettings
1504+defaultSnapshotRestoreSettings = SnapshotRestoreSettings {
1505+ snapRestoreWaitForCompletion = False
1506+ , snapRestoreIndices = Nothing
1507+ , snapRestoreIgnoreUnavailable = False
1508+ , snapRestoreIncludeGlobalState = True
1509+ , snapRestoreRenamePattern = Nothing
1510+ , snapRestoreRenameReplacement = Nothing
1511+ , snapRestorePartial = False
1512+ , snapRestoreIncludeAliases = True
1513+ , snapRestoreIndexSettingsOverrides = Nothing
1514+ , snapRestoreIgnoreIndexSettings = Nothing
1515+ }
1516+
1517+fsRepoType :: SnapshotRepoType
1518+fsRepoType = SnapshotRepoType "fs"
1519+
1520+{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
1521+type Reply = Network.HTTP.Client.Response LByteString
1522+
1523+instance FromJSON IndexSettingsSummary where
1524+ parseJSON = withObject "IndexSettingsSummary" parse
1525+ where parse o = case HM.toList o of
1526+ [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
1527+ <$> parseJSON v
1528+ <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
1529+ _ -> fail "Expected single-key object with index name"
1530+ redundant (NumberOfReplicas _) = True
1531+ redundant _ = False
1532+
1533+instance ToJSON VersionNumber where
1534+ toJSON = toJSON . Vers.showVersion . versionNumber
1535+
1536+instance FromJSON VersionNumber where
1537+ parseJSON = withText "VersionNumber" (parse . T.unpack)
1538+ where
1539+ parse s = case filter (null . snd)(RP.readP_to_S Vers.parseVersion s) of
1540+ [(v, _)] -> pure (VersionNumber v)
1541+ [] -> fail ("Invalid version string " ++ s)
1542+ xs -> fail ("Ambiguous version string " ++ s ++ " (" ++ intercalate ", " (Vers.showVersion . fst <$> xs) ++ ")")
1543+
1544+instance ToJSON Interval where
1545+ toJSON Year = "year"
1546+ toJSON Quarter = "quarter"
1547+ toJSON Month = "month"
1548+ toJSON Week = "week"
1549+ toJSON Day = "day"
1550+ toJSON Hour = "hour"
1551+ toJSON Minute = "minute"
1552+ toJSON Second = "second"
1553+ toJSON (FractionalInterval fraction interval) = toJSON $ show fraction ++ show interval
1554+
1555+parseStringInterval :: (Monad m) => String -> m NominalDiffTime
1556+parseStringInterval s = case span isNumber s of
1557+ ("", _) -> fail "Invalid interval"
1558+ (nS, unitS) -> case (readMay nS, readMay unitS) of
1559+ (Just n, Just unit) -> return (fromInteger (n * unitNDT unit))
1560+ (Nothing, _) -> fail "Invalid interval number"
1561+ (_, Nothing) -> fail "Invalid interval unit"
1562+ where
1563+ unitNDT Seconds = 1
1564+ unitNDT Minutes = 60
1565+ unitNDT Hours = 60 * 60
1566+ unitNDT Days = 24 * 60 * 60
1567+ unitNDT Weeks = 7 * 24 * 60 * 60
1568+
1569+instance ToJSON IndexSettings where
1570+ toJSON (IndexSettings s r) = object ["settings" .=
1571+ object ["index" .=
1572+ object ["number_of_shards" .= s, "number_of_replicas" .= r]
1573+ ]
1574+ ]
1575+
1576+instance FromJSON IndexSettings where
1577+ parseJSON = withObject "IndexSettings" parse
1578+ where parse o = do s <- o .: "settings"
1579+ i <- s .: "index"
1580+ IndexSettings <$> i .: "number_of_shards"
1581+ <*> i .: "number_of_replicas"
1582+
1583+instance Bounded DocVersion where
1584+ minBound = DocVersion 1
1585+ maxBound = DocVersion 9200000000000000000 -- 9.2e+18
1586+
1587+instance Enum DocVersion where
1588+ succ x
1589+ | x /= maxBound = DocVersion (succ $ docVersionNumber x)
1590+ | otherwise = succError "DocVersion"
1591+ pred x
1592+ | x /= minBound = DocVersion (pred $ docVersionNumber x)
1593+ | otherwise = predError "DocVersion"
1594+ toEnum i =
1595+ fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
1596+ fromEnum = docVersionNumber
1597+ enumFrom = boundedEnumFrom
1598+ enumFromThen = boundedEnumFromThen
1599+
1600+instance FromJSON ShardResult where
1601+ parseJSON (Object v) = ShardResult <$>
1602+ v .: "total" <*>
1603+ v .: "successful" <*>
1604+ v .: "failed"
1605+ parseJSON _ = empty
1606+
1607+instance FromJSON NodeBreakerStats where
1608+ parseJSON = withObject "NodeBreakerStats" parse
1609+ where
1610+ parse o = NodeBreakerStats <$> o .: "tripped"
1611+ <*> o .: "overhead"
1612+ <*> o .: "estimated_size_in_bytes"
1613+ <*> o .: "limit_size_in_bytes"
1614+
1615+instance FromJSON NodeHTTPStats where
1616+ parseJSON = withObject "NodeHTTPStats" parse
1617+ where
1618+ parse o = NodeHTTPStats <$> o .: "total_opened"
1619+ <*> o .: "current_open"
1620+
1621+instance FromJSON NodeTransportStats where
1622+ parseJSON = withObject "NodeTransportStats" parse
1623+ where
1624+ parse o = NodeTransportStats <$> o .: "tx_size_in_bytes"
1625+ <*> o .: "tx_count"
1626+ <*> o .: "rx_size_in_bytes"
1627+ <*> o .: "rx_count"
1628+ <*> o .: "server_open"
1629+
1630+instance FromJSON NodeFSStats where
1631+ parseJSON = withObject "NodeFSStats" parse
1632+ where
1633+ parse o = NodeFSStats <$> o .: "data"
1634+ <*> o .: "total"
1635+ <*> (posixMS <$> o .: "timestamp")
1636+
1637+instance FromJSON NodeDataPathStats where
1638+ parseJSON = withObject "NodeDataPathStats" parse
1639+ where
1640+ parse o =
1641+ NodeDataPathStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
1642+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
1643+ <*> o .:? "disk_io_size_in_bytes"
1644+ <*> o .:? "disk_write_size_in_bytes"
1645+ <*> o .:? "disk_read_size_in_bytes"
1646+ <*> o .:? "disk_io_op"
1647+ <*> o .:? "disk_writes"
1648+ <*> o .:? "disk_reads"
1649+ <*> o .: "available_in_bytes"
1650+ <*> o .: "free_in_bytes"
1651+ <*> o .: "total_in_bytes"
1652+ <*> o .:? "type"
1653+ <*> o .: "dev"
1654+ <*> o .: "mount"
1655+ <*> o .: "path"
1656+
1657+instance FromJSON NodeNetworkStats where
1658+ parseJSON = withObject "NodeNetworkStats" parse
1659+ where
1660+ parse o = do
1661+ tcp <- o .: "tcp"
1662+ NodeNetworkStats <$> tcp .: "out_rsts"
1663+ <*> tcp .: "in_errs"
1664+ <*> tcp .: "attempt_fails"
1665+ <*> tcp .: "estab_resets"
1666+ <*> tcp .: "retrans_segs"
1667+ <*> tcp .: "out_segs"
1668+ <*> tcp .: "in_segs"
1669+ <*> tcp .: "curr_estab"
1670+ <*> tcp .: "passive_opens"
1671+ <*> tcp .: "active_opens"
1672+
1673+instance FromJSON NodeThreadPoolsStats where
1674+ parseJSON = withObject "NodeThreadPoolsStats" parse
1675+ where
1676+ parse o = NodeThreadPoolsStats <$> o .: "snapshot"
1677+ <*> o .: "bulk"
1678+ <*> o .: "merge"
1679+ <*> o .: "get"
1680+ <*> o .: "management"
1681+ <*> o .:? "fetch_shard_store"
1682+ <*> o .: "optimize"
1683+ <*> o .: "flush"
1684+ <*> o .: "search"
1685+ <*> o .: "warmer"
1686+ <*> o .: "generic"
1687+ <*> o .: "suggest"
1688+ <*> o .: "refresh"
1689+ <*> o .: "index"
1690+ <*> o .:? "listener"
1691+ <*> o .:? "fetch_shard_started"
1692+ <*> o .: "percolate"
1693+instance FromJSON NodeThreadPoolStats where
1694+ parseJSON = withObject "NodeThreadPoolStats" parse
1695+ where
1696+ parse o = NodeThreadPoolStats <$> o .: "completed"
1697+ <*> o .: "largest"
1698+ <*> o .: "rejected"
1699+ <*> o .: "active"
1700+ <*> o .: "queue"
1701+ <*> o .: "threads"
1702+
1703+instance FromJSON NodeJVMStats where
1704+ parseJSON = withObject "NodeJVMStats" parse
1705+ where
1706+ parse o = do
1707+ bufferPools <- o .: "buffer_pools"
1708+ mapped <- bufferPools .: "mapped"
1709+ direct <- bufferPools .: "direct"
1710+ gc <- o .: "gc"
1711+ collectors <- gc .: "collectors"
1712+ oldC <- collectors .: "old"
1713+ youngC <- collectors .: "young"
1714+ threads <- o .: "threads"
1715+ mem <- o .: "mem"
1716+ pools <- mem .: "pools"
1717+ oldM <- pools .: "old"
1718+ survivorM <- pools .: "survivor"
1719+ youngM <- pools .: "young"
1720+ NodeJVMStats <$> pure mapped
1721+ <*> pure direct
1722+ <*> pure oldC
1723+ <*> pure youngC
1724+ <*> threads .: "peak_count"
1725+ <*> threads .: "count"
1726+ <*> pure oldM
1727+ <*> pure survivorM
1728+ <*> pure youngM
1729+ <*> mem .: "non_heap_committed_in_bytes"
1730+ <*> mem .: "non_heap_used_in_bytes"
1731+ <*> mem .: "heap_max_in_bytes"
1732+ <*> mem .: "heap_committed_in_bytes"
1733+ <*> mem .: "heap_used_percent"
1734+ <*> mem .: "heap_used_in_bytes"
1735+ <*> (unMS <$> o .: "uptime_in_millis")
1736+ <*> (posixMS <$> o .: "timestamp")
1737+
1738+instance FromJSON JVMBufferPoolStats where
1739+ parseJSON = withObject "JVMBufferPoolStats" parse
1740+ where
1741+ parse o = JVMBufferPoolStats <$> o .: "total_capacity_in_bytes"
1742+ <*> o .: "used_in_bytes"
1743+ <*> o .: "count"
1744+
1745+instance FromJSON JVMGCStats where
1746+ parseJSON = withObject "JVMGCStats" parse
1747+ where
1748+ parse o = JVMGCStats <$> (unMS <$> o .: "collection_time_in_millis")
1749+ <*> o .: "collection_count"
1750+
1751+instance FromJSON JVMPoolStats where
1752+ parseJSON = withObject "JVMPoolStats" parse
1753+ where
1754+ parse o = JVMPoolStats <$> o .: "peak_max_in_bytes"
1755+ <*> o .: "peak_used_in_bytes"
1756+ <*> o .: "max_in_bytes"
1757+ <*> o .: "used_in_bytes"
1758+
1759+instance FromJSON NodeProcessStats where
1760+ parseJSON = withObject "NodeProcessStats" parse
1761+ where
1762+ parse o = do
1763+ mem <- o .: "mem"
1764+ cpu <- o .: "cpu"
1765+ NodeProcessStats <$> mem .: "total_virtual_in_bytes"
1766+ <*> mem .: "share_in_bytes"
1767+ <*> mem .: "resident_in_bytes"
1768+ <*> (unMS <$> cpu .: "total_in_millis")
1769+ <*> (unMS <$> cpu .: "user_in_millis")
1770+ <*> (unMS <$> cpu .: "sys_in_millis")
1771+ <*> cpu .: "percent"
1772+ <*> o .: "open_file_descriptors"
1773+ <*> (posixMS <$> o .: "timestamp")
1774+
1775+instance FromJSON NodeOSStats where
1776+ parseJSON = withObject "NodeOSStats" parse
1777+ where
1778+ parse o = do
1779+ swap <- o .: "swap"
1780+ mem <- o .: "mem"
1781+ cpu <- o .: "cpu"
1782+ load <- o .:? "load_average"
1783+ NodeOSStats <$> swap .: "free_in_bytes"
1784+ <*> swap .: "used_in_bytes"
1785+ <*> mem .: "actual_used_in_bytes"
1786+ <*> mem .: "actual_free_in_bytes"
1787+ <*> mem .: "used_percent"
1788+ <*> mem .: "free_percent"
1789+ <*> mem .: "used_in_bytes"
1790+ <*> mem .: "free_in_bytes"
1791+ <*> cpu .: "stolen"
1792+ <*> cpu .: "usage"
1793+ <*> cpu .: "idle"
1794+ <*> cpu .: "user"
1795+ <*> cpu .: "sys"
1796+ <*> pure load
1797+ <*> (unMS <$> o .: "uptime_in_millis")
1798+ <*> (posixMS <$> o .: "timestamp")
1799+
1800+instance FromJSON LoadAvgs where
1801+ parseJSON = withArray "LoadAvgs" parse
1802+ where
1803+ parse v = case V.toList v of
1804+ [one, five, fifteen] -> LoadAvgs <$> parseJSON one
1805+ <*> parseJSON five
1806+ <*> parseJSON fifteen
1807+ _ -> fail "Expecting a triple of Doubles"
1808+
1809+instance FromJSON NodeIndicesStats where
1810+ parseJSON = withObject "NodeIndicesStats" parse
1811+ where
1812+ parse o = do
1813+ let (.::) mv k = case mv of
1814+ Just v -> Just <$> v .: k
1815+ Nothing -> pure Nothing
1816+ mRecovery <- o .:? "recovery"
1817+ mQueryCache <- o .:? "query_cache"
1818+ suggest <- o .: "suggest"
1819+ translog <- o .: "translog"
1820+ segments <- o .: "segments"
1821+ completion <- o .: "completion"
1822+ percolate <- o .: "percolate"
1823+ fielddata <- o .: "fielddata"
1824+ idCache <- o .: "id_cache"
1825+ filterCache <- o .: "filter_cache"
1826+ warmer <- o .: "warmer"
1827+ flush <- o .: "flush"
1828+ refresh <- o .: "refresh"
1829+ merges <- o .: "merges"
1830+ search <- o .: "search"
1831+ getStats <- o .: "get"
1832+ indexing <- o .: "indexing"
1833+ store <- o .: "store"
1834+ docs <- o .: "docs"
1835+ NodeIndicesStats <$> (fmap unMS <$> mRecovery .:: "throttle_time_in_millis")
1836+ <*> mRecovery .:: "current_as_target"
1837+ <*> mRecovery .:: "current_as_source"
1838+ <*> mQueryCache .:: "miss_count"
1839+ <*> mQueryCache .:: "hit_count"
1840+ <*> mQueryCache .:: "evictions"
1841+ <*> mQueryCache .:: "memory_size_in_bytes"
1842+ <*> suggest .: "current"
1843+ <*> (unMS <$> suggest .: "time_in_millis")
1844+ <*> suggest .: "total"
1845+ <*> translog .: "size_in_bytes"
1846+ <*> translog .: "operations"
1847+ <*> segments .:? "fixed_bit_set_memory_in_bytes"
1848+ <*> segments .: "version_map_memory_in_bytes"
1849+ <*> segments .:? "index_writer_max_memory_in_bytes"
1850+ <*> segments .: "index_writer_memory_in_bytes"
1851+ <*> segments .: "memory_in_bytes"
1852+ <*> segments .: "count"
1853+ <*> completion .: "size_in_bytes"
1854+ <*> percolate .: "queries"
1855+ <*> percolate .: "memory_size_in_bytes"
1856+ <*> percolate .: "current"
1857+ <*> (unMS <$> percolate .: "time_in_millis")
1858+ <*> percolate .: "total"
1859+ <*> fielddata .: "evictions"
1860+ <*> fielddata .: "memory_size_in_bytes"
1861+ <*> idCache .: "memory_size_in_bytes"
1862+ <*> filterCache .: "evictions"
1863+ <*> filterCache .: "memory_size_in_bytes"
1864+ <*> (unMS <$> warmer .: "total_time_in_millis")
1865+ <*> warmer .: "total"
1866+ <*> warmer .: "current"
1867+ <*> (unMS <$> flush .: "total_time_in_millis")
1868+ <*> flush .: "total"
1869+ <*> (unMS <$> refresh .: "total_time_in_millis")
1870+ <*> refresh .: "total"
1871+ <*> merges .: "total_size_in_bytes"
1872+ <*> merges .: "total_docs"
1873+ <*> (unMS <$> merges .: "total_time_in_millis")
1874+ <*> merges .: "total"
1875+ <*> merges .: "current_size_in_bytes"
1876+ <*> merges .: "current_docs"
1877+ <*> merges .: "current"
1878+ <*> search .: "fetch_current"
1879+ <*> (unMS <$> search .: "fetch_time_in_millis")
1880+ <*> search .: "fetch_total"
1881+ <*> search .: "query_current"
1882+ <*> (unMS <$> search .: "query_time_in_millis")
1883+ <*> search .: "query_total"
1884+ <*> search .: "open_contexts"
1885+ <*> getStats .: "current"
1886+ <*> (unMS <$> getStats .: "missing_time_in_millis")
1887+ <*> getStats .: "missing_total"
1888+ <*> (unMS <$> getStats .: "exists_time_in_millis")
1889+ <*> getStats .: "exists_total"
1890+ <*> (unMS <$> getStats .: "time_in_millis")
1891+ <*> getStats .: "total"
1892+ <*> (fmap unMS <$> indexing .:? "throttle_time_in_millis")
1893+ <*> indexing .:? "is_throttled"
1894+ <*> indexing .:? "noop_update_total"
1895+ <*> indexing .: "delete_current"
1896+ <*> (unMS <$> indexing .: "delete_time_in_millis")
1897+ <*> indexing .: "delete_total"
1898+ <*> indexing .: "index_current"
1899+ <*> (unMS <$> indexing .: "index_time_in_millis")
1900+ <*> indexing .: "index_total"
1901+ <*> (unMS <$> store .: "throttle_time_in_millis")
1902+ <*> store .: "size_in_bytes"
1903+ <*> docs .: "deleted"
1904+ <*> docs .: "count"
1905+
1906+instance FromJSON NodeBreakersStats where
1907+ parseJSON = withObject "NodeBreakersStats" parse
1908+ where
1909+ parse o = NodeBreakersStats <$> o .: "parent"
1910+ <*> o .: "request"
1911+ <*> o .: "fielddata"
1912+
1913+instance FromJSON NodePluginInfo where
1914+ parseJSON = withObject "NodePluginInfo" parse
1915+ where
1916+ parse o = NodePluginInfo <$> o .: "site"
1917+ <*> o .: "jvm"
1918+ <*> o .: "description"
1919+ <*> o .: "version"
1920+ <*> o .: "name"
1921+
1922+instance FromJSON NodeHTTPInfo where
1923+ parseJSON = withObject "NodeHTTPInfo" parse
1924+ where
1925+ parse o = NodeHTTPInfo <$> o .: "max_content_length_in_bytes"
1926+ <*> parseJSON (Object o)
1927+
1928+instance FromJSON NodeTransportInfo where
1929+ parseJSON = withObject "NodeTransportInfo" parse
1930+ where
1931+ parse o = NodeTransportInfo <$> (maybe (return mempty) parseProfiles =<< o .:? "profiles")
1932+ <*> parseJSON (Object o)
1933+ parseProfiles (Object o) | HM.null o = return []
1934+ parseProfiles v@(Array _) = parseJSON v
1935+ parseProfiles Null = return []
1936+ parseProfiles _ = fail "Could not parse profiles"
1937+
1938+instance FromJSON NodeNetworkInfo where
1939+ parseJSON = withObject "NodeNetworkInfo" parse
1940+ where
1941+ parse o = NodeNetworkInfo <$> o .: "primary_interface"
1942+ <*> (unMS <$> o .: "refresh_interval_in_millis")
1943+
1944+instance FromJSON NodeNetworkInterface where
1945+ parseJSON = withObject "NodeNetworkInterface" parse
1946+ where
1947+ parse o = NodeNetworkInterface <$> o .: "mac_address"
1948+ <*> o .: "name"
1949+ <*> o .: "address"
1950+
1951+instance FromJSON NodeJVMInfo where
1952+ parseJSON = withObject "NodeJVMInfo" parse
1953+ where
1954+ parse o = NodeJVMInfo <$> o .: "memory_pools"
1955+ <*> o .: "gc_collectors"
1956+ <*> o .: "mem"
1957+ <*> (posixMS <$> o .: "start_time_in_millis")
1958+ <*> o .: "vm_vendor"
1959+ <*> o .: "vm_version"
1960+ <*> o .: "vm_name"
1961+ <*> (unJVMVersion <$> o .: "version")
1962+ <*> o .: "pid"
1963+
1964+instance FromJSON NodeThreadPoolsInfo where
1965+ parseJSON = withObject "NodeThreadPoolsInfo" parse
1966+ where
1967+ parse o = NodeThreadPoolsInfo <$> o .: "refresh"
1968+ <*> o .: "management"
1969+ <*> o .: "percolate"
1970+ <*> o .:? "listener"
1971+ <*> o .:? "fetch_shard_started"
1972+ <*> o .: "search"
1973+ <*> o .: "flush"
1974+ <*> o .: "warmer"
1975+ <*> o .: "optimize"
1976+ <*> o .: "bulk"
1977+ <*> o .: "suggest"
1978+ <*> o .: "merge"
1979+ <*> o .: "snapshot"
1980+ <*> o .: "get"
1981+ <*> o .:? "fetch_shard_store"
1982+ <*> o .: "index"
1983+ <*> o .: "generic"
1984+
1985+instance FromJSON NodeProcessInfo where
1986+ parseJSON = withObject "NodeProcessInfo" parse
1987+ where
1988+ parse o = NodeProcessInfo <$> o .: "mlockall"
1989+ <*> o .: "max_file_descriptors"
1990+ <*> o .: "id"
1991+ <*> (unMS <$> o .: "refresh_interval_in_millis")
1992+
1993+instance FromJSON NodeOSInfo where
1994+ parseJSON = withObject "NodeOSInfo" parse
1995+ where
1996+ parse o = do
1997+ swap <- o .: "swap"
1998+ mem <- o .: "mem"
1999+ NodeOSInfo <$> swap .: "total_in_bytes"
2000+ <*> mem .: "total_in_bytes"
2001+ <*> o .: "cpu"
2002+ <*> o .: "available_processors"
2003+ <*> (unMS <$> o .: "refresh_interval_in_millis")
2004+
2005+instance FromJSON ReplicaBounds where
2006+ parseJSON v = withText "ReplicaBounds" parseText v
2007+ <|> withBool "ReplicaBounds" parseBool v
2008+ where parseText t = case T.splitOn "-" t of
2009+ [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
2010+ [a, b] -> ReplicasBounded <$> parseReadText a
2011+ <*> parseReadText b
2012+ _ -> fail ("Could not parse ReplicaBounds: " <> show t)
2013+ parseBool False = pure ReplicasUnbounded
2014+ parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
2015+
2016+instance FromJSON NominalDiffTimeJSON where
2017+ parseJSON = withText "NominalDiffTime" parse
2018+ where parse t = case T.takeEnd 1 t of
2019+ "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
2020+ _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
2021+
2022+instance FromJSON AllocationPolicy where
2023+ parseJSON = withText "AllocationPolicy" parse
2024+ where parse "all" = pure AllocAll
2025+ parse "primaries" = pure AllocPrimaries
2026+ parse "new_primaries" = pure AllocNewPrimaries
2027+ parse "none" = pure AllocNone
2028+ parse t = fail ("Invlaid AllocationPolicy: " <> show t)
2029+
2030+instance ToJSON CompoundFormat where
2031+ toJSON (CompoundFileFormat x) = Bool x
2032+ toJSON (MergeSegmentVsTotalIndex x) = toJSON x
2033+
2034+instance FromJSON CompoundFormat where
2035+ parseJSON v = CompoundFileFormat <$> parseJSON v
2036+ <|> MergeSegmentVsTotalIndex <$> parseJSON v
2037+
2038+instance FromJSON NodeFSTotalStats where
2039+ parseJSON = withObject "NodeFSTotalStats" parse
2040+ where
2041+ parse o = NodeFSTotalStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
2042+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
2043+ <*> o .:? "disk_io_size_in_bytes"
2044+ <*> o .:? "disk_write_size_in_bytes"
2045+ <*> o .:? "disk_read_size_in_bytes"
2046+ <*> o .:? "disk_io_op"
2047+ <*> o .:? "disk_writes"
2048+ <*> o .:? "disk_reads"
2049+ <*> o .: "available_in_bytes"
2050+ <*> o .: "free_in_bytes"
2051+ <*> o .: "total_in_bytes"
2052+
2053+instance FromJSON BoundTransportAddress where
2054+ parseJSON = withObject "BoundTransportAddress" parse
2055+ where
2056+ parse o = BoundTransportAddress <$> o .: "publish_address"
2057+ <*> o .: "bound_address"
2058+
2059+instance FromJSON JVMMemoryInfo where
2060+ parseJSON = withObject "JVMMemoryInfo" parse
2061+ where
2062+ parse o = JVMMemoryInfo <$> o .: "direct_max_in_bytes"
2063+ <*> o .: "non_heap_max_in_bytes"
2064+ <*> o .: "non_heap_init_in_bytes"
2065+ <*> o .: "heap_max_in_bytes"
2066+ <*> o .: "heap_init_in_bytes"
2067+
2068+instance FromJSON JVMVersion where
2069+ parseJSON (String t) =
2070+ JVMVersion <$> parseJSON (String (T.replace "_" "." t))
2071+ parseJSON v = JVMVersion <$> parseJSON v
2072+
2073+instance FromJSON NodeThreadPoolInfo where
2074+ parseJSON = withObject "NodeThreadPoolInfo" parse
2075+ where
2076+ parse o = do
2077+ ka <- maybe (return Nothing) (fmap Just . parseStringInterval) =<< o .:? "keep_alive"
2078+ NodeThreadPoolInfo <$> (parseJSON . unStringlyTypeJSON =<< o .: "queue_size")
2079+ <*> pure ka
2080+ <*> o .:? "min"
2081+ <*> o .:? "max"
2082+ <*> o .: "type"
2083+
2084+instance FromJSON CPUInfo where
2085+ parseJSON = withObject "CPUInfo" parse
2086+ where
2087+ parse o = CPUInfo <$> o .: "cache_size_in_bytes"
2088+ <*> o .: "cores_per_socket"
2089+ <*> o .: "total_sockets"
2090+ <*> o .: "total_cores"
2091+ <*> o .: "mhz"
2092+ <*> o .: "model"
2093+ <*> o .: "vendor"
2094+
2095+instance FromJSON ThreadPoolSize where
2096+ parseJSON v = parseAsNumber v <|> parseAsString v
2097+ where
2098+ parseAsNumber = parseAsInt <=< parseJSON
2099+ parseAsInt (-1) = return ThreadPoolUnbounded
2100+ parseAsInt n
2101+ | n >= 0 = return (ThreadPoolBounded n)
2102+ | otherwise = fail "Thread pool size must be >= -1."
2103+ parseAsString = withText "ThreadPoolSize" $ \t ->
2104+ case first (readMay . T.unpack) (T.span isNumber t) of
2105+ (Just n, "k") -> return (ThreadPoolBounded (n * 1000))
2106+ (Just n, "") -> return (ThreadPoolBounded n)
2107+ _ -> fail ("Invalid thread pool size " <> T.unpack t)
2108+
2109+instance FromJSON ThreadPoolType where
2110+ parseJSON = withText "ThreadPoolType" parse
2111+ where
2112+ parse "scaling" = return ThreadPoolScaling
2113+ parse "fixed" = return ThreadPoolFixed
2114+ parse "cached" = return ThreadPoolCached
2115+ parse e = fail ("Unexpected thread pool type" <> T.unpack e)
2116+
2117+data Source =
2118+ NoSource
2119+ | SourcePatterns PatternOrPatterns
2120+ | SourceIncludeExclude Include Exclude
2121+ deriving (Show, Eq)
2122+
2123+instance ToJSON Source where
2124+ toJSON NoSource = toJSON False
2125+ toJSON (SourcePatterns patterns) = toJSON patterns
2126+ toJSON (SourceIncludeExclude incl excl) = object [ "include" .= incl, "exclude" .= excl ]
2127+
2128+data PatternOrPatterns =
2129+ PopPattern Pattern
2130+ | PopPatterns [Pattern]
2131+ deriving (Eq, Show)
2132+
2133+data Include = Include [Pattern] deriving (Eq, Show)
2134+data Exclude = Exclude [Pattern] deriving (Eq, Show)
2135+
2136+newtype Pattern = Pattern Text deriving (Eq, Show)
2137+
2138+newtype ScrollId = ScrollId Text deriving (Eq, Show, Ord, ToJSON, FromJSON)
2139+
2140+instance ToJSON PatternOrPatterns where
2141+ toJSON (PopPattern pattern) = toJSON pattern
2142+ toJSON (PopPatterns patterns) = toJSON patterns
2143+
2144+instance ToJSON Include where
2145+ toJSON (Include patterns) = toJSON patterns
2146+
2147+instance ToJSON Exclude where
2148+ toJSON (Exclude patterns) = toJSON patterns
2149+
2150+instance ToJSON Pattern where
2151+ toJSON (Pattern pattern) = toJSON pattern
2152+
2153+instance FromJSON NodesStats where
2154+ parseJSON = withObject "NodesStats" parse
2155+ where
2156+ parse o = do
2157+ nodes <- o .: "nodes"
2158+ stats <- forM (HM.toList nodes) $ \(fullNID, v) -> do
2159+ node <- parseJSON v
2160+ parseNodeStats (FullNodeId fullNID) node
2161+ cn <- o .: "cluster_name"
2162+ return (NodesStats stats cn)
2163+
2164+instance ToJSON UpdatableIndexSetting where
2165+ toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
2166+ toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
2167+ toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
2168+ toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
2169+ toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
2170+ toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
2171+ toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
2172+ toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
2173+ toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
2174+ toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
2175+ toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
2176+ toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
2177+ toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
2178+ toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
2179+ toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
2180+ toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
2181+ toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
2182+ toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
2183+ toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
2184+ toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
2185+ toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
2186+ toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
2187+ toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
2188+ toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
2189+ toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
2190+ toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
2191+ toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
2192+ toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
2193+
2194+instance FromJSON UpdatableIndexSetting where
2195+ parseJSON = withObject "UpdatableIndexSetting" parse
2196+ where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
2197+ <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
2198+ <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
2199+ <|> indexConcurrency `taggedAt` ["index", "concurrency"]
2200+ <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
2201+ <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
2202+ <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
2203+ <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
2204+ <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
2205+ <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
2206+ <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
2207+ <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
2208+ <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
2209+ <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
2210+ <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
2211+ <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
2212+ <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
2213+ <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
2214+ <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
2215+ <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
2216+ <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
2217+ <|> compoundFormat `taggedAt` ["index", "compound_format"]
2218+ <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
2219+ <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
2220+ <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
2221+ <|> blocksRead `taggedAt` ["blocks", "read"]
2222+ <|> blocksWrite `taggedAt` ["blocks", "write"]
2223+ <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
2224+ where taggedAt f ks = taggedAt' f (Object o) ks
2225+ taggedAt' f v [] = f =<< (parseJSON v <|> (parseJSON (unStringlyTypeJSON v)))
2226+ taggedAt' f v (k:ks) = withObject "Object" (\o -> do v' <- o .: k
2227+ taggedAt' f v' ks) v
2228+ numberOfReplicas = pure . NumberOfReplicas
2229+ autoExpandReplicas = pure . AutoExpandReplicas
2230+ refreshInterval = pure . RefreshInterval . ndtJSON
2231+ indexConcurrency = pure . IndexConcurrency
2232+ failOnMergeFailure = pure . FailOnMergeFailure
2233+ translogFlushThresholdOps = pure . TranslogFlushThresholdOps
2234+ translogFlushThresholdSize = pure . TranslogFlushThresholdSize
2235+ translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
2236+ translogDisableFlush = pure . TranslogDisableFlush
2237+ cacheFilterMaxSize = pure . CacheFilterMaxSize
2238+ cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
2239+ gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
2240+ routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
2241+ routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
2242+ routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
2243+ routingAllocationEnable = pure . RoutingAllocationEnable
2244+ routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
2245+ recoveryInitialShards = pure . RecoveryInitialShards
2246+ gcDeletes = pure . GCDeletes . ndtJSON
2247+ ttlDisablePurge = pure . TTLDisablePurge
2248+ translogFSType = pure . TranslogFSType
2249+ compoundFormat = pure . IndexCompoundFormat
2250+ compoundOnFlush = pure . IndexCompoundOnFlush
2251+ warmerEnabled = pure . WarmerEnabled
2252+ blocksReadOnly = pure . BlocksReadOnly
2253+ blocksRead = pure . BlocksRead
2254+ blocksWrite = pure . BlocksWrite
2255+ blocksMetaData = pure . BlocksMetaData
2256+
2257+instance ToJSON ReplicaBounds where
2258+ toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
2259+ toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
2260+ toJSON ReplicasUnbounded = Bool False
2261+
2262+instance ToJSON NominalDiffTimeJSON where
2263+ toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
2264+
2265+instance ToJSON AllocationPolicy where
2266+ toJSON AllocAll = String "all"
2267+ toJSON AllocPrimaries = String "primaries"
2268+ toJSON AllocNewPrimaries = String "new_primaries"
2269+ toJSON AllocNone = String "none"
2270+
2271+instance ToJSON IndexTemplate where
2272+ toJSON (IndexTemplate p s m) = merge
2273+ (object [ "template" .= p
2274+ , "mappings" .= foldl' merge (object []) m
2275+ ])
2276+ (toJSON s)
2277+ where
2278+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
2279+ merge o Null = o
2280+ merge _ _ = undefined
2281+
2282+instance FromJSON IndexAliasesSummary where
2283+ parseJSON = withObject "IndexAliasesSummary" parse
2284+ where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
2285+ go ixn = withObject "index aliases" $ \ia -> do
2286+ aliases <- ia .:? "aliases" .!= mempty
2287+ forM (HM.toList aliases) $ \(aName, v) -> do
2288+ let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
2289+ IndexAliasSummary indexAlias <$> parseJSON v
2290+
2291+instance ToJSON IndexAliasAction where
2292+ toJSON (AddAlias ia opts) = object ["add" .= (iaObj <> optsObj)]
2293+ where Object iaObj = toJSON ia
2294+ Object optsObj = toJSON opts
2295+ toJSON (RemoveAlias ia) = object ["remove" .= iaObj]
2296+ where Object iaObj = toJSON ia
2297+
2298+instance ToJSON IndexAlias where
2299+ toJSON IndexAlias {..} = object ["index" .= srcIndex
2300+ , "alias" .= indexAlias
2301+ ]
2302+
2303+instance FromJSON AliasRouting where
2304+ parseJSON = withObject "AliasRouting" parse
2305+ where parse o = parseAll o <|> parseGranular o
2306+ parseAll o = AllAliasRouting <$> o .: "routing"
2307+ parseGranular o = do
2308+ sr <- o .:? "search_routing"
2309+ ir <- o .:? "index_routing"
2310+ if isNothing sr && isNothing ir
2311+ then fail "Both search_routing and index_routing can't be blank"
2312+ else return (GranularAliasRouting sr ir)
2313+
2314+instance FromJSON IndexAliasCreate where
2315+ parseJSON v = withObject "IndexAliasCreate" parse v
2316+ where parse o = IndexAliasCreate <$> optional (parseJSON v)
2317+ <*> o .:? "filter"
2318+
2319+instance ToJSON IndexAliasCreate where
2320+ toJSON IndexAliasCreate {..} = Object (filterObj <> routingObj)
2321+ where filterObj = maybe mempty (HM.singleton "filter" . toJSON) aliasCreateFilter
2322+ Object routingObj = maybe (Object mempty) toJSON aliasCreateRouting
2323+
2324+instance ToJSON AliasRouting where
2325+ toJSON (AllAliasRouting v) = object ["routing" .= v]
2326+ toJSON (GranularAliasRouting srch idx) = object (catMaybes prs)
2327+ where prs = [("search_routing" .=) <$> srch
2328+ ,("index_routing" .=) <$> idx]
2329+
2330+instance ToJSON SearchAliasRouting where
2331+ toJSON (SearchAliasRouting rvs) = toJSON (T.intercalate "," (routingValue <$> toList rvs))
2332+
2333+instance FromJSON SearchAliasRouting where
2334+ parseJSON = withText "SearchAliasRouting" parse
2335+ where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
2336+
2337+instance FromJSON EsError where
2338+ parseJSON (Object v) = EsError <$>
2339+ v .: "status" <*>
2340+ (v .: "error" <|> (v .: "error" >>= (.: "reason")))
2341+ parseJSON _ = empty
src/Database/V1/Bloodhound/Internal/Highlight.hsnew file+144
@@ -0,0 +1,144 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Database.V1.Bloodhound.Internal.Highlight where
4+
5+
6+import Bloodhound.Import
7+
8+import qualified Data.Map as M
9+
10+import Database.V1.Bloodhound.Internal.Newtypes
11+import Database.V1.Bloodhound.Internal.Query
12+
13+type HitHighlight = M.Map Text [Text]
14+
15+data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
16+ , highlightFields :: [FieldHighlight]
17+ } deriving (Show, Eq)
18+
19+instance ToJSON Highlights where
20+ toJSON (Highlights global fields) =
21+ omitNulls (("fields" .= fields)
22+ : highlightSettingsPairs global)
23+
24+data HighlightSettings = Plain PlainHighlight
25+ | Postings PostingsHighlight
26+ | FastVector FastVectorHighlight
27+ deriving (Show, Eq)
28+
29+data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
30+ deriving (Show, Eq)
31+
32+data PlainHighlight =
33+ PlainHighlight { plainCommon :: Maybe CommonHighlight
34+ , plainNonPost :: Maybe NonPostings } deriving (Show, Eq)
35+
36+ -- This requires that index_options are set to 'offset' in the mapping.
37+data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Show, Eq)
38+
39+-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
40+data FastVectorHighlight =
41+ FastVectorHighlight { fvCommon :: Maybe CommonHighlight
42+ , fvNonPostSettings :: Maybe NonPostings
43+ , boundaryChars :: Maybe Text
44+ , boundaryMaxScan :: Maybe Int
45+ , fragmentOffset :: Maybe Int
46+ , matchedFields :: [Text]
47+ , phraseLimit :: Maybe Int
48+ } deriving (Show, Eq)
49+
50+data CommonHighlight =
51+ CommonHighlight { order :: Maybe Text
52+ , forceSource :: Maybe Bool
53+ , tag :: Maybe HighlightTag
54+ , encoder :: Maybe HighlightEncoder
55+ , noMatchSize :: Maybe Int
56+ , highlightQuery :: Maybe Query
57+ , requireFieldMatch :: Maybe Bool
58+ } deriving (Show, Eq)
59+
60+-- Settings that are only applicable to FastVector and Plain highlighters.
61+data NonPostings =
62+ NonPostings { fragmentSize :: Maybe Int
63+ , numberOfFragments :: Maybe Int} deriving (Show, Eq)
64+
65+data HighlightEncoder = DefaultEncoder
66+ | HTMLEncoder
67+ deriving (Show, Eq)
68+
69+-- NOTE: Should the tags use some kind of HTML type, rather than Text?
70+data HighlightTag = TagSchema Text
71+ | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
72+ deriving (Show, Eq)
73+
74+highlightSettingsPairs :: Maybe HighlightSettings -> [Pair]
75+highlightSettingsPairs Nothing = []
76+highlightSettingsPairs (Just (Plain plh)) = plainHighPairs (Just plh)
77+highlightSettingsPairs (Just (Postings ph)) = postHighPairs (Just ph)
78+highlightSettingsPairs (Just (FastVector fvh)) = fastVectorHighPairs (Just fvh)
79+
80+plainHighPairs :: Maybe PlainHighlight -> [Pair]
81+plainHighPairs Nothing = []
82+plainHighPairs (Just (PlainHighlight plCom plNonPost)) =
83+ [ "type" .= String "plain"]
84+ ++ commonHighlightPairs plCom
85+ ++ nonPostingsToPairs plNonPost
86+
87+postHighPairs :: Maybe PostingsHighlight -> [Pair]
88+postHighPairs Nothing = []
89+postHighPairs (Just (PostingsHighlight pCom)) =
90+ ("type" .= String "postings")
91+ : commonHighlightPairs pCom
92+
93+fastVectorHighPairs :: Maybe FastVectorHighlight -> [Pair]
94+fastVectorHighPairs Nothing = []
95+fastVectorHighPairs (Just
96+ (FastVectorHighlight fvCom fvNonPostSettings' fvBoundChars
97+ fvBoundMaxScan fvFragOff fvMatchedFields
98+ fvPhraseLim)) =
99+ [ "type" .= String "fvh"
100+ , "boundary_chars" .= fvBoundChars
101+ , "boundary_max_scan" .= fvBoundMaxScan
102+ , "fragment_offset" .= fvFragOff
103+ , "matched_fields" .= fvMatchedFields
104+ , "phraseLimit" .= fvPhraseLim]
105+ ++ commonHighlightPairs fvCom
106+ ++ nonPostingsToPairs fvNonPostSettings'
107+
108+commonHighlightPairs :: Maybe CommonHighlight -> [Pair]
109+commonHighlightPairs Nothing = []
110+commonHighlightPairs (Just (CommonHighlight chScore chForceSource chTag chEncoder
111+ chNoMatchSize chHighlightQuery
112+ chRequireFieldMatch)) =
113+ [ "order" .= chScore
114+ , "force_source" .= chForceSource
115+ , "encoder" .= chEncoder
116+ , "no_match_size" .= chNoMatchSize
117+ , "highlight_query" .= chHighlightQuery
118+ , "require_fieldMatch" .= chRequireFieldMatch]
119+ ++ highlightTagToPairs chTag
120+
121+nonPostingsToPairs :: Maybe NonPostings -> [Pair]
122+nonPostingsToPairs Nothing = []
123+nonPostingsToPairs (Just (NonPostings npFragSize npNumOfFrags)) =
124+ [ "fragment_size" .= npFragSize
125+ , "number_of_fragments" .= npNumOfFrags]
126+
127+highlightTagToPairs :: Maybe HighlightTag -> [Pair]
128+highlightTagToPairs (Just (TagSchema _)) = [ "scheme" .= String "default"]
129+highlightTagToPairs (Just (CustomTags (pre, post))) = [ "pre_tags" .= pre
130+ , "post_tags" .= post]
131+highlightTagToPairs Nothing = []
132+
133+instance ToJSON FieldHighlight where
134+ toJSON (FieldHighlight (FieldName fName) (Just fSettings)) =
135+ object [ fName .= fSettings ]
136+ toJSON (FieldHighlight (FieldName fName) Nothing) =
137+ object [ fName .= emptyObject ]
138+
139+instance ToJSON HighlightSettings where
140+ toJSON hs = omitNulls (highlightSettingsPairs (Just hs))
141+
142+instance ToJSON HighlightEncoder where
143+ toJSON DefaultEncoder = String "default"
144+ toJSON HTMLEncoder = String "html"
src/Database/V1/Bloodhound/Internal/Newtypes.hsnew file+211
@@ -0,0 +1,211 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.V1.Bloodhound.Internal.Newtypes where
5+
6+import Bloodhound.Import
7+
8+newtype From = From Int deriving (Eq, Show, ToJSON)
9+newtype Size = Size Int deriving (Eq, Show, ToJSON, FromJSON)
10+
11+
12+{-| 'FieldName' is used all over the place wherever a specific field within
13+ a document needs to be specified, usually in 'Query's or 'Filter's.
14+-}
15+newtype FieldName =
16+ FieldName Text
17+ deriving (Eq, Show, ToJSON, FromJSON)
18+
19+newtype Boost =
20+ Boost Double
21+ deriving (Eq, Show, ToJSON, FromJSON)
22+
23+newtype BoostTerms =
24+ BoostTerms Double
25+ deriving (Eq, Show, ToJSON, FromJSON)
26+
27+{-| 'ReplicaCount' is part of 'IndexSettings' -}
28+newtype ReplicaCount =
29+ ReplicaCount Int
30+ deriving (Eq, Show, ToJSON)
31+
32+{-| 'ShardCount' is part of 'IndexSettings' -}
33+newtype ShardCount =
34+ ShardCount Int
35+ deriving (Eq, Show, ToJSON)
36+
37+
38+{-| 'TemplateName' is used to describe which template to query/create/delete
39+-}
40+newtype TemplateName = TemplateName Text deriving (Eq, Show, ToJSON, FromJSON)
41+
42+{-| 'TemplatePattern' represents a pattern which is matched against index names
43+-}
44+newtype TemplatePattern = TemplatePattern Text deriving (Eq, Show, ToJSON, FromJSON)
45+
46+{-| 'MappingName' is part of mappings which are how ES describes and schematizes
47+ the data in the indices.
48+-}
49+newtype MappingName = MappingName Text deriving (Eq, Show, ToJSON, FromJSON)
50+
51+{-| 'DocId' is a generic wrapper value for expressing unique Document IDs.
52+ Can be set by the user or created by ES itself. Often used in client
53+ functions for poking at specific documents.
54+-}
55+newtype DocId = DocId Text deriving (Eq, Show, ToJSON, FromJSON)
56+
57+{-| 'QueryString' is used to wrap query text bodies, be they human written or not.
58+-}
59+newtype QueryString = QueryString Text deriving (Eq, Show, ToJSON, FromJSON)
60+
61+{-| 'Script' is often used in place of 'FieldName' to specify more
62+complex ways of extracting a value from a document.
63+-}
64+newtype Script = Script { scriptText :: Text } deriving (Eq, Show)
65+
66+{-| 'CacheName' is used in 'RegexpFilter' for describing the
67+ 'CacheKey' keyed caching behavior.
68+-}
69+newtype CacheName = CacheName Text deriving (Eq, Show, ToJSON, FromJSON)
70+
71+{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
72+-}
73+newtype CacheKey =
74+ CacheKey Text deriving (Eq, Show, ToJSON, FromJSON)
75+newtype Existence =
76+ Existence Bool deriving (Eq, Show, ToJSON, FromJSON)
77+newtype NullValue =
78+ NullValue Bool deriving (Eq, Show, ToJSON, FromJSON)
79+newtype CutoffFrequency =
80+ CutoffFrequency Double deriving (Eq, Show, ToJSON, FromJSON)
81+newtype Analyzer =
82+ Analyzer Text deriving (Eq, Show, ToJSON, FromJSON)
83+newtype MaxExpansions =
84+ MaxExpansions Int deriving (Eq, Show, ToJSON, FromJSON)
85+
86+{-| 'Lenient', if set to true, will cause format based failures to be
87+ ignored. I don't know what the bloody default is, Elasticsearch
88+ documentation didn't say what it was. Let me know if you figure it out.
89+-}
90+newtype Lenient =
91+ Lenient Bool deriving (Eq, Show, ToJSON, FromJSON)
92+newtype Tiebreaker =
93+ Tiebreaker Double deriving (Eq, Show, ToJSON, FromJSON)
94+
95+{-| 'MinimumMatch' controls how many should clauses in the bool query should
96+ match. Can be an absolute value (2) or a percentage (30%) or a
97+ combination of both.
98+-}
99+newtype MinimumMatch =
100+ MinimumMatch Int deriving (Eq, Show, ToJSON, FromJSON)
101+newtype DisableCoord =
102+ DisableCoord Bool deriving (Eq, Show, ToJSON, FromJSON)
103+newtype IgnoreTermFrequency =
104+ IgnoreTermFrequency Bool deriving (Eq, Show, ToJSON, FromJSON)
105+newtype MinimumTermFrequency =
106+ MinimumTermFrequency Int deriving (Eq, Show, ToJSON, FromJSON)
107+newtype MaxQueryTerms =
108+ MaxQueryTerms Int deriving (Eq, Show, ToJSON, FromJSON)
109+newtype Fuzziness =
110+ Fuzziness Double deriving (Eq, Show, ToJSON, FromJSON)
111+
112+{-| 'PrefixLength' is the prefix length used in queries, defaults to 0. -}
113+newtype PrefixLength =
114+ PrefixLength Int deriving (Eq, Show, ToJSON, FromJSON)
115+newtype TypeName =
116+ TypeName Text deriving (Eq, Show, ToJSON, FromJSON)
117+newtype PercentMatch =
118+ PercentMatch Double deriving (Eq, Show, ToJSON, FromJSON)
119+newtype StopWord =
120+ StopWord Text deriving (Eq, Show, ToJSON, FromJSON)
121+newtype QueryPath =
122+ QueryPath Text deriving (Eq, Show, ToJSON, FromJSON)
123+
124+{-| Allowing a wildcard at the beginning of a word (eg "*ing") is particularly
125+ heavy, because all terms in the index need to be examined, just in case
126+ they match. Leading wildcards can be disabled by setting
127+ 'AllowLeadingWildcard' to false. -}
128+newtype AllowLeadingWildcard =
129+ AllowLeadingWildcard Bool deriving (Eq, Show, ToJSON, FromJSON)
130+newtype LowercaseExpanded =
131+ LowercaseExpanded Bool deriving (Eq, Show, ToJSON, FromJSON)
132+newtype EnablePositionIncrements =
133+ EnablePositionIncrements Bool deriving (Eq, Show, ToJSON, FromJSON)
134+
135+{-| By default, wildcard terms in a query are not analyzed.
136+ Setting 'AnalyzeWildcard' to true enables best-effort analysis.
137+-}
138+newtype AnalyzeWildcard = AnalyzeWildcard Bool deriving (Eq, Show, ToJSON, FromJSON)
139+
140+{-| 'GeneratePhraseQueries' defaults to false.
141+-}
142+newtype GeneratePhraseQueries =
143+ GeneratePhraseQueries Bool deriving (Eq, Show, ToJSON, FromJSON)
144+
145+{-| 'Locale' is used for string conversions - defaults to ROOT.
146+-}
147+newtype Locale = Locale Text deriving (Eq, Show, ToJSON, FromJSON)
148+newtype MaxWordLength = MaxWordLength Int deriving (Eq, Show, ToJSON, FromJSON)
149+newtype MinWordLength = MinWordLength Int deriving (Eq, Show, ToJSON, FromJSON)
150+
151+{-| 'PhraseSlop' sets the default slop for phrases, 0 means exact
152+ phrase matches. Default is 0.
153+-}
154+newtype PhraseSlop = PhraseSlop Int deriving (Eq, Show, ToJSON, FromJSON)
155+newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, ToJSON, FromJSON)
156+newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, ToJSON, FromJSON)
157+
158+-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
159+newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
160+
161+instance FromJSON POSIXMS where
162+ parseJSON = withScientific "POSIXMS" (return . parse)
163+ where parse n = let n' = truncate n :: Integer
164+ in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
165+
166+{-| 'IndexName' is used to describe which index to query/create/delete
167+-}
168+newtype IndexName = IndexName Text deriving (Eq, Show, ToJSON, FromJSON)
169+
170+newtype IndexAliasName = IndexAliasName { indexAliasName :: IndexName } deriving (Eq, Show, ToJSON)
171+
172+type Score = Maybe Double
173+
174+newtype ShardId = ShardId { shardId :: Int }
175+ deriving (Eq, Show, FromJSON)
176+
177+-- | Milliseconds
178+newtype MS = MS NominalDiffTime
179+
180+
181+-- keeps the unexported constructor warnings at bay
182+unMS :: MS -> NominalDiffTime
183+unMS (MS t) = t
184+
185+instance FromJSON MS where
186+ parseJSON = withScientific "MS" (return . MS . parse)
187+ where
188+ parse n = fromInteger ((truncate n) * 1000)
189+
190+newtype MaybeNA a = MaybeNA { unMaybeNA :: Maybe a }
191+ deriving (Show, Eq)
192+
193+instance FromJSON a => FromJSON (MaybeNA a) where
194+ parseJSON (String "NA") = pure $ MaybeNA Nothing
195+ parseJSON o = MaybeNA . Just <$> parseJSON o
196+
197+newtype SnapshotName = SnapshotName { snapshotName :: Text }
198+ deriving (Show, Eq, Ord, ToJSON, FromJSON)
199+
200+instance FromJSON ShardCount where
201+ parseJSON v = parseAsInt v
202+ <|> parseAsString v
203+ where parseAsInt = fmap ShardCount . parseJSON
204+ parseAsString = withText "ShardCount" (fmap ShardCount . parseReadText)
205+
206+
207+instance FromJSON ReplicaCount where
208+ parseJSON v = parseAsInt v
209+ <|> parseAsString v
210+ where parseAsInt = fmap ReplicaCount . parseJSON
211+ parseAsString = withText "ReplicaCount" (fmap ReplicaCount . parseReadText)
src/Database/V1/Bloodhound/Internal/Query.hsnew file+1689
@@ -0,0 +1,1689 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+module Database.V1.Bloodhound.Internal.Query where
6+
7+
8+import Bloodhound.Import
9+
10+import qualified Data.HashMap.Strict as HM
11+import qualified Data.Text as T
12+
13+import Database.V1.Bloodhound.Internal.Newtypes
14+import Database.V1.Bloodhound.Types.Class
15+
16+
17+data GeoPoint =
18+ GeoPoint { geoField :: FieldName
19+ , latLon :: LatLon} deriving (Eq, Show)
20+
21+instance ToJSON GeoPoint where
22+ toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
23+ object [ geoPointField .= geoPointLatLon ]
24+
25+
26+data LatLon = LatLon { lat :: Double
27+ , lon :: Double } deriving (Eq, Show)
28+
29+instance ToJSON LatLon where
30+ toJSON (LatLon lLat lLon) =
31+ object ["lat" .= lLat
32+ , "lon" .= lLon]
33+
34+instance FromJSON LatLon where
35+ parseJSON = withObject "LatLon" parse
36+ where parse o = LatLon <$> o .: "lat"
37+ <*> o .: "lon"
38+
39+data DistanceUnit = Miles
40+ | Yards
41+ | Feet
42+ | Inches
43+ | Kilometers
44+ | Meters
45+ | Centimeters
46+ | Millimeters
47+ | NauticalMiles deriving (Eq, Show)
48+
49+instance ToJSON DistanceUnit where
50+ toJSON Miles = String "mi"
51+ toJSON Yards = String "yd"
52+ toJSON Feet = String "ft"
53+ toJSON Inches = String "in"
54+ toJSON Kilometers = String "km"
55+ toJSON Meters = String "m"
56+ toJSON Centimeters = String "cm"
57+ toJSON Millimeters = String "mm"
58+ toJSON NauticalMiles = String "nmi"
59+
60+instance FromJSON DistanceUnit where
61+ parseJSON = withText "DistanceUnit" parse
62+ where parse "mi" = pure Miles
63+ parse "yd" = pure Yards
64+ parse "ft" = pure Feet
65+ parse "in" = pure Inches
66+ parse "km" = pure Kilometers
67+ parse "m" = pure Meters
68+ parse "cm" = pure Centimeters
69+ parse "mm" = pure Millimeters
70+ parse "nmi" = pure NauticalMiles
71+ parse u = fail ("Unrecognized DistanceUnit: " <> show u)
72+
73+{-| 'Cache' is for telling ES whether it should cache a 'Filter' not.
74+ 'Query's cannot be cached.
75+-}
76+type Cache = Bool -- caching on/off
77+
78+
79+data Filter = AndFilter [Filter] Cache
80+ | OrFilter [Filter] Cache
81+ | NotFilter Filter Cache
82+ | IdentityFilter
83+ | BoolFilter BoolMatch
84+ | ExistsFilter FieldName -- always cached
85+ | GeoBoundingBoxFilter GeoBoundingBoxConstraint
86+ | GeoDistanceFilter GeoPoint Distance DistanceType OptimizeBbox Cache
87+ | GeoDistanceRangeFilter GeoPoint DistanceRange
88+ | GeoPolygonFilter FieldName [LatLon]
89+ | IdsFilter MappingName [DocId]
90+ | LimitFilter Int
91+ | MissingFilter FieldName Existence NullValue
92+ | PrefixFilter FieldName PrefixValue Cache
93+ | QueryFilter Query Cache
94+ | RangeFilter FieldName RangeValue RangeExecution Cache
95+ | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
96+ | TermFilter Term Cache
97+ deriving (Eq, Show)
98+
99+instance Semigroup Filter where
100+ a <> b = AndFilter [a, b] defaultCache
101+
102+instance Monoid Filter where
103+ mempty = IdentityFilter
104+ mappend = (<>)
105+
106+instance Seminearring Filter where
107+ a <||> b = OrFilter [a, b] defaultCache
108+
109+data BoolMatch = MustMatch Term Cache
110+ | MustNotMatch Term Cache
111+ | ShouldMatch [Term] Cache deriving (Eq, Show)
112+
113+data Term = Term { termField :: Text
114+ , termValue :: Text } deriving (Eq, Show)
115+
116+
117+data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
118+ | NoOptimizeBbox deriving (Eq, Show)
119+
120+
121+data Distance =
122+ Distance { coefficient :: Double
123+ , unit :: DistanceUnit } deriving (Eq, Show)
124+
125+data DistanceRange =
126+ DistanceRange { distanceFrom :: Distance
127+ , distanceTo :: Distance } deriving (Eq, Show)
128+
129+-- "memory" or "indexed"
130+data GeoFilterType = GeoFilterMemory
131+ | GeoFilterIndexed deriving (Eq, Show)
132+
133+data GeoBoundingBoxConstraint =
134+ GeoBoundingBoxConstraint { geoBBField :: FieldName
135+ , constraintBox :: GeoBoundingBox
136+ , bbConstraintcache :: Cache
137+ , geoType :: GeoFilterType
138+ } deriving (Eq, Show)
139+
140+
141+data DistanceType = Arc
142+ | SloppyArc -- doesn't exist <1.0
143+ | Plane deriving (Eq, Show)
144+
145+data GeoBoundingBox =
146+ GeoBoundingBox { topLeft :: LatLon
147+ , bottomRight :: LatLon } deriving (Eq, Show)
148+
149+{-| 'PrefixValue' is used in 'PrefixQuery' as the main query component.
150+-}
151+type PrefixValue = Text
152+
153+
154+data RangeValue = RangeDateLte LessThanEqD
155+ | RangeDateLt LessThanD
156+ | RangeDateGte GreaterThanEqD
157+ | RangeDateGt GreaterThanD
158+ | RangeDateGtLt GreaterThanD LessThanD
159+ | RangeDateGteLte GreaterThanEqD LessThanEqD
160+ | RangeDateGteLt GreaterThanEqD LessThanD
161+ | RangeDateGtLte GreaterThanD LessThanEqD
162+ | RangeDoubleLte LessThanEq
163+ | RangeDoubleLt LessThan
164+ | RangeDoubleGte GreaterThanEq
165+ | RangeDoubleGt GreaterThan
166+ | RangeDoubleGtLt GreaterThan LessThan
167+ | RangeDoubleGteLte GreaterThanEq LessThanEq
168+ | RangeDoubleGteLt GreaterThanEq LessThan
169+ | RangeDoubleGtLte GreaterThan LessThanEq
170+ deriving (Eq, Show)
171+
172+
173+newtype LessThan = LessThan Double deriving (Eq, Show)
174+newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
175+newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
176+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
177+
178+newtype LessThanD = LessThanD UTCTime deriving (Eq, Show)
179+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show)
180+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show)
181+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show)
182+
183+
184+data Query =
185+ TermQuery Term (Maybe Boost)
186+ | TermsQuery Text (NonEmpty Text)
187+ | QueryMatchQuery MatchQuery
188+ | QueryMultiMatchQuery MultiMatchQuery
189+ | QueryBoolQuery BoolQuery
190+ | QueryBoostingQuery BoostingQuery
191+ | QueryCommonTermsQuery CommonTermsQuery
192+ | ConstantScoreFilter Filter Boost
193+ | ConstantScoreQuery Query Boost
194+ | QueryDisMaxQuery DisMaxQuery
195+ | QueryFilteredQuery FilteredQuery
196+ | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
197+ | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
198+ | QueryFuzzyQuery FuzzyQuery
199+ | QueryHasChildQuery HasChildQuery
200+ | QueryHasParentQuery HasParentQuery
201+ | IdsQuery MappingName [DocId]
202+ | QueryIndicesQuery IndicesQuery
203+ | MatchAllQuery (Maybe Boost)
204+ | QueryMoreLikeThisQuery MoreLikeThisQuery
205+ | QueryMoreLikeThisFieldQuery MoreLikeThisFieldQuery
206+ | QueryNestedQuery NestedQuery
207+ | QueryPrefixQuery PrefixQuery
208+ | QueryQueryStringQuery QueryStringQuery
209+ | QuerySimpleQueryStringQuery SimpleQueryStringQuery
210+ | QueryRangeQuery RangeQuery
211+ | QueryRegexpQuery RegexpQuery
212+ | QueryTemplateQueryInline TemplateQueryInline
213+ deriving (Eq, Show)
214+
215+data RegexpQuery =
216+ RegexpQuery { regexpQueryField :: FieldName
217+ , regexpQuery :: Regexp
218+ , regexpQueryFlags :: RegexpFlags
219+ , regexpQueryBoost :: Maybe Boost
220+ } deriving (Eq, Show)
221+
222+data RangeQuery =
223+ RangeQuery { rangeQueryField :: FieldName
224+ , rangeQueryRange :: RangeValue
225+ , rangeQueryBoost :: Boost } deriving (Eq, Show)
226+
227+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
228+mkRangeQuery f r = RangeQuery f r (Boost 1.0)
229+
230+data SimpleQueryStringQuery =
231+ SimpleQueryStringQuery
232+ { simpleQueryStringQuery :: QueryString
233+ , simpleQueryStringField :: Maybe FieldOrFields
234+ , simpleQueryStringOperator :: Maybe BooleanOperator
235+ , simpleQueryStringAnalyzer :: Maybe Analyzer
236+ , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
237+ , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
238+ , simpleQueryStringLocale :: Maybe Locale
239+ } deriving (Eq, Show)
240+
241+data SimpleQueryFlag =
242+ SimpleQueryAll
243+ | SimpleQueryNone
244+ | SimpleQueryAnd
245+ | SimpleQueryOr
246+ | SimpleQueryPrefix
247+ | SimpleQueryPhrase
248+ | SimpleQueryPrecedence
249+ | SimpleQueryEscape
250+ | SimpleQueryWhitespace
251+ | SimpleQueryFuzzy
252+ | SimpleQueryNear
253+ | SimpleQuerySlop deriving (Eq, Show)
254+
255+-- use_dis_max and tie_breaker when fields are plural?
256+data QueryStringQuery =
257+ QueryStringQuery
258+ { queryStringQuery :: QueryString
259+ , queryStringDefaultField :: Maybe FieldName
260+ , queryStringOperator :: Maybe BooleanOperator
261+ , queryStringAnalyzer :: Maybe Analyzer
262+ , queryStringAllowLeadingWildcard :: Maybe AllowLeadingWildcard
263+ , queryStringLowercaseExpanded :: Maybe LowercaseExpanded
264+ , queryStringEnablePositionIncrements :: Maybe EnablePositionIncrements
265+ , queryStringFuzzyMaxExpansions :: Maybe MaxExpansions
266+ , queryStringFuzziness :: Maybe Fuzziness
267+ , queryStringFuzzyPrefixLength :: Maybe PrefixLength
268+ , queryStringPhraseSlop :: Maybe PhraseSlop
269+ , queryStringBoost :: Maybe Boost
270+ , queryStringAnalyzeWildcard :: Maybe AnalyzeWildcard
271+ , queryStringGeneratePhraseQueries :: Maybe GeneratePhraseQueries
272+ , queryStringMinimumShouldMatch :: Maybe MinimumMatch
273+ , queryStringLenient :: Maybe Lenient
274+ , queryStringLocale :: Maybe Locale
275+ } deriving (Eq, Show)
276+
277+mkQueryStringQuery :: QueryString -> QueryStringQuery
278+mkQueryStringQuery qs =
279+ QueryStringQuery qs Nothing Nothing
280+ Nothing Nothing Nothing Nothing
281+ Nothing Nothing Nothing Nothing
282+ Nothing Nothing Nothing Nothing
283+ Nothing Nothing
284+
285+data FieldOrFields = FofField FieldName
286+ | FofFields (NonEmpty FieldName) deriving (Eq, Show)
287+
288+data PrefixQuery =
289+ PrefixQuery
290+ { prefixQueryField :: FieldName
291+ , prefixQueryPrefixValue :: Text
292+ , prefixQueryBoost :: Maybe Boost } deriving (Eq, Show)
293+
294+data NestedQuery =
295+ NestedQuery
296+ { nestedQueryPath :: QueryPath
297+ , nestedQueryScoreType :: ScoreType
298+ , nestedQuery :: Query } deriving (Eq, Show)
299+
300+data MoreLikeThisFieldQuery =
301+ MoreLikeThisFieldQuery
302+ { moreLikeThisFieldText :: Text
303+ , moreLikeThisFieldFields :: FieldName
304+ -- default 0.3 (30%)
305+ , moreLikeThisFieldPercentMatch :: Maybe PercentMatch
306+ , moreLikeThisFieldMinimumTermFreq :: Maybe MinimumTermFrequency
307+ , moreLikeThisFieldMaxQueryTerms :: Maybe MaxQueryTerms
308+ , moreLikeThisFieldStopWords :: Maybe (NonEmpty StopWord)
309+ , moreLikeThisFieldMinDocFrequency :: Maybe MinDocFrequency
310+ , moreLikeThisFieldMaxDocFrequency :: Maybe MaxDocFrequency
311+ , moreLikeThisFieldMinWordLength :: Maybe MinWordLength
312+ , moreLikeThisFieldMaxWordLength :: Maybe MaxWordLength
313+ , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
314+ , moreLikeThisFieldBoost :: Maybe Boost
315+ , moreLikeThisFieldAnalyzer :: Maybe Analyzer
316+ } deriving (Eq, Show)
317+
318+data MoreLikeThisQuery =
319+ MoreLikeThisQuery
320+ { moreLikeThisText :: Text
321+ , moreLikeThisFields :: Maybe (NonEmpty FieldName)
322+ -- default 0.3 (30%)
323+ , moreLikeThisPercentMatch :: Maybe PercentMatch
324+ , moreLikeThisMinimumTermFreq :: Maybe MinimumTermFrequency
325+ , moreLikeThisMaxQueryTerms :: Maybe MaxQueryTerms
326+ , moreLikeThisStopWords :: Maybe (NonEmpty StopWord)
327+ , moreLikeThisMinDocFrequency :: Maybe MinDocFrequency
328+ , moreLikeThisMaxDocFrequency :: Maybe MaxDocFrequency
329+ , moreLikeThisMinWordLength :: Maybe MinWordLength
330+ , moreLikeThisMaxWordLength :: Maybe MaxWordLength
331+ , moreLikeThisBoostTerms :: Maybe BoostTerms
332+ , moreLikeThisBoost :: Maybe Boost
333+ , moreLikeThisAnalyzer :: Maybe Analyzer
334+ } deriving (Eq, Show)
335+
336+data IndicesQuery =
337+ IndicesQuery
338+ { indicesQueryIndices :: [IndexName]
339+ , indicesQuery :: Query
340+ -- default "all"
341+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show)
342+
343+data HasParentQuery =
344+ HasParentQuery
345+ { hasParentQueryType :: TypeName
346+ , hasParentQuery :: Query
347+ , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
348+
349+data HasChildQuery =
350+ HasChildQuery
351+ { hasChildQueryType :: TypeName
352+ , hasChildQuery :: Query
353+ , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
354+
355+data ScoreType =
356+ ScoreTypeMax
357+ | ScoreTypeSum
358+ | ScoreTypeAvg
359+ | ScoreTypeNone deriving (Eq, Show)
360+
361+data FuzzyQuery =
362+ FuzzyQuery { fuzzyQueryField :: FieldName
363+ , fuzzyQueryValue :: Text
364+ , fuzzyQueryPrefixLength :: PrefixLength
365+ , fuzzyQueryMaxExpansions :: MaxExpansions
366+ , fuzzyQueryFuzziness :: Fuzziness
367+ , fuzzyQueryBoost :: Maybe Boost
368+ } deriving (Eq, Show)
369+
370+data FuzzyLikeFieldQuery =
371+ FuzzyLikeFieldQuery
372+ { fuzzyLikeField :: FieldName
373+ -- anaphora is good for the soul.
374+ , fuzzyLikeFieldText :: Text
375+ , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
376+ , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
377+ , fuzzyLikeFieldFuzziness :: Fuzziness
378+ , fuzzyLikeFieldPrefixLength :: PrefixLength
379+ , fuzzyLikeFieldBoost :: Boost
380+ , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
381+ } deriving (Eq, Show)
382+
383+data FuzzyLikeThisQuery =
384+ FuzzyLikeThisQuery
385+ { fuzzyLikeFields :: [FieldName]
386+ , fuzzyLikeText :: Text
387+ , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
388+ , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
389+ , fuzzyLikeFuzziness :: Fuzziness
390+ , fuzzyLikePrefixLength :: PrefixLength
391+ , fuzzyLikeBoost :: Boost
392+ , fuzzyLikeAnalyzer :: Maybe Analyzer
393+ } deriving (Eq, Show)
394+
395+data FilteredQuery =
396+ FilteredQuery
397+ { filteredQuery :: Query
398+ , filteredFilter :: Filter } deriving (Eq, Show)
399+
400+data DisMaxQuery =
401+ DisMaxQuery { disMaxQueries :: [Query]
402+ -- default 0.0
403+ , disMaxTiebreaker :: Tiebreaker
404+ , disMaxBoost :: Maybe Boost
405+ } deriving (Eq, Show)
406+
407+data MatchQuery =
408+ MatchQuery { matchQueryField :: FieldName
409+ , matchQueryQueryString :: QueryString
410+ , matchQueryOperator :: BooleanOperator
411+ , matchQueryZeroTerms :: ZeroTermsQuery
412+ , matchQueryCutoffFrequency :: Maybe CutoffFrequency
413+ , matchQueryMatchType :: Maybe MatchQueryType
414+ , matchQueryAnalyzer :: Maybe Analyzer
415+ , matchQueryMaxExpansions :: Maybe MaxExpansions
416+ , matchQueryLenient :: Maybe Lenient
417+ , matchQueryBoost :: Maybe Boost } deriving (Eq, Show)
418+
419+{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
420+ enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
421+-}
422+mkMatchQuery :: FieldName -> QueryString -> MatchQuery
423+mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
424+
425+data MatchQueryType =
426+ MatchPhrase
427+ | MatchPhrasePrefix deriving (Eq, Show)
428+
429+data MultiMatchQuery =
430+ MultiMatchQuery { multiMatchQueryFields :: [FieldName]
431+ , multiMatchQueryString :: QueryString
432+ , multiMatchQueryOperator :: BooleanOperator
433+ , multiMatchQueryZeroTerms :: ZeroTermsQuery
434+ , multiMatchQueryTiebreaker :: Maybe Tiebreaker
435+ , multiMatchQueryType :: Maybe MultiMatchQueryType
436+ , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
437+ , multiMatchQueryAnalyzer :: Maybe Analyzer
438+ , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
439+ , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Show)
440+
441+{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
442+ enabling you to provide only the list of 'FieldName's and 'QueryString' to
443+ make a 'MultiMatchQuery'.
444+-}
445+
446+mkMultiMatchQuery :: [FieldName] -> QueryString -> MultiMatchQuery
447+mkMultiMatchQuery matchFields query =
448+ MultiMatchQuery matchFields query
449+ Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
450+
451+data MultiMatchQueryType =
452+ MultiMatchBestFields
453+ | MultiMatchMostFields
454+ | MultiMatchCrossFields
455+ | MultiMatchPhrase
456+ | MultiMatchPhrasePrefix deriving (Eq, Show)
457+
458+data BoolQuery =
459+ BoolQuery { boolQueryMustMatch :: [Query]
460+ , boolQueryMustNotMatch :: [Query]
461+ , boolQueryShouldMatch :: [Query]
462+ , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
463+ , boolQueryBoost :: Maybe Boost
464+ , boolQueryDisableCoord :: Maybe DisableCoord
465+ } deriving (Eq, Show)
466+
467+mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
468+mkBoolQuery must mustNot should =
469+ BoolQuery must mustNot should Nothing Nothing Nothing
470+
471+data BoostingQuery =
472+ BoostingQuery { positiveQuery :: Query
473+ , negativeQuery :: Query
474+ , negativeBoost :: Boost } deriving (Eq, Show)
475+
476+data CommonTermsQuery =
477+ CommonTermsQuery { commonField :: FieldName
478+ , commonQuery :: QueryString
479+ , commonCutoffFrequency :: CutoffFrequency
480+ , commonLowFreqOperator :: BooleanOperator
481+ , commonHighFreqOperator :: BooleanOperator
482+ , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
483+ , commonBoost :: Maybe Boost
484+ , commonAnalyzer :: Maybe Analyzer
485+ , commonDisableCoord :: Maybe DisableCoord
486+ } deriving (Eq, Show)
487+
488+data CommonMinimumMatch =
489+ CommonMinimumMatchHighLow MinimumMatchHighLow
490+ | CommonMinimumMatch MinimumMatch
491+ deriving (Eq, Show)
492+
493+data MinimumMatchHighLow =
494+ MinimumMatchHighLow { lowFreq :: MinimumMatch
495+ , highFreq :: MinimumMatch } deriving (Eq, Show)
496+
497+
498+data TemplateQueryInline =
499+ TemplateQueryInline { inline :: Query
500+ , params :: TemplateQueryKeyValuePairs
501+ }
502+ deriving (Eq, Show)
503+
504+instance ToJSON TemplateQueryInline where
505+ toJSON TemplateQueryInline{..} = object [ "query" .= inline
506+ , "params" .= params
507+ ]
508+
509+instance FromJSON TemplateQueryInline where
510+ parseJSON = withObject "TemplateQueryInline" parse
511+ where parse o = TemplateQueryInline
512+ <$> o .: "query"
513+ <*> o .: "params"
514+
515+{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
516+ JSON encoding baked in. Used all over the place.
517+-}
518+data BooleanOperator = And | Or deriving (Eq, Show)
519+
520+type TemplateQueryKey = Text
521+type TemplateQueryValue = Text
522+
523+newtype TemplateQueryKeyValuePairs = TemplateQueryKeyValuePairs (HM.HashMap TemplateQueryKey TemplateQueryValue)
524+ deriving (Eq, Show)
525+
526+instance ToJSON TemplateQueryKeyValuePairs where
527+ toJSON (TemplateQueryKeyValuePairs x) = Object $ HM.map toJSON x
528+
529+instance FromJSON TemplateQueryKeyValuePairs where
530+ parseJSON (Object o) = pure . TemplateQueryKeyValuePairs $ HM.mapMaybe getValue o
531+ where getValue (String x) = Just x
532+ getValue _ = Nothing
533+ parseJSON _ = fail "error parsing TemplateQueryKeyValuePairs"
534+
535+newtype Regexp = Regexp Text deriving (Eq, Show, FromJSON)
536+
537+data RegexpFlags = AllRegexpFlags
538+ | NoRegexpFlags
539+ | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Show)
540+
541+data RegexpFlag = AnyString
542+ | Automaton
543+ | Complement
544+ | Empty
545+ | Intersection
546+ | Interval deriving (Eq, Show)
547+
548+data RangeExecution = RangeExecutionIndex
549+ | RangeExecutionFielddata deriving (Eq, Show)
550+
551+data ZeroTermsQuery = ZeroTermsNone
552+ | ZeroTermsAll deriving (Eq, Show)
553+
554+instance ToJSON Query where
555+ toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
556+ object [ "term" .=
557+ object [termQueryField .= object merged]]
558+ where
559+ base = [ "value" .= termQueryValue ]
560+ boosted = maybe [] (return . ("boost" .=)) boost
561+ merged = mappend base boosted
562+
563+ toJSON (TermsQuery fieldName terms) =
564+ object [ "terms" .= object conjoined ]
565+ where conjoined = [fieldName .= terms]
566+
567+ toJSON (IdsQuery idsQueryMappingName docIds) =
568+ object [ "ids" .= object conjoined ]
569+ where conjoined = [ "type" .= idsQueryMappingName
570+ , "values" .= fmap toJSON docIds ]
571+
572+ toJSON (QueryQueryStringQuery qQueryStringQuery) =
573+ object [ "query_string" .= qQueryStringQuery ]
574+
575+ toJSON (QueryMatchQuery matchQuery) =
576+ object [ "match" .= matchQuery ]
577+
578+ toJSON (QueryMultiMatchQuery multiMatchQuery) =
579+ toJSON multiMatchQuery
580+
581+ toJSON (QueryBoolQuery boolQuery) =
582+ object [ "bool" .= boolQuery ]
583+
584+ toJSON (QueryBoostingQuery boostingQuery) =
585+ object [ "boosting" .= boostingQuery ]
586+
587+ toJSON (QueryCommonTermsQuery commonTermsQuery) =
588+ object [ "common" .= commonTermsQuery ]
589+
590+ toJSON (ConstantScoreFilter csFilter boost) =
591+ object ["constant_score" .= object ["filter" .= csFilter
592+ , "boost" .= boost]]
593+
594+ toJSON (ConstantScoreQuery query boost) =
595+ object ["constant_score" .= object ["query" .= query
596+ , "boost" .= boost]]
597+
598+ toJSON (QueryDisMaxQuery disMaxQuery) =
599+ object [ "dis_max" .= disMaxQuery ]
600+
601+ toJSON (QueryFilteredQuery qFilteredQuery) =
602+ object [ "filtered" .= qFilteredQuery ]
603+
604+ toJSON (QueryFuzzyLikeThisQuery fuzzyQuery) =
605+ object [ "fuzzy_like_this" .= fuzzyQuery ]
606+
607+ toJSON (QueryFuzzyLikeFieldQuery fuzzyFieldQuery) =
608+ object [ "fuzzy_like_this_field" .= fuzzyFieldQuery ]
609+
610+ toJSON (QueryFuzzyQuery fuzzyQuery) =
611+ object [ "fuzzy" .= fuzzyQuery ]
612+
613+ toJSON (QueryHasChildQuery childQuery) =
614+ object [ "has_child" .= childQuery ]
615+
616+ toJSON (QueryHasParentQuery parentQuery) =
617+ object [ "has_parent" .= parentQuery ]
618+
619+ toJSON (QueryIndicesQuery qIndicesQuery) =
620+ object [ "indices" .= qIndicesQuery ]
621+
622+ toJSON (MatchAllQuery boost) =
623+ object [ "match_all" .= omitNulls [ "boost" .= boost ] ]
624+
625+ toJSON (QueryMoreLikeThisQuery query) =
626+ object [ "more_like_this" .= query ]
627+
628+ toJSON (QueryMoreLikeThisFieldQuery query) =
629+ object [ "more_like_this_field" .= query ]
630+
631+ toJSON (QueryNestedQuery query) =
632+ object [ "nested" .= query ]
633+
634+ toJSON (QueryPrefixQuery query) =
635+ object [ "prefix" .= query ]
636+
637+ toJSON (QueryRangeQuery query) =
638+ object [ "range" .= query ]
639+
640+ toJSON (QueryRegexpQuery query) =
641+ object [ "regexp" .= query ]
642+
643+ toJSON (QuerySimpleQueryStringQuery query) =
644+ object [ "simple_query_string" .= query ]
645+
646+ toJSON (QueryTemplateQueryInline templateQuery) =
647+ object [ "template" .= templateQuery ]
648+
649+instance FromJSON Query where
650+ parseJSON v = withObject "Query" parse v
651+ where parse o = termQuery `taggedWith` "term"
652+ <|> termsQuery `taggedWith` "terms"
653+ <|> idsQuery `taggedWith` "ids"
654+ <|> queryQueryStringQuery `taggedWith` "query_string"
655+ <|> queryMatchQuery `taggedWith` "match"
656+ <|> queryMultiMatchQuery
657+ <|> queryBoolQuery `taggedWith` "bool"
658+ <|> queryBoostingQuery `taggedWith` "boosting"
659+ <|> queryCommonTermsQuery `taggedWith` "common"
660+ <|> constantScoreFilter `taggedWith` "constant_score"
661+ <|> constantScoreQuery `taggedWith` "constant_score"
662+ <|> queryDisMaxQuery `taggedWith` "dis_max"
663+ <|> queryFilteredQuery `taggedWith` "filtered"
664+ <|> queryFuzzyLikeThisQuery `taggedWith` "fuzzy_like_this"
665+ <|> queryFuzzyLikeFieldQuery `taggedWith` "fuzzy_like_this_field"
666+ <|> queryFuzzyQuery `taggedWith` "fuzzy"
667+ <|> queryHasChildQuery `taggedWith` "has_child"
668+ <|> queryHasParentQuery `taggedWith` "has_parent"
669+ <|> queryIndicesQuery `taggedWith` "indices"
670+ <|> matchAllQuery `taggedWith` "match_all"
671+ <|> queryMoreLikeThisQuery `taggedWith` "more_like_this"
672+ <|> queryMoreLikeThisFieldQuery `taggedWith` "more_like_this_field"
673+ <|> queryNestedQuery `taggedWith` "nested"
674+ <|> queryPrefixQuery `taggedWith` "prefix"
675+ <|> queryRangeQuery `taggedWith` "range"
676+ <|> queryRegexpQuery `taggedWith` "regexp"
677+ <|> querySimpleQueryStringQuery `taggedWith` "simple_query_string"
678+ <|> queryTemplateQueryInline `taggedWith` "template"
679+ where taggedWith parser k = parser =<< o .: k
680+ termQuery = fieldTagged $ \(FieldName fn) o ->
681+ TermQuery <$> (Term fn <$> o .: "value") <*> o .:? "boost"
682+ termsQuery o = case HM.toList o of
683+ [(fn, vs)] -> do vals <- parseJSON vs
684+ case vals of
685+ x:xs -> return (TermsQuery fn (x :| xs))
686+ _ -> fail "Expected non empty list of values"
687+ _ -> fail "Expected object with 1 field-named key"
688+ idsQuery o = IdsQuery <$> o .: "type"
689+ <*> o .: "values"
690+ queryQueryStringQuery = pure . QueryQueryStringQuery
691+ queryMatchQuery = pure . QueryMatchQuery
692+ queryMultiMatchQuery = QueryMultiMatchQuery <$> parseJSON v
693+ queryBoolQuery = pure . QueryBoolQuery
694+ queryBoostingQuery = pure . QueryBoostingQuery
695+ queryCommonTermsQuery = pure . QueryCommonTermsQuery
696+ constantScoreFilter o = case HM.lookup "filter" o of
697+ Just x -> ConstantScoreFilter <$> parseJSON x
698+ <*> o .: "boost"
699+ _ -> fail "Does not appear to be a ConstantScoreFilter"
700+ constantScoreQuery o = case HM.lookup "query" o of
701+ Just x -> ConstantScoreQuery <$> parseJSON x
702+ <*> o .: "boost"
703+ _ -> fail "Does not appear to be a ConstantScoreQuery"
704+ queryDisMaxQuery = pure . QueryDisMaxQuery
705+ queryFilteredQuery = pure . QueryFilteredQuery
706+ queryFuzzyLikeThisQuery = pure . QueryFuzzyLikeThisQuery
707+ queryFuzzyLikeFieldQuery = pure . QueryFuzzyLikeFieldQuery
708+ queryFuzzyQuery = pure . QueryFuzzyQuery
709+ queryHasChildQuery = pure . QueryHasChildQuery
710+ queryHasParentQuery = pure . QueryHasParentQuery
711+ queryIndicesQuery = pure . QueryIndicesQuery
712+ matchAllQuery o = MatchAllQuery <$> o .:? "boost"
713+ queryMoreLikeThisQuery = pure . QueryMoreLikeThisQuery
714+ queryMoreLikeThisFieldQuery = pure . QueryMoreLikeThisFieldQuery
715+ queryNestedQuery = pure . QueryNestedQuery
716+ queryPrefixQuery = pure . QueryPrefixQuery
717+ queryRangeQuery = pure . QueryRangeQuery
718+ queryRegexpQuery = pure . QueryRegexpQuery
719+ querySimpleQueryStringQuery = pure . QuerySimpleQueryStringQuery
720+ queryTemplateQueryInline = pure . QueryTemplateQueryInline
721+
722+instance ToJSON SimpleQueryStringQuery where
723+ toJSON SimpleQueryStringQuery {..} =
724+ omitNulls (base ++ maybeAdd)
725+ where base = [ "query" .= simpleQueryStringQuery ]
726+ maybeAdd = [ "fields" .= simpleQueryStringField
727+ , "default_operator" .= simpleQueryStringOperator
728+ , "analyzer" .= simpleQueryStringAnalyzer
729+ , "flags" .= simpleQueryStringFlags
730+ , "lowercase_expanded_terms" .= simpleQueryStringLowercaseExpanded
731+ , "locale" .= simpleQueryStringLocale ]
732+
733+instance FromJSON SimpleQueryStringQuery where
734+ parseJSON = withObject "SimpleQueryStringQuery" parse
735+ where parse o = SimpleQueryStringQuery <$> o .: "query"
736+ <*> o .:? "fields"
737+ <*> o .:? "default_operator"
738+ <*> o .:? "analyzer"
739+ <*> (parseFlags <$> o .:? "flags")
740+ <*> o .:? "lowercase_expanded_terms"
741+ <*> o .:? "locale"
742+ parseFlags (Just (x:xs)) = Just (x :| xs)
743+ parseFlags _ = Nothing
744+
745+instance ToJSON FieldOrFields where
746+ toJSON (FofField fieldName) =
747+ toJSON fieldName
748+ toJSON (FofFields fieldNames) =
749+ toJSON fieldNames
750+
751+instance FromJSON FieldOrFields where
752+ parseJSON v = FofField <$> parseJSON v
753+ <|> FofFields <$> (parseNEJSON =<< parseJSON v)
754+
755+instance ToJSON SimpleQueryFlag where
756+ toJSON SimpleQueryAll = "ALL"
757+ toJSON SimpleQueryNone = "NONE"
758+ toJSON SimpleQueryAnd = "AND"
759+ toJSON SimpleQueryOr = "OR"
760+ toJSON SimpleQueryPrefix = "PREFIX"
761+ toJSON SimpleQueryPhrase = "PHRASE"
762+ toJSON SimpleQueryPrecedence = "PRECEDENCE"
763+ toJSON SimpleQueryEscape = "ESCAPE"
764+ toJSON SimpleQueryWhitespace = "WHITESPACE"
765+ toJSON SimpleQueryFuzzy = "FUZZY"
766+ toJSON SimpleQueryNear = "NEAR"
767+ toJSON SimpleQuerySlop = "SLOP"
768+
769+instance FromJSON SimpleQueryFlag where
770+ parseJSON = withText "SimpleQueryFlag" parse
771+ where parse "ALL" = pure SimpleQueryAll
772+ parse "NONE" = pure SimpleQueryNone
773+ parse "AND" = pure SimpleQueryAnd
774+ parse "OR" = pure SimpleQueryOr
775+ parse "PREFIX" = pure SimpleQueryPrefix
776+ parse "PHRASE" = pure SimpleQueryPhrase
777+ parse "PRECEDENCE" = pure SimpleQueryPrecedence
778+ parse "ESCAPE" = pure SimpleQueryEscape
779+ parse "WHITESPACE" = pure SimpleQueryWhitespace
780+ parse "FUZZY" = pure SimpleQueryFuzzy
781+ parse "NEAR" = pure SimpleQueryNear
782+ parse "SLOP" = pure SimpleQuerySlop
783+ parse f = fail ("Unexpected SimpleQueryFlag: " <> show f)
784+
785+instance ToJSON RegexpQuery where
786+ toJSON (RegexpQuery (FieldName rqQueryField)
787+ (Regexp regexpQueryQuery) rqQueryFlags
788+ rqQueryBoost) =
789+ object [ rqQueryField .= omitNulls base ]
790+ where base = [ "value" .= regexpQueryQuery
791+ , "flags" .= rqQueryFlags
792+ , "boost" .= rqQueryBoost ]
793+
794+instance FromJSON RegexpQuery where
795+ parseJSON = withObject "RegexpQuery" parse
796+ where parse = fieldTagged $ \fn o ->
797+ RegexpQuery fn
798+ <$> o .: "value"
799+ <*> o .: "flags"
800+ <*> o .:? "boost"
801+
802+instance ToJSON QueryStringQuery where
803+ toJSON (QueryStringQuery qsQueryString
804+ qsDefaultField qsOperator
805+ qsAnalyzer qsAllowWildcard
806+ qsLowercaseExpanded qsEnablePositionIncrements
807+ qsFuzzyMaxExpansions qsFuzziness
808+ qsFuzzyPrefixLength qsPhraseSlop
809+ qsBoost qsAnalyzeWildcard
810+ qsGeneratePhraseQueries qsMinimumShouldMatch
811+ qsLenient qsLocale) =
812+ omitNulls base
813+ where
814+ base = [ "query" .= qsQueryString
815+ , "default_field" .= qsDefaultField
816+ , "default_operator" .= qsOperator
817+ , "analyzer" .= qsAnalyzer
818+ , "allow_leading_wildcard" .= qsAllowWildcard
819+ , "lowercase_expanded_terms" .= qsLowercaseExpanded
820+ , "enable_position_increments" .= qsEnablePositionIncrements
821+ , "fuzzy_max_expansions" .= qsFuzzyMaxExpansions
822+ , "fuzziness" .= qsFuzziness
823+ , "fuzzy_prefix_length" .= qsFuzzyPrefixLength
824+ , "phrase_slop" .= qsPhraseSlop
825+ , "boost" .= qsBoost
826+ , "analyze_wildcard" .= qsAnalyzeWildcard
827+ , "auto_generate_phrase_queries" .= qsGeneratePhraseQueries
828+ , "minimum_should_match" .= qsMinimumShouldMatch
829+ , "lenient" .= qsLenient
830+ , "locale" .= qsLocale ]
831+
832+instance FromJSON QueryStringQuery where
833+ parseJSON = withObject "QueryStringQuery" parse
834+ where parse o = QueryStringQuery
835+ <$> o .: "query"
836+ <*> o .:? "default_field"
837+ <*> o .:? "default_operator"
838+ <*> o .:? "analyzer"
839+ <*> o .:? "allow_leading_wildcard"
840+ <*> o .:? "lowercase_expanded_terms"
841+ <*> o .:? "enable_position_increments"
842+ <*> o .:? "fuzzy_max_expansions"
843+ <*> o .:? "fuzziness"
844+ <*> o .:? "fuzzy_prefix_length"
845+ <*> o .:? "phrase_slop"
846+ <*> o .:? "boost"
847+ <*> o .:? "analyze_wildcard"
848+ <*> o .:? "auto_generate_phrase_queries"
849+ <*> o .:? "minimum_should_match"
850+ <*> o .:? "lenient"
851+ <*> o .:? "locale"
852+
853+instance ToJSON RangeQuery where
854+ toJSON (RangeQuery (FieldName fieldName) range boost) =
855+ object [ fieldName .= object conjoined ]
856+ where conjoined = [ "boost" .= boost ] ++ (rangeValueToPair range)
857+
858+instance FromJSON RangeQuery where
859+ parseJSON = withObject "RangeQuery" parse
860+ where parse = fieldTagged $ \fn o ->
861+ RangeQuery fn
862+ <$> parseJSON (Object o)
863+ <*> o .: "boost"
864+
865+instance FromJSON RangeValue where
866+ parseJSON = withObject "RangeValue" parse
867+ where parse o = parseDate o
868+ <|> parseDouble o
869+ parseDate o = do lt <- o .:? "lt"
870+ lte <- o .:? "lte"
871+ gt <- o .:? "gt"
872+ gte <- o .:? "gte"
873+ case (lt, lte, gt, gte) of
874+ (Just a, _, Just b, _) -> return (RangeDateGtLt (GreaterThanD b) (LessThanD a))
875+ (Just a, _, _, Just b)-> return (RangeDateGteLt (GreaterThanEqD b) (LessThanD a))
876+ (_, Just a, Just b, _)-> return (RangeDateGtLte (GreaterThanD b) (LessThanEqD a))
877+ (_, Just a, _, Just b)-> return (RangeDateGteLte (GreaterThanEqD b) (LessThanEqD a))
878+ (_, _, Just a, _)-> return (RangeDateGt (GreaterThanD a))
879+ (Just a, _, _, _)-> return (RangeDateLt (LessThanD a))
880+ (_, _, _, Just a)-> return (RangeDateGte (GreaterThanEqD a))
881+ (_, Just a, _, _)-> return (RangeDateLte (LessThanEqD a))
882+ (Nothing, Nothing, Nothing, Nothing) -> mzero
883+ parseDouble o = do lt <- o .:? "lt"
884+ lte <- o .:? "lte"
885+ gt <- o .:? "gt"
886+ gte <- o .:? "gte"
887+ case (lt, lte, gt, gte) of
888+ (Just a, _, Just b, _) -> return (RangeDoubleGtLt (GreaterThan b) (LessThan a))
889+ (Just a, _, _, Just b)-> return (RangeDoubleGteLt (GreaterThanEq b) (LessThan a))
890+ (_, Just a, Just b, _)-> return (RangeDoubleGtLte (GreaterThan b) (LessThanEq a))
891+ (_, Just a, _, Just b)-> return (RangeDoubleGteLte (GreaterThanEq b) (LessThanEq a))
892+ (_, _, Just a, _)-> return (RangeDoubleGt (GreaterThan a))
893+ (Just a, _, _, _)-> return (RangeDoubleLt (LessThan a))
894+ (_, _, _, Just a)-> return (RangeDoubleGte (GreaterThanEq a))
895+ (_, Just a, _, _)-> return (RangeDoubleLte (LessThanEq a))
896+ (Nothing, Nothing, Nothing, Nothing) -> mzero
897+
898+instance ToJSON PrefixQuery where
899+ toJSON (PrefixQuery (FieldName fieldName) queryValue boost) =
900+ object [ fieldName .= omitNulls base ]
901+ where base = [ "value" .= queryValue
902+ , "boost" .= boost ]
903+
904+instance FromJSON PrefixQuery where
905+ parseJSON = withObject "PrefixQuery" parse
906+ where parse = fieldTagged $ \fn o ->
907+ PrefixQuery fn
908+ <$> o .: "value"
909+ <*> o .:? "boost"
910+
911+instance ToJSON NestedQuery where
912+ toJSON (NestedQuery nqPath nqScoreType nqQuery) =
913+ object [ "path" .= nqPath
914+ , "score_mode" .= nqScoreType
915+ , "query" .= nqQuery ]
916+
917+instance FromJSON NestedQuery where
918+ parseJSON = withObject "NestedQuery" parse
919+ where parse o = NestedQuery
920+ <$> o .: "path"
921+ <*> o .: "score_mode"
922+ <*> o .: "query"
923+
924+instance ToJSON MoreLikeThisFieldQuery where
925+ toJSON (MoreLikeThisFieldQuery text (FieldName fieldName)
926+ percent mtf mqt stopwords mindf maxdf
927+ minwl maxwl boostTerms boost analyzer) =
928+ object [ fieldName .= omitNulls base ]
929+ where base = [ "like_text" .= text
930+ , "percent_terms_to_match" .= percent
931+ , "min_term_freq" .= mtf
932+ , "max_query_terms" .= mqt
933+ , "stop_words" .= stopwords
934+ , "min_doc_freq" .= mindf
935+ , "max_doc_freq" .= maxdf
936+ , "min_word_length" .= minwl
937+ , "max_word_length" .= maxwl
938+ , "boost_terms" .= boostTerms
939+ , "boost" .= boost
940+ , "analyzer" .= analyzer ]
941+
942+instance FromJSON MoreLikeThisFieldQuery where
943+ parseJSON = withObject "MoreLikeThisFieldQuery" parse
944+ where parse = fieldTagged $ \fn o ->
945+ MoreLikeThisFieldQuery
946+ <$> o .: "like_text"
947+ <*> pure fn
948+ <*> o .:? "percent_terms_to_match"
949+ <*> o .:? "min_term_freq"
950+ <*> o .:? "max_query_terms"
951+ -- <*> (optionalNE =<< o .:? "stop_words")
952+ <*> o .:? "stop_words"
953+ <*> o .:? "min_doc_freq"
954+ <*> o .:? "max_doc_freq"
955+ <*> o .:? "min_word_length"
956+ <*> o .:? "max_word_length"
957+ <*> o .:? "boost_terms"
958+ <*> o .:? "boost"
959+ <*> o .:? "analyzer"
960+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
961+
962+instance ToJSON MoreLikeThisQuery where
963+ toJSON (MoreLikeThisQuery text fields percent
964+ mtf mqt stopwords mindf maxdf
965+ minwl maxwl boostTerms boost analyzer) =
966+ omitNulls base
967+ where base = [ "like_text" .= text
968+ , "fields" .= fields
969+ , "percent_terms_to_match" .= percent
970+ , "min_term_freq" .= mtf
971+ , "max_query_terms" .= mqt
972+ , "stop_words" .= stopwords
973+ , "min_doc_freq" .= mindf
974+ , "max_doc_freq" .= maxdf
975+ , "min_word_length" .= minwl
976+ , "max_word_length" .= maxwl
977+ , "boost_terms" .= boostTerms
978+ , "boost" .= boost
979+ , "analyzer" .= analyzer ]
980+
981+instance FromJSON MoreLikeThisQuery where
982+ parseJSON = withObject "MoreLikeThisQuery" parse
983+ where parse o = MoreLikeThisQuery
984+ <$> o .: "like_text"
985+ -- <*> (optionalNE =<< o .:? "fields")
986+ <*> o .:? "fields"
987+ <*> o .:? "percent_terms_to_match"
988+ <*> o .:? "min_term_freq"
989+ <*> o .:? "max_query_terms"
990+ -- <*> (optionalNE =<< o .:? "stop_words")
991+ <*> o .:? "stop_words"
992+ <*> o .:? "min_doc_freq"
993+ <*> o .:? "max_doc_freq"
994+ <*> o .:? "min_word_length"
995+ <*> o .:? "max_word_length"
996+ <*> o .:? "boost_terms"
997+ <*> o .:? "boost"
998+ <*> o .:? "analyzer"
999+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
1000+
1001+instance ToJSON IndicesQuery where
1002+ toJSON (IndicesQuery indices query noMatch) =
1003+ omitNulls [ "indices" .= indices
1004+ , "no_match_query" .= noMatch
1005+ , "query" .= query ]
1006+
1007+instance FromJSON IndicesQuery where
1008+ parseJSON = withObject "IndicesQuery" parse
1009+ where parse o = IndicesQuery
1010+ <$> o .:? "indices" .!= []
1011+ <*> o .: "query"
1012+ <*> o .:? "no_match_query"
1013+
1014+instance ToJSON HasParentQuery where
1015+ toJSON (HasParentQuery queryType query scoreType) =
1016+ omitNulls [ "parent_type" .= queryType
1017+ , "score_type" .= scoreType
1018+ , "query" .= query ]
1019+
1020+instance FromJSON HasParentQuery where
1021+ parseJSON = withObject "HasParentQuery" parse
1022+ where parse o = HasParentQuery
1023+ <$> o .: "parent_type"
1024+ <*> o .: "query"
1025+ <*> o .:? "score_type"
1026+
1027+instance ToJSON HasChildQuery where
1028+ toJSON (HasChildQuery queryType query scoreType) =
1029+ omitNulls [ "query" .= query
1030+ , "score_type" .= scoreType
1031+ , "type" .= queryType ]
1032+
1033+instance FromJSON HasChildQuery where
1034+ parseJSON = withObject "HasChildQuery" parse
1035+ where parse o = HasChildQuery
1036+ <$> o .: "type"
1037+ <*> o .: "query"
1038+ <*> o .:? "score_type"
1039+
1040+instance ToJSON FuzzyQuery where
1041+ toJSON (FuzzyQuery (FieldName fieldName) queryText
1042+ prefixLength maxEx fuzziness boost) =
1043+ object [ fieldName .= omitNulls base ]
1044+ where base = [ "value" .= queryText
1045+ , "fuzziness" .= fuzziness
1046+ , "prefix_length" .= prefixLength
1047+ , "boost" .= boost
1048+ , "max_expansions" .= maxEx ]
1049+
1050+instance FromJSON FuzzyQuery where
1051+ parseJSON = withObject "FuzzyQuery" parse
1052+ where parse = fieldTagged $ \fn o ->
1053+ FuzzyQuery fn
1054+ <$> o .: "value"
1055+ <*> o .: "prefix_length"
1056+ <*> o .: "max_expansions"
1057+ <*> o .: "fuzziness"
1058+ <*> o .:? "boost"
1059+
1060+instance ToJSON FuzzyLikeFieldQuery where
1061+ toJSON (FuzzyLikeFieldQuery (FieldName fieldName)
1062+ fieldText maxTerms ignoreFreq fuzziness prefixLength
1063+ boost analyzer) =
1064+ object [ fieldName .=
1065+ omitNulls [ "like_text" .= fieldText
1066+ , "max_query_terms" .= maxTerms
1067+ , "ignore_tf" .= ignoreFreq
1068+ , "fuzziness" .= fuzziness
1069+ , "prefix_length" .= prefixLength
1070+ , "analyzer" .= analyzer
1071+ , "boost" .= boost ]]
1072+
1073+instance FromJSON FuzzyLikeFieldQuery where
1074+ parseJSON = withObject "FuzzyLikeFieldQuery" parse
1075+ where parse = fieldTagged $ \fn o ->
1076+ FuzzyLikeFieldQuery fn
1077+ <$> o .: "like_text"
1078+ <*> o .: "max_query_terms"
1079+ <*> o .: "ignore_tf"
1080+ <*> o .: "fuzziness"
1081+ <*> o .: "prefix_length"
1082+ <*> o .: "boost"
1083+ <*> o .:? "analyzer"
1084+
1085+instance ToJSON FuzzyLikeThisQuery where
1086+ toJSON (FuzzyLikeThisQuery fields text maxTerms
1087+ ignoreFreq fuzziness prefixLength boost analyzer) =
1088+ omitNulls base
1089+ where base = [ "fields" .= fields
1090+ , "like_text" .= text
1091+ , "max_query_terms" .= maxTerms
1092+ , "ignore_tf" .= ignoreFreq
1093+ , "fuzziness" .= fuzziness
1094+ , "prefix_length" .= prefixLength
1095+ , "analyzer" .= analyzer
1096+ , "boost" .= boost ]
1097+
1098+instance FromJSON FuzzyLikeThisQuery where
1099+ parseJSON = withObject "FuzzyLikeThisQuery" parse
1100+ where parse o = FuzzyLikeThisQuery
1101+ <$> o .:? "fields" .!= []
1102+ <*> o .: "like_text"
1103+ <*> o .: "max_query_terms"
1104+ <*> o .: "ignore_tf"
1105+ <*> o .: "fuzziness"
1106+ <*> o .: "prefix_length"
1107+ <*> o .: "boost"
1108+ <*> o .:? "analyzer"
1109+
1110+instance ToJSON FilteredQuery where
1111+ toJSON (FilteredQuery query fFilter) =
1112+ object [ "query" .= query
1113+ , "filter" .= fFilter ]
1114+
1115+instance FromJSON FilteredQuery where
1116+ parseJSON = withObject "FilteredQuery" parse
1117+ where parse o = FilteredQuery
1118+ <$> o .: "query"
1119+ <*> o .: "filter"
1120+
1121+instance ToJSON DisMaxQuery where
1122+ toJSON (DisMaxQuery queries tiebreaker boost) =
1123+ omitNulls base
1124+ where base = [ "queries" .= queries
1125+ , "boost" .= boost
1126+ , "tie_breaker" .= tiebreaker ]
1127+
1128+instance FromJSON DisMaxQuery where
1129+ parseJSON = withObject "DisMaxQuery" parse
1130+ where parse o = DisMaxQuery
1131+ <$> o .:? "queries" .!= []
1132+ <*> o .: "tie_breaker"
1133+ <*> o .:? "boost"
1134+
1135+instance ToJSON CommonTermsQuery where
1136+ toJSON (CommonTermsQuery (FieldName fieldName)
1137+ (QueryString query) cf lfo hfo msm
1138+ boost analyzer disableCoord) =
1139+ object [fieldName .= omitNulls base ]
1140+ where base = [ "query" .= query
1141+ , "cutoff_frequency" .= cf
1142+ , "low_freq_operator" .= lfo
1143+ , "minimum_should_match" .= msm
1144+ , "boost" .= boost
1145+ , "analyzer" .= analyzer
1146+ , "disable_coord" .= disableCoord
1147+ , "high_freq_operator" .= hfo ]
1148+
1149+instance FromJSON CommonTermsQuery where
1150+ parseJSON = withObject "CommonTermsQuery" parse
1151+ where parse = fieldTagged $ \fn o ->
1152+ CommonTermsQuery fn
1153+ <$> o .: "query"
1154+ <*> o .: "cutoff_frequency"
1155+ <*> o .: "low_freq_operator"
1156+ <*> o .: "high_freq_operator"
1157+ <*> o .:? "minimum_should_match"
1158+ <*> o .:? "boost"
1159+ <*> o .:? "analyzer"
1160+ <*> o .:? "disable_coord"
1161+
1162+instance ToJSON CommonMinimumMatch where
1163+ toJSON (CommonMinimumMatch mm) = toJSON mm
1164+ toJSON (CommonMinimumMatchHighLow (MinimumMatchHighLow lowF highF)) =
1165+ object [ "low_freq" .= lowF
1166+ , "high_freq" .= highF ]
1167+
1168+instance FromJSON CommonMinimumMatch where
1169+ parseJSON v = parseMinimum v
1170+ <|> parseMinimumHighLow v
1171+ where parseMinimum = fmap CommonMinimumMatch . parseJSON
1172+ parseMinimumHighLow = fmap CommonMinimumMatchHighLow . withObject "CommonMinimumMatchHighLow" (\o ->
1173+ MinimumMatchHighLow
1174+ <$> o .: "low_freq"
1175+ <*> o .: "high_freq")
1176+
1177+
1178+instance ToJSON BoostingQuery where
1179+ toJSON (BoostingQuery bqPositiveQuery bqNegativeQuery bqNegativeBoost) =
1180+ object [ "positive" .= bqPositiveQuery
1181+ , "negative" .= bqNegativeQuery
1182+ , "negative_boost" .= bqNegativeBoost ]
1183+
1184+instance FromJSON BoostingQuery where
1185+ parseJSON = withObject "BoostingQuery" parse
1186+ where parse o = BoostingQuery
1187+ <$> o .: "positive"
1188+ <*> o .: "negative"
1189+ <*> o .: "negative_boost"
1190+
1191+instance ToJSON BoolQuery where
1192+ toJSON (BoolQuery mustM notM shouldM bqMin boost disableCoord) =
1193+ omitNulls base
1194+ where base = [ "must" .= mustM
1195+ , "must_not" .= notM
1196+ , "should" .= shouldM
1197+ , "minimum_should_match" .= bqMin
1198+ , "boost" .= boost
1199+ , "disable_coord" .= disableCoord ]
1200+
1201+instance FromJSON BoolQuery where
1202+ parseJSON = withObject "BoolQuery" parse
1203+ where parse o = BoolQuery
1204+ <$> o .:? "must" .!= []
1205+ <*> o .:? "must_not" .!= []
1206+ <*> o .:? "should" .!= []
1207+ <*> o .:? "minimum_should_match"
1208+ <*> o .:? "boost"
1209+ <*> o .:? "disable_coord"
1210+
1211+instance ToJSON MatchQuery where
1212+ toJSON (MatchQuery (FieldName fieldName)
1213+ (QueryString mqQueryString) booleanOperator
1214+ zeroTermsQuery cutoffFrequency matchQueryType
1215+ analyzer maxExpansions lenient boost) =
1216+ object [ fieldName .= omitNulls base ]
1217+ where base = [ "query" .= mqQueryString
1218+ , "operator" .= booleanOperator
1219+ , "zero_terms_query" .= zeroTermsQuery
1220+ , "cutoff_frequency" .= cutoffFrequency
1221+ , "type" .= matchQueryType
1222+ , "analyzer" .= analyzer
1223+ , "max_expansions" .= maxExpansions
1224+ , "lenient" .= lenient
1225+ , "boost" .= boost ]
1226+
1227+instance FromJSON MatchQuery where
1228+ parseJSON = withObject "MatchQuery" parse
1229+ where parse = fieldTagged $ \fn o ->
1230+ MatchQuery fn
1231+ <$> o .: "query"
1232+ <*> o .: "operator"
1233+ <*> o .: "zero_terms_query"
1234+ <*> o .:? "cutoff_frequency"
1235+ <*> o .:? "type"
1236+ <*> o .:? "analyzer"
1237+ <*> o .:? "max_expansions"
1238+ <*> o .:? "lenient"
1239+ <*> o .:? "boost"
1240+
1241+instance ToJSON MultiMatchQuery where
1242+ toJSON (MultiMatchQuery fields (QueryString query) boolOp
1243+ ztQ tb mmqt cf analyzer maxEx lenient) =
1244+ object ["multi_match" .= omitNulls base]
1245+ where base = [ "fields" .= fmap toJSON fields
1246+ , "query" .= query
1247+ , "operator" .= boolOp
1248+ , "zero_terms_query" .= ztQ
1249+ , "tie_breaker" .= tb
1250+ , "type" .= mmqt
1251+ , "cutoff_frequency" .= cf
1252+ , "analyzer" .= analyzer
1253+ , "max_expansions" .= maxEx
1254+ , "lenient" .= lenient ]
1255+
1256+instance FromJSON MultiMatchQuery where
1257+ parseJSON = withObject "MultiMatchQuery" parse
1258+ where parse raw = do o <- raw .: "multi_match"
1259+ MultiMatchQuery
1260+ <$> o .:? "fields" .!= []
1261+ <*> o .: "query"
1262+ <*> o .: "operator"
1263+ <*> o .: "zero_terms_query"
1264+ <*> o .:? "tie_breaker"
1265+ <*> o .:? "type"
1266+ <*> o .:? "cutoff_frequency"
1267+ <*> o .:? "analyzer"
1268+ <*> o .:? "max_expansions"
1269+ <*> o .:? "lenient"
1270+
1271+instance ToJSON Filter where
1272+ toJSON (AndFilter filters cache) =
1273+ object ["and" .=
1274+ object [ "filters" .= fmap toJSON filters
1275+ , "_cache" .= cache]]
1276+
1277+ toJSON (OrFilter filters cache) =
1278+ object ["or" .=
1279+ object [ "filters" .= fmap toJSON filters
1280+ , "_cache" .= cache]]
1281+
1282+ toJSON (NotFilter notFilter cache) =
1283+ object ["not" .=
1284+ object ["filter" .= notFilter
1285+ , "_cache" .= cache]]
1286+
1287+ toJSON (IdentityFilter) =
1288+ object ["match_all" .= object []]
1289+
1290+ toJSON (TermFilter (Term termFilterField termFilterValue) cache) =
1291+ object ["term" .= object base]
1292+ where base = [termFilterField .= termFilterValue,
1293+ "_cache" .= cache]
1294+
1295+ toJSON (ExistsFilter (FieldName fieldName)) =
1296+ object ["exists" .= object
1297+ ["field" .= fieldName]]
1298+
1299+ toJSON (BoolFilter boolMatch) =
1300+ object ["bool" .= boolMatch]
1301+
1302+ toJSON (GeoBoundingBoxFilter bbConstraint) =
1303+ object ["geo_bounding_box" .= bbConstraint]
1304+
1305+ toJSON (GeoDistanceFilter (GeoPoint (FieldName distanceGeoField) geoDistLatLon)
1306+ distance distanceType optimizeBbox cache) =
1307+ object ["geo_distance" .=
1308+ object ["distance" .= distance
1309+ , "distance_type" .= distanceType
1310+ , "optimize_bbox" .= optimizeBbox
1311+ , distanceGeoField .= geoDistLatLon
1312+ , "_cache" .= cache]]
1313+
1314+ toJSON (GeoDistanceRangeFilter (GeoPoint (FieldName gddrField) drLatLon)
1315+ (DistanceRange geoDistRangeDistFrom drDistanceTo)) =
1316+ object ["geo_distance_range" .=
1317+ object ["from" .= geoDistRangeDistFrom
1318+ , "to" .= drDistanceTo
1319+ , gddrField .= drLatLon]]
1320+
1321+ toJSON (GeoPolygonFilter (FieldName geoPolygonFilterField) latLons) =
1322+ object ["geo_polygon" .=
1323+ object [geoPolygonFilterField .=
1324+ object ["points" .= fmap toJSON latLons]]]
1325+
1326+ toJSON (IdsFilter (MappingName mappingName) values) =
1327+ object ["ids" .=
1328+ object ["type" .= mappingName
1329+ , "values" .= fmap unpackId values]]
1330+
1331+ toJSON (LimitFilter limit) =
1332+ object ["limit" .= object ["value" .= limit]]
1333+
1334+ toJSON (MissingFilter (FieldName fieldName) (Existence existence) (NullValue nullValue)) =
1335+ object ["missing" .=
1336+ object [ "field" .= fieldName
1337+ , "existence" .= existence
1338+ , "null_value" .= nullValue]]
1339+
1340+ toJSON (PrefixFilter (FieldName fieldName) fieldValue cache) =
1341+ object ["prefix" .=
1342+ object [fieldName .= fieldValue
1343+ , "_cache" .= cache]]
1344+
1345+ toJSON (QueryFilter query False) =
1346+ object ["query" .= toJSON query ]
1347+ toJSON (QueryFilter query True) =
1348+ object ["fquery" .=
1349+ object [ "query" .= toJSON query
1350+ , "_cache" .= True ]]
1351+
1352+ toJSON (RangeFilter (FieldName fieldName) rangeValue rangeExecution cache) =
1353+ object ["range" .=
1354+ object [ fieldName .= object (rangeValueToPair rangeValue)
1355+ , "execution" .= rangeExecution
1356+ , "_cache" .= cache]]
1357+
1358+ toJSON (RegexpFilter (FieldName fieldName)
1359+ (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
1360+ object ["regexp" .=
1361+ object [fieldName .=
1362+ object ["value" .= regexText
1363+ , "flags" .= flags]
1364+ , "_name" .= cacheName
1365+ , "_cache" .= cache
1366+ , "_cache_key" .= cacheKey]]
1367+
1368+instance FromJSON Filter where
1369+ parseJSON = withObject "Filter" parse
1370+ where parse o = andFilter `taggedWith` "and"
1371+ <|> orFilter `taggedWith` "or"
1372+ <|> notFilter `taggedWith` "not"
1373+ <|> identityFilter `taggedWith` "match_all"
1374+ <|> boolFilter `taggedWith` "bool"
1375+ <|> existsFilter `taggedWith` "exists"
1376+ <|> geoBoundingBoxFilter `taggedWith` "geo_bounding_box"
1377+ <|> geoDistanceFilter `taggedWith` "geo_distance"
1378+ <|> geoDistanceRangeFilter `taggedWith` "geo_distance_range"
1379+ <|> geoPolygonFilter `taggedWith` "geo_polygon"
1380+ <|> idsFilter `taggedWith` "ids"
1381+ <|> limitFilter `taggedWith` "limit"
1382+ <|> missingFilter `taggedWith` "missing"
1383+ <|> prefixFilter `taggedWith` "prefix"
1384+ <|> queryFilter `taggedWith` "query"
1385+ <|> fqueryFilter `taggedWith` "fquery"
1386+ <|> rangeFilter `taggedWith` "range"
1387+ <|> regexpFilter `taggedWith` "regexp"
1388+ <|> termFilter `taggedWith` "term"
1389+ where taggedWith parser k = parser =<< o .: k
1390+ andFilter o = AndFilter <$> o .: "filters"
1391+ <*> o .:? "_cache" .!= defaultCache
1392+ orFilter o = OrFilter <$> o .: "filters"
1393+ <*> o .:? "_cache" .!= defaultCache
1394+ notFilter o = NotFilter <$> o .: "filter"
1395+ <*> o .: "_cache" .!= defaultCache
1396+ identityFilter :: Object -> Parser Filter
1397+ identityFilter m
1398+ | HM.null m = pure IdentityFilter
1399+ | otherwise = fail ("Identityfilter expected empty object but got " <> show m)
1400+ boolFilter = pure . BoolFilter
1401+ existsFilter o = ExistsFilter <$> o .: "field"
1402+ geoBoundingBoxFilter = pure . GeoBoundingBoxFilter
1403+ geoDistanceFilter o = do
1404+ case HM.toList (deleteSeveral ["distance", "distance_type", "optimize_bbox", "_cache"] o) of
1405+ [(fn, v)] -> do
1406+ gp <- GeoPoint (FieldName fn) <$> parseJSON v
1407+ GeoDistanceFilter gp <$> o .: "distance"
1408+ <*> o .: "distance_type"
1409+ <*> o .: "optimize_bbox"
1410+ <*> o .:? "_cache" .!= defaultCache
1411+ _ -> fail "Could not find GeoDistanceFilter field name"
1412+ geoDistanceRangeFilter o = do
1413+ case HM.toList (deleteSeveral ["from", "to"] o) of
1414+ [(fn, v)] -> do
1415+ gp <- GeoPoint (FieldName fn) <$> parseJSON v
1416+ rng <- DistanceRange <$> o .: "from" <*> o .: "to"
1417+ return (GeoDistanceRangeFilter gp rng)
1418+ _ -> fail "Could not find GeoDistanceRangeFilter field name"
1419+ geoPolygonFilter = fieldTagged $ \fn o -> GeoPolygonFilter fn <$> o .: "points"
1420+ idsFilter o = IdsFilter <$> o .: "type"
1421+ <*> o .: "values"
1422+ limitFilter o = LimitFilter <$> o .: "value"
1423+ missingFilter o = MissingFilter <$> o .: "field"
1424+ <*> o .: "existence"
1425+ <*> o .: "null_value"
1426+ prefixFilter o = case HM.toList (HM.delete "_cache" o) of
1427+ [(fn, String v)] -> PrefixFilter (FieldName fn) v <$> o .:? "_cache" .!= defaultCache
1428+ _ -> fail "Could not parse PrefixFilter"
1429+
1430+ queryFilter q = pure (QueryFilter q False)
1431+ fqueryFilter o = QueryFilter <$> o .: "query" <*> pure True
1432+ rangeFilter o = case HM.toList (deleteSeveral ["execution", "_cache"] o) of
1433+ [(fn, v)] -> RangeFilter (FieldName fn)
1434+ <$> parseJSON v
1435+ <*> o .: "execution"
1436+ <*> o .:? "_cache" .!= defaultCache
1437+ _ -> fail "Could not find field name for RangeFilter"
1438+ regexpFilter o = case HM.toList (deleteSeveral ["_name", "_cache", "_cache_key"] o) of
1439+ [(fn, Object o')] -> RegexpFilter (FieldName fn)
1440+ <$> o' .: "value"
1441+ <*> o' .: "flags"
1442+ <*> o .: "_name"
1443+ <*> o .:? "_cache" .!= defaultCache
1444+ <*> o .: "_cache_key"
1445+ _ -> fail "Could not find field name for RegexpFilter"
1446+ termFilter o = case HM.toList (HM.delete "_cache" o) of
1447+ [(termField, String termVal)] -> TermFilter (Term termField termVal)
1448+ <$> o .:? "_cache" .!= defaultCache
1449+ _ -> fail "Could not find term field for TermFilter"
1450+
1451+instance ToJSON BooleanOperator where
1452+ toJSON And = String "and"
1453+ toJSON Or = String "or"
1454+
1455+instance FromJSON BooleanOperator where
1456+ parseJSON = withText "BooleanOperator" parse
1457+ where parse "and" = pure And
1458+ parse "or" = pure Or
1459+ parse o = fail ("Unexpected BooleanOperator: " <> show o)
1460+
1461+instance ToJSON ZeroTermsQuery where
1462+ toJSON ZeroTermsNone = String "none"
1463+ toJSON ZeroTermsAll = String "all"
1464+
1465+instance FromJSON ZeroTermsQuery where
1466+ parseJSON = withText "ZeroTermsQuery" parse
1467+ where parse "none" = pure ZeroTermsNone
1468+ parse "all" = pure ZeroTermsAll
1469+ parse q = fail ("Unexpected ZeroTermsQuery: " <> show q)
1470+
1471+fieldTagged :: Monad m => (FieldName -> Object -> m a) -> Object -> m a
1472+fieldTagged f o = case HM.toList o of
1473+ [(k, Object o')] -> f (FieldName k) o'
1474+ _ -> fail "Expected object with 1 field-named key"
1475+
1476+instance ToJSON RangeExecution where
1477+ toJSON RangeExecutionIndex = "index"
1478+ toJSON RangeExecutionFielddata = "fielddata"
1479+
1480+
1481+instance FromJSON RangeExecution where
1482+ parseJSON = withText "RangeExecution" parse
1483+ where parse "index" = pure RangeExecutionIndex
1484+ parse "fielddata" = pure RangeExecutionFielddata
1485+ parse t = error ("Unrecognized RangeExecution " <> show t)
1486+
1487+instance ToJSON RegexpFlags where
1488+ toJSON AllRegexpFlags = String "ALL"
1489+ toJSON NoRegexpFlags = String "NONE"
1490+ toJSON (SomeRegexpFlags (h :| fs)) = String $ T.intercalate "|" flagStrs
1491+ where flagStrs = map flagStr . nub $ h:fs
1492+ flagStr AnyString = "ANYSTRING"
1493+ flagStr Automaton = "AUTOMATON"
1494+ flagStr Complement = "COMPLEMENT"
1495+ flagStr Empty = "EMPTY"
1496+ flagStr Intersection = "INTERSECTION"
1497+ flagStr Interval = "INTERVAL"
1498+
1499+instance FromJSON RegexpFlags where
1500+ parseJSON = withText "RegexpFlags" parse
1501+ where parse "ALL" = pure AllRegexpFlags
1502+ parse "NONE" = pure NoRegexpFlags
1503+ parse t = SomeRegexpFlags <$> parseNEJSON (String <$> T.splitOn "|" t)
1504+
1505+instance FromJSON RegexpFlag where
1506+ parseJSON = withText "RegexpFlag" parse
1507+ where parse "ANYSTRING" = pure AnyString
1508+ parse "AUTOMATON" = pure Automaton
1509+ parse "COMPLEMENT" = pure Complement
1510+ parse "EMPTY" = pure Empty
1511+ parse "INTERSECTION" = pure Intersection
1512+ parse "INTERVAL" = pure Interval
1513+ parse f = fail ("Unknown RegexpFlag: " <> show f)
1514+
1515+rangeValueToPair :: RangeValue -> [Pair]
1516+rangeValueToPair rv = case rv of
1517+ RangeDateLte (LessThanEqD t) -> ["lte" .= t]
1518+ RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
1519+ RangeDateLt (LessThanD t) -> ["lt" .= t]
1520+ RangeDateGt (GreaterThanD t) -> ["gt" .= t]
1521+ RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
1522+ RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
1523+ RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
1524+ RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
1525+ RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
1526+ RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
1527+ RangeDoubleLt (LessThan t) -> ["lt" .= t]
1528+ RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
1529+ RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
1530+ RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
1531+ RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
1532+ RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1533+
1534+instance ToJSON ScoreType where
1535+ toJSON ScoreTypeMax = "max"
1536+ toJSON ScoreTypeAvg = "avg"
1537+ toJSON ScoreTypeSum = "sum"
1538+ toJSON ScoreTypeNone = "none"
1539+
1540+instance FromJSON ScoreType where
1541+ parseJSON = withText "ScoreType" parse
1542+ where parse "max" = pure ScoreTypeMax
1543+ parse "avg" = pure ScoreTypeAvg
1544+ parse "sum" = pure ScoreTypeSum
1545+ parse "none" = pure ScoreTypeNone
1546+ parse t = fail ("Unexpected ScoreType: " <> show t)
1547+
1548+instance ToJSON MatchQueryType where
1549+ toJSON MatchPhrase = "phrase"
1550+ toJSON MatchPhrasePrefix = "phrase_prefix"
1551+
1552+instance FromJSON MatchQueryType where
1553+ parseJSON = withText "MatchQueryType" parse
1554+ where parse "phrase" = pure MatchPhrase
1555+ parse "phrase_prefix" = pure MatchPhrasePrefix
1556+ parse t = fail ("Unexpected MatchQueryType: " <> show t)
1557+
1558+instance ToJSON MultiMatchQueryType where
1559+ toJSON MultiMatchBestFields = "best_fields"
1560+ toJSON MultiMatchMostFields = "most_fields"
1561+ toJSON MultiMatchCrossFields = "cross_fields"
1562+ toJSON MultiMatchPhrase = "phrase"
1563+ toJSON MultiMatchPhrasePrefix = "phrase_prefix"
1564+
1565+instance FromJSON MultiMatchQueryType where
1566+ parseJSON = withText "MultiMatchPhrasePrefix" parse
1567+ where parse "best_fields" = pure MultiMatchBestFields
1568+ parse "most_fields" = pure MultiMatchMostFields
1569+ parse "cross_fields" = pure MultiMatchCrossFields
1570+ parse "phrase" = pure MultiMatchPhrase
1571+ parse "phrase_prefix" = pure MultiMatchPhrasePrefix
1572+ parse t = fail ("Unexpected MultiMatchPhrasePrefix: " <> show t)
1573+
1574+defaultCache :: Cache
1575+defaultCache = False
1576+
1577+instance ToJSON BoolMatch where
1578+ toJSON (MustMatch term cache) = object ["must" .= term,
1579+ "_cache" .= cache]
1580+ toJSON (MustNotMatch term cache) = object ["must_not" .= term,
1581+ "_cache" .= cache]
1582+ toJSON (ShouldMatch terms cache) = object ["should" .= fmap toJSON terms,
1583+ "_cache" .= cache]
1584+
1585+instance FromJSON BoolMatch where
1586+ parseJSON = withObject "BoolMatch" parse
1587+ where parse o = mustMatch `taggedWith` "must"
1588+ <|> mustNotMatch `taggedWith` "must_not"
1589+ <|> shouldMatch `taggedWith` "should"
1590+ where taggedWith parser k = parser =<< o .: k
1591+ mustMatch t = MustMatch t <$> o .:? "_cache" .!= defaultCache
1592+ mustNotMatch t = MustNotMatch t <$> o .:? "_cache" .!= defaultCache
1593+ shouldMatch t = ShouldMatch t <$> o .:? "_cache" .!= defaultCache
1594+
1595+instance ToJSON GeoBoundingBoxConstraint where
1596+ toJSON (GeoBoundingBoxConstraint
1597+ (FieldName gbbcGeoBBField) gbbcConstraintBox cache type') =
1598+ object [gbbcGeoBBField .= gbbcConstraintBox
1599+ , "_cache" .= cache
1600+ , "type" .= type']
1601+
1602+instance FromJSON GeoBoundingBoxConstraint where
1603+ parseJSON = withObject "GeoBoundingBoxConstraint" parse
1604+ where parse o = case HM.toList (deleteSeveral ["type", "_cache"] o) of
1605+ [(fn, v)] -> GeoBoundingBoxConstraint (FieldName fn)
1606+ <$> parseJSON v
1607+ <*> o .:? "_cache" .!= defaultCache
1608+ <*> o .: "type"
1609+ _ -> fail "Could not find field name for GeoBoundingBoxConstraint"
1610+
1611+instance ToJSON Distance where
1612+ toJSON (Distance dCoefficient dUnit) =
1613+ String boltedTogether where
1614+ coefText = showText dCoefficient
1615+ (String unitText) = toJSON dUnit
1616+ boltedTogether = mappend coefText unitText
1617+
1618+instance FromJSON Distance where
1619+ parseJSON = withText "Distance" parse
1620+ where parse t = Distance <$> parseCoeff nT
1621+ <*> parseJSON (String unitT)
1622+ where (nT, unitT) = T.span validForNumber t
1623+ -- may be a better way to do this
1624+ validForNumber '-' = True
1625+ validForNumber '.' = True
1626+ validForNumber 'e' = True
1627+ validForNumber c = isNumber c
1628+ parseCoeff "" = fail "Empty string cannot be parsed as number"
1629+ parseCoeff s = return (read (T.unpack s))
1630+
1631+instance ToJSON DistanceType where
1632+ toJSON Arc = String "arc"
1633+ toJSON SloppyArc = String "sloppy_arc"
1634+ toJSON Plane = String "plane"
1635+
1636+instance FromJSON DistanceType where
1637+ parseJSON = withText "DistanceType" parse
1638+ where parse "arc" = pure Arc
1639+ parse "sloppy_arc" = pure SloppyArc
1640+ parse "plane" = pure Plane
1641+ parse t = fail ("Unrecognized DistanceType: " <> show t)
1642+
1643+instance ToJSON OptimizeBbox where
1644+ toJSON NoOptimizeBbox = String "none"
1645+ toJSON (OptimizeGeoFilterType gft) = toJSON gft
1646+
1647+instance FromJSON OptimizeBbox where
1648+ parseJSON v = withText "NoOptimizeBbox" parseNoOptimize v
1649+ <|> parseOptimize v
1650+ where parseNoOptimize "none" = pure NoOptimizeBbox
1651+ parseNoOptimize _ = mzero
1652+ parseOptimize = fmap OptimizeGeoFilterType . parseJSON
1653+
1654+instance ToJSON Term where
1655+ toJSON (Term field value) = object ["term" .= object
1656+ [field .= value]]
1657+
1658+instance FromJSON Term where
1659+ parseJSON = withObject "Term" parse
1660+ where parse o = do termObj <- o .: "term"
1661+ case HM.toList termObj of
1662+ [(fn, v)] -> Term fn <$> parseJSON v
1663+ _ -> fail "Expected object with 1 field-named key"
1664+
1665+instance ToJSON GeoBoundingBox where
1666+ toJSON (GeoBoundingBox gbbTopLeft gbbBottomRight) =
1667+ object ["top_left" .= gbbTopLeft
1668+ , "bottom_right" .= gbbBottomRight]
1669+
1670+instance FromJSON GeoBoundingBox where
1671+ parseJSON = withObject "GeoBoundingBox" parse
1672+ where parse o = GeoBoundingBox
1673+ <$> o .: "top_left"
1674+ <*> o .: "bottom_right"
1675+
1676+instance ToJSON GeoFilterType where
1677+ toJSON GeoFilterMemory = String "memory"
1678+ toJSON GeoFilterIndexed = String "indexed"
1679+
1680+instance FromJSON GeoFilterType where
1681+ parseJSON = withText "GeoFilterType" parse
1682+ where parse "memory" = pure GeoFilterMemory
1683+ parse "indexed" = pure GeoFilterIndexed
1684+ parse t = fail ("Unrecognized GeoFilterType: " <> show t)
1685+
1686+{-| 'unpackId' is a silly convenience function that gets used once.
1687+-}
1688+unpackId :: DocId -> Text
1689+unpackId (DocId docId) = docId
src/Database/V1/Bloodhound/Internal/Sort.hsnew file+106
@@ -0,0 +1,106 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Database.V1.Bloodhound.Internal.Sort where
4+
5+
6+import Bloodhound.Import
7+
8+import Database.V1.Bloodhound.Internal.Newtypes
9+import Database.V1.Bloodhound.Internal.Query
10+
11+
12+{-| 'Sort' is a synonym for a list of 'SortSpec's. Sort behavior is order
13+ dependent with later sorts acting as tie-breakers for earlier sorts.
14+-}
15+type Sort = [SortSpec]
16+
17+
18+{-| The two main kinds of 'SortSpec' are 'DefaultSortSpec' and
19+ 'GeoDistanceSortSpec'. The latter takes a 'SortOrder', 'GeoPoint', and
20+ 'DistanceUnit' to express "nearness" to a single geographical point as a
21+ sort specification.
22+
23+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
24+-}
25+data SortSpec = DefaultSortSpec DefaultSort
26+ | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Show)
27+
28+instance ToJSON SortSpec where
29+ toJSON (DefaultSortSpec
30+ (DefaultSort (FieldName dsSortFieldName) dsSortOrder dsIgnoreUnmapped
31+ dsSortMode dsMissingSort dsNestedFilter)) =
32+ object [dsSortFieldName .= omitNulls base] where
33+ base = [ "order" .= dsSortOrder
34+ , "ignore_unmapped" .= dsIgnoreUnmapped
35+ , "mode" .= dsSortMode
36+ , "missing" .= dsMissingSort
37+ , "nested_filter" .= dsNestedFilter ]
38+
39+ toJSON (GeoDistanceSortSpec gdsSortOrder (GeoPoint (FieldName field) gdsLatLon) units) =
40+ object [ "unit" .= units
41+ , field .= gdsLatLon
42+ , "order" .= gdsSortOrder ]
43+
44+{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
45+ 'mkSort' convenience function for when you want to specify only the most
46+ common parameters.
47+
48+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
49+-}
50+data DefaultSort =
51+ DefaultSort { sortFieldName :: FieldName
52+ , sortOrder :: SortOrder
53+ -- default False
54+ , ignoreUnmapped :: Bool
55+ , sortMode :: Maybe SortMode
56+ , missingSort :: Maybe Missing
57+ , nestedFilter :: Maybe Filter } deriving (Eq, Show)
58+
59+{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
60+ encoded into "asc" or "desc" when turned into JSON.
61+
62+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
63+-}
64+data SortOrder = Ascending
65+ | Descending deriving (Eq, Show)
66+
67+
68+instance ToJSON SortOrder where
69+ toJSON Ascending = String "asc"
70+ toJSON Descending = String "desc"
71+
72+{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
73+
74+http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_sort_mode_option
75+-}
76+data SortMode = SortMin
77+ | SortMax
78+ | SortSum
79+ | SortAvg deriving (Eq, Show)
80+
81+instance ToJSON SortMode where
82+ toJSON SortMin = String "min"
83+ toJSON SortMax = String "max"
84+ toJSON SortSum = String "sum"
85+ toJSON SortAvg = String "avg"
86+
87+{-| 'Missing' prescribes how to handle missing fields. A missing field can be
88+ sorted last, first, or using a custom value as a substitute.
89+
90+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_missing_values>
91+-}
92+data Missing = LastMissing
93+ | FirstMissing
94+ | CustomMissing Text deriving (Eq, Show)
95+
96+instance ToJSON Missing where
97+ toJSON LastMissing = String "_last"
98+ toJSON FirstMissing = String "_first"
99+ toJSON (CustomMissing txt) = String txt
100+
101+-- {-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
102+-- that you can concisely describe the usual kind of 'SortSpec's you want.
103+-- -}
104+mkSort :: FieldName -> SortOrder -> DefaultSort
105+mkSort fieldName sOrder = DefaultSort fieldName sOrder False Nothing Nothing Nothing
106+
src/Database/V1/Bloodhound/Internal/StringlyTyped.hsnew file+26
@@ -0,0 +1,26 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Database.V1.Bloodhound.Internal.StringlyTyped where
4+
5+import Bloodhound.Import
6+
7+import qualified Data.Text as T
8+
9+newtype StringlyTypedDouble = StringlyTypedDouble { unStringlyTypedDouble :: Double }
10+
11+
12+instance FromJSON StringlyTypedDouble where
13+ parseJSON = fmap StringlyTypedDouble . parseJSON . unStringlyTypeJSON
14+
15+
16+-- | For some reason in several settings APIs, all leaf values get returned
17+-- as strings. This function attepmts to recover from this for all
18+-- non-recursive JSON types. If nothing can be done, the value is left alone.
19+unStringlyTypeJSON :: Value -> Value
20+unStringlyTypeJSON (String "true") = Bool True
21+unStringlyTypeJSON (String "false") = Bool False
22+unStringlyTypeJSON (String "null") = Null
23+unStringlyTypeJSON v@(String t) = case readMay (T.unpack t) of
24+ Just n -> Number n
25+ Nothing -> v
26+unStringlyTypeJSON v = v
src/Database/V1/Bloodhound/Internal/Suggest.hsnew file+252
@@ -0,0 +1,252 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE RecordWildCards #-}
3+
4+module Database.V1.Bloodhound.Internal.Suggest where
5+
6+
7+import Bloodhound.Import
8+
9+import qualified Data.HashMap.Strict as HM
10+
11+import Database.V1.Bloodhound.Internal.Newtypes
12+import Database.V1.Bloodhound.Internal.Query
13+
14+data Suggest = Suggest { suggestText :: Text
15+ , suggestName :: Text
16+ , suggestType :: SuggestType
17+ }
18+ deriving (Show, Eq)
19+
20+instance ToJSON Suggest where
21+ toJSON Suggest{..} = object [ "text" .= suggestText
22+ , suggestName .= suggestType
23+ ]
24+
25+instance FromJSON Suggest where
26+ parseJSON (Object o) = do
27+ suggestText' <- o .: "text"
28+ let dropTextList = HM.toList $ HM.filterWithKey (\x _ -> x /= "text") o
29+ suggestName' <- case dropTextList of
30+ [(x, _)] -> return x
31+ _ -> fail "error parsing Suggest field name"
32+ suggestType' <- o .: suggestName'
33+ return $ Suggest suggestText' suggestName' suggestType'
34+ parseJSON x = typeMismatch "Suggest" x
35+
36+data SuggestType = SuggestTypePhraseSuggester PhraseSuggester
37+ deriving (Show, Eq)
38+
39+instance ToJSON SuggestType where
40+ toJSON (SuggestTypePhraseSuggester x) = object ["phrase" .= x]
41+
42+instance FromJSON SuggestType where
43+ parseJSON = withObject "SuggestType" parse
44+ where parse o = phraseSuggester `taggedWith` "phrase"
45+ where taggedWith parser k = parser =<< o .: k
46+ phraseSuggester = pure . SuggestTypePhraseSuggester
47+
48+data PhraseSuggester =
49+ PhraseSuggester { phraseSuggesterField :: FieldName
50+ , phraseSuggesterGramSize :: Maybe Int
51+ , phraseSuggesterRealWordErrorLikelihood :: Maybe Int
52+ , phraseSuggesterConfidence :: Maybe Int
53+ , phraseSuggesterMaxErrors :: Maybe Int
54+ , phraseSuggesterSeparator :: Maybe Text
55+ , phraseSuggesterSize :: Maybe Size
56+ , phraseSuggesterAnalyzer :: Maybe Analyzer
57+ , phraseSuggesterShardSize :: Maybe Int
58+ , phraseSuggesterHighlight :: Maybe PhraseSuggesterHighlighter
59+ , phraseSuggesterCollate :: Maybe PhraseSuggesterCollate
60+ , phraseSuggesterCandidateGenerators :: [DirectGenerators]
61+ }
62+ deriving (Show, Eq)
63+
64+instance ToJSON PhraseSuggester where
65+ toJSON PhraseSuggester{..} = omitNulls [ "field" .= phraseSuggesterField
66+ , "gram_size" .= phraseSuggesterGramSize
67+ , "real_word_error_likelihood" .= phraseSuggesterRealWordErrorLikelihood
68+ , "confidence" .= phraseSuggesterConfidence
69+ , "max_errors" .= phraseSuggesterMaxErrors
70+ , "separator" .= phraseSuggesterSeparator
71+ , "size" .= phraseSuggesterSize
72+ , "analyzer" .= phraseSuggesterAnalyzer
73+ , "shard_size" .= phraseSuggesterShardSize
74+ , "highlight" .= phraseSuggesterHighlight
75+ , "collate" .= phraseSuggesterCollate
76+ , "direct_generator" .= phraseSuggesterCandidateGenerators
77+ ]
78+
79+instance FromJSON PhraseSuggester where
80+ parseJSON = withObject "PhraseSuggester" parse
81+ where parse o = PhraseSuggester
82+ <$> o .: "field"
83+ <*> o .:? "gram_size"
84+ <*> o .:? "real_word_error_likelihood"
85+ <*> o .:? "confidence"
86+ <*> o .:? "max_errors"
87+ <*> o .:? "separator"
88+ <*> o .:? "size"
89+ <*> o .:? "analyzer"
90+ <*> o .:? "shard_size"
91+ <*> o .:? "highlight"
92+ <*> o .:? "collate"
93+ <*> o .:? "direct_generator" .!= []
94+
95+mkPhraseSuggester :: FieldName -> PhraseSuggester
96+mkPhraseSuggester fName =
97+ PhraseSuggester fName Nothing Nothing Nothing Nothing Nothing Nothing
98+ Nothing Nothing Nothing Nothing []
99+
100+data PhraseSuggesterHighlighter =
101+ PhraseSuggesterHighlighter { phraseSuggesterHighlighterPreTag :: Text
102+ , phraseSuggesterHighlighterPostTag :: Text
103+ }
104+ deriving (Show, Eq)
105+
106+instance ToJSON PhraseSuggesterHighlighter where
107+ toJSON PhraseSuggesterHighlighter{..} =
108+ object [ "pre_tag" .= phraseSuggesterHighlighterPreTag
109+ , "post_tag" .= phraseSuggesterHighlighterPostTag
110+ ]
111+
112+instance FromJSON PhraseSuggesterHighlighter where
113+ parseJSON = withObject "PhraseSuggesterHighlighter" parse
114+ where parse o = PhraseSuggesterHighlighter
115+ <$> o .: "pre_tag"
116+ <*> o .: "post_tag"
117+
118+data PhraseSuggesterCollate =
119+ PhraseSuggesterCollate { phraseSuggesterCollateTemplateQuery :: TemplateQueryInline
120+ , phraseSuggesterCollatePrune :: Bool
121+ }
122+ deriving (Show, Eq)
123+
124+instance ToJSON PhraseSuggesterCollate where
125+ toJSON PhraseSuggesterCollate{..} = object [ "query" .= object
126+ [ "inline" .= (inline phraseSuggesterCollateTemplateQuery)
127+ ]
128+ , "params" .= (params phraseSuggesterCollateTemplateQuery)
129+ , "prune" .= phraseSuggesterCollatePrune
130+ ]
131+
132+instance FromJSON PhraseSuggesterCollate where
133+ parseJSON (Object o) = do
134+ query' <- o .: "query"
135+ inline' <- query' .: "inline"
136+ params' <- o .: "params"
137+ prune' <- o .:? "prune" .!= False
138+ return $ PhraseSuggesterCollate (TemplateQueryInline inline' params') prune'
139+ parseJSON x = typeMismatch "PhraseSuggesterCollate" x
140+
141+data DirectGenerators = DirectGenerators
142+ { directGeneratorsField :: FieldName
143+ , directGeneratorsSize :: Maybe Int
144+ , directGeneratorSuggestMode :: DirectGeneratorSuggestModeTypes
145+ , directGeneratorMaxEdits :: Maybe Double
146+ , directGeneratorPrefixLength :: Maybe Int
147+ , directGeneratorMinWordLength :: Maybe Int
148+ , directGeneratorMaxInspections :: Maybe Int
149+ , directGeneratorMinDocFreq :: Maybe Double
150+ , directGeneratorMaxTermFreq :: Maybe Double
151+ , directGeneratorPreFilter :: Maybe Text
152+ , directGeneratorPostFilter :: Maybe Text
153+ }
154+ deriving (Show, Eq)
155+
156+
157+instance ToJSON DirectGenerators where
158+ toJSON DirectGenerators{..} = omitNulls [ "field" .= directGeneratorsField
159+ , "size" .= directGeneratorsSize
160+ , "suggest_mode" .= directGeneratorSuggestMode
161+ , "max_edits" .= directGeneratorMaxEdits
162+ , "prefix_length" .= directGeneratorPrefixLength
163+ , "min_word_length" .= directGeneratorMinWordLength
164+ , "max_inspections" .= directGeneratorMaxInspections
165+ , "min_doc_freq" .= directGeneratorMinDocFreq
166+ , "max_term_freq" .= directGeneratorMaxTermFreq
167+ , "pre_filter" .= directGeneratorPreFilter
168+ , "post_filter" .= directGeneratorPostFilter
169+ ]
170+
171+instance FromJSON DirectGenerators where
172+ parseJSON = withObject "DirectGenerators" parse
173+ where parse o = DirectGenerators
174+ <$> o .: "field"
175+ <*> o .:? "size"
176+ <*> o .: "suggest_mode"
177+ <*> o .:? "max_edits"
178+ <*> o .:? "prefix_length"
179+ <*> o .:? "min_word_length"
180+ <*> o .:? "max_inspections"
181+ <*> o .:? "min_doc_freq"
182+ <*> o .:? "max_term_freq"
183+ <*> o .:? "pre_filter"
184+ <*> o .:? "post_filter"
185+
186+mkDirectGenerators :: FieldName -> DirectGenerators
187+mkDirectGenerators fn = DirectGenerators fn Nothing DirectGeneratorSuggestModeMissing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
188+
189+data DirectGeneratorSuggestModeTypes = DirectGeneratorSuggestModeMissing
190+ | DirectGeneratorSuggestModePopular
191+ | DirectGeneratorSuggestModeAlways
192+ deriving (Show, Eq)
193+
194+instance ToJSON DirectGeneratorSuggestModeTypes where
195+ toJSON DirectGeneratorSuggestModeMissing = "missing"
196+ toJSON DirectGeneratorSuggestModePopular = "popular"
197+ toJSON DirectGeneratorSuggestModeAlways = "always"
198+
199+instance FromJSON DirectGeneratorSuggestModeTypes where
200+ parseJSON = withText "DirectGeneratorSuggestModeTypes" parse
201+ where parse "missing" = pure DirectGeneratorSuggestModeMissing
202+ parse "popular" = pure DirectGeneratorSuggestModePopular
203+ parse "always" = pure DirectGeneratorSuggestModeAlways
204+ parse f = fail ("Unexpected DirectGeneratorSuggestModeTypes: " <> show f)
205+
206+data SuggestOptions =
207+ SuggestOptions { suggestOptionsText :: Text
208+ , suggestOptionsScore :: Double
209+ , suggestOptionsFreq :: Maybe Int
210+ , suggestOptionsHighlighted :: Maybe Text
211+ }
212+ deriving (Eq, Show)
213+
214+instance FromJSON SuggestOptions where
215+ parseJSON = withObject "SuggestOptions" parse
216+ where parse o = SuggestOptions
217+ <$> o .: "text"
218+ <*> o .: "score"
219+ <*> o .:? "freq"
220+ <*> o .:? "highlighted"
221+
222+data SuggestResponse =
223+ SuggestResponse { suggestResponseText :: Text
224+ , suggestResponseOffset :: Int
225+ , suggestResponseLength :: Int
226+ , suggestResponseOptions :: [SuggestOptions]
227+ }
228+ deriving (Eq, Show)
229+
230+instance FromJSON SuggestResponse where
231+ parseJSON = withObject "SuggestResponse" parse
232+ where parse o = SuggestResponse
233+ <$> o .: "text"
234+ <*> o .: "offset"
235+ <*> o .: "length"
236+ <*> o .: "options"
237+
238+data NamedSuggestionResponse =
239+ NamedSuggestionResponse { nsrName :: Text
240+ , nsrResponses :: [SuggestResponse]
241+ }
242+ deriving (Eq, Show)
243+
244+instance FromJSON NamedSuggestionResponse where
245+ parseJSON (Object o) = do
246+ suggestionName' <- case HM.toList o of
247+ [(x, _)] -> return x
248+ _ -> fail "error parsing NamedSuggestionResponse name"
249+ suggestionResponses' <- o .: suggestionName'
250+ return $ NamedSuggestionResponse suggestionName' suggestionResponses'
251+
252+ parseJSON x = typeMismatch "NamedSuggestionResponse" x
src/Database/V1/Bloodhound/Types.hs+33−5161
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
66{-# LANGUAGE MultiParamTypeClasses #-}
7−-- {-# LANGUAGE NoMonomorphismRestriction #-}
87{-# LANGUAGE NamedFieldPuns #-}
98{-# LANGUAGE OverloadedStrings #-}
109{-# LANGUAGE RecordWildCards #-}
@@ -379,734 +378,29 @@ module Database.V1.Bloodhound.Types
379378 , EsPassword(..)
380379 ) where
381380
382−import Control.Applicative as A
383−import Control.Arrow (first)
384−import Control.Monad.Catch
385−import Control.Monad.Except
386−import Control.Monad.Reader (MonadReader (..),
387− ReaderT (..))
388−import Control.Monad.State (MonadState)
389−import Control.Monad.Writer (MonadWriter)
390−import Data.Aeson
391−import Data.Aeson.Types (Pair, Parser,
392− emptyObject,
393− parseEither, parseMaybe,
394− typeMismatch)
395−import qualified Data.ByteString.Lazy.Char8 as L
396−import Data.Char
397−import Data.Hashable (Hashable)
398−import qualified Data.HashMap.Strict as HM
399−import Data.List (foldl', intercalate,
400− nub)
401−import Data.List.NonEmpty (NonEmpty (..), toList)
402−import qualified Data.Map.Strict as M
403−import Data.Maybe
404−import Data.Scientific (Scientific)
405−import Data.Semigroup
406−import Data.Text (Text)
407−import qualified Data.Text as T
408−import Data.Time.Calendar
409−import Data.Time.Clock (NominalDiffTime,
410− UTCTime)
411−import Data.Time.Clock.POSIX
412−import qualified Data.Traversable as DT
413−import Data.Typeable (Typeable)
414−import qualified Data.Vector as V
415−import qualified Data.Version as Vers
416−import GHC.Enum
417−import GHC.Generics (Generic)
418−import Network.HTTP.Client
419−import qualified Network.HTTP.Types.Method as NHTM
420−import qualified Text.ParserCombinators.ReadP as RP
421−import qualified Text.Read as TR
381+import Bloodhound.Import
422382
423383import Database.V1.Bloodhound.Types.Class
424−import Database.V1.Bloodhound.Types.Internal
384+import Database.V1.Bloodhound.Internal.Aggregation
385+import Database.V1.Bloodhound.Internal.Client
386+import Database.V1.Bloodhound.Internal.Highlight
387+import Database.V1.Bloodhound.Internal.Newtypes
388+import Database.V1.Bloodhound.Internal.Query
389+import Database.V1.Bloodhound.Internal.Sort
390+import Database.V1.Bloodhound.Internal.Suggest
425391
426−-- $setup
427−-- >>> :set -XOverloadedStrings
428−-- >>> import Data.Aeson
429−-- >>> import Database.V1.Bloodhound
430−-- >>> let testServer = (Server "http://localhost:9200")
431−-- >>> let testIndex = IndexName "twitter"
432−-- >>> let testMapping = MappingName "tweet"
433−-- >>> let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
434−
435−-- defaultIndexSettings is exported by Database.Bloodhound as well
436−-- no trailing slashes in servers, library handles building the path.
437−
438−-- | Create a 'BHEnv' with all optional fields defaulted. HTTP hook
439−-- will be a noop. You can use the exported fields to customize it further, e.g.:
440−--
441−-- >> (mkBHEnv myServer myManager) { bhRequestHook = customHook }
442−mkBHEnv :: Server -> Manager -> BHEnv
443−mkBHEnv s m = BHEnv s m return
444−
445−newtype BH m a = BH {
446− unBH :: ReaderT BHEnv m a
447− } deriving ( Functor
448− , A.Applicative
449− , Monad
450− , MonadIO
451− , MonadState s
452− , MonadWriter w
453− , MonadError e
454− , Alternative
455− , MonadPlus
456− , MonadFix
457− , MonadThrow
458− , MonadCatch
459− , MonadMask)
460−
461−instance MonadTrans BH where
462− lift = BH . lift
463−
464−instance (MonadReader r m) => MonadReader r (BH m) where
465− ask = lift ask
466− local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
467− local f (m r)
468−
469−instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
470− getBHEnv = BH getBHEnv
471−
472−runBH :: BHEnv -> BH m a -> m a
473−runBH e f = runReaderT (unBH f) e
474−
475−{-| 'Version' is embedded in 'Status' -}
476−data Version = Version { number :: VersionNumber
477− , build_hash :: BuildHash
478− , build_timestamp :: UTCTime
479− , build_snapshot :: Bool
480− , lucene_version :: VersionNumber } deriving (Eq, Read, Show, Generic, Typeable)
481−
482−-- | Traditional software versioning number
483−newtype VersionNumber = VersionNumber { versionNumber :: Vers.Version
484− } deriving (Eq, Read, Show, Generic, Typeable, Ord)
485−
486−{-| 'Status' is a data type for describing the JSON body returned by
487− Elasticsearch when you query its status. This was deprecated in 1.2.0.
488−
489− <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
490−-}
491−
492−data Status = Status { ok :: Maybe Bool
493− , status :: Int
494− , name :: Text
495− , version :: Version
496− , tagline :: Text } deriving (Eq, Read, Show, Generic)
497−
498−{-| 'IndexSettings' is used to configure the shards and replicas when you create
499− an Elasticsearch Index.
500−
501− <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
502−-}
503−
504−data IndexSettings =
505− IndexSettings { indexShards :: ShardCount
506− , indexReplicas :: ReplicaCount } deriving (Eq, Read, Show, Generic, Typeable)
507−
508−{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
509−defaultIndexSettings :: IndexSettings
510−defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
511−
512−
513−{-| 'IndexOptimizationSettings' is used to configure index optimization. See
514− <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-optimize.html>
515− for more info.
516−-}
517−data IndexOptimizationSettings =
518− IndexOptimizationSettings { maxNumSegments :: Maybe Int
519− -- ^ 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.
520− , onlyExpungeDeletes :: Bool
521− -- ^ 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.
522− , flushAfterOptimize :: Bool
523− -- ^ Should a flush be performed after the optimize.
524− } deriving (Eq, Show, Generic, Typeable)
525−
526−
527−{-| 'defaultIndexOptimizationSettings' implements the default settings that
528− Elasticsearch uses for index optimization. 'maxNumSegments' is Nothing,
529− 'onlyExpungeDeletes' is False, and flushAfterOptimize is True.
530−-}
531−defaultIndexOptimizationSettings :: IndexOptimizationSettings
532−defaultIndexOptimizationSettings = IndexOptimizationSettings Nothing False True
533−
534−{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
535−
536− <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
537−-}
538−data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
539− -- ^ The number of replicas each shard has.
540− | AutoExpandReplicas ReplicaBounds
541− | BlocksReadOnly Bool
542− -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
543− | BlocksRead Bool
544− -- ^ Set to True to disable read operations against the index.
545− | BlocksWrite Bool
546− -- ^ Set to True to disable write operations against the index.
547− | BlocksMetaData Bool
548− -- ^ Set to True to disable metadata operations against the index.
549− | RefreshInterval NominalDiffTime
550− -- ^ The async refresh interval of a shard
551− | IndexConcurrency Int
552− | FailOnMergeFailure Bool
553− | TranslogFlushThresholdOps Int
554− -- ^ When to flush on operations.
555− | TranslogFlushThresholdSize Bytes
556− -- ^ When to flush based on translog (bytes) size.
557− | TranslogFlushThresholdPeriod NominalDiffTime
558− -- ^ When to flush based on a period of not flushing.
559− | TranslogDisableFlush Bool
560− -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
561− | CacheFilterMaxSize (Maybe Bytes)
562− -- ^ The maximum size of filter cache (per segment in shard).
563− | CacheFilterExpire (Maybe NominalDiffTime)
564− -- ^ The expire after access time for filter cache.
565− | GatewaySnapshotInterval NominalDiffTime
566− -- ^ The gateway snapshot interval (only applies to shared gateways).
567− | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
568− -- ^ A node matching any rule will be allowed to host shards from the index.
569− | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
570− -- ^ A node matching any rule will NOT be allowed to host shards from the index.
571− | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
572− -- ^ Only nodes matching all rules will be allowed to host shards from the index.
573− | RoutingAllocationEnable AllocationPolicy
574− -- ^ Enables shard allocation for a specific index.
575− | RoutingAllocationShardsPerNode ShardCount
576− -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
577− | RecoveryInitialShards InitialShardCount
578− -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
579− | GCDeletes NominalDiffTime
580− | TTLDisablePurge Bool
581− -- ^ Disables temporarily the purge of expired docs.
582− | TranslogFSType FSType
583− | IndexCompoundFormat CompoundFormat
584− | IndexCompoundOnFlush Bool
585− | WarmerEnabled Bool
586− deriving (Eq, Show, Generic, Typeable)
587−
588−data AllocationPolicy = AllocAll
589− -- ^ Allows shard allocation for all shards.
590− | AllocPrimaries
591− -- ^ Allows shard allocation only for primary shards.
592− | AllocNewPrimaries
593− -- ^ Allows shard allocation only for primary shards for new indices.
594− | AllocNone
595− -- ^ No shard allocation is allowed
596− deriving (Eq, Read, Show, Generic, Typeable)
597−
598−data ReplicaBounds = ReplicasBounded Int Int
599− | ReplicasLowerBounded Int
600− | ReplicasUnbounded
601− deriving (Eq, Read, Show, Generic, Typeable)
602−
603−-- | A measure of bytes used for various configurations. You may want
604−-- to use smart constructors like 'gigabytes' for larger values.
605−--
606−-- >>> gigabytes 9
607−-- Bytes 9000000000
608−--
609−-- >>> megabytes 9
610−-- Bytes 9000000
611−--
612−-- >>> kilobytes 9
613−-- Bytes 9000
614−newtype Bytes = Bytes Int deriving (Eq, Read, Show, Generic, Typeable, Ord, ToJSON, FromJSON)
615−
616−gigabytes :: Int -> Bytes
617−gigabytes n = megabytes (1000 * n)
618−
619−
620−megabytes :: Int -> Bytes
621−megabytes n = kilobytes (1000 * n)
622−
623−
624−kilobytes :: Int -> Bytes
625−kilobytes n = Bytes (1000 * n)
626−
627−
628−data FSType = FSSimple
629− | FSBuffered deriving (Eq, Read, Show, Generic, Typeable, Ord)
630−
631−data InitialShardCount = QuorumShards
632− | QuorumMinus1Shards
633− | FullShards
634− | FullMinus1Shards
635− | ExplicitShards Int
636− deriving (Eq, Read, Show, Generic, Typeable)
637−
638−data NodeAttrFilter = NodeAttrFilter { nodeAttrFilterName :: NodeAttrName
639− , nodeAttrFilterValues :: NonEmpty Text}
640− deriving (Eq, Read, Show, Generic, Ord, Typeable)
641−
642−newtype NodeAttrName = NodeAttrName Text deriving (Eq, Read, Show, Ord, Generic, Typeable)
643−
644−data CompoundFormat = CompoundFileFormat Bool
645− | MergeSegmentVsTotalIndex Double
646− -- ^ percentage between 0 and 1 where 0 is false, 1 is true
647− deriving (Eq, Read, Show, Generic, Typeable)
648−
649−newtype NominalDiffTimeJSON = NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
650−
651−data IndexSettingsSummary = IndexSettingsSummary { sSummaryIndexName :: IndexName
652− , sSummaryFixedSettings :: IndexSettings
653− , sSummaryUpdateable :: [UpdatableIndexSetting]}
654− deriving (Eq, Show, Generic, Typeable)
655−
656−{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
657−type Reply = Network.HTTP.Client.Response L.ByteString
658−type Method = NHTM.Method
659−
660−{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
661−
662− <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
663−-}
664−data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Read, Show, Generic, Typeable)
665−
666−data FieldType = GeoPointType
667− | GeoShapeType
668− | FloatType
669− | IntegerType
670− | LongType
671− | ShortType
672− | ByteType deriving (Eq, Read, Show, Generic, Typeable)
673−
674−data FieldDefinition =
675− FieldDefinition { fieldType :: FieldType } deriving (Eq, Read, Show, Generic, Typeable)
676−
677−{-| An 'IndexTemplate' defines a template that will automatically be
678− applied to new indices created. The templates include both
679− 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
680− controls if the template will be applied to the index created.
681− Specify mappings as follows: @[toJSON TweetMapping, ...]@
682−
683− https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
684−-}
685−data IndexTemplate =
686− IndexTemplate { templatePattern :: TemplatePattern
687− , templateSettings :: Maybe IndexSettings
688− , templateMappings :: [Value]
689− }
690−
691−data MappingField =
692− MappingField { mappingFieldName :: FieldName
693− , fieldDefinition :: FieldDefinition } deriving (Eq, Read, Show, Generic, Typeable)
694−
695−{-| Support for type reification of 'Mapping's is currently incomplete, for
696− now the mapping API verbiage expects a 'ToJSON'able blob.
697−
698− Indexes have mappings, mappings are schemas for the documents contained in the
699− index. I'd recommend having only one mapping per index, always having a mapping,
700− and keeping different kinds of documents separated if possible.
701−-}
702−data Mapping = Mapping { typeName :: TypeName
703− , mappingFields :: [MappingField] } deriving (Eq, Read, Show, Generic, Typeable)
704−
705−{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
706− operation index, create, delete, and update. 'BulkIndex' behaves like an
707− "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
708−
709− <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk>
710−-}
711−data BulkOperation =
712− BulkIndex IndexName MappingName DocId Value
713− | BulkCreate IndexName MappingName DocId Value
714− | BulkDelete IndexName MappingName DocId
715− | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Read, Show, Generic, Typeable)
716−
717−{-| 'EsResult' describes the standard wrapper JSON document that you see in
718− successful Elasticsearch lookups or lookups that couldn't find the document.
719−-}
720−data EsResult a = EsResult { _index :: Text
721− , _type :: Text
722− , _id :: Text
723− , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Read, Show, Generic, Typeable)
724−
725−{-| 'EsResultFound' contains the document and its metadata inside of an
726− 'EsResult' when the document was successfully found.
727−-}
728−data EsResultFound a = EsResultFound { _version :: DocVersion
729− , _source :: a } deriving (Eq, Read, Show, Generic, Typeable)
730−
731−{-| 'EsError' is the generic type that will be returned when there was a
732− problem. If you can't parse the expected response, its a good idea to
733− try parsing this.
734−-}
735−data EsError = EsError { errorStatus :: Int
736− , errorMessage :: Text } deriving (Eq, Read, Show, Generic, Typeable)
737−
738−{-| 'EsProtocolException' will be thrown if Bloodhound cannot parse a response
739−returned by the Elasticsearch server. If you encounter this error, please
740−verify that your domain data types and FromJSON instances are working properly
741−(for example, the 'a' of '[Hit a]' in 'SearchResult.searchHits.hits'). If you're
742−sure that your mappings are correct, then this error may be an indication of an
743−incompatibility between Bloodhound and Elasticsearch. Please open a bug report
744−and be sure to include the exception body.
745−-}
746−data EsProtocolException = EsProtocolException { esProtoExBody :: L.ByteString }
747− deriving (Eq, Read, Show, Generic, Typeable)
748−
749−instance Exception EsProtocolException
750−
751−data IndexAlias = IndexAlias { srcIndex :: IndexName
752− , indexAlias :: IndexAliasName } deriving (Eq, Read, Show, Generic, Typeable)
753−
754−newtype IndexAliasName = IndexAliasName { indexAliasName :: IndexName } deriving (Eq, Read, Show, Generic, ToJSON)
755−
756−data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
757− | RemoveAlias IndexAlias deriving (Read, Show, Eq, Generic, Typeable)
758−
759−data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
760− , aliasCreateFilter :: Maybe Filter}
761− deriving (Read, Show, Eq, Generic, Typeable)
762−
763−data AliasRouting = AllAliasRouting RoutingValue
764− | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
765− deriving (Read, Show, Eq, Generic, Typeable)
766−
767−newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Read, Show, Eq, Generic, Typeable)
768−
769−newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Read, Show, Eq, Generic, ToJSON, FromJSON, Typeable)
770−
771−newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Read, Show, Eq, Generic, ToJSON, FromJSON, Typeable)
772−
773−newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Read, Show, Eq, Generic, Typeable)
774−
775−{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
776−data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
777− , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Read, Show, Eq, Generic, Typeable)
778−
779−{-| 'DocVersion' is an integer version number for a document between 1
780−and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
781−-}
782−newtype DocVersion = DocVersion {
783− docVersionNumber :: Int
784− } deriving (Eq, Read, Show, Generic, Ord, ToJSON)
785−
786−-- | Smart constructor for in-range doc version
787−mkDocVersion :: Int -> Maybe DocVersion
788−mkDocVersion i
789− | i >= (docVersionNumber minBound) && i <= (docVersionNumber maxBound) =
790− Just $ DocVersion i
791− | otherwise = Nothing
792−
793−
794−{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
795−own version numbers instead of ones from ES.
796−-}
797−newtype ExternalDocVersion = ExternalDocVersion DocVersion
798− deriving (Eq, Read, Show, Generic, Ord, Bounded, Enum, ToJSON)
799−
800−{-| 'VersionControl' is specified when indexing documents as a
801−optimistic concurrency control.
802−-}
803−data VersionControl = NoVersionControl
804− -- ^ Don't send a version. This is a pure overwrite.
805− | InternalVersion DocVersion
806− -- ^ Use the default ES versioning scheme. Only
807− -- index the document if the version is the same
808− -- as the one specified. Only applicable to
809− -- updates, as you should be getting Version from
810− -- a search result.
811− | ExternalGT ExternalDocVersion
812− -- ^ Use your own version numbering. Only index
813− -- the document if the version is strictly higher
814− -- OR the document doesn't exist. The given
815− -- version will be used as the new version number
816− -- for the stored document. N.B. All updates must
817− -- increment this number, meaning there is some
818− -- global, external ordering of updates.
819− | ExternalGTE ExternalDocVersion
820− -- ^ Use your own version numbering. Only index
821− -- the document if the version is equal or higher
822− -- than the stored version. Will succeed if there
823− -- is no existing document. The given version will
824− -- be used as the new version number for the
825− -- stored document. Use with care, as this could
826− -- result in data loss.
827− | ForceVersion ExternalDocVersion
828− -- ^ The document will always be indexed and the
829− -- given version will be the new version. This is
830− -- typically used for correcting errors. Use with
831− -- care, as this could result in data loss.
832− deriving (Read, Show, Eq, Generic, Ord)
833−
834−{-| 'DocumentParent' is used to specify a parent document.
835−-}
836−newtype DocumentParent = DocumentParent DocId
837− deriving (Eq, Read, Show, Generic, Typeable)
838−
839−{-| 'IndexDocumentSettings' are special settings supplied when indexing
840−a document. For the best backwards compatiblity when new fields are
841−added, you should probably prefer to start with 'defaultIndexDocumentSettings'
842−-}
843−data IndexDocumentSettings =
844− IndexDocumentSettings { idsVersionControl :: VersionControl
845− , idsParent :: Maybe DocumentParent
846− } deriving (Eq, Read, Show, Generic, Typeable)
847−
848−{-| Reasonable default settings. Chooses no version control and no parent.
849−-}
850−defaultIndexDocumentSettings :: IndexDocumentSettings
851−defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
852−
853−{-| 'Sort' is a synonym for a list of 'SortSpec's. Sort behavior is order
854− dependent with later sorts acting as tie-breakers for earlier sorts.
855−-}
856−type Sort = [SortSpec]
857−
858−{-| The two main kinds of 'SortSpec' are 'DefaultSortSpec' and
859− 'GeoDistanceSortSpec'. The latter takes a 'SortOrder', 'GeoPoint', and
860− 'DistanceUnit' to express "nearness" to a single geographical point as a
861− sort specification.
862−
863−<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
864−-}
865−data SortSpec = DefaultSortSpec DefaultSort
866− | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit deriving (Eq, Read, Show, Generic, Typeable)
867−
868−{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
869− 'mkSort' convenience function for when you want to specify only the most
870− common parameters.
871−
872−<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
873−-}
874−data DefaultSort =
875− DefaultSort { sortFieldName :: FieldName
876− , sortOrder :: SortOrder
877− -- default False
878− , ignoreUnmapped :: Bool
879− , sortMode :: Maybe SortMode
880− , missingSort :: Maybe Missing
881− , nestedFilter :: Maybe Filter } deriving (Eq, Read, Show, Generic, Typeable)
882−
883−{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
884− encoded into "asc" or "desc" when turned into JSON.
885−
886−<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
887−-}
888−data SortOrder = Ascending
889− | Descending deriving (Eq, Read, Show, Generic, Typeable)
890−
891−{-| 'Missing' prescribes how to handle missing fields. A missing field can be
892− sorted last, first, or using a custom value as a substitute.
893−
894−<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_missing_values>
895−-}
896−data Missing = LastMissing
897− | FirstMissing
898− | CustomMissing Text deriving (Eq, Read, Show, Generic, Typeable)
899−
900−{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
901−
902−http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_sort_mode_option
903−-}
904−data SortMode = SortMin
905− | SortMax
906− | SortSum
907− | SortAvg deriving (Eq, Read, Show, Generic, Typeable)
908−
909−{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
910− that you can concisely describe the usual kind of 'SortSpec's you want.
911−-}
912−mkSort :: FieldName -> SortOrder -> DefaultSort
913−mkSort fieldName sOrder = DefaultSort fieldName sOrder False Nothing Nothing Nothing
914−
915−{-| 'Cache' is for telling ES whether it should cache a 'Filter' not.
916− 'Query's cannot be cached.
917−-}
918−type Cache = Bool -- caching on/off
919−defaultCache :: Cache
920−defaultCache = False
921−
922−{-| 'PrefixValue' is used in 'PrefixQuery' as the main query component.
923−-}
924−type PrefixValue = Text
925−
926−{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
927− JSON encoding baked in. Used all over the place.
928−-}
929−data BooleanOperator = And | Or deriving (Eq, Read, Show, Generic, Typeable)
930−
931−{-| 'ShardCount' is part of 'IndexSettings'
932−-}
933−newtype ShardCount = ShardCount Int deriving (Eq, Read, Show, Generic, ToJSON, Typeable)
934−
935−{-| 'ReplicaCount' is part of 'IndexSettings'
936−-}
937−newtype ReplicaCount = ReplicaCount Int deriving (Eq, Read, Show, Generic, ToJSON, Typeable)
938−
939−{-| 'IndexName' is used to describe which index to query/create/delete
940−-}
941−newtype IndexName = IndexName Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
942−
943−{-| 'IndexSelection' is used for APIs which take a single index, a list of
944− indexes, or the special @_all@ index.
945−-}
946−--TODO: this does not fully support <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/multi-index.html multi-index syntax>. It wouldn't be too hard to implement but you'd have to add the optional parameters (ignore_unavailable, allow_no_indices, expand_wildcards) to any APIs using it. Also would be a breaking API.
947−data IndexSelection = IndexList (NonEmpty IndexName)
948− | AllIndexes deriving (Eq, Generic, Show, Typeable)
949−
950−{-| 'NodeSelection' is used for most cluster APIs. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes here> for more details.
951−-}
952−data NodeSelection = LocalNode
953− -- ^ Whatever node receives this request
954− | NodeList (NonEmpty NodeSelector)
955− | AllNodes deriving (Eq, Generic, Show, Typeable)
956−
957−
958−-- | An exact match or pattern to identify a node. Note that All of
959−-- these options support wildcarding, so your node name, server, attr
960−-- name can all contain * characters to be a fuzzy match.
961−data NodeSelector = NodeByName NodeName
962− | NodeByFullNodeId FullNodeId
963− | NodeByHost Server
964− -- ^ e.g. 10.0.0.1 or even 10.0.0.*
965− | NodeByAttribute NodeAttrName Text
966− -- ^ NodeAttrName can be a pattern, e.g. rack*. The value can too.
967− deriving (Eq, Generic, Show, Typeable)
968−
969−{-| 'TemplateName' is used to describe which template to query/create/delete
970−-}
971−newtype TemplateName = TemplateName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
972−
973−{-| 'TemplatePattern' represents a pattern which is matched against index names
974−-}
975−newtype TemplatePattern = TemplatePattern Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
976−
977−{-| 'MappingName' is part of mappings which are how ES describes and schematizes
978− the data in the indices.
979−-}
980−newtype MappingName = MappingName Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
981−
982−{-| 'DocId' is a generic wrapper value for expressing unique Document IDs.
983− Can be set by the user or created by ES itself. Often used in client
984− functions for poking at specific documents.
985−-}
986−newtype DocId = DocId Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
987−
988−{-| 'QueryString' is used to wrap query text bodies, be they human written or not.
989−-}
990−newtype QueryString = QueryString Text deriving (Eq, Generic, Read, Show, ToJSON, FromJSON, Typeable)
991−
992−{-| 'FieldName' is used all over the place wherever a specific field within
993− a document needs to be specified, usually in 'Query's or 'Filter's.
994−-}
995−newtype FieldName = FieldName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
996−
997−
998−{-| 'Script' is often used in place of 'FieldName' to specify more
999−complex ways of extracting a value from a document.
1000−-}
1001−newtype Script = Script { scriptText :: Text } deriving (Eq, Read, Show, Generic, Typeable)
1002−
1003−{-| 'CacheName' is used in 'RegexpFilter' for describing the
1004− 'CacheKey' keyed caching behavior.
1005−-}
1006−newtype CacheName = CacheName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1007−
1008−{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
1009−-}
1010−newtype CacheKey =
1011− CacheKey Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1012−newtype Existence =
1013− Existence Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1014−newtype NullValue =
1015− NullValue Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1016−newtype CutoffFrequency =
1017− CutoffFrequency Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1018−newtype Analyzer =
1019− Analyzer Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1020−newtype MaxExpansions =
1021− MaxExpansions Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1022−
1023−{-| 'Lenient', if set to true, will cause format based failures to be
1024− ignored. I don't know what the bloody default is, Elasticsearch
1025− documentation didn't say what it was. Let me know if you figure it out.
1026−-}
1027−newtype Lenient =
1028− Lenient Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1029−newtype Tiebreaker =
1030− Tiebreaker Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1031−newtype Boost =
1032− Boost Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1033−newtype BoostTerms =
1034− BoostTerms Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1035−
1036−{-| 'MinimumMatch' controls how many should clauses in the bool query should
1037− match. Can be an absolute value (2) or a percentage (30%) or a
1038− combination of both.
1039−-}
1040−newtype MinimumMatch =
1041− MinimumMatch Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1042−newtype DisableCoord =
1043− DisableCoord Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1044−newtype IgnoreTermFrequency =
1045− IgnoreTermFrequency Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1046−newtype MinimumTermFrequency =
1047− MinimumTermFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1048−newtype MaxQueryTerms =
1049− MaxQueryTerms Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1050−newtype Fuzziness =
1051− Fuzziness Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1052−
1053−{-| 'PrefixLength' is the prefix length used in queries, defaults to 0. -}
1054−newtype PrefixLength =
1055− PrefixLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1056−newtype TypeName =
1057− TypeName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1058−newtype PercentMatch =
1059− PercentMatch Double deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1060−newtype StopWord =
1061− StopWord Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1062−newtype QueryPath =
1063− QueryPath Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1064−
1065−{-| Allowing a wildcard at the beginning of a word (eg "*ing") is particularly
1066− heavy, because all terms in the index need to be examined, just in case
1067− they match. Leading wildcards can be disabled by setting
1068− 'AllowLeadingWildcard' to false. -}
1069−newtype AllowLeadingWildcard =
1070− AllowLeadingWildcard Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1071−newtype LowercaseExpanded =
1072− LowercaseExpanded Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1073−newtype EnablePositionIncrements =
1074− EnablePositionIncrements Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1075−
1076−{-| By default, wildcard terms in a query are not analyzed.
1077− Setting 'AnalyzeWildcard' to true enables best-effort analysis.
1078−-}
1079−newtype AnalyzeWildcard = AnalyzeWildcard Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1080−
1081−{-| 'GeneratePhraseQueries' defaults to false.
1082−-}
1083−newtype GeneratePhraseQueries =
1084− GeneratePhraseQueries Bool deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1085−
1086−{-| 'Locale' is used for string conversions - defaults to ROOT.
1087−-}
1088−newtype Locale = Locale Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1089−newtype MaxWordLength = MaxWordLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1090−newtype MinWordLength = MinWordLength Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1091−
1092−{-| 'PhraseSlop' sets the default slop for phrases, 0 means exact
1093− phrase matches. Default is 0.
1094−-}
1095−newtype PhraseSlop = PhraseSlop Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1096−newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1097−newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1098−
1099−-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
1100−newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
1101−
1102−{-| 'unpackId' is a silly convenience function that gets used once.
1103−-}
1104−unpackId :: DocId -> Text
1105−unpackId (DocId docId) = docId
392+data SearchResult a =
393+ SearchResult { took :: Int
394+ , timedOut :: Bool
395+ , shards :: ShardResult
396+ , searchHits :: SearchHits a
397+ , aggregations :: Maybe AggregationResults
398+ , scrollId :: Maybe ScrollId
399+ , suggest :: Maybe NamedSuggestionResponse -- ^ Only one Suggestion request / response per Search is supported.
400+ }
401+ deriving (Eq, Show)
1106402
1107403type TrackSortScores = Bool
1108−newtype From = From Int deriving (Eq, Read, Show, Generic, ToJSON)
1109−newtype Size = Size Int deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
1110404
1111405data Search = Search { queryBody :: Maybe Query
1112406 , filterBody :: Maybe Filter
@@ -1121,7 +415,7 @@ data Search = Search { queryBody :: Maybe Query
1121415 , fields :: Maybe [FieldName]
1122416 , source :: Maybe Source
1123417 , suggestBody :: Maybe Suggest -- ^ Only one Suggestion request / response per Search is supported.
1124− } deriving (Eq, Read, Show, Generic, Typeable)
418+ } deriving (Eq, Show)
1125419
1126420data SearchType = SearchTypeQueryThenFetch
1127421 | SearchTypeDfsQueryThenFetch
@@ -1129,2700 +423,22 @@ data SearchType = SearchTypeQueryThenFetch
1129423 | SearchTypeScan
1130424 | SearchTypeQueryAndFetch
1131425 | SearchTypeDfsQueryAndFetch
1132− deriving (Eq, Read, Show, Generic, Typeable)
1133−
1134−data Source =
1135− NoSource
1136− | SourcePatterns PatternOrPatterns
1137− | SourceIncludeExclude Include Exclude
1138− deriving (Read, Show, Eq, Generic, Typeable)
1139−
1140−data PatternOrPatterns = PopPattern Pattern
1141− | PopPatterns [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
1142−
1143−data Include = Include [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
1144−data Exclude = Exclude [Pattern] deriving (Eq, Read, Show, Generic, Typeable)
1145−
1146−newtype Pattern = Pattern Text deriving (Eq, Read, Show, Generic, Typeable)
1147−
1148−data Highlights = Highlights { globalsettings :: Maybe HighlightSettings
1149− , highlightFields :: [FieldHighlight]
1150− } deriving (Read, Show, Eq, Generic, Typeable)
1151−
1152−data FieldHighlight = FieldHighlight FieldName (Maybe HighlightSettings)
1153− deriving (Read, Show, Eq, Generic, Typeable)
1154−
1155−
1156−data HighlightSettings = Plain PlainHighlight
1157− | Postings PostingsHighlight
1158− | FastVector FastVectorHighlight
1159− deriving (Read, Show, Eq, Generic, Typeable)
1160−data PlainHighlight =
1161− PlainHighlight { plainCommon :: Maybe CommonHighlight
1162− , plainNonPost :: Maybe NonPostings } deriving (Read, Show, Eq, Generic, Typeable)
1163−
1164− -- This requires that index_options are set to 'offset' in the mapping.
1165−data PostingsHighlight = PostingsHighlight (Maybe CommonHighlight) deriving (Read, Show, Eq, Generic, Typeable)
1166−
1167−-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
1168−data FastVectorHighlight =
1169− FastVectorHighlight { fvCommon :: Maybe CommonHighlight
1170− , fvNonPostSettings :: Maybe NonPostings
1171− , boundaryChars :: Maybe Text
1172− , boundaryMaxScan :: Maybe Int
1173− , fragmentOffset :: Maybe Int
1174− , matchedFields :: [Text]
1175− , phraseLimit :: Maybe Int
1176− } deriving (Read, Show, Eq, Generic, Typeable)
1177−
1178−data CommonHighlight =
1179− CommonHighlight { order :: Maybe Text
1180− , forceSource :: Maybe Bool
1181− , tag :: Maybe HighlightTag
1182− , encoder :: Maybe HighlightEncoder
1183− , noMatchSize :: Maybe Int
1184− , highlightQuery :: Maybe Query
1185− , requireFieldMatch :: Maybe Bool
1186− } deriving (Read, Show, Eq, Generic, Typeable)
1187−
1188−-- Settings that are only applicable to FastVector and Plain highlighters.
1189−data NonPostings =
1190− NonPostings { fragmentSize :: Maybe Int
1191− , numberOfFragments :: Maybe Int} deriving (Read, Show, Eq, Generic, Typeable)
1192−
1193−data HighlightEncoder = DefaultEncoder
1194− | HTMLEncoder
1195− deriving (Read, Show, Eq, Generic, Typeable)
1196−
1197−-- NOTE: Should the tags use some kind of HTML type, rather than Text?
1198−data HighlightTag = TagSchema Text
1199− | CustomTags ([Text], [Text]) -- Only uses more than the first value in the lists if fvh
1200− deriving (Read, Show, Eq, Generic, Typeable)
1201−
1202−
1203−data Query =
1204− TermQuery Term (Maybe Boost)
1205− | TermsQuery Text (NonEmpty Text)
1206− | QueryMatchQuery MatchQuery
1207− | QueryMultiMatchQuery MultiMatchQuery
1208− | QueryBoolQuery BoolQuery
1209− | QueryBoostingQuery BoostingQuery
1210− | QueryCommonTermsQuery CommonTermsQuery
1211− | ConstantScoreFilter Filter Boost
1212− | ConstantScoreQuery Query Boost
1213− | QueryDisMaxQuery DisMaxQuery
1214− | QueryFilteredQuery FilteredQuery
1215− | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
1216− | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
1217− | QueryFuzzyQuery FuzzyQuery
1218− | QueryHasChildQuery HasChildQuery
1219− | QueryHasParentQuery HasParentQuery
1220− | IdsQuery MappingName [DocId]
1221− | QueryIndicesQuery IndicesQuery
1222− | MatchAllQuery (Maybe Boost)
1223− | QueryMoreLikeThisQuery MoreLikeThisQuery
1224− | QueryMoreLikeThisFieldQuery MoreLikeThisFieldQuery
1225− | QueryNestedQuery NestedQuery
1226− | QueryPrefixQuery PrefixQuery
1227− | QueryQueryStringQuery QueryStringQuery
1228− | QuerySimpleQueryStringQuery SimpleQueryStringQuery
1229− | QueryRangeQuery RangeQuery
1230− | QueryRegexpQuery RegexpQuery
1231− | QueryTemplateQueryInline TemplateQueryInline
1232− deriving (Eq, Read, Show, Generic, Typeable)
1233−
1234−data RegexpQuery =
1235− RegexpQuery { regexpQueryField :: FieldName
1236− , regexpQuery :: Regexp
1237− , regexpQueryFlags :: RegexpFlags
1238− , regexpQueryBoost :: Maybe Boost
1239− } deriving (Eq, Read, Show, Generic, Typeable)
1240−
1241−data RangeQuery =
1242− RangeQuery { rangeQueryField :: FieldName
1243− , rangeQueryRange :: RangeValue
1244− , rangeQueryBoost :: Boost } deriving (Eq, Read, Show, Generic, Typeable)
1245−
1246−mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
1247−mkRangeQuery f r = RangeQuery f r (Boost 1.0)
1248−
1249−data SimpleQueryStringQuery =
1250− SimpleQueryStringQuery
1251− { simpleQueryStringQuery :: QueryString
1252− , simpleQueryStringField :: Maybe FieldOrFields
1253− , simpleQueryStringOperator :: Maybe BooleanOperator
1254− , simpleQueryStringAnalyzer :: Maybe Analyzer
1255− , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
1256− , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
1257− , simpleQueryStringLocale :: Maybe Locale
1258− } deriving (Eq, Read, Show, Generic, Typeable)
1259−
1260−data SimpleQueryFlag =
1261− SimpleQueryAll
1262− | SimpleQueryNone
1263− | SimpleQueryAnd
1264− | SimpleQueryOr
1265− | SimpleQueryPrefix
1266− | SimpleQueryPhrase
1267− | SimpleQueryPrecedence
1268− | SimpleQueryEscape
1269− | SimpleQueryWhitespace
1270− | SimpleQueryFuzzy
1271− | SimpleQueryNear
1272− | SimpleQuerySlop deriving (Eq, Read, Show, Generic, Typeable)
1273−
1274−-- use_dis_max and tie_breaker when fields are plural?
1275−data QueryStringQuery =
1276− QueryStringQuery
1277− { queryStringQuery :: QueryString
1278− , queryStringDefaultField :: Maybe FieldName
1279− , queryStringOperator :: Maybe BooleanOperator
1280− , queryStringAnalyzer :: Maybe Analyzer
1281− , queryStringAllowLeadingWildcard :: Maybe AllowLeadingWildcard
1282− , queryStringLowercaseExpanded :: Maybe LowercaseExpanded
1283− , queryStringEnablePositionIncrements :: Maybe EnablePositionIncrements
1284− , queryStringFuzzyMaxExpansions :: Maybe MaxExpansions
1285− , queryStringFuzziness :: Maybe Fuzziness
1286− , queryStringFuzzyPrefixLength :: Maybe PrefixLength
1287− , queryStringPhraseSlop :: Maybe PhraseSlop
1288− , queryStringBoost :: Maybe Boost
1289− , queryStringAnalyzeWildcard :: Maybe AnalyzeWildcard
1290− , queryStringGeneratePhraseQueries :: Maybe GeneratePhraseQueries
1291− , queryStringMinimumShouldMatch :: Maybe MinimumMatch
1292− , queryStringLenient :: Maybe Lenient
1293− , queryStringLocale :: Maybe Locale
1294− } deriving (Eq, Read, Show, Generic, Typeable)
1295−
1296−mkQueryStringQuery :: QueryString -> QueryStringQuery
1297−mkQueryStringQuery qs =
1298− QueryStringQuery qs Nothing Nothing
1299− Nothing Nothing Nothing Nothing
1300− Nothing Nothing Nothing Nothing
1301− Nothing Nothing Nothing Nothing
1302− Nothing Nothing
1303−
1304−data FieldOrFields = FofField FieldName
1305− | FofFields (NonEmpty FieldName) deriving (Eq, Read, Show, Generic, Typeable)
1306−
1307−data PrefixQuery =
1308− PrefixQuery
1309− { prefixQueryField :: FieldName
1310− , prefixQueryPrefixValue :: Text
1311− , prefixQueryBoost :: Maybe Boost } deriving (Eq, Read, Show, Generic, Typeable)
1312−
1313−data NestedQuery =
1314− NestedQuery
1315− { nestedQueryPath :: QueryPath
1316− , nestedQueryScoreType :: ScoreType
1317− , nestedQuery :: Query } deriving (Eq, Read, Show, Generic, Typeable)
1318−
1319−data MoreLikeThisFieldQuery =
1320− MoreLikeThisFieldQuery
1321− { moreLikeThisFieldText :: Text
1322− , moreLikeThisFieldFields :: FieldName
1323− -- default 0.3 (30%)
1324− , moreLikeThisFieldPercentMatch :: Maybe PercentMatch
1325− , moreLikeThisFieldMinimumTermFreq :: Maybe MinimumTermFrequency
1326− , moreLikeThisFieldMaxQueryTerms :: Maybe MaxQueryTerms
1327− , moreLikeThisFieldStopWords :: Maybe (NonEmpty StopWord)
1328− , moreLikeThisFieldMinDocFrequency :: Maybe MinDocFrequency
1329− , moreLikeThisFieldMaxDocFrequency :: Maybe MaxDocFrequency
1330− , moreLikeThisFieldMinWordLength :: Maybe MinWordLength
1331− , moreLikeThisFieldMaxWordLength :: Maybe MaxWordLength
1332− , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
1333− , moreLikeThisFieldBoost :: Maybe Boost
1334− , moreLikeThisFieldAnalyzer :: Maybe Analyzer
1335− } deriving (Eq, Read, Show, Generic, Typeable)
1336−
1337−data MoreLikeThisQuery =
1338− MoreLikeThisQuery
1339− { moreLikeThisText :: Text
1340− , moreLikeThisFields :: Maybe (NonEmpty FieldName)
1341− -- default 0.3 (30%)
1342− , moreLikeThisPercentMatch :: Maybe PercentMatch
1343− , moreLikeThisMinimumTermFreq :: Maybe MinimumTermFrequency
1344− , moreLikeThisMaxQueryTerms :: Maybe MaxQueryTerms
1345− , moreLikeThisStopWords :: Maybe (NonEmpty StopWord)
1346− , moreLikeThisMinDocFrequency :: Maybe MinDocFrequency
1347− , moreLikeThisMaxDocFrequency :: Maybe MaxDocFrequency
1348− , moreLikeThisMinWordLength :: Maybe MinWordLength
1349− , moreLikeThisMaxWordLength :: Maybe MaxWordLength
1350− , moreLikeThisBoostTerms :: Maybe BoostTerms
1351− , moreLikeThisBoost :: Maybe Boost
1352− , moreLikeThisAnalyzer :: Maybe Analyzer
1353− } deriving (Eq, Read, Show, Generic, Typeable)
1354−
1355−data IndicesQuery =
1356− IndicesQuery
1357− { indicesQueryIndices :: [IndexName]
1358− , indicesQuery :: Query
1359− -- default "all"
1360− , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Read, Show, Generic, Typeable)
1361−
1362−data HasParentQuery =
1363− HasParentQuery
1364− { hasParentQueryType :: TypeName
1365− , hasParentQuery :: Query
1366− , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Read, Show, Generic, Typeable)
1367−
1368−data HasChildQuery =
1369− HasChildQuery
1370− { hasChildQueryType :: TypeName
1371− , hasChildQuery :: Query
1372− , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Read, Show, Generic, Typeable)
1373−
1374−data ScoreType =
1375− ScoreTypeMax
1376− | ScoreTypeSum
1377− | ScoreTypeAvg
1378− | ScoreTypeNone deriving (Eq, Read, Show, Generic, Typeable)
1379−
1380−data FuzzyQuery =
1381− FuzzyQuery { fuzzyQueryField :: FieldName
1382− , fuzzyQueryValue :: Text
1383− , fuzzyQueryPrefixLength :: PrefixLength
1384− , fuzzyQueryMaxExpansions :: MaxExpansions
1385− , fuzzyQueryFuzziness :: Fuzziness
1386− , fuzzyQueryBoost :: Maybe Boost
1387− } deriving (Eq, Read, Show, Generic, Typeable)
1388−
1389−data FuzzyLikeFieldQuery =
1390− FuzzyLikeFieldQuery
1391− { fuzzyLikeField :: FieldName
1392− -- anaphora is good for the soul.
1393− , fuzzyLikeFieldText :: Text
1394− , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
1395− , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
1396− , fuzzyLikeFieldFuzziness :: Fuzziness
1397− , fuzzyLikeFieldPrefixLength :: PrefixLength
1398− , fuzzyLikeFieldBoost :: Boost
1399− , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
1400− } deriving (Eq, Read, Show, Generic, Typeable)
1401−
1402−data FuzzyLikeThisQuery =
1403− FuzzyLikeThisQuery
1404− { fuzzyLikeFields :: [FieldName]
1405− , fuzzyLikeText :: Text
1406− , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
1407− , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
1408− , fuzzyLikeFuzziness :: Fuzziness
1409− , fuzzyLikePrefixLength :: PrefixLength
1410− , fuzzyLikeBoost :: Boost
1411− , fuzzyLikeAnalyzer :: Maybe Analyzer
1412− } deriving (Eq, Read, Show, Generic, Typeable)
1413−
1414−data FilteredQuery =
1415− FilteredQuery
1416− { filteredQuery :: Query
1417− , filteredFilter :: Filter } deriving (Eq, Read, Show, Generic, Typeable)
1418−
1419−data DisMaxQuery =
1420− DisMaxQuery { disMaxQueries :: [Query]
1421− -- default 0.0
1422− , disMaxTiebreaker :: Tiebreaker
1423− , disMaxBoost :: Maybe Boost
1424− } deriving (Eq, Read, Show, Generic, Typeable)
1425−
1426−data MatchQuery =
1427− MatchQuery { matchQueryField :: FieldName
1428− , matchQueryQueryString :: QueryString
1429− , matchQueryOperator :: BooleanOperator
1430− , matchQueryZeroTerms :: ZeroTermsQuery
1431− , matchQueryCutoffFrequency :: Maybe CutoffFrequency
1432− , matchQueryMatchType :: Maybe MatchQueryType
1433− , matchQueryAnalyzer :: Maybe Analyzer
1434− , matchQueryMaxExpansions :: Maybe MaxExpansions
1435− , matchQueryLenient :: Maybe Lenient
1436− , matchQueryBoost :: Maybe Boost } deriving (Eq, Read, Show, Generic, Typeable)
1437−
1438−{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
1439− enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
1440−-}
1441−mkMatchQuery :: FieldName -> QueryString -> MatchQuery
1442−mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1443−
1444−data MatchQueryType =
1445− MatchPhrase
1446− | MatchPhrasePrefix deriving (Eq, Read, Show, Generic, Typeable)
1447−
1448−data MultiMatchQuery =
1449− MultiMatchQuery { multiMatchQueryFields :: [FieldName]
1450− , multiMatchQueryString :: QueryString
1451− , multiMatchQueryOperator :: BooleanOperator
1452− , multiMatchQueryZeroTerms :: ZeroTermsQuery
1453− , multiMatchQueryTiebreaker :: Maybe Tiebreaker
1454− , multiMatchQueryType :: Maybe MultiMatchQueryType
1455− , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
1456− , multiMatchQueryAnalyzer :: Maybe Analyzer
1457− , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
1458− , multiMatchQueryLenient :: Maybe Lenient } deriving (Eq, Read, Show, Generic, Typeable)
1459−
1460−{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
1461− enabling you to provide only the list of 'FieldName's and 'QueryString' to
1462− make a 'MultiMatchQuery'.
1463−-}
1464−
1465−mkMultiMatchQuery :: [FieldName] -> QueryString -> MultiMatchQuery
1466−mkMultiMatchQuery matchFields query =
1467− MultiMatchQuery matchFields query
1468− Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
1469−
1470−data MultiMatchQueryType =
1471− MultiMatchBestFields
1472− | MultiMatchMostFields
1473− | MultiMatchCrossFields
1474− | MultiMatchPhrase
1475− | MultiMatchPhrasePrefix deriving (Eq, Read, Show, Generic, Typeable)
1476−
1477−data BoolQuery =
1478− BoolQuery { boolQueryMustMatch :: [Query]
1479− , boolQueryMustNotMatch :: [Query]
1480− , boolQueryShouldMatch :: [Query]
1481− , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
1482− , boolQueryBoost :: Maybe Boost
1483− , boolQueryDisableCoord :: Maybe DisableCoord
1484− } deriving (Eq, Read, Show, Generic, Typeable)
1485−
1486−mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
1487−mkBoolQuery must mustNot should =
1488− BoolQuery must mustNot should Nothing Nothing Nothing
1489−
1490−data BoostingQuery =
1491− BoostingQuery { positiveQuery :: Query
1492− , negativeQuery :: Query
1493− , negativeBoost :: Boost } deriving (Eq, Read, Show, Generic, Typeable)
1494−
1495−data CommonTermsQuery =
1496− CommonTermsQuery { commonField :: FieldName
1497− , commonQuery :: QueryString
1498− , commonCutoffFrequency :: CutoffFrequency
1499− , commonLowFreqOperator :: BooleanOperator
1500− , commonHighFreqOperator :: BooleanOperator
1501− , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
1502− , commonBoost :: Maybe Boost
1503− , commonAnalyzer :: Maybe Analyzer
1504− , commonDisableCoord :: Maybe DisableCoord
1505− } deriving (Eq, Read, Show, Generic, Typeable)
1506−
1507−data CommonMinimumMatch =
1508− CommonMinimumMatchHighLow MinimumMatchHighLow
1509− | CommonMinimumMatch MinimumMatch
1510− deriving (Eq, Read, Show, Generic, Typeable)
1511−
1512−data MinimumMatchHighLow =
1513− MinimumMatchHighLow { lowFreq :: MinimumMatch
1514− , highFreq :: MinimumMatch } deriving (Eq, Read, Show, Generic, Typeable)
1515−
1516−data Filter = AndFilter [Filter] Cache
1517− | OrFilter [Filter] Cache
1518− | NotFilter Filter Cache
1519− | IdentityFilter
1520− | BoolFilter BoolMatch
1521− | ExistsFilter FieldName -- always cached
1522− | GeoBoundingBoxFilter GeoBoundingBoxConstraint
1523− | GeoDistanceFilter GeoPoint Distance DistanceType OptimizeBbox Cache
1524− | GeoDistanceRangeFilter GeoPoint DistanceRange
1525− | GeoPolygonFilter FieldName [LatLon]
1526− | IdsFilter MappingName [DocId]
1527− | LimitFilter Int
1528− | MissingFilter FieldName Existence NullValue
1529− | PrefixFilter FieldName PrefixValue Cache
1530− | QueryFilter Query Cache
1531− | RangeFilter FieldName RangeValue RangeExecution Cache
1532− | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
1533− | TermFilter Term Cache
1534− deriving (Eq, Read, Show, Generic, Typeable)
1535−
1536−data ZeroTermsQuery = ZeroTermsNone
1537− | ZeroTermsAll deriving (Eq, Read, Show, Generic, Typeable)
1538−
1539−data RangeExecution = RangeExecutionIndex
1540− | RangeExecutionFielddata deriving (Eq, Read, Show, Generic, Typeable)
1541−
1542−newtype Regexp = Regexp Text deriving (Eq, Read, Show, Generic, Typeable, FromJSON)
1543−
1544−data RegexpFlags = AllRegexpFlags
1545− | NoRegexpFlags
1546− | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Read, Show, Generic, Typeable)
1547−
1548−data RegexpFlag = AnyString
1549− | Automaton
1550− | Complement
1551− | Empty
1552− | Intersection
1553− | Interval deriving (Eq, Read, Show, Generic, Typeable)
1554−
1555−newtype LessThan = LessThan Double deriving (Eq, Read, Show, Generic, Typeable)
1556−newtype LessThanEq = LessThanEq Double deriving (Eq, Read, Show, Generic, Typeable)
1557−newtype GreaterThan = GreaterThan Double deriving (Eq, Read, Show, Generic, Typeable)
1558−newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Read, Show, Generic, Typeable)
1559−
1560−newtype LessThanD = LessThanD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1561−newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1562−newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1563−newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Read, Show, Generic, Typeable)
1564−
1565−data RangeValue = RangeDateLte LessThanEqD
1566− | RangeDateLt LessThanD
1567− | RangeDateGte GreaterThanEqD
1568− | RangeDateGt GreaterThanD
1569− | RangeDateGtLt GreaterThanD LessThanD
1570− | RangeDateGteLte GreaterThanEqD LessThanEqD
1571− | RangeDateGteLt GreaterThanEqD LessThanD
1572− | RangeDateGtLte GreaterThanD LessThanEqD
1573− | RangeDoubleLte LessThanEq
1574− | RangeDoubleLt LessThan
1575− | RangeDoubleGte GreaterThanEq
1576− | RangeDoubleGt GreaterThan
1577− | RangeDoubleGtLt GreaterThan LessThan
1578− | RangeDoubleGteLte GreaterThanEq LessThanEq
1579− | RangeDoubleGteLt GreaterThanEq LessThan
1580− | RangeDoubleGtLte GreaterThan LessThanEq
1581− deriving (Eq, Read, Show, Generic, Typeable)
1582−
1583−rangeValueToPair :: RangeValue -> [Pair]
1584−rangeValueToPair rv = case rv of
1585− RangeDateLte (LessThanEqD t) -> ["lte" .= t]
1586− RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
1587− RangeDateLt (LessThanD t) -> ["lt" .= t]
1588− RangeDateGt (GreaterThanD t) -> ["gt" .= t]
1589− RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
1590− RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
1591− RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
1592− RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
1593− RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
1594− RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
1595− RangeDoubleLt (LessThan t) -> ["lt" .= t]
1596− RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
1597− RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
1598− RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
1599− RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
1600− RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1601−
1602−data Term = Term { termField :: Text
1603− , termValue :: Text } deriving (Eq, Read, Show, Generic, Typeable)
1604−
1605−data BoolMatch = MustMatch Term Cache
1606− | MustNotMatch Term Cache
1607− | ShouldMatch [Term] Cache deriving (Eq, Read, Show, Generic, Typeable)
1608−
1609−-- "memory" or "indexed"
1610−data GeoFilterType = GeoFilterMemory
1611− | GeoFilterIndexed deriving (Eq, Read, Show, Generic, Typeable)
1612−
1613−data LatLon = LatLon { lat :: Double
1614− , lon :: Double } deriving (Eq, Read, Show, Generic, Typeable)
1615−
1616−data GeoBoundingBox =
1617− GeoBoundingBox { topLeft :: LatLon
1618− , bottomRight :: LatLon } deriving (Eq, Read, Show, Generic, Typeable)
1619−
1620−data GeoBoundingBoxConstraint =
1621− GeoBoundingBoxConstraint { geoBBField :: FieldName
1622− , constraintBox :: GeoBoundingBox
1623− , bbConstraintcache :: Cache
1624− , geoType :: GeoFilterType
1625− } deriving (Eq, Read, Show, Generic, Typeable)
1626−
1627−data GeoPoint =
1628− GeoPoint { geoField :: FieldName
1629− , latLon :: LatLon} deriving (Eq, Read, Show, Generic, Typeable)
1630−
1631−data DistanceUnit = Miles
1632− | Yards
1633− | Feet
1634− | Inches
1635− | Kilometers
1636− | Meters
1637− | Centimeters
1638− | Millimeters
1639− | NauticalMiles deriving (Eq, Read, Show, Generic, Typeable)
1640−
1641−data DistanceType = Arc
1642− | SloppyArc -- doesn't exist <1.0
1643− | Plane deriving (Eq, Read, Show, Generic, Typeable)
1644−
1645−data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1646− | NoOptimizeBbox deriving (Eq, Read, Show, Generic, Typeable)
1647−
1648−data Distance =
1649− Distance { coefficient :: Double
1650− , unit :: DistanceUnit } deriving (Eq, Read, Show, Generic, Typeable)
1651−
1652−data DistanceRange =
1653− DistanceRange { distanceFrom :: Distance
1654− , distanceTo :: Distance } deriving (Eq, Read, Show, Generic, Typeable)
1655−
1656−type TemplateQueryKey = Text
1657−type TemplateQueryValue = Text
1658−
1659−newtype TemplateQueryKeyValuePairs = TemplateQueryKeyValuePairs (HM.HashMap TemplateQueryKey TemplateQueryValue)
1660− deriving (Eq, Read, Show, Generic, Typeable)
1661−
1662−instance ToJSON TemplateQueryKeyValuePairs where
1663− toJSON (TemplateQueryKeyValuePairs x) = Object $ HM.map toJSON x
1664−
1665−instance FromJSON TemplateQueryKeyValuePairs where
1666− parseJSON (Object o) = pure . TemplateQueryKeyValuePairs $ HM.mapMaybe getValue o
1667− where getValue (String x) = Just x
1668− getValue _ = Nothing
1669− parseJSON _ = fail "error parsing TemplateQueryKeyValuePairs"
1670−
1671−data TemplateQueryInline =
1672− TemplateQueryInline { inline :: Query
1673− , params :: TemplateQueryKeyValuePairs
1674− }
1675− deriving (Eq, Read, Show, Generic, Typeable)
1676−
1677−instance ToJSON TemplateQueryInline where
1678− toJSON TemplateQueryInline{..} = object [ "query" .= inline
1679− , "params" .= params
1680− ]
1681−
1682−instance FromJSON TemplateQueryInline where
1683− parseJSON = withObject "TemplateQueryInline" parse
1684− where parse o = TemplateQueryInline
1685− <$> o .: "query"
1686− <*> o .: "params"
1687−
1688−data SearchResult a =
1689− SearchResult { took :: Int
1690− , timedOut :: Bool
1691− , shards :: ShardResult
1692− , searchHits :: SearchHits a
1693− , aggregations :: Maybe AggregationResults
1694− , scrollId :: Maybe ScrollId
1695− , suggest :: Maybe NamedSuggestionResponse -- ^ Only one Suggestion request / response per Search is supported.
1696− }
1697− deriving (Eq, Read, Show, Generic, Typeable)
1698−
1699−newtype ScrollId = ScrollId Text deriving (Eq, Read, Show, Generic, Ord, ToJSON, FromJSON)
1700−
1701−type Score = Maybe Double
1702−
1703−data SearchHits a =
1704− SearchHits { hitsTotal :: Int
1705− , maxScore :: Score
1706− , hits :: [Hit a] } deriving (Eq, Read, Show, Generic, Typeable)
1707−
1708−instance Semigroup (SearchHits a) where
1709− (SearchHits ta ma ha) <> (SearchHits tb mb hb) = SearchHits (ta + tb) (max ma mb) (ha <> hb)
1710−
1711−instance Monoid (SearchHits a) where
1712− mempty = SearchHits 0 Nothing mempty
1713− mappend = (<>)
1714−
1715−data Hit a =
1716− Hit { hitIndex :: IndexName
1717− , hitType :: MappingName
1718− , hitDocId :: DocId
1719− , hitScore :: Score
1720− , hitSource :: Maybe a
1721− , hitHighlight :: Maybe HitHighlight } deriving (Eq, Read, Show, Generic, Typeable)
1722−
1723−data ShardResult =
1724− ShardResult { shardTotal :: Int
1725− , shardsSuccessful :: Int
1726− , shardsFailed :: Int } deriving (Eq, Read, Show, Generic, Typeable)
1727−
1728−type HitHighlight = M.Map Text [Text]
1729−
1730−showText :: Show a => a -> Text
1731−showText = T.pack . show
1732−
1733−readMay :: Read a => String -> Maybe a
1734−readMay s = case reads s of
1735− (a, ""):_ -> Just a
1736− _ -> Nothing
1737−
1738−parseReadText :: Read a => Text -> Parser a
1739−parseReadText = maybe mzero return . readMay . T.unpack
1740−
1741−type Aggregations = M.Map Text Aggregation
1742−
1743−emptyAggregations :: Aggregations
1744−emptyAggregations = M.empty
1745−
1746−mkAggregations :: Text -> Aggregation -> Aggregations
1747−mkAggregations name aggregation = M.insert name aggregation emptyAggregations
1748−
1749−data TermOrder = TermOrder{ termSortField :: Text
1750− , termSortOrder :: SortOrder } deriving (Eq, Read, Show, Generic, Typeable)
1751−
1752−data TermInclusion = TermInclusion Text
1753− | TermPattern Text Text deriving (Eq, Read, Show, Generic, Typeable)
1754−
1755−data CollectionMode = BreadthFirst
1756− | DepthFirst deriving (Eq, Read, Show, Generic, Typeable)
1757−
1758−data ExecutionHint = Ordinals
1759− | GlobalOrdinals
1760− | GlobalOrdinalsHash
1761− | GlobalOrdinalsLowCardinality
1762− | Map deriving (Eq, Read, Show, Generic, Typeable)
1763−
1764−data TimeInterval = Weeks
1765− | Days
1766− | Hours
1767− | Minutes
1768− | Seconds deriving Eq
1769−
1770−data Interval = Year
1771− | Quarter
1772− | Month
1773− | Week
1774− | Day
1775− | Hour
1776− | Minute
1777− | Second
1778− | FractionalInterval Float TimeInterval deriving (Eq, Read, Show, Generic, Typeable)
1779−
1780−data Aggregation = TermsAgg TermsAggregation
1781− | CardinalityAgg CardinalityAggregation
1782− | DateHistogramAgg DateHistogramAggregation
1783− | ValueCountAgg ValueCountAggregation
1784− | FilterAgg FilterAggregation
1785− | DateRangeAgg DateRangeAggregation
1786− | MissingAgg MissingAggregation
1787− | TopHitsAgg TopHitsAggregation
1788− deriving (Eq, Read, Show, Generic, Typeable)
1789−
1790−data TopHitsAggregation = TopHitsAggregation
1791− { taFrom :: Maybe From
1792− , taSize :: Maybe Size
1793− , taSort :: Maybe Sort
1794− } deriving (Eq, Read, Show)
1795−
1796−data MissingAggregation = MissingAggregation
1797− { maField :: Text
1798− } deriving (Eq, Read, Show, Generic, Typeable)
1799−
1800−data TermsAggregation = TermsAggregation { term :: Either Text Text
1801− , termInclude :: Maybe TermInclusion
1802− , termExclude :: Maybe TermInclusion
1803− , termOrder :: Maybe TermOrder
1804− , termMinDocCount :: Maybe Int
1805− , termSize :: Maybe Int
1806− , termShardSize :: Maybe Int
1807− , termCollectMode :: Maybe CollectionMode
1808− , termExecutionHint :: Maybe ExecutionHint
1809− , termAggs :: Maybe Aggregations
1810− } deriving (Eq, Read, Show, Generic, Typeable)
1811−
1812−data CardinalityAggregation = CardinalityAggregation { cardinalityField :: FieldName,
1813− precisionThreshold :: Maybe Int
1814− } deriving (Eq, Read, Show, Generic, Typeable)
1815−
1816−data DateHistogramAggregation = DateHistogramAggregation { dateField :: FieldName
1817− , dateInterval :: Interval
1818− , dateFormat :: Maybe Text
1819− -- pre and post deprecated in 1.5
1820− , datePreZone :: Maybe Text
1821− , datePostZone :: Maybe Text
1822− , datePreOffset :: Maybe Text
1823− , datePostOffset :: Maybe Text
1824− , dateAggs :: Maybe Aggregations
1825− } deriving (Eq, Read, Show, Generic, Typeable)
426+ deriving (Eq, Show)
1826427
428+instance ToJSON Search where
429+ toJSON (Search query sFilter sort searchAggs highlight sTrackSortScores sFrom sSize _ sFields sSource sSuggest) =
430+ omitNulls [ "query" .= query
431+ , "filter" .= sFilter
432+ , "sort" .= sort
433+ , "aggregations" .= searchAggs
434+ , "highlight" .= highlight
435+ , "from" .= sFrom
436+ , "size" .= sSize
437+ , "track_scores" .= sTrackSortScores
438+ , "fields" .= sFields
439+ , "_source" .= sSource
440+ , "suggest" .= sSuggest]
1827441
1828−data DateRangeAggregation = DateRangeAggregation { draField :: FieldName
1829− , draFormat :: Maybe Text
1830− , draRanges :: NonEmpty DateRangeAggRange
1831− } deriving (Eq, Read, Show, Generic, Typeable)
1832−
1833−data DateRangeAggRange = DateRangeFrom DateMathExpr
1834− | DateRangeTo DateMathExpr
1835− | DateRangeFromAndTo DateMathExpr DateMathExpr deriving (Eq, Read, Show, Generic, Typeable)
1836−
1837−-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
1838−data DateMathExpr = DateMathExpr DateMathAnchor [DateMathModifier] deriving (Eq, Read, Show, Generic, Typeable)
1839−
1840−
1841−-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
1842−data DateMathAnchor = DMNow
1843− | DMDate Day deriving (Eq, Read, Show, Generic, Typeable)
1844−
1845−data DateMathModifier = AddTime Int DateMathUnit
1846− | SubtractTime Int DateMathUnit
1847− | RoundDownTo DateMathUnit deriving (Eq, Read, Show, Generic, Typeable)
1848−
1849−data DateMathUnit = DMYear
1850− | DMMonth
1851− | DMWeek
1852− | DMDay
1853− | DMHour
1854− | DMMinute
1855− | DMSecond deriving (Eq, Read, Show, Generic, Typeable)
1856−
1857−-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
1858−data ValueCountAggregation = FieldValueCount FieldName
1859− | ScriptValueCount Script deriving (Eq, Read, Show, Generic, Typeable)
1860−
1861−-- | 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.
1862−data FilterAggregation = FilterAggregation { faFilter :: Filter
1863− , faAggs :: Maybe Aggregations} deriving (Eq, Read, Show, Generic, Typeable)
1864−
1865−mkTermsAggregation :: Text -> TermsAggregation
1866−mkTermsAggregation t = TermsAggregation (Left t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1867−
1868−mkTermsScriptAggregation :: Text -> TermsAggregation
1869−mkTermsScriptAggregation t = TermsAggregation (Right t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1870−
1871−mkDateHistogram :: FieldName -> Interval -> DateHistogramAggregation
1872−mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
1873−
1874−mkCardinalityAggregation :: FieldName -> CardinalityAggregation
1875−mkCardinalityAggregation t = CardinalityAggregation t Nothing
1876−
1877−instance ToJSON Version where
1878− toJSON Version {..} = object ["number" .= number
1879− ,"build_hash" .= build_hash
1880− ,"build_timestamp" .= build_timestamp
1881− ,"build_snapshot" .= build_snapshot
1882− ,"lucene_version" .= lucene_version]
1883−
1884−instance FromJSON Version where
1885− parseJSON = withObject "Version" parse
1886− where parse o = Version
1887− <$> o .: "number"
1888− <*> o .: "build_hash"
1889− <*> o .: "build_timestamp"
1890− <*> o .: "build_snapshot"
1891− <*> o .: "lucene_version"
1892−
1893−instance ToJSON VersionNumber where
1894− toJSON = toJSON . Vers.showVersion . versionNumber
1895−
1896−instance FromJSON VersionNumber where
1897− parseJSON = withText "VersionNumber" (parse . T.unpack)
1898− where
1899− parse s = case filter (null . snd)(RP.readP_to_S Vers.parseVersion s) of
1900− [(v, _)] -> pure (VersionNumber v)
1901− [] -> fail ("Invalid version string " ++ s)
1902− xs -> fail ("Ambiguous version string " ++ s ++ " (" ++ intercalate ", " (Vers.showVersion . fst <$> xs) ++ ")")
1903−
1904−instance ToJSON TermOrder where
1905− toJSON (TermOrder termSortField termSortOrder) = object [termSortField .= termSortOrder]
1906−
1907−instance ToJSON TermInclusion where
1908− toJSON (TermInclusion x) = toJSON x
1909− toJSON (TermPattern pattern flags) = omitNulls [ "pattern" .= pattern,
1910− "flags" .= flags]
1911−
1912−instance ToJSON CollectionMode where
1913− toJSON BreadthFirst = "breadth_first"
1914− toJSON DepthFirst = "depth_first"
1915−
1916−instance ToJSON ExecutionHint where
1917− toJSON Ordinals = "ordinals"
1918− toJSON GlobalOrdinals = "global_ordinals"
1919− toJSON GlobalOrdinalsHash = "global_ordinals_hash"
1920− toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
1921− toJSON Map = "map"
1922−
1923−instance ToJSON Interval where
1924− toJSON Year = "year"
1925− toJSON Quarter = "quarter"
1926− toJSON Month = "month"
1927− toJSON Week = "week"
1928− toJSON Day = "day"
1929− toJSON Hour = "hour"
1930− toJSON Minute = "minute"
1931− toJSON Second = "second"
1932− toJSON (FractionalInterval fraction interval) = toJSON $ show fraction ++ show interval
1933−
1934−instance Show TimeInterval where
1935− show Weeks = "w"
1936− show Days = "d"
1937− show Hours = "h"
1938− show Minutes = "m"
1939− show Seconds = "s"
1940−
1941−instance Read TimeInterval where
1942− readPrec = f =<< TR.get
1943− where
1944− f 'w' = return Weeks
1945− f 'd' = return Days
1946− f 'h' = return Hours
1947− f 'm' = return Minutes
1948− f 's' = return Seconds
1949− f _ = fail "TimeInterval expected one of w, d, h, m, s"
1950−
1951−instance ToJSON Aggregation where
1952− toJSON (TermsAgg (TermsAggregation term include exclude order minDocCount size shardSize collectMode executionHint termAggs)) =
1953− omitNulls ["terms" .= omitNulls [ toJSON' term,
1954− "include" .= include,
1955− "exclude" .= exclude,
1956− "order" .= order,
1957− "min_doc_count" .= minDocCount,
1958− "size" .= size,
1959− "shard_size" .= shardSize,
1960− "collect_mode" .= collectMode,
1961− "execution_hint" .= executionHint
1962− ],
1963− "aggs" .= termAggs ]
1964− where
1965− toJSON' x = case x of { Left y -> "field" .= y; Right y -> "script" .= y }
1966−
1967− toJSON (CardinalityAgg (CardinalityAggregation field precisionThreshold)) =
1968− object ["cardinality" .= omitNulls [ "field" .= field,
1969− "precisionThreshold" .= precisionThreshold
1970− ]
1971− ]
1972−
1973− toJSON (DateHistogramAgg (DateHistogramAggregation field interval format preZone postZone preOffset postOffset dateHistoAggs)) =
1974− omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
1975− "interval" .= interval,
1976− "format" .= format,
1977− "pre_zone" .= preZone,
1978− "post_zone" .= postZone,
1979− "pre_offset" .= preOffset,
1980− "post_offset" .= postOffset
1981− ],
1982− "aggs" .= dateHistoAggs ]
1983− toJSON (ValueCountAgg a) = object ["value_count" .= v]
1984− where v = case a of
1985− (FieldValueCount (FieldName n)) -> object ["field" .= n]
1986− (ScriptValueCount (Script s)) -> object ["script" .= s]
1987− toJSON (FilterAgg (FilterAggregation filt ags)) =
1988− omitNulls [ "filter" .= filt
1989− , "aggs" .= ags]
1990− toJSON (DateRangeAgg a) = object [ "date_range" .= a
1991− ]
1992− toJSON (MissingAgg (MissingAggregation{..})) =
1993− object ["missing" .= object ["field" .= maField]]
1994−
1995− toJSON (TopHitsAgg (TopHitsAggregation mfrom msize msort)) =
1996− omitNulls ["top_hits" .= omitNulls [ "size" .= msize
1997− , "from" .= mfrom
1998− , "sort" .= msort
1999− ]
2000− ]
2001−
2002−instance ToJSON DateRangeAggregation where
2003− toJSON DateRangeAggregation {..} =
2004− omitNulls [ "field" .= draField
2005− , "format" .= draFormat
2006− , "ranges" .= toList draRanges
2007− ]
2008−
2009−instance ToJSON DateRangeAggRange where
2010− toJSON (DateRangeFrom e) = object [ "from" .= e ]
2011− toJSON (DateRangeTo e) = object [ "to" .= e ]
2012− toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
2013−
2014−instance ToJSON DateMathExpr where
2015− toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
2016− where fmtA DMNow = "now"
2017− fmtA (DMDate date) = (T.pack $ showGregorian date) <> "||"
2018− fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
2019− fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
2020− fmtMod (RoundDownTo u) = "/" <> fmtU u
2021− fmtU DMYear = "y"
2022− fmtU DMMonth = "M"
2023− fmtU DMWeek = "w"
2024− fmtU DMDay = "d"
2025− fmtU DMHour = "h"
2026− fmtU DMMinute = "m"
2027− fmtU DMSecond = "s"
2028−
2029−
2030−type AggregationResults = M.Map Text Value
2031−
2032−class BucketAggregation a where
2033− key :: a -> BucketValue
2034− docCount :: a -> Int
2035− aggs :: a -> Maybe AggregationResults
2036−
2037−
2038−data Bucket a = Bucket { buckets :: [a]} deriving (Read, Show)
2039−
2040−data BucketValue = TextValue Text
2041− | ScientificValue Scientific
2042− | BoolValue Bool deriving (Read, Show)
2043−
2044−data MissingResult = MissingResult { missingDocCount :: Int } deriving (Show)
2045−
2046−data TopHitResult a = TopHitResult { tarHits :: (SearchHits a)
2047− } deriving Show
2048−
2049−data TermsResult = TermsResult { termKey :: BucketValue
2050− , termsDocCount :: Int
2051− , termsAggs :: Maybe AggregationResults } deriving (Read, Show)
2052−
2053−data DateHistogramResult = DateHistogramResult { dateKey :: Int
2054− , dateKeyStr :: Maybe Text
2055− , dateDocCount :: Int
2056− , dateHistogramAggs :: Maybe AggregationResults } deriving (Read, Show)
2057−
2058−data DateRangeResult = DateRangeResult { dateRangeKey :: Text
2059− , dateRangeFrom :: Maybe UTCTime
2060− , dateRangeFromAsString :: Maybe Text
2061− , dateRangeTo :: Maybe UTCTime
2062− , dateRangeToAsString :: Maybe Text
2063− , dateRangeDocCount :: Int
2064− , dateRangeAggs :: Maybe AggregationResults } deriving (Read, Show, Eq, Generic, Typeable)
2065−
2066−toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
2067−toTerms = toAggResult
2068−
2069−toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
2070−toDateHistogram = toAggResult
2071−
2072−toMissing :: Text -> AggregationResults -> Maybe MissingResult
2073−toMissing = toAggResult
2074−
2075−toTopHits :: (FromJSON a) => Text -> AggregationResults -> Maybe (TopHitResult a)
2076−toTopHits = toAggResult
2077−
2078−toAggResult :: (FromJSON a) => Text -> AggregationResults -> Maybe a
2079−toAggResult t a = M.lookup t a >>= deserialize
2080− where deserialize = parseMaybe parseJSON
2081−
2082−instance BucketAggregation TermsResult where
2083− key = termKey
2084− docCount = termsDocCount
2085− aggs = termsAggs
2086−
2087−instance BucketAggregation DateHistogramResult where
2088− key = TextValue . showText . dateKey
2089− docCount = dateDocCount
2090− aggs = dateHistogramAggs
2091−
2092−instance BucketAggregation DateRangeResult where
2093− key = TextValue . dateRangeKey
2094− docCount = dateRangeDocCount
2095− aggs = dateRangeAggs
2096−
2097−instance (FromJSON a) => FromJSON (Bucket a) where
2098− parseJSON (Object v) = Bucket <$>
2099− v .: "buckets"
2100− parseJSON _ = mempty
2101−
2102−instance FromJSON BucketValue where
2103− parseJSON (String t) = return $ TextValue t
2104− parseJSON (Number s) = return $ ScientificValue s
2105− parseJSON (Bool b) = return $ BoolValue b
2106− parseJSON _ = mempty
2107−
2108−instance FromJSON MissingResult where
2109− parseJSON = withObject "MissingResult" parse
2110− where parse v = MissingResult <$> v .: "doc_count"
2111−
2112−instance FromJSON TermsResult where
2113− parseJSON (Object v) = TermsResult <$>
2114− v .: "key" <*>
2115− v .: "doc_count" <*>
2116− (pure $ getNamedSubAgg v ["key", "doc_count"])
2117− parseJSON _ = mempty
2118−
2119−instance FromJSON DateHistogramResult where
2120− parseJSON (Object v) = DateHistogramResult <$>
2121− v .: "key" <*>
2122− v .:? "key_as_string" <*>
2123− v .: "doc_count" <*>
2124− (pure $ getNamedSubAgg v [ "key"
2125− , "doc_count"
2126− , "key_as_string"
2127− ]
2128− )
2129− parseJSON _ = mempty
2130−
2131−instance FromJSON DateRangeResult where
2132− parseJSON = withObject "DateRangeResult" parse
2133− where parse v = DateRangeResult <$>
2134− v .: "key" <*>
2135− (fmap posixMS <$> v .:? "from") <*>
2136− v .:? "from_as_string" <*>
2137− (fmap posixMS <$> v .:? "to") <*>
2138− v .:? "to_as_string" <*>
2139− v .: "doc_count" <*>
2140− (pure $ getNamedSubAgg v [ "key"
2141− , "from"
2142− , "from_as_string"
2143− , "to"
2144− , "to_as_string"
2145− , "doc_count"
2146− ]
2147− )
2148−
2149−instance FromJSON POSIXMS where
2150− parseJSON = withScientific "POSIXMS" (return . parse)
2151− where parse n = let n' = truncate n :: Integer
2152− in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
2153−
2154−instance (FromJSON a) => FromJSON (TopHitResult a) where
2155− parseJSON (Object v) = TopHitResult <$>
2156− v .: "hits"
2157− parseJSON _ = fail "Failure in FromJSON (TopHitResult a)"
2158−
2159−instance Semigroup Filter where
2160− a <> b = AndFilter [a, b] defaultCache
2161−
2162−instance Monoid Filter where
2163− mempty = IdentityFilter
2164− mappend = (<>)
2165−
2166−instance Seminearring Filter where
2167− a <||> b = OrFilter [a, b] defaultCache
2168−
2169−instance ToJSON Filter where
2170− toJSON (AndFilter filters cache) =
2171− object ["and" .=
2172− object [ "filters" .= fmap toJSON filters
2173− , "_cache" .= cache]]
2174−
2175− toJSON (OrFilter filters cache) =
2176− object ["or" .=
2177− object [ "filters" .= fmap toJSON filters
2178− , "_cache" .= cache]]
2179−
2180− toJSON (NotFilter notFilter cache) =
2181− object ["not" .=
2182− object ["filter" .= notFilter
2183− , "_cache" .= cache]]
2184−
2185− toJSON (IdentityFilter) =
2186− object ["match_all" .= object []]
2187−
2188− toJSON (TermFilter (Term termFilterField termFilterValue) cache) =
2189− object ["term" .= object base]
2190− where base = [termFilterField .= termFilterValue,
2191− "_cache" .= cache]
2192−
2193− toJSON (ExistsFilter (FieldName fieldName)) =
2194− object ["exists" .= object
2195− ["field" .= fieldName]]
2196−
2197− toJSON (BoolFilter boolMatch) =
2198− object ["bool" .= boolMatch]
2199−
2200− toJSON (GeoBoundingBoxFilter bbConstraint) =
2201− object ["geo_bounding_box" .= bbConstraint]
2202−
2203− toJSON (GeoDistanceFilter (GeoPoint (FieldName distanceGeoField) geoDistLatLon)
2204− distance distanceType optimizeBbox cache) =
2205− object ["geo_distance" .=
2206− object ["distance" .= distance
2207− , "distance_type" .= distanceType
2208− , "optimize_bbox" .= optimizeBbox
2209− , distanceGeoField .= geoDistLatLon
2210− , "_cache" .= cache]]
2211−
2212− toJSON (GeoDistanceRangeFilter (GeoPoint (FieldName gddrField) drLatLon)
2213− (DistanceRange geoDistRangeDistFrom drDistanceTo)) =
2214− object ["geo_distance_range" .=
2215− object ["from" .= geoDistRangeDistFrom
2216− , "to" .= drDistanceTo
2217− , gddrField .= drLatLon]]
2218−
2219− toJSON (GeoPolygonFilter (FieldName geoPolygonFilterField) latLons) =
2220− object ["geo_polygon" .=
2221− object [geoPolygonFilterField .=
2222− object ["points" .= fmap toJSON latLons]]]
2223−
2224− toJSON (IdsFilter (MappingName mappingName) values) =
2225− object ["ids" .=
2226− object ["type" .= mappingName
2227− , "values" .= fmap unpackId values]]
2228−
2229− toJSON (LimitFilter limit) =
2230− object ["limit" .= object ["value" .= limit]]
2231−
2232− toJSON (MissingFilter (FieldName fieldName) (Existence existence) (NullValue nullValue)) =
2233− object ["missing" .=
2234− object [ "field" .= fieldName
2235− , "existence" .= existence
2236− , "null_value" .= nullValue]]
2237−
2238− toJSON (PrefixFilter (FieldName fieldName) fieldValue cache) =
2239− object ["prefix" .=
2240− object [fieldName .= fieldValue
2241− , "_cache" .= cache]]
2242−
2243− toJSON (QueryFilter query False) =
2244− object ["query" .= toJSON query ]
2245− toJSON (QueryFilter query True) =
2246− object ["fquery" .=
2247− object [ "query" .= toJSON query
2248− , "_cache" .= True ]]
2249−
2250− toJSON (RangeFilter (FieldName fieldName) rangeValue rangeExecution cache) =
2251− object ["range" .=
2252− object [ fieldName .= object (rangeValueToPair rangeValue)
2253− , "execution" .= rangeExecution
2254− , "_cache" .= cache]]
2255−
2256− toJSON (RegexpFilter (FieldName fieldName)
2257− (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
2258− object ["regexp" .=
2259− object [fieldName .=
2260− object ["value" .= regexText
2261− , "flags" .= flags]
2262− , "_name" .= cacheName
2263− , "_cache" .= cache
2264− , "_cache_key" .= cacheKey]]
2265−
2266−instance FromJSON Filter where
2267− parseJSON = withObject "Filter" parse
2268− where parse o = andFilter `taggedWith` "and"
2269− <|> orFilter `taggedWith` "or"
2270− <|> notFilter `taggedWith` "not"
2271− <|> identityFilter `taggedWith` "match_all"
2272− <|> boolFilter `taggedWith` "bool"
2273− <|> existsFilter `taggedWith` "exists"
2274− <|> geoBoundingBoxFilter `taggedWith` "geo_bounding_box"
2275− <|> geoDistanceFilter `taggedWith` "geo_distance"
2276− <|> geoDistanceRangeFilter `taggedWith` "geo_distance_range"
2277− <|> geoPolygonFilter `taggedWith` "geo_polygon"
2278− <|> idsFilter `taggedWith` "ids"
2279− <|> limitFilter `taggedWith` "limit"
2280− <|> missingFilter `taggedWith` "missing"
2281− <|> prefixFilter `taggedWith` "prefix"
2282− <|> queryFilter `taggedWith` "query"
2283− <|> fqueryFilter `taggedWith` "fquery"
2284− <|> rangeFilter `taggedWith` "range"
2285− <|> regexpFilter `taggedWith` "regexp"
2286− <|> termFilter `taggedWith` "term"
2287− where taggedWith parser k = parser =<< o .: k
2288− andFilter o = AndFilter <$> o .: "filters"
2289− <*> o .:? "_cache" .!= defaultCache
2290− orFilter o = OrFilter <$> o .: "filters"
2291− <*> o .:? "_cache" .!= defaultCache
2292− notFilter o = NotFilter <$> o .: "filter"
2293− <*> o .: "_cache" .!= defaultCache
2294− identityFilter :: Object -> Parser Filter
2295− identityFilter m
2296− | HM.null m = pure IdentityFilter
2297− | otherwise = fail ("Identityfilter expected empty object but got " <> show m)
2298− boolFilter = pure . BoolFilter
2299− existsFilter o = ExistsFilter <$> o .: "field"
2300− geoBoundingBoxFilter = pure . GeoBoundingBoxFilter
2301− geoDistanceFilter o = do
2302− case HM.toList (deleteSeveral ["distance", "distance_type", "optimize_bbox", "_cache"] o) of
2303− [(fn, v)] -> do
2304− gp <- GeoPoint (FieldName fn) <$> parseJSON v
2305− GeoDistanceFilter gp <$> o .: "distance"
2306− <*> o .: "distance_type"
2307− <*> o .: "optimize_bbox"
2308− <*> o .:? "_cache" .!= defaultCache
2309− _ -> fail "Could not find GeoDistanceFilter field name"
2310− geoDistanceRangeFilter o = do
2311− case HM.toList (deleteSeveral ["from", "to"] o) of
2312− [(fn, v)] -> do
2313− gp <- GeoPoint (FieldName fn) <$> parseJSON v
2314− rng <- DistanceRange <$> o .: "from" <*> o .: "to"
2315− return (GeoDistanceRangeFilter gp rng)
2316− _ -> fail "Could not find GeoDistanceRangeFilter field name"
2317− geoPolygonFilter = fieldTagged $ \fn o -> GeoPolygonFilter fn <$> o .: "points"
2318− idsFilter o = IdsFilter <$> o .: "type"
2319− <*> o .: "values"
2320− limitFilter o = LimitFilter <$> o .: "value"
2321− missingFilter o = MissingFilter <$> o .: "field"
2322− <*> o .: "existence"
2323− <*> o .: "null_value"
2324− prefixFilter o = case HM.toList (HM.delete "_cache" o) of
2325− [(fn, String v)] -> PrefixFilter (FieldName fn) v <$> o .:? "_cache" .!= defaultCache
2326− _ -> fail "Could not parse PrefixFilter"
2327−
2328− queryFilter q = pure (QueryFilter q False)
2329− fqueryFilter o = QueryFilter <$> o .: "query" <*> pure True
2330− rangeFilter o = case HM.toList (deleteSeveral ["execution", "_cache"] o) of
2331− [(fn, v)] -> RangeFilter (FieldName fn)
2332− <$> parseJSON v
2333− <*> o .: "execution"
2334− <*> o .:? "_cache" .!= defaultCache
2335− _ -> fail "Could not find field name for RangeFilter"
2336− regexpFilter o = case HM.toList (deleteSeveral ["_name", "_cache", "_cache_key"] o) of
2337− [(fn, Object o')] -> RegexpFilter (FieldName fn)
2338− <$> o' .: "value"
2339− <*> o' .: "flags"
2340− <*> o .: "_name"
2341− <*> o .:? "_cache" .!= defaultCache
2342− <*> o .: "_cache_key"
2343− _ -> fail "Could not find field name for RegexpFilter"
2344− termFilter o = case HM.toList (HM.delete "_cache" o) of
2345− [(termField, String termVal)] -> TermFilter (Term termField termVal)
2346− <$> o .:? "_cache" .!= defaultCache
2347− _ -> fail "Could not find term field for TermFilter"
2348−
2349−fieldTagged :: Monad m => (FieldName -> Object -> m a) -> Object -> m a
2350−fieldTagged f o = case HM.toList o of
2351− [(k, Object o')] -> f (FieldName k) o'
2352− _ -> fail "Expected object with 1 field-named key"
2353−
2354−-- Try to get an AggregationResults when we don't know the
2355−-- field name. We filter out the known keys to try to minimize the noise.
2356−getNamedSubAgg :: Object -> [Text] -> Maybe AggregationResults
2357−getNamedSubAgg o knownKeys = maggRes
2358− where unknownKeys = HM.filterWithKey (\k _ -> k `notElem` knownKeys) o
2359− maggRes
2360− | HM.null unknownKeys = Nothing
2361− | otherwise = Just . M.fromList $ HM.toList unknownKeys
2362−
2363−instance ToJSON GeoPoint where
2364− toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
2365− object [ geoPointField .= geoPointLatLon ]
2366−
2367−
2368−instance ToJSON Query where
2369− toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
2370− object [ "term" .=
2371− object [termQueryField .= object merged]]
2372− where
2373− base = [ "value" .= termQueryValue ]
2374− boosted = maybe [] (return . ("boost" .=)) boost
2375− merged = mappend base boosted
2376−
2377− toJSON (TermsQuery fieldName terms) =
2378− object [ "terms" .= object conjoined ]
2379− where conjoined = [fieldName .= terms]
2380−
2381− toJSON (IdsQuery idsQueryMappingName docIds) =
2382− object [ "ids" .= object conjoined ]
2383− where conjoined = [ "type" .= idsQueryMappingName
2384− , "values" .= fmap toJSON docIds ]
2385−
2386− toJSON (QueryQueryStringQuery qQueryStringQuery) =
2387− object [ "query_string" .= qQueryStringQuery ]
2388−
2389− toJSON (QueryMatchQuery matchQuery) =
2390− object [ "match" .= matchQuery ]
2391−
2392− toJSON (QueryMultiMatchQuery multiMatchQuery) =
2393− toJSON multiMatchQuery
2394−
2395− toJSON (QueryBoolQuery boolQuery) =
2396− object [ "bool" .= boolQuery ]
2397−
2398− toJSON (QueryBoostingQuery boostingQuery) =
2399− object [ "boosting" .= boostingQuery ]
2400−
2401− toJSON (QueryCommonTermsQuery commonTermsQuery) =
2402− object [ "common" .= commonTermsQuery ]
2403−
2404− toJSON (ConstantScoreFilter csFilter boost) =
2405− object ["constant_score" .= object ["filter" .= csFilter
2406− , "boost" .= boost]]
2407−
2408− toJSON (ConstantScoreQuery query boost) =
2409− object ["constant_score" .= object ["query" .= query
2410− , "boost" .= boost]]
2411−
2412− toJSON (QueryDisMaxQuery disMaxQuery) =
2413− object [ "dis_max" .= disMaxQuery ]
2414−
2415− toJSON (QueryFilteredQuery qFilteredQuery) =
2416− object [ "filtered" .= qFilteredQuery ]
2417−
2418− toJSON (QueryFuzzyLikeThisQuery fuzzyQuery) =
2419− object [ "fuzzy_like_this" .= fuzzyQuery ]
2420−
2421− toJSON (QueryFuzzyLikeFieldQuery fuzzyFieldQuery) =
2422− object [ "fuzzy_like_this_field" .= fuzzyFieldQuery ]
2423−
2424− toJSON (QueryFuzzyQuery fuzzyQuery) =
2425− object [ "fuzzy" .= fuzzyQuery ]
2426−
2427− toJSON (QueryHasChildQuery childQuery) =
2428− object [ "has_child" .= childQuery ]
2429−
2430− toJSON (QueryHasParentQuery parentQuery) =
2431− object [ "has_parent" .= parentQuery ]
2432−
2433− toJSON (QueryIndicesQuery qIndicesQuery) =
2434− object [ "indices" .= qIndicesQuery ]
2435−
2436− toJSON (MatchAllQuery boost) =
2437− object [ "match_all" .= omitNulls [ "boost" .= boost ] ]
2438−
2439− toJSON (QueryMoreLikeThisQuery query) =
2440− object [ "more_like_this" .= query ]
2441−
2442− toJSON (QueryMoreLikeThisFieldQuery query) =
2443− object [ "more_like_this_field" .= query ]
2444−
2445− toJSON (QueryNestedQuery query) =
2446− object [ "nested" .= query ]
2447−
2448− toJSON (QueryPrefixQuery query) =
2449− object [ "prefix" .= query ]
2450−
2451− toJSON (QueryRangeQuery query) =
2452− object [ "range" .= query ]
2453−
2454− toJSON (QueryRegexpQuery query) =
2455− object [ "regexp" .= query ]
2456−
2457− toJSON (QuerySimpleQueryStringQuery query) =
2458− object [ "simple_query_string" .= query ]
2459−
2460− toJSON (QueryTemplateQueryInline templateQuery) =
2461− object [ "template" .= templateQuery ]
2462−
2463−instance FromJSON Query where
2464− parseJSON v = withObject "Query" parse v
2465− where parse o = termQuery `taggedWith` "term"
2466− <|> termsQuery `taggedWith` "terms"
2467− <|> idsQuery `taggedWith` "ids"
2468− <|> queryQueryStringQuery `taggedWith` "query_string"
2469− <|> queryMatchQuery `taggedWith` "match"
2470− <|> queryMultiMatchQuery
2471− <|> queryBoolQuery `taggedWith` "bool"
2472− <|> queryBoostingQuery `taggedWith` "boosting"
2473− <|> queryCommonTermsQuery `taggedWith` "common"
2474− <|> constantScoreFilter `taggedWith` "constant_score"
2475− <|> constantScoreQuery `taggedWith` "constant_score"
2476− <|> queryDisMaxQuery `taggedWith` "dis_max"
2477− <|> queryFilteredQuery `taggedWith` "filtered"
2478− <|> queryFuzzyLikeThisQuery `taggedWith` "fuzzy_like_this"
2479− <|> queryFuzzyLikeFieldQuery `taggedWith` "fuzzy_like_this_field"
2480− <|> queryFuzzyQuery `taggedWith` "fuzzy"
2481− <|> queryHasChildQuery `taggedWith` "has_child"
2482− <|> queryHasParentQuery `taggedWith` "has_parent"
2483− <|> queryIndicesQuery `taggedWith` "indices"
2484− <|> matchAllQuery `taggedWith` "match_all"
2485− <|> queryMoreLikeThisQuery `taggedWith` "more_like_this"
2486− <|> queryMoreLikeThisFieldQuery `taggedWith` "more_like_this_field"
2487− <|> queryNestedQuery `taggedWith` "nested"
2488− <|> queryPrefixQuery `taggedWith` "prefix"
2489− <|> queryRangeQuery `taggedWith` "range"
2490− <|> queryRegexpQuery `taggedWith` "regexp"
2491− <|> querySimpleQueryStringQuery `taggedWith` "simple_query_string"
2492− <|> queryTemplateQueryInline `taggedWith` "template"
2493− where taggedWith parser k = parser =<< o .: k
2494− termQuery = fieldTagged $ \(FieldName fn) o ->
2495− TermQuery <$> (Term fn <$> o .: "value") <*> o .:? "boost"
2496− termsQuery o = case HM.toList o of
2497− [(fn, vs)] -> do vals <- parseJSON vs
2498− case vals of
2499− x:xs -> return (TermsQuery fn (x :| xs))
2500− _ -> fail "Expected non empty list of values"
2501− _ -> fail "Expected object with 1 field-named key"
2502− idsQuery o = IdsQuery <$> o .: "type"
2503− <*> o .: "values"
2504− queryQueryStringQuery = pure . QueryQueryStringQuery
2505− queryMatchQuery = pure . QueryMatchQuery
2506− queryMultiMatchQuery = QueryMultiMatchQuery <$> parseJSON v
2507− queryBoolQuery = pure . QueryBoolQuery
2508− queryBoostingQuery = pure . QueryBoostingQuery
2509− queryCommonTermsQuery = pure . QueryCommonTermsQuery
2510− constantScoreFilter o = case HM.lookup "filter" o of
2511− Just x -> ConstantScoreFilter <$> parseJSON x
2512− <*> o .: "boost"
2513− _ -> fail "Does not appear to be a ConstantScoreFilter"
2514− constantScoreQuery o = case HM.lookup "query" o of
2515− Just x -> ConstantScoreQuery <$> parseJSON x
2516− <*> o .: "boost"
2517− _ -> fail "Does not appear to be a ConstantScoreQuery"
2518− queryDisMaxQuery = pure . QueryDisMaxQuery
2519− queryFilteredQuery = pure . QueryFilteredQuery
2520− queryFuzzyLikeThisQuery = pure . QueryFuzzyLikeThisQuery
2521− queryFuzzyLikeFieldQuery = pure . QueryFuzzyLikeFieldQuery
2522− queryFuzzyQuery = pure . QueryFuzzyQuery
2523− queryHasChildQuery = pure . QueryHasChildQuery
2524− queryHasParentQuery = pure . QueryHasParentQuery
2525− queryIndicesQuery = pure . QueryIndicesQuery
2526− matchAllQuery o = MatchAllQuery <$> o .:? "boost"
2527− queryMoreLikeThisQuery = pure . QueryMoreLikeThisQuery
2528− queryMoreLikeThisFieldQuery = pure . QueryMoreLikeThisFieldQuery
2529− queryNestedQuery = pure . QueryNestedQuery
2530− queryPrefixQuery = pure . QueryPrefixQuery
2531− queryRangeQuery = pure . QueryRangeQuery
2532− queryRegexpQuery = pure . QueryRegexpQuery
2533− querySimpleQueryStringQuery = pure . QuerySimpleQueryStringQuery
2534− queryTemplateQueryInline = pure . QueryTemplateQueryInline
2535−
2536−
2537−omitNulls :: [(Text, Value)] -> Value
2538−omitNulls = object . filter notNull where
2539− notNull (_, Null) = False
2540− notNull (_, Array a) = (not . V.null) a
2541− notNull _ = True
2542−
2543−
2544−instance ToJSON SimpleQueryStringQuery where
2545− toJSON SimpleQueryStringQuery {..} =
2546− omitNulls (base ++ maybeAdd)
2547− where base = [ "query" .= simpleQueryStringQuery ]
2548− maybeAdd = [ "fields" .= simpleQueryStringField
2549− , "default_operator" .= simpleQueryStringOperator
2550− , "analyzer" .= simpleQueryStringAnalyzer
2551− , "flags" .= simpleQueryStringFlags
2552− , "lowercase_expanded_terms" .= simpleQueryStringLowercaseExpanded
2553− , "locale" .= simpleQueryStringLocale ]
2554−
2555−instance FromJSON SimpleQueryStringQuery where
2556− parseJSON = withObject "SimpleQueryStringQuery" parse
2557− where parse o = SimpleQueryStringQuery <$> o .: "query"
2558− <*> o .:? "fields"
2559− <*> o .:? "default_operator"
2560− <*> o .:? "analyzer"
2561− <*> (parseFlags <$> o .:? "flags")
2562− <*> o .:? "lowercase_expanded_terms"
2563− <*> o .:? "locale"
2564− parseFlags (Just (x:xs)) = Just (x :| xs)
2565− parseFlags _ = Nothing
2566−
2567−instance ToJSON FieldOrFields where
2568− toJSON (FofField fieldName) =
2569− toJSON fieldName
2570− toJSON (FofFields fieldNames) =
2571− toJSON fieldNames
2572−
2573−instance FromJSON FieldOrFields where
2574− parseJSON v = FofField <$> parseJSON v
2575− <|> FofFields <$> (parseNEJSON =<< parseJSON v)
2576−
2577−instance ToJSON SimpleQueryFlag where
2578− toJSON SimpleQueryAll = "ALL"
2579− toJSON SimpleQueryNone = "NONE"
2580− toJSON SimpleQueryAnd = "AND"
2581− toJSON SimpleQueryOr = "OR"
2582− toJSON SimpleQueryPrefix = "PREFIX"
2583− toJSON SimpleQueryPhrase = "PHRASE"
2584− toJSON SimpleQueryPrecedence = "PRECEDENCE"
2585− toJSON SimpleQueryEscape = "ESCAPE"
2586− toJSON SimpleQueryWhitespace = "WHITESPACE"
2587− toJSON SimpleQueryFuzzy = "FUZZY"
2588− toJSON SimpleQueryNear = "NEAR"
2589− toJSON SimpleQuerySlop = "SLOP"
2590−
2591−instance FromJSON SimpleQueryFlag where
2592− parseJSON = withText "SimpleQueryFlag" parse
2593− where parse "ALL" = pure SimpleQueryAll
2594− parse "NONE" = pure SimpleQueryNone
2595− parse "AND" = pure SimpleQueryAnd
2596− parse "OR" = pure SimpleQueryOr
2597− parse "PREFIX" = pure SimpleQueryPrefix
2598− parse "PHRASE" = pure SimpleQueryPhrase
2599− parse "PRECEDENCE" = pure SimpleQueryPrecedence
2600− parse "ESCAPE" = pure SimpleQueryEscape
2601− parse "WHITESPACE" = pure SimpleQueryWhitespace
2602− parse "FUZZY" = pure SimpleQueryFuzzy
2603− parse "NEAR" = pure SimpleQueryNear
2604− parse "SLOP" = pure SimpleQuerySlop
2605− parse f = fail ("Unexpected SimpleQueryFlag: " <> show f)
2606−
2607−instance ToJSON RegexpQuery where
2608− toJSON (RegexpQuery (FieldName rqQueryField)
2609− (Regexp regexpQueryQuery) rqQueryFlags
2610− rqQueryBoost) =
2611− object [ rqQueryField .= omitNulls base ]
2612− where base = [ "value" .= regexpQueryQuery
2613− , "flags" .= rqQueryFlags
2614− , "boost" .= rqQueryBoost ]
2615−
2616−instance FromJSON RegexpQuery where
2617− parseJSON = withObject "RegexpQuery" parse
2618− where parse = fieldTagged $ \fn o ->
2619− RegexpQuery fn
2620− <$> o .: "value"
2621− <*> o .: "flags"
2622− <*> o .:? "boost"
2623−
2624−instance ToJSON QueryStringQuery where
2625− toJSON (QueryStringQuery qsQueryString
2626− qsDefaultField qsOperator
2627− qsAnalyzer qsAllowWildcard
2628− qsLowercaseExpanded qsEnablePositionIncrements
2629− qsFuzzyMaxExpansions qsFuzziness
2630− qsFuzzyPrefixLength qsPhraseSlop
2631− qsBoost qsAnalyzeWildcard
2632− qsGeneratePhraseQueries qsMinimumShouldMatch
2633− qsLenient qsLocale) =
2634− omitNulls base
2635− where
2636− base = [ "query" .= qsQueryString
2637− , "default_field" .= qsDefaultField
2638− , "default_operator" .= qsOperator
2639− , "analyzer" .= qsAnalyzer
2640− , "allow_leading_wildcard" .= qsAllowWildcard
2641− , "lowercase_expanded_terms" .= qsLowercaseExpanded
2642− , "enable_position_increments" .= qsEnablePositionIncrements
2643− , "fuzzy_max_expansions" .= qsFuzzyMaxExpansions
2644− , "fuzziness" .= qsFuzziness
2645− , "fuzzy_prefix_length" .= qsFuzzyPrefixLength
2646− , "phrase_slop" .= qsPhraseSlop
2647− , "boost" .= qsBoost
2648− , "analyze_wildcard" .= qsAnalyzeWildcard
2649− , "auto_generate_phrase_queries" .= qsGeneratePhraseQueries
2650− , "minimum_should_match" .= qsMinimumShouldMatch
2651− , "lenient" .= qsLenient
2652− , "locale" .= qsLocale ]
2653−
2654−instance FromJSON QueryStringQuery where
2655− parseJSON = withObject "QueryStringQuery" parse
2656− where parse o = QueryStringQuery
2657− <$> o .: "query"
2658− <*> o .:? "default_field"
2659− <*> o .:? "default_operator"
2660− <*> o .:? "analyzer"
2661− <*> o .:? "allow_leading_wildcard"
2662− <*> o .:? "lowercase_expanded_terms"
2663− <*> o .:? "enable_position_increments"
2664− <*> o .:? "fuzzy_max_expansions"
2665− <*> o .:? "fuzziness"
2666− <*> o .:? "fuzzy_prefix_length"
2667− <*> o .:? "phrase_slop"
2668− <*> o .:? "boost"
2669− <*> o .:? "analyze_wildcard"
2670− <*> o .:? "auto_generate_phrase_queries"
2671− <*> o .:? "minimum_should_match"
2672− <*> o .:? "lenient"
2673− <*> o .:? "locale"
2674−
2675−instance ToJSON RangeQuery where
2676− toJSON (RangeQuery (FieldName fieldName) range boost) =
2677− object [ fieldName .= object conjoined ]
2678− where conjoined = [ "boost" .= boost ] ++ (rangeValueToPair range)
2679−
2680−instance FromJSON RangeQuery where
2681− parseJSON = withObject "RangeQuery" parse
2682− where parse = fieldTagged $ \fn o ->
2683− RangeQuery fn
2684− <$> parseJSON (Object o)
2685− <*> o .: "boost"
2686−
2687−instance FromJSON RangeValue where
2688− parseJSON = withObject "RangeValue" parse
2689− where parse o = parseDate o
2690− <|> parseDouble o
2691− parseDate o = do lt <- o .:? "lt"
2692− lte <- o .:? "lte"
2693− gt <- o .:? "gt"
2694− gte <- o .:? "gte"
2695− case (lt, lte, gt, gte) of
2696− (Just a, _, Just b, _) -> return (RangeDateGtLt (GreaterThanD b) (LessThanD a))
2697− (Just a, _, _, Just b)-> return (RangeDateGteLt (GreaterThanEqD b) (LessThanD a))
2698− (_, Just a, Just b, _)-> return (RangeDateGtLte (GreaterThanD b) (LessThanEqD a))
2699− (_, Just a, _, Just b)-> return (RangeDateGteLte (GreaterThanEqD b) (LessThanEqD a))
2700− (_, _, Just a, _)-> return (RangeDateGt (GreaterThanD a))
2701− (Just a, _, _, _)-> return (RangeDateLt (LessThanD a))
2702− (_, _, _, Just a)-> return (RangeDateGte (GreaterThanEqD a))
2703− (_, Just a, _, _)-> return (RangeDateLte (LessThanEqD a))
2704− (Nothing, Nothing, Nothing, Nothing) -> mzero
2705− parseDouble o = do lt <- o .:? "lt"
2706− lte <- o .:? "lte"
2707− gt <- o .:? "gt"
2708− gte <- o .:? "gte"
2709− case (lt, lte, gt, gte) of
2710− (Just a, _, Just b, _) -> return (RangeDoubleGtLt (GreaterThan b) (LessThan a))
2711− (Just a, _, _, Just b)-> return (RangeDoubleGteLt (GreaterThanEq b) (LessThan a))
2712− (_, Just a, Just b, _)-> return (RangeDoubleGtLte (GreaterThan b) (LessThanEq a))
2713− (_, Just a, _, Just b)-> return (RangeDoubleGteLte (GreaterThanEq b) (LessThanEq a))
2714− (_, _, Just a, _)-> return (RangeDoubleGt (GreaterThan a))
2715− (Just a, _, _, _)-> return (RangeDoubleLt (LessThan a))
2716− (_, _, _, Just a)-> return (RangeDoubleGte (GreaterThanEq a))
2717− (_, Just a, _, _)-> return (RangeDoubleLte (LessThanEq a))
2718− (Nothing, Nothing, Nothing, Nothing) -> mzero
2719−
2720−instance ToJSON PrefixQuery where
2721− toJSON (PrefixQuery (FieldName fieldName) queryValue boost) =
2722− object [ fieldName .= omitNulls base ]
2723− where base = [ "value" .= queryValue
2724− , "boost" .= boost ]
2725−
2726−instance FromJSON PrefixQuery where
2727− parseJSON = withObject "PrefixQuery" parse
2728− where parse = fieldTagged $ \fn o ->
2729− PrefixQuery fn
2730− <$> o .: "value"
2731− <*> o .:? "boost"
2732−
2733−instance ToJSON NestedQuery where
2734− toJSON (NestedQuery nqPath nqScoreType nqQuery) =
2735− object [ "path" .= nqPath
2736− , "score_mode" .= nqScoreType
2737− , "query" .= nqQuery ]
2738−
2739−instance FromJSON NestedQuery where
2740− parseJSON = withObject "NestedQuery" parse
2741− where parse o = NestedQuery
2742− <$> o .: "path"
2743− <*> o .: "score_mode"
2744− <*> o .: "query"
2745−
2746−instance ToJSON MoreLikeThisFieldQuery where
2747− toJSON (MoreLikeThisFieldQuery text (FieldName fieldName)
2748− percent mtf mqt stopwords mindf maxdf
2749− minwl maxwl boostTerms boost analyzer) =
2750− object [ fieldName .= omitNulls base ]
2751− where base = [ "like_text" .= text
2752− , "percent_terms_to_match" .= percent
2753− , "min_term_freq" .= mtf
2754− , "max_query_terms" .= mqt
2755− , "stop_words" .= stopwords
2756− , "min_doc_freq" .= mindf
2757− , "max_doc_freq" .= maxdf
2758− , "min_word_length" .= minwl
2759− , "max_word_length" .= maxwl
2760− , "boost_terms" .= boostTerms
2761− , "boost" .= boost
2762− , "analyzer" .= analyzer ]
2763−
2764−instance FromJSON MoreLikeThisFieldQuery where
2765− parseJSON = withObject "MoreLikeThisFieldQuery" parse
2766− where parse = fieldTagged $ \fn o ->
2767− MoreLikeThisFieldQuery
2768− <$> o .: "like_text"
2769− <*> pure fn
2770− <*> o .:? "percent_terms_to_match"
2771− <*> o .:? "min_term_freq"
2772− <*> o .:? "max_query_terms"
2773− -- <*> (optionalNE =<< o .:? "stop_words")
2774− <*> o .:? "stop_words"
2775− <*> o .:? "min_doc_freq"
2776− <*> o .:? "max_doc_freq"
2777− <*> o .:? "min_word_length"
2778− <*> o .:? "max_word_length"
2779− <*> o .:? "boost_terms"
2780− <*> o .:? "boost"
2781− <*> o .:? "analyzer"
2782− -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
2783−
2784−instance ToJSON MoreLikeThisQuery where
2785− toJSON (MoreLikeThisQuery text fields percent
2786− mtf mqt stopwords mindf maxdf
2787− minwl maxwl boostTerms boost analyzer) =
2788− omitNulls base
2789− where base = [ "like_text" .= text
2790− , "fields" .= fields
2791− , "percent_terms_to_match" .= percent
2792− , "min_term_freq" .= mtf
2793− , "max_query_terms" .= mqt
2794− , "stop_words" .= stopwords
2795− , "min_doc_freq" .= mindf
2796− , "max_doc_freq" .= maxdf
2797− , "min_word_length" .= minwl
2798− , "max_word_length" .= maxwl
2799− , "boost_terms" .= boostTerms
2800− , "boost" .= boost
2801− , "analyzer" .= analyzer ]
2802−
2803−instance FromJSON MoreLikeThisQuery where
2804− parseJSON = withObject "MoreLikeThisQuery" parse
2805− where parse o = MoreLikeThisQuery
2806− <$> o .: "like_text"
2807− -- <*> (optionalNE =<< o .:? "fields")
2808− <*> o .:? "fields"
2809− <*> o .:? "percent_terms_to_match"
2810− <*> o .:? "min_term_freq"
2811− <*> o .:? "max_query_terms"
2812− -- <*> (optionalNE =<< o .:? "stop_words")
2813− <*> o .:? "stop_words"
2814− <*> o .:? "min_doc_freq"
2815− <*> o .:? "max_doc_freq"
2816− <*> o .:? "min_word_length"
2817− <*> o .:? "max_word_length"
2818− <*> o .:? "boost_terms"
2819− <*> o .:? "boost"
2820− <*> o .:? "analyzer"
2821− -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
2822−
2823−instance ToJSON IndicesQuery where
2824− toJSON (IndicesQuery indices query noMatch) =
2825− omitNulls [ "indices" .= indices
2826− , "no_match_query" .= noMatch
2827− , "query" .= query ]
2828−
2829−instance FromJSON IndicesQuery where
2830− parseJSON = withObject "IndicesQuery" parse
2831− where parse o = IndicesQuery
2832− <$> o .:? "indices" .!= []
2833− <*> o .: "query"
2834− <*> o .:? "no_match_query"
2835−
2836−instance ToJSON HasParentQuery where
2837− toJSON (HasParentQuery queryType query scoreType) =
2838− omitNulls [ "parent_type" .= queryType
2839− , "score_type" .= scoreType
2840− , "query" .= query ]
2841−
2842−instance FromJSON HasParentQuery where
2843− parseJSON = withObject "HasParentQuery" parse
2844− where parse o = HasParentQuery
2845− <$> o .: "parent_type"
2846− <*> o .: "query"
2847− <*> o .:? "score_type"
2848−
2849−instance ToJSON HasChildQuery where
2850− toJSON (HasChildQuery queryType query scoreType) =
2851− omitNulls [ "query" .= query
2852− , "score_type" .= scoreType
2853− , "type" .= queryType ]
2854−
2855−instance FromJSON HasChildQuery where
2856− parseJSON = withObject "HasChildQuery" parse
2857− where parse o = HasChildQuery
2858− <$> o .: "type"
2859− <*> o .: "query"
2860− <*> o .:? "score_type"
2861−
2862−instance ToJSON FuzzyQuery where
2863− toJSON (FuzzyQuery (FieldName fieldName) queryText
2864− prefixLength maxEx fuzziness boost) =
2865− object [ fieldName .= omitNulls base ]
2866− where base = [ "value" .= queryText
2867− , "fuzziness" .= fuzziness
2868− , "prefix_length" .= prefixLength
2869− , "boost" .= boost
2870− , "max_expansions" .= maxEx ]
2871−
2872−instance FromJSON FuzzyQuery where
2873− parseJSON = withObject "FuzzyQuery" parse
2874− where parse = fieldTagged $ \fn o ->
2875− FuzzyQuery fn
2876− <$> o .: "value"
2877− <*> o .: "prefix_length"
2878− <*> o .: "max_expansions"
2879− <*> o .: "fuzziness"
2880− <*> o .:? "boost"
2881−
2882−instance ToJSON FuzzyLikeFieldQuery where
2883− toJSON (FuzzyLikeFieldQuery (FieldName fieldName)
2884− fieldText maxTerms ignoreFreq fuzziness prefixLength
2885− boost analyzer) =
2886− object [ fieldName .=
2887− omitNulls [ "like_text" .= fieldText
2888− , "max_query_terms" .= maxTerms
2889− , "ignore_tf" .= ignoreFreq
2890− , "fuzziness" .= fuzziness
2891− , "prefix_length" .= prefixLength
2892− , "analyzer" .= analyzer
2893− , "boost" .= boost ]]
2894−
2895−instance FromJSON FuzzyLikeFieldQuery where
2896− parseJSON = withObject "FuzzyLikeFieldQuery" parse
2897− where parse = fieldTagged $ \fn o ->
2898− FuzzyLikeFieldQuery fn
2899− <$> o .: "like_text"
2900− <*> o .: "max_query_terms"
2901− <*> o .: "ignore_tf"
2902− <*> o .: "fuzziness"
2903− <*> o .: "prefix_length"
2904− <*> o .: "boost"
2905− <*> o .:? "analyzer"
2906−
2907−instance ToJSON FuzzyLikeThisQuery where
2908− toJSON (FuzzyLikeThisQuery fields text maxTerms
2909− ignoreFreq fuzziness prefixLength boost analyzer) =
2910− omitNulls base
2911− where base = [ "fields" .= fields
2912− , "like_text" .= text
2913− , "max_query_terms" .= maxTerms
2914− , "ignore_tf" .= ignoreFreq
2915− , "fuzziness" .= fuzziness
2916− , "prefix_length" .= prefixLength
2917− , "analyzer" .= analyzer
2918− , "boost" .= boost ]
2919−
2920−instance FromJSON FuzzyLikeThisQuery where
2921− parseJSON = withObject "FuzzyLikeThisQuery" parse
2922− where parse o = FuzzyLikeThisQuery
2923− <$> o .:? "fields" .!= []
2924− <*> o .: "like_text"
2925− <*> o .: "max_query_terms"
2926− <*> o .: "ignore_tf"
2927− <*> o .: "fuzziness"
2928− <*> o .: "prefix_length"
2929− <*> o .: "boost"
2930− <*> o .:? "analyzer"
2931−
2932−instance ToJSON FilteredQuery where
2933− toJSON (FilteredQuery query fFilter) =
2934− object [ "query" .= query
2935− , "filter" .= fFilter ]
2936−
2937−instance FromJSON FilteredQuery where
2938− parseJSON = withObject "FilteredQuery" parse
2939− where parse o = FilteredQuery
2940− <$> o .: "query"
2941− <*> o .: "filter"
2942−
2943−instance ToJSON DisMaxQuery where
2944− toJSON (DisMaxQuery queries tiebreaker boost) =
2945− omitNulls base
2946− where base = [ "queries" .= queries
2947− , "boost" .= boost
2948− , "tie_breaker" .= tiebreaker ]
2949−
2950−instance FromJSON DisMaxQuery where
2951− parseJSON = withObject "DisMaxQuery" parse
2952− where parse o = DisMaxQuery
2953− <$> o .:? "queries" .!= []
2954− <*> o .: "tie_breaker"
2955− <*> o .:? "boost"
2956−
2957−instance ToJSON CommonTermsQuery where
2958− toJSON (CommonTermsQuery (FieldName fieldName)
2959− (QueryString query) cf lfo hfo msm
2960− boost analyzer disableCoord) =
2961− object [fieldName .= omitNulls base ]
2962− where base = [ "query" .= query
2963− , "cutoff_frequency" .= cf
2964− , "low_freq_operator" .= lfo
2965− , "minimum_should_match" .= msm
2966− , "boost" .= boost
2967− , "analyzer" .= analyzer
2968− , "disable_coord" .= disableCoord
2969− , "high_freq_operator" .= hfo ]
2970−
2971−instance FromJSON CommonTermsQuery where
2972− parseJSON = withObject "CommonTermsQuery" parse
2973− where parse = fieldTagged $ \fn o ->
2974− CommonTermsQuery fn
2975− <$> o .: "query"
2976− <*> o .: "cutoff_frequency"
2977− <*> o .: "low_freq_operator"
2978− <*> o .: "high_freq_operator"
2979− <*> o .:? "minimum_should_match"
2980− <*> o .:? "boost"
2981− <*> o .:? "analyzer"
2982− <*> o .:? "disable_coord"
2983−
2984−instance ToJSON CommonMinimumMatch where
2985− toJSON (CommonMinimumMatch mm) = toJSON mm
2986− toJSON (CommonMinimumMatchHighLow (MinimumMatchHighLow lowF highF)) =
2987− object [ "low_freq" .= lowF
2988− , "high_freq" .= highF ]
2989−
2990−instance FromJSON CommonMinimumMatch where
2991− parseJSON v = parseMinimum v
2992− <|> parseMinimumHighLow v
2993− where parseMinimum = fmap CommonMinimumMatch . parseJSON
2994− parseMinimumHighLow = fmap CommonMinimumMatchHighLow . withObject "CommonMinimumMatchHighLow" (\o ->
2995− MinimumMatchHighLow
2996− <$> o .: "low_freq"
2997− <*> o .: "high_freq")
2998−
2999−
3000−instance ToJSON BoostingQuery where
3001− toJSON (BoostingQuery bqPositiveQuery bqNegativeQuery bqNegativeBoost) =
3002− object [ "positive" .= bqPositiveQuery
3003− , "negative" .= bqNegativeQuery
3004− , "negative_boost" .= bqNegativeBoost ]
3005−
3006−instance FromJSON BoostingQuery where
3007− parseJSON = withObject "BoostingQuery" parse
3008− where parse o = BoostingQuery
3009− <$> o .: "positive"
3010− <*> o .: "negative"
3011− <*> o .: "negative_boost"
3012−
3013−instance ToJSON BoolQuery where
3014− toJSON (BoolQuery mustM notM shouldM bqMin boost disableCoord) =
3015− omitNulls base
3016− where base = [ "must" .= mustM
3017− , "must_not" .= notM
3018− , "should" .= shouldM
3019− , "minimum_should_match" .= bqMin
3020− , "boost" .= boost
3021− , "disable_coord" .= disableCoord ]
3022−
3023−instance FromJSON BoolQuery where
3024− parseJSON = withObject "BoolQuery" parse
3025− where parse o = BoolQuery
3026− <$> o .:? "must" .!= []
3027− <*> o .:? "must_not" .!= []
3028− <*> o .:? "should" .!= []
3029− <*> o .:? "minimum_should_match"
3030− <*> o .:? "boost"
3031− <*> o .:? "disable_coord"
3032−
3033−instance ToJSON MatchQuery where
3034− toJSON (MatchQuery (FieldName fieldName)
3035− (QueryString mqQueryString) booleanOperator
3036− zeroTermsQuery cutoffFrequency matchQueryType
3037− analyzer maxExpansions lenient boost) =
3038− object [ fieldName .= omitNulls base ]
3039− where base = [ "query" .= mqQueryString
3040− , "operator" .= booleanOperator
3041− , "zero_terms_query" .= zeroTermsQuery
3042− , "cutoff_frequency" .= cutoffFrequency
3043− , "type" .= matchQueryType
3044− , "analyzer" .= analyzer
3045− , "max_expansions" .= maxExpansions
3046− , "lenient" .= lenient
3047− , "boost" .= boost ]
3048−
3049−instance FromJSON MatchQuery where
3050− parseJSON = withObject "MatchQuery" parse
3051− where parse = fieldTagged $ \fn o ->
3052− MatchQuery fn
3053− <$> o .: "query"
3054− <*> o .: "operator"
3055− <*> o .: "zero_terms_query"
3056− <*> o .:? "cutoff_frequency"
3057− <*> o .:? "type"
3058− <*> o .:? "analyzer"
3059− <*> o .:? "max_expansions"
3060− <*> o .:? "lenient"
3061− <*> o .:? "boost"
3062−
3063−instance ToJSON MultiMatchQuery where
3064− toJSON (MultiMatchQuery fields (QueryString query) boolOp
3065− ztQ tb mmqt cf analyzer maxEx lenient) =
3066− object ["multi_match" .= omitNulls base]
3067− where base = [ "fields" .= fmap toJSON fields
3068− , "query" .= query
3069− , "operator" .= boolOp
3070− , "zero_terms_query" .= ztQ
3071− , "tie_breaker" .= tb
3072− , "type" .= mmqt
3073− , "cutoff_frequency" .= cf
3074− , "analyzer" .= analyzer
3075− , "max_expansions" .= maxEx
3076− , "lenient" .= lenient ]
3077−
3078−instance FromJSON MultiMatchQuery where
3079− parseJSON = withObject "MultiMatchQuery" parse
3080− where parse raw = do o <- raw .: "multi_match"
3081− MultiMatchQuery
3082− <$> o .:? "fields" .!= []
3083− <*> o .: "query"
3084− <*> o .: "operator"
3085− <*> o .: "zero_terms_query"
3086− <*> o .:? "tie_breaker"
3087− <*> o .:? "type"
3088− <*> o .:? "cutoff_frequency"
3089− <*> o .:? "analyzer"
3090− <*> o .:? "max_expansions"
3091− <*> o .:? "lenient"
3092−
3093−instance ToJSON MultiMatchQueryType where
3094− toJSON MultiMatchBestFields = "best_fields"
3095− toJSON MultiMatchMostFields = "most_fields"
3096− toJSON MultiMatchCrossFields = "cross_fields"
3097− toJSON MultiMatchPhrase = "phrase"
3098− toJSON MultiMatchPhrasePrefix = "phrase_prefix"
3099−
3100−instance FromJSON MultiMatchQueryType where
3101− parseJSON = withText "MultiMatchPhrasePrefix" parse
3102− where parse "best_fields" = pure MultiMatchBestFields
3103− parse "most_fields" = pure MultiMatchMostFields
3104− parse "cross_fields" = pure MultiMatchCrossFields
3105− parse "phrase" = pure MultiMatchPhrase
3106− parse "phrase_prefix" = pure MultiMatchPhrasePrefix
3107− parse t = fail ("Unexpected MultiMatchPhrasePrefix: " <> show t)
3108−
3109−instance ToJSON BooleanOperator where
3110− toJSON And = String "and"
3111− toJSON Or = String "or"
3112−
3113−instance FromJSON BooleanOperator where
3114− parseJSON = withText "BooleanOperator" parse
3115− where parse "and" = pure And
3116− parse "or" = pure Or
3117− parse o = fail ("Unexpected BooleanOperator: " <> show o)
3118−
3119−instance ToJSON ZeroTermsQuery where
3120− toJSON ZeroTermsNone = String "none"
3121− toJSON ZeroTermsAll = String "all"
3122−
3123−instance FromJSON ZeroTermsQuery where
3124− parseJSON = withText "ZeroTermsQuery" parse
3125− where parse "none" = pure ZeroTermsNone
3126− parse "all" = pure ZeroTermsAll
3127− parse q = fail ("Unexpected ZeroTermsQuery: " <> show q)
3128−
3129−instance ToJSON MatchQueryType where
3130− toJSON MatchPhrase = "phrase"
3131− toJSON MatchPhrasePrefix = "phrase_prefix"
3132−
3133−instance FromJSON MatchQueryType where
3134− parseJSON = withText "MatchQueryType" parse
3135− where parse "phrase" = pure MatchPhrase
3136− parse "phrase_prefix" = pure MatchPhrasePrefix
3137− parse t = fail ("Unexpected MatchQueryType: " <> show t)
3138−
3139−instance FromJSON Status where
3140− parseJSON (Object v) = Status <$>
3141− v .:? "ok" <*>
3142− (v .:? "status" .!= 200) <*>
3143− v .: "name" <*>
3144− v .: "version" <*>
3145− v .: "tagline"
3146− parseJSON _ = empty
3147−
3148−
3149−instance ToJSON IndexSettings where
3150− toJSON (IndexSettings s r) = object ["settings" .=
3151− object ["index" .=
3152− object ["number_of_shards" .= s, "number_of_replicas" .= r]
3153− ]
3154− ]
3155−
3156−instance FromJSON IndexSettings where
3157− parseJSON = withObject "IndexSettings" parse
3158− where parse o = do s <- o .: "settings"
3159− i <- s .: "index"
3160− IndexSettings <$> i .: "number_of_shards"
3161− <*> i .: "number_of_replicas"
3162−
3163−instance ToJSON UpdatableIndexSetting where
3164− toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
3165− toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
3166− toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
3167− toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
3168− toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
3169− toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
3170− toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
3171− toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
3172− toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
3173− toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
3174− toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
3175− toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
3176− toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
3177− toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
3178− toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
3179− toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
3180− toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
3181− toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
3182− toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
3183− toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
3184− toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
3185− toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
3186− toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
3187− toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
3188− toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
3189− toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
3190− toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
3191− toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
3192−
3193−instance FromJSON UpdatableIndexSetting where
3194− parseJSON = withObject "UpdatableIndexSetting" parse
3195− where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
3196− <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
3197− <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
3198− <|> indexConcurrency `taggedAt` ["index", "concurrency"]
3199− <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
3200− <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
3201− <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
3202− <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
3203− <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
3204− <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
3205− <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
3206− <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
3207− <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
3208− <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
3209− <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
3210− <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
3211− <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
3212− <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
3213− <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
3214− <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
3215− <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
3216− <|> compoundFormat `taggedAt` ["index", "compound_format"]
3217− <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
3218− <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
3219− <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
3220− <|> blocksRead `taggedAt` ["blocks", "read"]
3221− <|> blocksWrite `taggedAt` ["blocks", "write"]
3222− <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
3223− where taggedAt f ks = taggedAt' f (Object o) ks
3224− taggedAt' f v [] = f =<< (parseJSON v <|> (parseJSON (unStringlyTypeJSON v)))
3225− taggedAt' f v (k:ks) = withObject "Object" (\o -> do v' <- o .: k
3226− taggedAt' f v' ks) v
3227− numberOfReplicas = pure . NumberOfReplicas
3228− autoExpandReplicas = pure . AutoExpandReplicas
3229− refreshInterval = pure . RefreshInterval . ndtJSON
3230− indexConcurrency = pure . IndexConcurrency
3231− failOnMergeFailure = pure . FailOnMergeFailure
3232− translogFlushThresholdOps = pure . TranslogFlushThresholdOps
3233− translogFlushThresholdSize = pure . TranslogFlushThresholdSize
3234− translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
3235− translogDisableFlush = pure . TranslogDisableFlush
3236− cacheFilterMaxSize = pure . CacheFilterMaxSize
3237− cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
3238− gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
3239− routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
3240− routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
3241− routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
3242− routingAllocationEnable = pure . RoutingAllocationEnable
3243− routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
3244− recoveryInitialShards = pure . RecoveryInitialShards
3245− gcDeletes = pure . GCDeletes . ndtJSON
3246− ttlDisablePurge = pure . TTLDisablePurge
3247− translogFSType = pure . TranslogFSType
3248− compoundFormat = pure . IndexCompoundFormat
3249− compoundOnFlush = pure . IndexCompoundOnFlush
3250− warmerEnabled = pure . WarmerEnabled
3251− blocksReadOnly = pure . BlocksReadOnly
3252− blocksRead = pure . BlocksRead
3253− blocksWrite = pure . BlocksWrite
3254− blocksMetaData = pure . BlocksMetaData
3255−
3256−instance FromJSON IndexSettingsSummary where
3257− parseJSON = withObject "IndexSettingsSummary" parse
3258− where parse o = case HM.toList o of
3259− [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
3260− <$> parseJSON v
3261− <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
3262− _ -> fail "Expected single-key object with index name"
3263− redundant (NumberOfReplicas _) = True
3264− redundant _ = False
3265−
3266−-- | For some reason in several settings APIs, all leaf values get returned
3267−-- as strings. This function attepmts to recover from this for all
3268−-- non-recursive JSON types. If nothing can be done, the value is left alone.
3269−unStringlyTypeJSON :: Value -> Value
3270−unStringlyTypeJSON (String "true") = Bool True
3271−unStringlyTypeJSON (String "false") = Bool False
3272−unStringlyTypeJSON (String "null") = Null
3273−unStringlyTypeJSON v@(String t) = case readMay (T.unpack t) of
3274− Just n -> Number n
3275− Nothing -> v
3276−unStringlyTypeJSON v = v
3277−
3278−
3279−parseSettings :: Object -> Parser [UpdatableIndexSetting]
3280−parseSettings o = do
3281− o' <- o .: "index"
3282− -- slice the index object into singleton hashmaps and try to parse each
3283− parses <- forM (HM.toList o') $ \(k, v) -> do
3284− -- blocks are now nested into the "index" key, which is not how they're serialized
3285− let atRoot = Object (HM.singleton k v)
3286− let atIndex = Object (HM.singleton "index" atRoot)
3287− optional (parseJSON atRoot <|> parseJSON atIndex)
3288− return (catMaybes parses)
3289−
3290−oPath :: ToJSON a => NonEmpty Text -> a -> Value
3291−oPath (k :| []) v = object [k .= v]
3292−oPath (k:| (h:t)) v = object [k .= oPath (h :| t) v]
3293−
3294−attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
3295−attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
3296− | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
3297−
3298−parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
3299−parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
3300− where parse o = case HM.toList o of
3301− [] -> fail "Expected non-empty list of NodeAttrFilters"
3302− x:xs -> DT.mapM (uncurry parse') (x :| xs)
3303− parse' n = withText "Text" $ \t ->
3304− case T.splitOn "," t of
3305− fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
3306− [] -> fail "Expected non-empty list of filter values"
3307−
3308−instance ToJSON ReplicaBounds where
3309− toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
3310− toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
3311− toJSON ReplicasUnbounded = Bool False
3312−
3313−instance FromJSON ReplicaBounds where
3314− parseJSON v = withText "ReplicaBounds" parseText v
3315− <|> withBool "ReplicaBounds" parseBool v
3316− where parseText t = case T.splitOn "-" t of
3317− [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
3318− [a, b] -> ReplicasBounded <$> parseReadText a
3319− <*> parseReadText b
3320− _ -> fail ("Could not parse ReplicaBounds: " <> show t)
3321− parseBool False = pure ReplicasUnbounded
3322− parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
3323−
3324−instance ToJSON AllocationPolicy where
3325− toJSON AllocAll = String "all"
3326− toJSON AllocPrimaries = String "primaries"
3327− toJSON AllocNewPrimaries = String "new_primaries"
3328− toJSON AllocNone = String "none"
3329−
3330−instance FromJSON AllocationPolicy where
3331− parseJSON = withText "AllocationPolicy" parse
3332− where parse "all" = pure AllocAll
3333− parse "primaries" = pure AllocPrimaries
3334− parse "new_primaries" = pure AllocNewPrimaries
3335− parse "none" = pure AllocNone
3336− parse t = fail ("Invlaid AllocationPolicy: " <> show t)
3337−
3338−instance ToJSON InitialShardCount where
3339− toJSON QuorumShards = String "quorum"
3340− toJSON QuorumMinus1Shards = String "quorum-1"
3341− toJSON FullShards = String "full"
3342− toJSON FullMinus1Shards = String "full-1"
3343− toJSON (ExplicitShards x) = toJSON x
3344−
3345−instance FromJSON InitialShardCount where
3346− parseJSON v = withText "InitialShardCount" parseText v
3347− <|> ExplicitShards <$> parseJSON v
3348− where parseText "quorum" = pure QuorumShards
3349− parseText "quorum-1" = pure QuorumMinus1Shards
3350− parseText "full" = pure FullShards
3351− parseText "full-1" = pure FullMinus1Shards
3352− parseText _ = mzero
3353−
3354−instance ToJSON FSType where
3355− toJSON FSSimple = "simple"
3356− toJSON FSBuffered = "buffered"
3357−
3358−instance FromJSON FSType where
3359− parseJSON = withText "FSType" parse
3360− where parse "simple" = pure FSSimple
3361− parse "buffered" = pure FSBuffered
3362− parse t = fail ("Invalid FSType: " <> show t)
3363−
3364−instance ToJSON CompoundFormat where
3365− toJSON (CompoundFileFormat x) = Bool x
3366− toJSON (MergeSegmentVsTotalIndex x) = toJSON x
3367−
3368−instance FromJSON CompoundFormat where
3369− parseJSON v = CompoundFileFormat <$> parseJSON v
3370− <|> MergeSegmentVsTotalIndex <$> parseJSON v
3371−
3372−instance ToJSON NominalDiffTimeJSON where
3373− toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
3374−
3375−instance FromJSON NominalDiffTimeJSON where
3376− parseJSON = withText "NominalDiffTime" parse
3377− where parse t = case T.takeEnd 1 t of
3378− "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
3379− _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
3380−
3381−instance ToJSON IndexTemplate where
3382− toJSON (IndexTemplate p s m) = merge
3383− (object [ "template" .= p
3384− , "mappings" .= foldl' merge (object []) m
3385− ])
3386− (toJSON s)
3387− where
3388− merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
3389− merge o Null = o
3390− merge _ _ = undefined
3391−
3392−instance (FromJSON a) => FromJSON (EsResult a) where
3393− parseJSON jsonVal@(Object v) = do
3394− found <- v .:? "found" .!= False
3395− fr <- if found
3396− then parseJSON jsonVal
3397− else return Nothing
3398− EsResult <$> v .: "_index" <*>
3399− v .: "_type" <*>
3400− v .: "_id" <*>
3401− pure fr
3402− parseJSON _ = empty
3403−
3404−instance (FromJSON a) => FromJSON (EsResultFound a) where
3405− parseJSON (Object v) = EsResultFound <$>
3406− v .: "_version" <*>
3407− v .: "_source"
3408− parseJSON _ = empty
3409−
3410−instance FromJSON EsError where
3411− parseJSON (Object v) = EsError <$>
3412− v .: "status" <*>
3413− (v .: "error" <|> (v .: "error" >>= (.: "reason")))
3414− parseJSON _ = empty
3415−
3416−instance FromJSON IndexAliasesSummary where
3417− parseJSON = withObject "IndexAliasesSummary" parse
3418− where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
3419− go ixn = withObject "index aliases" $ \ia -> do
3420− aliases <- ia .:? "aliases" .!= mempty
3421− forM (HM.toList aliases) $ \(aName, v) -> do
3422− let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
3423− IndexAliasSummary indexAlias <$> parseJSON v
3424−
3425−
3426−instance ToJSON IndexAliasAction where
3427− toJSON (AddAlias ia opts) = object ["add" .= (iaObj <> optsObj)]
3428− where Object iaObj = toJSON ia
3429− Object optsObj = toJSON opts
3430− toJSON (RemoveAlias ia) = object ["remove" .= iaObj]
3431− where Object iaObj = toJSON ia
3432−
3433−instance ToJSON IndexAlias where
3434− toJSON IndexAlias {..} = object ["index" .= srcIndex
3435− , "alias" .= indexAlias
3436− ]
3437−
3438−instance ToJSON IndexAliasCreate where
3439− toJSON IndexAliasCreate {..} = Object (filterObj <> routingObj)
3440− where filterObj = maybe mempty (HM.singleton "filter" . toJSON) aliasCreateFilter
3441− Object routingObj = maybe (Object mempty) toJSON aliasCreateRouting
3442−
3443−instance ToJSON AliasRouting where
3444− toJSON (AllAliasRouting v) = object ["routing" .= v]
3445− toJSON (GranularAliasRouting srch idx) = object (catMaybes prs)
3446− where prs = [("search_routing" .=) <$> srch
3447− ,("index_routing" .=) <$> idx]
3448−
3449−instance FromJSON AliasRouting where
3450− parseJSON = withObject "AliasRouting" parse
3451− where parse o = parseAll o <|> parseGranular o
3452− parseAll o = AllAliasRouting <$> o .: "routing"
3453− parseGranular o = do
3454− sr <- o .:? "search_routing"
3455− ir <- o .:? "index_routing"
3456− if isNothing sr && isNothing ir
3457− then fail "Both search_routing and index_routing can't be blank"
3458− else return (GranularAliasRouting sr ir)
3459−
3460−instance FromJSON IndexAliasCreate where
3461− parseJSON v = withObject "IndexAliasCreate" parse v
3462− where parse o = IndexAliasCreate <$> optional (parseJSON v)
3463− <*> o .:? "filter"
3464−
3465−instance ToJSON SearchAliasRouting where
3466− toJSON (SearchAliasRouting rvs) = toJSON (T.intercalate "," (routingValue <$> toList rvs))
3467−
3468−instance FromJSON SearchAliasRouting where
3469− parseJSON = withText "SearchAliasRouting" parse
3470− where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
3471−
3472−instance ToJSON Search where
3473− toJSON (Search query sFilter sort searchAggs highlight sTrackSortScores sFrom sSize _ sFields sSource sSuggest) =
3474− omitNulls [ "query" .= query
3475− , "filter" .= sFilter
3476− , "sort" .= sort
3477− , "aggregations" .= searchAggs
3478− , "highlight" .= highlight
3479− , "from" .= sFrom
3480− , "size" .= sSize
3481− , "track_scores" .= sTrackSortScores
3482− , "fields" .= sFields
3483− , "_source" .= sSource
3484− , "suggest" .= sSuggest]
3485−
3486−
3487−instance ToJSON Source where
3488− toJSON NoSource = toJSON False
3489− toJSON (SourcePatterns patterns) = toJSON patterns
3490− toJSON (SourceIncludeExclude incl excl) = object [ "include" .= incl, "exclude" .= excl ]
3491−
3492−instance ToJSON PatternOrPatterns where
3493− toJSON (PopPattern pattern) = toJSON pattern
3494− toJSON (PopPatterns patterns) = toJSON patterns
3495−
3496−instance ToJSON Include where
3497− toJSON (Include patterns) = toJSON patterns
3498−
3499−instance ToJSON Exclude where
3500− toJSON (Exclude patterns) = toJSON patterns
3501−
3502−instance ToJSON Pattern where
3503− toJSON (Pattern pattern) = toJSON pattern
3504−
3505−
3506−instance ToJSON FieldHighlight where
3507− toJSON (FieldHighlight (FieldName fName) (Just fSettings)) =
3508− object [ fName .= fSettings ]
3509− toJSON (FieldHighlight (FieldName fName) Nothing) =
3510− object [ fName .= emptyObject ]
3511−
3512−instance ToJSON Highlights where
3513− toJSON (Highlights global fields) =
3514− omitNulls (("fields" .= fields)
3515− : highlightSettingsPairs global)
3516−
3517−instance ToJSON HighlightSettings where
3518− toJSON hs = omitNulls (highlightSettingsPairs (Just hs))
3519−
3520−highlightSettingsPairs :: Maybe HighlightSettings -> [Pair]
3521−highlightSettingsPairs Nothing = []
3522−highlightSettingsPairs (Just (Plain plh)) = plainHighPairs (Just plh)
3523−highlightSettingsPairs (Just (Postings ph)) = postHighPairs (Just ph)
3524−highlightSettingsPairs (Just (FastVector fvh)) = fastVectorHighPairs (Just fvh)
3525−
3526−
3527−plainHighPairs :: Maybe PlainHighlight -> [Pair]
3528−plainHighPairs Nothing = []
3529−plainHighPairs (Just (PlainHighlight plCom plNonPost)) =
3530− [ "type" .= String "plain"]
3531− ++ commonHighlightPairs plCom
3532− ++ nonPostingsToPairs plNonPost
3533−
3534−postHighPairs :: Maybe PostingsHighlight -> [Pair]
3535−postHighPairs Nothing = []
3536−postHighPairs (Just (PostingsHighlight pCom)) =
3537− ("type" .= String "postings")
3538− : commonHighlightPairs pCom
3539−
3540−fastVectorHighPairs :: Maybe FastVectorHighlight -> [Pair]
3541−fastVectorHighPairs Nothing = []
3542−fastVectorHighPairs (Just
3543− (FastVectorHighlight fvCom fvNonPostSettings fvBoundChars
3544− fvBoundMaxScan fvFragOff fvMatchedFields
3545− fvPhraseLim)) =
3546− [ "type" .= String "fvh"
3547− , "boundary_chars" .= fvBoundChars
3548− , "boundary_max_scan" .= fvBoundMaxScan
3549− , "fragment_offset" .= fvFragOff
3550− , "matched_fields" .= fvMatchedFields
3551− , "phraseLimit" .= fvPhraseLim]
3552− ++ commonHighlightPairs fvCom
3553− ++ nonPostingsToPairs fvNonPostSettings
3554−
3555−deleteSeveral :: (Eq k, Hashable k) => [k] -> HM.HashMap k v -> HM.HashMap k v
3556−deleteSeveral ks hm = foldr HM.delete hm ks
3557−
3558−commonHighlightPairs :: Maybe CommonHighlight -> [Pair]
3559−commonHighlightPairs Nothing = []
3560−commonHighlightPairs (Just (CommonHighlight chScore chForceSource chTag chEncoder
3561− chNoMatchSize chHighlightQuery
3562− chRequireFieldMatch)) =
3563− [ "order" .= chScore
3564− , "force_source" .= chForceSource
3565− , "encoder" .= chEncoder
3566− , "no_match_size" .= chNoMatchSize
3567− , "highlight_query" .= chHighlightQuery
3568− , "require_fieldMatch" .= chRequireFieldMatch]
3569− ++ highlightTagToPairs chTag
3570−
3571−
3572−nonPostingsToPairs :: Maybe NonPostings -> [Pair]
3573−nonPostingsToPairs Nothing = []
3574−nonPostingsToPairs (Just (NonPostings npFragSize npNumOfFrags)) =
3575− [ "fragment_size" .= npFragSize
3576− , "number_of_fragments" .= npNumOfFrags]
3577−
3578−parseNEJSON :: (FromJSON a) => [Value] -> Parser (NonEmpty a)
3579−parseNEJSON [] = fail "Expected non-empty list"
3580−parseNEJSON (x:xs) = DT.mapM parseJSON (x :| xs)
3581−
3582−
3583−instance ToJSON HighlightEncoder where
3584− toJSON DefaultEncoder = String "default"
3585− toJSON HTMLEncoder = String "html"
3586−
3587−highlightTagToPairs :: Maybe HighlightTag -> [Pair]
3588−highlightTagToPairs (Just (TagSchema _)) = [ "scheme" .= String "default"]
3589−highlightTagToPairs (Just (CustomTags (pre, post))) = [ "pre_tags" .= pre
3590− , "post_tags" .= post]
3591−highlightTagToPairs Nothing = []
3592−
3593−instance ToJSON SortSpec where
3594− toJSON (DefaultSortSpec
3595− (DefaultSort (FieldName dsSortFieldName) dsSortOrder dsIgnoreUnmapped
3596− dsSortMode dsMissingSort dsNestedFilter)) =
3597− object [dsSortFieldName .= omitNulls base] where
3598− base = [ "order" .= dsSortOrder
3599− , "ignore_unmapped" .= dsIgnoreUnmapped
3600− , "mode" .= dsSortMode
3601− , "missing" .= dsMissingSort
3602− , "nested_filter" .= dsNestedFilter ]
3603−
3604− toJSON (GeoDistanceSortSpec gdsSortOrder (GeoPoint (FieldName field) gdsLatLon) units) =
3605− object [ "unit" .= units
3606− , field .= gdsLatLon
3607− , "order" .= gdsSortOrder ]
3608−
3609−
3610−instance ToJSON SortOrder where
3611− toJSON Ascending = String "asc"
3612− toJSON Descending = String "desc"
3613−
3614−
3615−instance ToJSON SortMode where
3616− toJSON SortMin = String "min"
3617− toJSON SortMax = String "max"
3618− toJSON SortSum = String "sum"
3619− toJSON SortAvg = String "avg"
3620−
3621−
3622−instance ToJSON Missing where
3623− toJSON LastMissing = String "_last"
3624− toJSON FirstMissing = String "_first"
3625− toJSON (CustomMissing txt) = String txt
3626−
3627−
3628−instance ToJSON ScoreType where
3629− toJSON ScoreTypeMax = "max"
3630− toJSON ScoreTypeAvg = "avg"
3631− toJSON ScoreTypeSum = "sum"
3632− toJSON ScoreTypeNone = "none"
3633−
3634−instance FromJSON ScoreType where
3635− parseJSON = withText "ScoreType" parse
3636− where parse "max" = pure ScoreTypeMax
3637− parse "avg" = pure ScoreTypeAvg
3638− parse "sum" = pure ScoreTypeSum
3639− parse "none" = pure ScoreTypeNone
3640− parse t = fail ("Unexpected ScoreType: " <> show t)
3641−
3642−instance ToJSON Distance where
3643− toJSON (Distance dCoefficient dUnit) =
3644− String boltedTogether where
3645− coefText = showText dCoefficient
3646− (String unitText) = toJSON dUnit
3647− boltedTogether = mappend coefText unitText
3648−
3649−instance FromJSON Distance where
3650− parseJSON = withText "Distance" parse
3651− where parse t = Distance <$> parseCoeff nT
3652− <*> parseJSON (String unitT)
3653− where (nT, unitT) = T.span validForNumber t
3654− -- may be a better way to do this
3655− validForNumber '-' = True
3656− validForNumber '.' = True
3657− validForNumber 'e' = True
3658− validForNumber c = isNumber c
3659− parseCoeff "" = fail "Empty string cannot be parsed as number"
3660− parseCoeff s = return (read (T.unpack s))
3661−
3662−instance ToJSON DistanceUnit where
3663− toJSON Miles = String "mi"
3664− toJSON Yards = String "yd"
3665− toJSON Feet = String "ft"
3666− toJSON Inches = String "in"
3667− toJSON Kilometers = String "km"
3668− toJSON Meters = String "m"
3669− toJSON Centimeters = String "cm"
3670− toJSON Millimeters = String "mm"
3671− toJSON NauticalMiles = String "nmi"
3672−
3673−
3674−instance FromJSON DistanceUnit where
3675− parseJSON = withText "DistanceUnit" parse
3676− where parse "mi" = pure Miles
3677− parse "yd" = pure Yards
3678− parse "ft" = pure Feet
3679− parse "in" = pure Inches
3680− parse "km" = pure Kilometers
3681− parse "m" = pure Meters
3682− parse "cm" = pure Centimeters
3683− parse "mm" = pure Millimeters
3684− parse "nmi" = pure NauticalMiles
3685− parse u = fail ("Unrecognized DistanceUnit: " <> show u)
3686−
3687−instance ToJSON DistanceType where
3688− toJSON Arc = String "arc"
3689− toJSON SloppyArc = String "sloppy_arc"
3690− toJSON Plane = String "plane"
3691−
3692−instance FromJSON DistanceType where
3693− parseJSON = withText "DistanceType" parse
3694− where parse "arc" = pure Arc
3695− parse "sloppy_arc" = pure SloppyArc
3696− parse "plane" = pure Plane
3697− parse t = fail ("Unrecognized DistanceType: " <> show t)
3698−
3699−
3700−instance ToJSON OptimizeBbox where
3701− toJSON NoOptimizeBbox = String "none"
3702− toJSON (OptimizeGeoFilterType gft) = toJSON gft
3703−
3704−instance FromJSON OptimizeBbox where
3705− parseJSON v = withText "NoOptimizeBbox" parseNoOptimize v
3706− <|> parseOptimize v
3707− where parseNoOptimize "none" = pure NoOptimizeBbox
3708− parseNoOptimize _ = mzero
3709− parseOptimize = fmap OptimizeGeoFilterType . parseJSON
3710−
3711−instance ToJSON GeoBoundingBoxConstraint where
3712− toJSON (GeoBoundingBoxConstraint
3713− (FieldName gbbcGeoBBField) gbbcConstraintBox cache type') =
3714− object [gbbcGeoBBField .= gbbcConstraintBox
3715− , "_cache" .= cache
3716− , "type" .= type']
3717−
3718−instance FromJSON GeoBoundingBoxConstraint where
3719− parseJSON = withObject "GeoBoundingBoxConstraint" parse
3720− where parse o = case HM.toList (deleteSeveral ["type", "_cache"] o) of
3721− [(fn, v)] -> GeoBoundingBoxConstraint (FieldName fn)
3722− <$> parseJSON v
3723− <*> o .:? "_cache" .!= defaultCache
3724− <*> o .: "type"
3725− _ -> fail "Could not find field name for GeoBoundingBoxConstraint"
3726−
3727−instance ToJSON GeoFilterType where
3728− toJSON GeoFilterMemory = String "memory"
3729− toJSON GeoFilterIndexed = String "indexed"
3730−
3731−instance FromJSON GeoFilterType where
3732− parseJSON = withText "GeoFilterType" parse
3733− where parse "memory" = pure GeoFilterMemory
3734− parse "indexed" = pure GeoFilterIndexed
3735− parse t = fail ("Unrecognized GeoFilterType: " <> show t)
3736−
3737−instance ToJSON GeoBoundingBox where
3738− toJSON (GeoBoundingBox gbbTopLeft gbbBottomRight) =
3739− object ["top_left" .= gbbTopLeft
3740− , "bottom_right" .= gbbBottomRight]
3741−
3742−instance FromJSON GeoBoundingBox where
3743− parseJSON = withObject "GeoBoundingBox" parse
3744− where parse o = GeoBoundingBox
3745− <$> o .: "top_left"
3746− <*> o .: "bottom_right"
3747−
3748−instance ToJSON LatLon where
3749− toJSON (LatLon lLat lLon) =
3750− object ["lat" .= lLat
3751− , "lon" .= lLon]
3752−
3753−instance FromJSON LatLon where
3754− parseJSON = withObject "LatLon" parse
3755− where parse o = LatLon <$> o .: "lat"
3756− <*> o .: "lon"
3757−
3758−-- index for smaller ranges, fielddata for longer ranges
3759−instance ToJSON RangeExecution where
3760− toJSON RangeExecutionIndex = "index"
3761− toJSON RangeExecutionFielddata = "fielddata"
3762−
3763−
3764−instance FromJSON RangeExecution where
3765− parseJSON = withText "RangeExecution" parse
3766− where parse "index" = pure RangeExecutionIndex
3767− parse "fielddata" = pure RangeExecutionFielddata
3768− parse t = error ("Unrecognized RangeExecution " <> show t)
3769−
3770−instance ToJSON RegexpFlags where
3771− toJSON AllRegexpFlags = String "ALL"
3772− toJSON NoRegexpFlags = String "NONE"
3773− toJSON (SomeRegexpFlags (h :| fs)) = String $ T.intercalate "|" flagStrs
3774− where flagStrs = map flagStr . nub $ h:fs
3775− flagStr AnyString = "ANYSTRING"
3776− flagStr Automaton = "AUTOMATON"
3777− flagStr Complement = "COMPLEMENT"
3778− flagStr Empty = "EMPTY"
3779− flagStr Intersection = "INTERSECTION"
3780− flagStr Interval = "INTERVAL"
3781−
3782−instance FromJSON RegexpFlags where
3783− parseJSON = withText "RegexpFlags" parse
3784− where parse "ALL" = pure AllRegexpFlags
3785− parse "NONE" = pure NoRegexpFlags
3786− parse t = SomeRegexpFlags <$> parseNEJSON (String <$> T.splitOn "|" t)
3787−
3788−instance FromJSON RegexpFlag where
3789− parseJSON = withText "RegexpFlag" parse
3790− where parse "ANYSTRING" = pure AnyString
3791− parse "AUTOMATON" = pure Automaton
3792− parse "COMPLEMENT" = pure Complement
3793− parse "EMPTY" = pure Empty
3794− parse "INTERSECTION" = pure Intersection
3795− parse "INTERVAL" = pure Interval
3796− parse f = fail ("Unknown RegexpFlag: " <> show f)
3797−
3798−instance ToJSON Term where
3799− toJSON (Term field value) = object ["term" .= object
3800− [field .= value]]
3801−
3802−instance FromJSON Term where
3803− parseJSON = withObject "Term" parse
3804− where parse o = do termObj <- o .: "term"
3805− case HM.toList termObj of
3806− [(fn, v)] -> Term fn <$> parseJSON v
3807− _ -> fail "Expected object with 1 field-named key"
3808−
3809−instance ToJSON BoolMatch where
3810− toJSON (MustMatch term cache) = object ["must" .= term,
3811− "_cache" .= cache]
3812− toJSON (MustNotMatch term cache) = object ["must_not" .= term,
3813− "_cache" .= cache]
3814− toJSON (ShouldMatch terms cache) = object ["should" .= fmap toJSON terms,
3815− "_cache" .= cache]
3816−
3817−instance FromJSON BoolMatch where
3818− parseJSON = withObject "BoolMatch" parse
3819− where parse o = mustMatch `taggedWith` "must"
3820− <|> mustNotMatch `taggedWith` "must_not"
3821− <|> shouldMatch `taggedWith` "should"
3822− where taggedWith parser k = parser =<< o .: k
3823− mustMatch t = MustMatch t <$> o .:? "_cache" .!= defaultCache
3824− mustNotMatch t = MustNotMatch t <$> o .:? "_cache" .!= defaultCache
3825− shouldMatch t = ShouldMatch t <$> o .:? "_cache" .!= defaultCache
3826442
3827443instance (FromJSON a) => FromJSON (SearchResult a) where
3828444 parseJSON (Object v) = SearchResult <$>
@@ -3834,1747 +450,3 @@ instance (FromJSON a) => FromJSON (SearchResult a) where
3834450 v .:? "_scroll_id" <*>
3835451 v .:? "suggest"
3836452 parseJSON _ = empty
3837−
3838−instance (FromJSON a) => FromJSON (SearchHits a) where
3839− parseJSON (Object v) = SearchHits <$>
3840− v .: "total" <*>
3841− v .: "max_score" <*>
3842− v .: "hits"
3843− parseJSON _ = empty
3844−
3845−instance (FromJSON a) => FromJSON (Hit a) where
3846− parseJSON (Object v) = Hit <$>
3847− v .: "_index" <*>
3848− v .: "_type" <*>
3849− v .: "_id" <*>
3850− v .: "_score" <*>
3851− v .:? "_source" <*>
3852− v .:? "highlight"
3853− parseJSON _ = empty
3854−
3855−instance FromJSON ShardResult where
3856− parseJSON (Object v) = ShardResult <$>
3857− v .: "total" <*>
3858− v .: "successful" <*>
3859− v .: "failed"
3860− parseJSON _ = empty
3861−
3862−
3863−instance FromJSON DocVersion where
3864− parseJSON v = do
3865− i <- parseJSON v
3866− maybe (fail "DocVersion out of range") return $ mkDocVersion i
3867−
3868−-- This insanity is because ES *sometimes* returns Replica/Shard counts as strings
3869−instance FromJSON ReplicaCount where
3870− parseJSON v = parseAsInt v
3871− <|> parseAsString v
3872− where parseAsInt = fmap ReplicaCount . parseJSON
3873− parseAsString = withText "ReplicaCount" (fmap ReplicaCount . parseReadText)
3874−
3875−instance FromJSON ShardCount where
3876− parseJSON v = parseAsInt v
3877− <|> parseAsString v
3878− where parseAsInt = fmap ShardCount . parseJSON
3879− parseAsString = withText "ShardCount" (fmap ShardCount . parseReadText)
3880−
3881−instance Bounded DocVersion where
3882− minBound = DocVersion 1
3883− maxBound = DocVersion 9200000000000000000 -- 9.2e+18
3884−
3885−instance Enum DocVersion where
3886− succ x
3887− | x /= maxBound = DocVersion (succ $ docVersionNumber x)
3888− | otherwise = succError "DocVersion"
3889− pred x
3890− | x /= minBound = DocVersion (pred $ docVersionNumber x)
3891− | otherwise = predError "DocVersion"
3892− toEnum i =
3893− fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
3894− fromEnum = docVersionNumber
3895− enumFrom = boundedEnumFrom
3896− enumFromThen = boundedEnumFromThen
3897−
3898−-- | Username type used for HTTP Basic authentication. See 'basicAuthHook'.
3899−newtype EsUsername = EsUsername { esUsername :: Text } deriving (Read, Show, Eq)
3900−
3901−-- | Password type used for HTTP Basic authentication. See 'basicAuthHook'.
3902−newtype EsPassword = EsPassword { esPassword :: Text } deriving (Read, Show, Eq)
3903−
3904−
3905−data SnapshotRepoSelection = SnapshotRepoList (NonEmpty SnapshotRepoPattern)
3906− | AllSnapshotRepos deriving (Eq, Generic, Show, Typeable)
3907−
3908−
3909−-- | Either specifies an exact repo name or one with globs in it,
3910−-- e.g. @RepoPattern "foo*"@ __NOTE__: Patterns are not supported on ES < 1.7
3911−data SnapshotRepoPattern = ExactRepo SnapshotRepoName
3912− | RepoPattern Text
3913− deriving (Eq, Generic, Show, Typeable)
3914−
3915−-- | The unique name of a snapshot repository.
3916−newtype SnapshotRepoName = SnapshotRepoName { snapshotRepoName :: Text }
3917− deriving (Eq, Ord, Generic, Show, Typeable, ToJSON, FromJSON)
3918−
3919−
3920−-- | A generic representation of a snapshot repo. This is what gets
3921−-- sent to and parsed from the server. For repo types enabled by
3922−-- plugins that aren't exported by this library, consider making a
3923−-- custom type which implements 'SnapshotRepo'. If it is a common repo
3924−-- type, consider submitting a pull request to have it included in the
3925−-- library proper
3926−data GenericSnapshotRepo = GenericSnapshotRepo {
3927− gSnapshotRepoName :: SnapshotRepoName
3928− , gSnapshotRepoType :: SnapshotRepoType
3929− , gSnapshotRepoSettings :: GenericSnapshotRepoSettings
3930− } deriving (Eq, Generic, Show, Typeable)
3931−
3932−
3933−instance SnapshotRepo GenericSnapshotRepo where
3934− toGSnapshotRepo = id
3935− fromGSnapshotRepo = Right
3936−
3937−
3938−newtype SnapshotRepoType = SnapshotRepoType { snapshotRepoType :: Text }
3939− deriving (Eq, Ord, Generic, Show, Typeable, ToJSON, FromJSON)
3940−
3941−
3942−-- | Opaque representation of snapshot repo settings. Instances of
3943−-- 'SnapshotRepo' will produce this.
3944−newtype GenericSnapshotRepoSettings = GenericSnapshotRepoSettings { gSnapshotRepoSettingsObject :: Object }
3945− deriving (Eq, Generic, Show, Typeable, ToJSON)
3946−
3947−
3948− -- Regardless of whether you send strongly typed json, my version of
3949− -- ES sends back stringly typed json in the settings, e.g. booleans
3950− -- as strings, so we'll try to convert them.
3951−instance FromJSON GenericSnapshotRepoSettings where
3952− parseJSON = fmap (GenericSnapshotRepoSettings . fmap unStringlyTypeJSON). parseJSON
3953−
3954−-- | The result of running 'verifySnapshotRepo'.
3955−newtype SnapshotVerification = SnapshotVerification { snapshotNodeVerifications :: [SnapshotNodeVerification] }
3956− deriving (Eq, Generic, Show, Typeable)
3957−
3958−
3959−instance FromJSON SnapshotVerification where
3960− parseJSON = withObject "SnapshotVerification" parse
3961− where
3962− parse o = do
3963− o2 <- o .: "nodes"
3964− SnapshotVerification <$> mapM (uncurry parse') (HM.toList o2)
3965− parse' rawFullId = withObject "SnapshotNodeVerification" $ \o ->
3966− SnapshotNodeVerification (FullNodeId rawFullId) <$> o .: "name"
3967−
3968−
3969−-- | A node that has verified a snapshot
3970−data SnapshotNodeVerification = SnapshotNodeVerification {
3971− snvFullId :: FullNodeId
3972− , snvNodeName :: NodeName
3973− } deriving (Eq, Generic, Show, Typeable)
3974−
3975−
3976−-- | Unique, automatically-generated name assigned to nodes that are
3977−-- usually returned in node-oriented APIs.
3978−newtype FullNodeId = FullNodeId { fullNodeId :: Text }
3979− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3980−
3981−
3982−-- | A human-readable node name that is supplied by the user in the
3983−-- node config or automatically generated by Elasticsearch.
3984−newtype NodeName = NodeName { nodeName :: Text }
3985− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3986−
3987−newtype ClusterName = ClusterName { clusterName :: Text }
3988− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3989−
3990−data NodesInfo = NodesInfo {
3991− nodesInfo :: [NodeInfo]
3992− , nodesClusterName :: ClusterName
3993− } deriving (Eq, Show, Generic, Typeable)
3994−
3995−data NodesStats = NodesStats {
3996− nodesStats :: [NodeStats]
3997− , nodesStatsClusterName :: ClusterName
3998− } deriving (Eq, Show, Generic, Typeable)
3999−
4000−data NodeStats = NodeStats {
4001− nodeStatsName :: NodeName
4002− , nodeStatsFullId :: FullNodeId
4003− , nodeStatsBreakersStats :: Maybe NodeBreakersStats
4004− , nodeStatsHTTP :: NodeHTTPStats
4005− , nodeStatsTransport :: NodeTransportStats
4006− , nodeStatsFS :: NodeFSStats
4007− , nodeStatsNetwork :: NodeNetworkStats
4008− , nodeStatsThreadPool :: NodeThreadPoolsStats
4009− , nodeStatsJVM :: NodeJVMStats
4010− , nodeStatsProcess :: NodeProcessStats
4011− , nodeStatsOS :: NodeOSStats
4012− , nodeStatsIndices :: NodeIndicesStats
4013− } deriving (Eq, Show, Generic, Typeable)
4014−
4015−data NodeBreakersStats = NodeBreakersStats {
4016− nodeStatsParentBreaker :: NodeBreakerStats
4017− , nodeStatsRequestBreaker :: NodeBreakerStats
4018− , nodeStatsFieldDataBreaker :: NodeBreakerStats
4019− } deriving (Eq, Show, Generic, Typeable)
4020−
4021−data NodeBreakerStats = NodeBreakerStats {
4022− nodeBreakersTripped :: Int
4023− , nodeBreakersOverhead :: Double
4024− , nodeBreakersEstSize :: Bytes
4025− , nodeBreakersLimitSize :: Bytes
4026− } deriving (Eq, Show, Generic, Typeable)
4027−
4028−data NodeHTTPStats = NodeHTTPStats {
4029− nodeHTTPTotalOpened :: Int
4030− , nodeHTTPCurrentOpen :: Int
4031− } deriving (Eq, Show, Generic, Typeable)
4032−
4033−data NodeTransportStats = NodeTransportStats {
4034− nodeTransportTXSize :: Bytes
4035− , nodeTransportCount :: Int
4036− , nodeTransportRXSize :: Bytes
4037− , nodeTransportRXCount :: Int
4038− , nodeTransportServerOpen :: Int
4039− } deriving (Eq, Show, Generic, Typeable)
4040−
4041−data NodeFSStats = NodeFSStats {
4042− nodeFSDataPaths :: [NodeDataPathStats]
4043− , nodeFSTotal :: NodeFSTotalStats
4044− , nodeFSTimestamp :: UTCTime
4045− } deriving (Eq, Show, Generic, Typeable)
4046−
4047−data NodeDataPathStats = NodeDataPathStats {
4048− nodeDataPathDiskServiceTime :: Maybe Double
4049− , nodeDataPathDiskQueue :: Maybe Double
4050− , nodeDataPathIOSize :: Maybe Bytes
4051− , nodeDataPathWriteSize :: Maybe Bytes
4052− , nodeDataPathReadSize :: Maybe Bytes
4053− , nodeDataPathIOOps :: Maybe Int
4054− , nodeDataPathWrites :: Maybe Int
4055− , nodeDataPathReads :: Maybe Int
4056− , nodeDataPathAvailable :: Bytes
4057− , nodeDataPathFree :: Bytes
4058− , nodeDataPathTotal :: Bytes
4059− , nodeDataPathType :: Maybe Text
4060− , nodeDataPathDevice :: Text
4061− , nodeDataPathMount :: Text
4062− , nodeDataPathPath :: Text
4063− } deriving (Eq, Show, Generic, Typeable)
4064−
4065−data NodeFSTotalStats = NodeFSTotalStats {
4066− nodeFSTotalDiskServiceTime :: Maybe Double
4067− , nodeFSTotalDiskQueue :: Maybe Double
4068− , nodeFSTotalIOSize :: Maybe Bytes
4069− , nodeFSTotalWriteSize :: Maybe Bytes
4070− , nodeFSTotalReadSize :: Maybe Bytes
4071− , nodeFSTotalIOOps :: Maybe Int
4072− , nodeFSTotalWrites :: Maybe Int
4073− , nodeFSTotalReads :: Maybe Int
4074− , nodeFSTotalAvailable :: Bytes
4075− , nodeFSTotalFree :: Bytes
4076− , nodeFSTotalTotal :: Bytes
4077− } deriving (Eq, Show, Generic, Typeable)
4078−
4079−data NodeNetworkStats = NodeNetworkStats {
4080− nodeNetTCPOutRSTs :: Int
4081− , nodeNetTCPInErrs :: Int
4082− , nodeNetTCPAttemptFails :: Int
4083− , nodeNetTCPEstabResets :: Int
4084− , nodeNetTCPRetransSegs :: Int
4085− , nodeNetTCPOutSegs :: Int
4086− , nodeNetTCPInSegs :: Int
4087− , nodeNetTCPCurrEstab :: Int
4088− , nodeNetTCPPassiveOpens :: Int
4089− , nodeNetTCPActiveOpens :: Int
4090− } deriving (Eq, Show, Generic, Typeable)
4091−
4092−data NodeThreadPoolsStats = NodeThreadPoolsStats {
4093− nodeThreadPoolsStatsSnapshot :: NodeThreadPoolStats
4094− , nodeThreadPoolsStatsBulk :: NodeThreadPoolStats
4095− , nodeThreadPoolsStatsMerge :: NodeThreadPoolStats
4096− , nodeThreadPoolsStatsGet :: NodeThreadPoolStats
4097− , nodeThreadPoolsStatsManagement :: NodeThreadPoolStats
4098− , nodeThreadPoolsStatsFetchShardStore :: Maybe NodeThreadPoolStats
4099− , nodeThreadPoolsStatsOptimize :: NodeThreadPoolStats
4100− , nodeThreadPoolsStatsFlush :: NodeThreadPoolStats
4101− , nodeThreadPoolsStatsSearch :: NodeThreadPoolStats
4102− , nodeThreadPoolsStatsWarmer :: NodeThreadPoolStats
4103− , nodeThreadPoolsStatsGeneric :: NodeThreadPoolStats
4104− , nodeThreadPoolsStatsSuggest :: NodeThreadPoolStats
4105− , nodeThreadPoolsStatsRefresh :: NodeThreadPoolStats
4106− , nodeThreadPoolsStatsIndex :: NodeThreadPoolStats
4107− , nodeThreadPoolsStatsListener :: Maybe NodeThreadPoolStats
4108− , nodeThreadPoolsStatsFetchShardStarted :: Maybe NodeThreadPoolStats
4109− , nodeThreadPoolsStatsPercolate :: NodeThreadPoolStats
4110− } deriving (Eq, Show, Generic, Typeable)
4111−
4112−data NodeThreadPoolStats = NodeThreadPoolStats {
4113− nodeThreadPoolCompleted :: Int
4114− , nodeThreadPoolLargest :: Int
4115− , nodeThreadPoolRejected :: Int
4116− , nodeThreadPoolActive :: Int
4117− , nodeThreadPoolQueue :: Int
4118− , nodeThreadPoolThreads :: Int
4119− } deriving (Eq, Show, Generic, Typeable)
4120−
4121−data NodeJVMStats = NodeJVMStats {
4122− nodeJVMStatsMappedBufferPool :: JVMBufferPoolStats
4123− , nodeJVMStatsDirectBufferPool :: JVMBufferPoolStats
4124− , nodeJVMStatsGCOldCollector :: JVMGCStats
4125− , nodeJVMStatsGCYoungCollector :: JVMGCStats
4126− , nodeJVMStatsPeakThreadsCount :: Int
4127− , nodeJVMStatsThreadsCount :: Int
4128− , nodeJVMStatsOldPool :: JVMPoolStats
4129− , nodeJVMStatsSurvivorPool :: JVMPoolStats
4130− , nodeJVMStatsYoungPool :: JVMPoolStats
4131− , nodeJVMStatsNonHeapCommitted :: Bytes
4132− , nodeJVMStatsNonHeapUsed :: Bytes
4133− , nodeJVMStatsHeapMax :: Bytes
4134− , nodeJVMStatsHeapCommitted :: Bytes
4135− , nodeJVMStatsHeapUsedPercent :: Int
4136− , nodeJVMStatsHeapUsed :: Bytes
4137− , nodeJVMStatsUptime :: NominalDiffTime
4138− , nodeJVMStatsTimestamp :: UTCTime
4139− } deriving (Eq, Show, Generic, Typeable)
4140−
4141−data JVMBufferPoolStats = JVMBufferPoolStats {
4142− jvmBufferPoolStatsTotalCapacity :: Bytes
4143− , jvmBufferPoolStatsUsed :: Bytes
4144− , jvmBufferPoolStatsCount :: Int
4145− } deriving (Eq, Show, Generic, Typeable)
4146−
4147−data JVMGCStats = JVMGCStats {
4148− jvmGCStatsCollectionTime :: NominalDiffTime
4149− , jvmGCStatsCollectionCount :: Int
4150− } deriving (Eq, Show, Generic, Typeable)
4151−
4152−data JVMPoolStats = JVMPoolStats {
4153− jvmPoolStatsPeakMax :: Bytes
4154− , jvmPoolStatsPeakUsed :: Bytes
4155− , jvmPoolStatsMax :: Bytes
4156− , jvmPoolStatsUsed :: Bytes
4157− } deriving (Eq, Show, Generic, Typeable)
4158−
4159−data NodeProcessStats = NodeProcessStats {
4160− nodeProcessMemTotalVirtual :: Bytes
4161− , nodeProcessMemShare :: Bytes
4162− , nodeProcessMemResident :: Bytes
4163− , nodeProcessCPUTotal :: NominalDiffTime
4164− , nodeProcessCPUUser :: NominalDiffTime
4165− , nodeProcessCPUSys :: NominalDiffTime
4166− , nodeProcessCPUPercent :: Int
4167− , nodeProcessOpenFDs :: Int
4168− , nodeProcessTimestamp :: UTCTime
4169− } deriving (Eq, Show, Generic, Typeable)
4170−
4171−data NodeOSStats = NodeOSStats {
4172− nodeOSSwapFree :: Bytes
4173− , nodeOSSwapUsed :: Bytes
4174− , nodeOSMemActualUsed :: Bytes
4175− , nodeOSMemActualFree :: Bytes
4176− , nodeOSMemUsedPercent :: Int
4177− , nodeOSMemFreePercent :: Int
4178− , nodeOSMemUsed :: Bytes
4179− , nodeOSMemFree :: Bytes
4180− , nodeOSCPUStolen :: Int
4181− , nodeOSCPUUsage :: Int
4182− , nodeOSCPUIdle :: Int
4183− , nodeOSCPUUser :: Int
4184− , nodeOSCPUSys :: Int
4185− , nodeOSLoad :: Maybe LoadAvgs
4186− , nodeOSUptime :: NominalDiffTime
4187− , nodeOSTimestamp :: UTCTime
4188− } deriving (Eq, Show, Generic, Typeable)
4189−
4190−data LoadAvgs = LoadAvgs {
4191− loadAvg1Min :: Double
4192− , loadAvg5Min :: Double
4193− , loadAvg15Min :: Double
4194− } deriving (Eq, Show, Generic, Typeable)
4195−
4196−data NodeIndicesStats = NodeIndicesStats {
4197− nodeIndicesStatsRecoveryThrottleTime :: Maybe NominalDiffTime
4198− , nodeIndicesStatsRecoveryCurrentAsTarget :: Maybe Int
4199− , nodeIndicesStatsRecoveryCurrentAsSource :: Maybe Int
4200− , nodeIndicesStatsQueryCacheMisses :: Maybe Int
4201− , nodeIndicesStatsQueryCacheHits :: Maybe Int
4202− , nodeIndicesStatsQueryCacheEvictions :: Maybe Int
4203− , nodeIndicesStatsQueryCacheSize :: Maybe Bytes
4204− , nodeIndicesStatsSuggestCurrent :: Int
4205− , nodeIndicesStatsSuggestTime :: NominalDiffTime
4206− , nodeIndicesStatsSuggestTotal :: Int
4207− , nodeIndicesStatsTranslogSize :: Bytes
4208− , nodeIndicesStatsTranslogOps :: Int
4209− , nodeIndicesStatsSegFixedBitSetMemory :: Maybe Bytes
4210− , nodeIndicesStatsSegVersionMapMemory :: Bytes
4211− , nodeIndicesStatsSegIndexWriterMaxMemory :: Maybe Bytes
4212− , nodeIndicesStatsSegIndexWriterMemory :: Bytes
4213− , nodeIndicesStatsSegMemory :: Bytes
4214− , nodeIndicesStatsSegCount :: Int
4215− , nodeIndicesStatsCompletionSize :: Bytes
4216− , nodeIndicesStatsPercolateQueries :: Int
4217− , nodeIndicesStatsPercolateMemory :: Bytes
4218− , nodeIndicesStatsPercolateCurrent :: Int
4219− , nodeIndicesStatsPercolateTime :: NominalDiffTime
4220− , nodeIndicesStatsPercolateTotal :: Int
4221− , nodeIndicesStatsFieldDataEvictions :: Int
4222− , nodeIndicesStatsFieldDataMemory :: Bytes
4223− , nodeIndicesStatsIDCacheMemory :: Bytes
4224− , nodeIndicesStatsFilterCacheEvictions :: Int
4225− , nodeIndicesStatsFilterCacheMemory :: Bytes
4226− , nodeIndicesStatsWarmerTotalTime :: NominalDiffTime
4227− , nodeIndicesStatsWarmerTotal :: Int
4228− , nodeIndicesStatsWarmerCurrent :: Int
4229− , nodeIndicesStatsFlushTotalTime :: NominalDiffTime
4230− , nodeIndicesStatsFlushTotal :: Int
4231− , nodeIndicesStatsRefreshTotalTime :: NominalDiffTime
4232− , nodeIndicesStatsRefreshTotal :: Int
4233− , nodeIndicesStatsMergesTotalSize :: Bytes
4234− , nodeIndicesStatsMergesTotalDocs :: Int
4235− , nodeIndicesStatsMergesTotalTime :: NominalDiffTime
4236− , nodeIndicesStatsMergesTotal :: Int
4237− , nodeIndicesStatsMergesCurrentSize :: Bytes
4238− , nodeIndicesStatsMergesCurrentDocs :: Int
4239− , nodeIndicesStatsMergesCurrent :: Int
4240− , nodeIndicesStatsSearchFetchCurrent :: Int
4241− , nodeIndicesStatsSearchFetchTime :: NominalDiffTime
4242− , nodeIndicesStatsSearchFetchTotal :: Int
4243− , nodeIndicesStatsSearchQueryCurrent :: Int
4244− , nodeIndicesStatsSearchQueryTime :: NominalDiffTime
4245− , nodeIndicesStatsSearchQueryTotal :: Int
4246− , nodeIndicesStatsSearchOpenContexts :: Int
4247− , nodeIndicesStatsGetCurrent :: Int
4248− , nodeIndicesStatsGetMissingTime :: NominalDiffTime
4249− , nodeIndicesStatsGetMissingTotal :: Int
4250− , nodeIndicesStatsGetExistsTime :: NominalDiffTime
4251− , nodeIndicesStatsGetExistsTotal :: Int
4252− , nodeIndicesStatsGetTime :: NominalDiffTime
4253− , nodeIndicesStatsGetTotal :: Int
4254− , nodeIndicesStatsIndexingThrottleTime :: Maybe NominalDiffTime
4255− , nodeIndicesStatsIndexingIsThrottled :: Maybe Bool
4256− , nodeIndicesStatsIndexingNoopUpdateTotal :: Maybe Int
4257− , nodeIndicesStatsIndexingDeleteCurrent :: Int
4258− , nodeIndicesStatsIndexingDeleteTime :: NominalDiffTime
4259− , nodeIndicesStatsIndexingDeleteTotal :: Int
4260− , nodeIndicesStatsIndexingIndexCurrent :: Int
4261− , nodeIndicesStatsIndexingIndexTime :: NominalDiffTime
4262− , nodeIndicesStatsIndexingTotal :: Int
4263− , nodeIndicesStatsStoreThrottleTime :: NominalDiffTime
4264− , nodeIndicesStatsStoreSize :: Bytes
4265− , nodeIndicesStatsDocsDeleted :: Int
4266− , nodeIndicesStatsDocsCount :: Int
4267− } deriving (Eq, Show, Generic, Typeable)
4268−
4269−-- | A quirky address format used throughout Elasticsearch. An example
4270−-- would be inet[/1.1.1.1:9200]. inet may be a placeholder for a
4271−-- <https://en.wikipedia.org/wiki/Fully_qualified_domain_name FQDN>.
4272−newtype EsAddress = EsAddress { esAddress :: Text }
4273− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
4274−
4275−-- | Typically a 7 character hex string.
4276−newtype BuildHash = BuildHash { buildHash :: Text }
4277− deriving (Eq, Ord, Generic, Read, Show, Typeable, FromJSON, ToJSON)
4278−
4279−newtype PluginName = PluginName { pluginName :: Text }
4280− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
4281−
4282−data NodeInfo = NodeInfo {
4283− nodeInfoHTTPAddress :: EsAddress
4284− , nodeInfoBuild :: BuildHash
4285− , nodeInfoESVersion :: VersionNumber
4286− , nodeInfoIP :: Server
4287− , nodeInfoHost :: Server
4288− , nodeInfoTransportAddress :: EsAddress
4289− , nodeInfoName :: NodeName
4290− , nodeInfoFullId :: FullNodeId
4291− , nodeInfoPlugins :: [NodePluginInfo]
4292− , nodeInfoHTTP :: NodeHTTPInfo
4293− , nodeInfoTransport :: NodeTransportInfo
4294− , nodeInfoNetwork :: NodeNetworkInfo
4295− , nodeInfoThreadPool :: NodeThreadPoolsInfo
4296− , nodeInfoJVM :: NodeJVMInfo
4297− , nodeInfoProcess :: NodeProcessInfo
4298− , nodeInfoOS :: NodeOSInfo
4299− , nodeInfoSettings :: Object
4300− -- ^ The members of the settings objects are not consistent,
4301− -- dependent on plugins, etc.
4302− } deriving (Eq, Show, Generic, Typeable)
4303−
4304−data NodePluginInfo = NodePluginInfo {
4305− nodePluginSite :: Bool
4306− -- ^ Is this a site plugin?
4307− , nodePluginJVM :: Bool
4308− -- ^ Is this plugin running on the JVM
4309− , nodePluginDescription :: Text
4310− , nodePluginVersion :: MaybeNA VersionNumber
4311− , nodePluginName :: PluginName
4312− } deriving (Eq, Show, Generic, Typeable)
4313−
4314−data NodeHTTPInfo = NodeHTTPInfo {
4315− nodeHTTPMaxContentLength :: Bytes
4316− , nodeHTTPTransportAddress :: BoundTransportAddress
4317− } deriving (Eq, Show, Generic, Typeable)
4318−
4319−data NodeTransportInfo = NodeTransportInfo {
4320− nodeTransportProfiles :: [BoundTransportAddress]
4321− , nodeTransportAddress :: BoundTransportAddress
4322− } deriving (Eq, Show, Generic, Typeable)
4323−
4324−data BoundTransportAddress = BoundTransportAddress {
4325− publishAddress :: EsAddress
4326− , boundAddress :: EsAddress
4327− } deriving (Eq, Show, Generic, Typeable)
4328−
4329−data NodeNetworkInfo = NodeNetworkInfo {
4330− nodeNetworkPrimaryInterface :: NodeNetworkInterface
4331− , nodeNetworkRefreshInterval :: NominalDiffTime
4332− } deriving (Eq, Show, Generic, Typeable)
4333−
4334−newtype MacAddress = MacAddress { macAddress :: Text }
4335− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
4336−
4337−newtype NetworkInterfaceName = NetworkInterfaceName { networkInterfaceName :: Text }
4338− deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
4339−
4340−data NodeNetworkInterface = NodeNetworkInterface {
4341− nodeNetIfaceMacAddress :: MacAddress
4342− , nodeNetIfaceName :: NetworkInterfaceName
4343− , nodeNetIfaceAddress :: Server
4344− } deriving (Eq, Show, Generic, Typeable)
4345−
4346−data NodeThreadPoolsInfo = NodeThreadPoolsInfo {
4347− nodeThreadPoolsRefresh :: NodeThreadPoolInfo
4348− , nodeThreadPoolsManagement :: NodeThreadPoolInfo
4349− , nodeThreadPoolsPercolate :: NodeThreadPoolInfo
4350− , nodeThreadPoolsListener :: Maybe NodeThreadPoolInfo
4351− , nodeThreadPoolsFetchShardStarted :: Maybe NodeThreadPoolInfo
4352− , nodeThreadPoolsSearch :: NodeThreadPoolInfo
4353− , nodeThreadPoolsFlush :: NodeThreadPoolInfo
4354− , nodeThreadPoolsWarmer :: NodeThreadPoolInfo
4355− , nodeThreadPoolsOptimize :: NodeThreadPoolInfo
4356− , nodeThreadPoolsBulk :: NodeThreadPoolInfo
4357− , nodeThreadPoolsSuggest :: NodeThreadPoolInfo
4358− , nodeThreadPoolsMerge :: NodeThreadPoolInfo
4359− , nodeThreadPoolsSnapshot :: NodeThreadPoolInfo
4360− , nodeThreadPoolsGet :: NodeThreadPoolInfo
4361− , nodeThreadPoolsFetchShardStore :: Maybe NodeThreadPoolInfo
4362− , nodeThreadPoolsIndex :: NodeThreadPoolInfo
4363− , nodeThreadPoolsGeneric :: NodeThreadPoolInfo
4364− } deriving (Eq, Show, Generic, Typeable)
4365−
4366−data NodeThreadPoolInfo = NodeThreadPoolInfo {
4367− nodeThreadPoolQueueSize :: ThreadPoolSize
4368− , nodeThreadPoolKeepalive :: Maybe NominalDiffTime
4369− , nodeThreadPoolMin :: Maybe Int
4370− , nodeThreadPoolMax :: Maybe Int
4371− , nodeThreadPoolType :: ThreadPoolType
4372− } deriving (Eq, Show, Generic, Typeable)
4373−
4374−data ThreadPoolSize = ThreadPoolBounded Int
4375− | ThreadPoolUnbounded
4376− deriving (Eq, Show, Generic, Typeable)
4377−
4378−data ThreadPoolType = ThreadPoolScaling
4379− | ThreadPoolFixed
4380− | ThreadPoolCached
4381− deriving (Eq, Show, Generic, Typeable)
4382−
4383−data NodeJVMInfo = NodeJVMInfo {
4384− nodeJVMInfoMemoryPools :: [JVMMemoryPool]
4385− , nodeJVMInfoMemoryPoolsGCCollectors :: [JVMGCCollector]
4386− , nodeJVMInfoMemoryInfo :: JVMMemoryInfo
4387− , nodeJVMInfoStartTime :: UTCTime
4388− , nodeJVMInfoVMVendor :: Text
4389− , nodeJVMVMVersion :: VersionNumber
4390− -- ^ JVM doesn't seme to follow normal version conventions
4391− , nodeJVMVMName :: Text
4392− , nodeJVMVersion :: VersionNumber
4393− , nodeJVMPID :: PID
4394− } deriving (Eq, Show, Generic, Typeable)
4395−
4396−-- | Handles quirks in the way JVM versions are rendered (1.7.0_101 -> 1.7.0.101)
4397−newtype JVMVersion = JVMVersion { unJVMVersion :: VersionNumber }
4398−
4399−data JVMMemoryInfo = JVMMemoryInfo {
4400− jvmMemoryInfoDirectMax :: Bytes
4401− , jvmMemoryInfoNonHeapMax :: Bytes
4402− , jvmMemoryInfoNonHeapInit :: Bytes
4403− , jvmMemoryInfoHeapMax :: Bytes
4404− , jvmMemoryInfoHeapInit :: Bytes
4405− } deriving (Eq, Show, Generic, Typeable)
4406−
4407−newtype JVMMemoryPool = JVMMemoryPool {
4408− jvmMemoryPool :: Text
4409− } deriving (Eq, Show, Generic, Typeable, FromJSON)
4410−
4411−newtype JVMGCCollector = JVMGCCollector {
4412− jvmGCCollector :: Text
4413− } deriving (Eq, Show, Generic, Typeable, FromJSON)
4414−
4415−newtype PID = PID {
4416− pid :: Int
4417− } deriving (Eq, Show, Generic, Typeable, FromJSON)
4418−
4419−data NodeOSInfo = NodeOSInfo {
4420− nodeOSSwap :: Bytes
4421− , nodeOSMem :: Bytes
4422− , nodeOSCPUInfo :: CPUInfo
4423− , nodeOSAvailableProcessors :: Int
4424− , nodeOSRefreshInterval :: NominalDiffTime
4425− } deriving (Eq, Show, Generic, Typeable)
4426−
4427−data CPUInfo = CPUInfo {
4428− cpuCacheSize :: Bytes
4429− , cpuCoresPerSocket :: Int
4430− , cpuTotalSockets :: Int
4431− , cpuTotalCores :: Int
4432− , cpuMHZ :: Int
4433− , cpuModel :: Text
4434− , cpuVendor :: Text
4435− } deriving (Eq, Show, Generic, Typeable)
4436−
4437−data NodeProcessInfo = NodeProcessInfo {
4438− nodeProcessMLockAll :: Bool
4439− -- ^ See <https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
4440− , nodeProcessMaxFileDescriptors :: Int
4441− , nodeProcessId :: PID
4442− , nodeProcessRefreshInterval :: NominalDiffTime
4443− } deriving (Eq, Show, Generic, Typeable)
4444−
4445−data SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings {
4446− repoUpdateVerify :: Bool
4447− -- ^ After creation/update, synchronously check that nodes can
4448− -- write to this repo. Defaults to True. You may use False if you
4449− -- need a faster response and plan on verifying manually later
4450− -- with 'verifySnapshotRepo'.
4451− } deriving (Eq, Show, Generic, Typeable)
4452−
4453−
4454−-- | Reasonable defaults for repo creation/update
4455−--
4456−-- * repoUpdateVerify True
4457−defaultSnapshotRepoUpdateSettings :: SnapshotRepoUpdateSettings
4458−defaultSnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings True
4459−
4460−
4461−-- | A filesystem-based snapshot repo that ships with
4462−-- Elasticsearch. This is an instance of 'SnapshotRepo' so it can be
4463−-- used with 'updateSnapshotRepo'
4464−data FsSnapshotRepo = FsSnapshotRepo {
4465− fsrName :: SnapshotRepoName
4466− , fsrLocation :: FilePath
4467− , fsrCompressMetadata :: Bool
4468− , fsrChunkSize :: Maybe Bytes
4469− -- ^ Size by which to split large files during snapshotting.
4470− , fsrMaxRestoreBytesPerSec :: Maybe Bytes
4471− -- ^ Throttle node restore rate. If not supplied, defaults to 40mb/sec
4472− , fsrMaxSnapshotBytesPerSec :: Maybe Bytes
4473− -- ^ Throttle node snapshot rate. If not supplied, defaults to 40mb/sec
4474− } deriving (Eq, Generic, Show, Typeable)
4475−
4476−
4477−instance SnapshotRepo FsSnapshotRepo where
4478− toGSnapshotRepo FsSnapshotRepo {..} =
4479− GenericSnapshotRepo fsrName fsRepoType (GenericSnapshotRepoSettings settings)
4480− where
4481− Object settings = object $ [ "location" .= fsrLocation
4482− , "compress" .= fsrCompressMetadata
4483− ] ++ optionalPairs
4484− optionalPairs = catMaybes [ ("chunk_size" .=) <$> fsrChunkSize
4485− , ("max_restore_bytes_per_sec" .=) <$> fsrMaxRestoreBytesPerSec
4486− , ("max_snapshot_bytes_per_sec" .=) <$> fsrMaxSnapshotBytesPerSec
4487− ]
4488− fromGSnapshotRepo GenericSnapshotRepo {..}
4489− | gSnapshotRepoType == fsRepoType = do
4490− let o = gSnapshotRepoSettingsObject gSnapshotRepoSettings
4491− parseRepo $ do
4492− FsSnapshotRepo gSnapshotRepoName <$> o .: "location"
4493− <*> o .:? "compress" .!= False
4494− <*> o .:? "chunk_size"
4495− <*> o .:? "max_restore_bytes_per_sec"
4496− <*> o .:? "max_snapshot_bytes_per_sec"
4497− | otherwise = Left (RepoTypeMismatch fsRepoType gSnapshotRepoType)
4498−
4499−
4500−parseRepo :: Parser a -> Either SnapshotRepoConversionError a
4501−parseRepo parser = case parseEither (const parser) () of
4502− Left e -> Left (OtherRepoConversionError (T.pack e))
4503− Right a -> Right a
4504−
4505−
4506−fsRepoType :: SnapshotRepoType
4507−fsRepoType = SnapshotRepoType "fs"
4508−
4509−-- | Law: fromGSnapshotRepo (toGSnapshotRepo r) == Right r
4510−class SnapshotRepo r where
4511− toGSnapshotRepo :: r -> GenericSnapshotRepo
4512− fromGSnapshotRepo :: GenericSnapshotRepo -> Either SnapshotRepoConversionError r
4513−
4514−
4515−data SnapshotRepoConversionError = RepoTypeMismatch SnapshotRepoType SnapshotRepoType
4516− -- ^ Expected type and actual type
4517− | OtherRepoConversionError Text
4518− deriving (Show, Eq, Generic, Typeable)
4519−
4520−
4521−instance Exception SnapshotRepoConversionError
4522−
4523−
4524−data SnapshotCreateSettings = SnapshotCreateSettings {
4525− snapWaitForCompletion :: Bool
4526− -- ^ Should the API call return immediately after initializing
4527− -- the snapshot or wait until completed? Note that if this is
4528− -- enabled it could wait a long time, so you should adjust your
4529− -- 'ManagerSettings' accordingly to set long timeouts or
4530− -- explicitly handle timeouts.
4531− , snapIndices :: Maybe IndexSelection
4532− -- ^ Nothing will snapshot all indices. Just [] is permissable and
4533− -- will essentially be a no-op snapshot.
4534− , snapIgnoreUnavailable :: Bool
4535− -- ^ If set to True, any matched indices that don't exist will be
4536− -- ignored. Otherwise it will be an error and fail.
4537− , snapIncludeGlobalState :: Bool
4538− , snapPartial :: Bool
4539− -- ^ If some indices failed to snapshot (e.g. if not all primary
4540− -- shards are available), should the process proceed?
4541− } deriving (Eq, Generic, Show, Typeable)
4542−
4543−
4544−-- | Reasonable defaults for snapshot creation
4545−--
4546−-- * snapWaitForCompletion False
4547−-- * snapIndices Nothing
4548−-- * snapIgnoreUnavailable False
4549−-- * snapIncludeGlobalState True
4550−-- * snapPartial False
4551−defaultSnapshotCreateSettings :: SnapshotCreateSettings
4552−defaultSnapshotCreateSettings = SnapshotCreateSettings {
4553− snapWaitForCompletion = False
4554− , snapIndices = Nothing
4555− , snapIgnoreUnavailable = False
4556− , snapIncludeGlobalState = True
4557− , snapPartial = False
4558− }
4559−
4560−
4561−data SnapshotSelection = SnapshotList (NonEmpty SnapshotPattern)
4562− | AllSnapshots deriving (Eq, Generic, Show, Typeable)
4563−
4564−
4565−-- | Either specifies an exact snapshot name or one with globs in it,
4566−-- e.g. @SnapPattern "foo*"@ __NOTE__: Patterns are not supported on
4567−-- ES < 1.7
4568−data SnapshotPattern = ExactSnap SnapshotName
4569− | SnapPattern Text
4570− deriving (Eq, Generic, Show, Typeable)
4571−
4572−
4573−-- | General information about the state of a snapshot. Has some
4574−-- redundancies with 'SnapshotStatus'
4575−data SnapshotInfo = SnapshotInfo {
4576− snapInfoShards :: ShardResult
4577− , snapInfoFailures :: [SnapshotShardFailure]
4578− , snapInfoDuration :: NominalDiffTime
4579− , snapInfoEndTime :: UTCTime
4580− , snapInfoStartTime :: UTCTime
4581− , snapInfoState :: SnapshotState
4582− , snapInfoIndices :: [IndexName]
4583− , snapInfoName :: SnapshotName
4584− } deriving (Eq, Generic, Show, Typeable)
4585−
4586−
4587−instance FromJSON SnapshotInfo where
4588− parseJSON = withObject "SnapshotInfo" parse
4589− where
4590− parse o = SnapshotInfo <$> o .: "shards"
4591− <*> o .: "failures"
4592− <*> (unMS <$> o .: "duration_in_millis")
4593− <*> (posixMS <$> o .: "end_time_in_millis")
4594− <*> (posixMS <$> o .: "start_time_in_millis")
4595− <*> o .: "state"
4596− <*> o .: "indices"
4597− <*> o .: "snapshot"
4598−
4599−data SnapshotShardFailure = SnapshotShardFailure {
4600− snapShardFailureIndex :: IndexName
4601− , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
4602− , snapShardFailureReason :: Text
4603− , snapShardFailureShardId :: ShardId
4604− } deriving (Eq, Show, Generic, Typeable)
4605−
4606−
4607−instance FromJSON SnapshotShardFailure where
4608− parseJSON = withObject "SnapshotShardFailure" parse
4609− where
4610− parse o = SnapshotShardFailure <$> o .: "index"
4611− <*> o .:? "node_id"
4612− <*> o .: "reason"
4613− <*> o .: "shard_id"
4614−
4615−
4616−newtype ShardId = ShardId { shardId :: Int }
4617− deriving (Eq, Show, Generic, Typeable, FromJSON)
4618−
4619−-- | Milliseconds
4620−newtype MS = MS NominalDiffTime
4621−
4622−
4623−-- keeps the unexported constructor warnings at bay
4624−unMS :: MS -> NominalDiffTime
4625−unMS (MS t) = t
4626−
4627−
4628−instance FromJSON MS where
4629− parseJSON = withScientific "MS" (return . MS . parse)
4630− where
4631− parse n = fromInteger ((truncate n) * 1000)
4632−
4633−
4634−data SnapshotState = SnapshotInit
4635− | SnapshotStarted
4636− | SnapshotSuccess
4637− | SnapshotFailed
4638− | SnapshotAborted
4639− | SnapshotMissing
4640− | SnapshotWaiting
4641− deriving (Show, Eq, Generic, Typeable)
4642−
4643−instance FromJSON SnapshotState where
4644− parseJSON = withText "SnapshotState" parse
4645− where
4646− parse "INIT" = return SnapshotInit
4647− parse "STARTED" = return SnapshotStarted
4648− parse "SUCCESS" = return SnapshotSuccess
4649− parse "FAILED" = return SnapshotFailed
4650− parse "ABORTED" = return SnapshotAborted
4651− parse "MISSING" = return SnapshotMissing
4652− parse "WAITING" = return SnapshotWaiting
4653− parse t = fail ("Invalid snapshot state " <> T.unpack t)
4654−
4655−
4656−newtype SnapshotName = SnapshotName { snapshotName :: Text }
4657− deriving (Show, Eq, Ord, Generic, Typeable, ToJSON, FromJSON)
4658−
4659−
4660−data SnapshotRestoreSettings = SnapshotRestoreSettings {
4661− snapRestoreWaitForCompletion :: Bool
4662− -- ^ Should the API call return immediately after initializing
4663− -- the restore or wait until completed? Note that if this is
4664− -- enabled, it could wait a long time, so you should adjust your
4665− -- 'ManagerSettings' accordingly to set long timeouts or
4666− -- explicitly handle timeouts.
4667− , snapRestoreIndices :: Maybe IndexSelection
4668− -- ^ Nothing will restore all indices in the snapshot. Just [] is
4669− -- permissable and will essentially be a no-op restore.
4670− , snapRestoreIgnoreUnavailable :: Bool
4671− -- ^ If set to True, any indices that do not exist will be ignored
4672− -- during snapshot rather than failing the restore.
4673− , snapRestoreIncludeGlobalState :: Bool
4674− -- ^ If set to false, will ignore any global state in the snapshot
4675− -- and will not restore it.
4676− , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
4677− -- ^ A regex pattern for matching indices. Used with
4678− -- 'snapRestoreRenameReplacement', the restore can reference the
4679− -- matched index and create a new index name upon restore.
4680− , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
4681− -- ^ Expression of how index renames should be constructed.
4682− , snapRestorePartial :: Bool
4683− -- ^ If some indices fail to restore, should the process proceed?
4684− , snapRestoreIncludeAliases :: Bool
4685− -- ^ Should the restore also restore the aliases captured in the
4686− -- snapshot.
4687− , snapRestoreIndexSettingsOverrides :: Maybe RestoreIndexSettings
4688− -- ^ Settings to apply during the restore process. __NOTE:__ This
4689− -- option is not supported in ES < 1.5 and should be set to
4690− -- Nothing in that case.
4691− , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
4692− -- ^ This type could be more rich but it isn't clear which
4693− -- settings are allowed to be ignored during restore, so we're
4694− -- going with including this feature in a basic form rather than
4695− -- omitting it. One example here would be
4696− -- "index.refresh_interval". Any setting specified here will
4697− -- revert back to the server default during the restore process.
4698− } deriving (Eq, Generic, Show, Typeable)
4699−
4700−-- | Regex-stype pattern, e.g. "index_(.+)" to match index names
4701−newtype RestoreRenamePattern = RestoreRenamePattern { rrPattern :: Text }
4702− deriving (Show, Eq, Generic, Typeable, Ord, ToJSON)
4703−
4704−
4705−-- | A single token in a index renaming scheme for a restore. These
4706−-- are concatenated into a string before being sent to
4707−-- Elasticsearch. Check out these Java
4708−-- <https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html docs> to find out more if you're into that sort of thing.
4709−data RestoreRenameToken = RRTLit Text
4710− -- ^ Just a literal string of characters
4711− | RRSubWholeMatch
4712− -- ^ Equivalent to $0. The entire matched pattern, not any subgroup
4713− | RRSubGroup RRGroupRefNum
4714− -- ^ A specific reference to a group number
4715− deriving (Show, Eq, Generic, Typeable)
4716−
4717−
4718−-- | A group number for regex matching. Only values from 1-9 are
4719−-- supported. Construct with 'mkRRGroupRefNum'
4720−newtype RRGroupRefNum = RRGroupRefNum { rrGroupRefNum :: Int }
4721− deriving (Show, Eq, Generic, Typeable, Ord)
4722−
4723−instance Bounded RRGroupRefNum where
4724− minBound = RRGroupRefNum 1
4725− maxBound = RRGroupRefNum 9
4726−
4727−
4728−-- | Only allows valid group number references (1-9).
4729−mkRRGroupRefNum :: Int -> Maybe RRGroupRefNum
4730−mkRRGroupRefNum i
4731− | i >= (rrGroupRefNum minBound) && i <= (rrGroupRefNum maxBound) =
4732− Just $ RRGroupRefNum i
4733− | otherwise = Nothing
4734−
4735−
4736−-- | Reasonable defaults for snapshot restores
4737−--
4738−-- * snapRestoreWaitForCompletion False
4739−-- * snapRestoreIndices Nothing
4740−-- * snapRestoreIgnoreUnavailable False
4741−-- * snapRestoreIncludeGlobalState True
4742−-- * snapRestoreRenamePattern Nothing
4743−-- * snapRestoreRenameReplacement Nothing
4744−-- * snapRestorePartial False
4745−-- * snapRestoreIncludeAliases True
4746−-- * snapRestoreIndexSettingsOverrides Nothing
4747−-- * snapRestoreIgnoreIndexSettings Nothing
4748−defaultSnapshotRestoreSettings :: SnapshotRestoreSettings
4749−defaultSnapshotRestoreSettings = SnapshotRestoreSettings {
4750− snapRestoreWaitForCompletion = False
4751− , snapRestoreIndices = Nothing
4752− , snapRestoreIgnoreUnavailable = False
4753− , snapRestoreIncludeGlobalState = True
4754− , snapRestoreRenamePattern = Nothing
4755− , snapRestoreRenameReplacement = Nothing
4756− , snapRestorePartial = False
4757− , snapRestoreIncludeAliases = True
4758− , snapRestoreIndexSettingsOverrides = Nothing
4759− , snapRestoreIgnoreIndexSettings = Nothing
4760− }
4761−
4762−
4763−-- | Index settings that can be overridden. The docs only mention you
4764−-- can update number of replicas, but there may be more. You
4765−-- definitely cannot override shard count.
4766−data RestoreIndexSettings = RestoreIndexSettings {
4767− restoreOverrideReplicas :: Maybe ReplicaCount
4768− } deriving (Show, Eq, Generic, Typeable)
4769−
4770−
4771−instance ToJSON RestoreIndexSettings where
4772− toJSON RestoreIndexSettings {..} = object prs
4773− where
4774− prs = catMaybes [("index.number_of_replicas" .=) <$> restoreOverrideReplicas]
4775−
4776−
4777−instance FromJSON NodesInfo where
4778− parseJSON = withObject "NodesInfo" parse
4779− where
4780− parse o = do
4781− nodes <- o .: "nodes"
4782− infos <- forM (HM.toList nodes) $ \(fullNID, v) -> do
4783− node <- parseJSON v
4784− parseNodeInfo (FullNodeId fullNID) node
4785− cn <- o .: "cluster_name"
4786− return (NodesInfo infos cn)
4787−
4788−instance FromJSON NodesStats where
4789− parseJSON = withObject "NodesStats" parse
4790− where
4791− parse o = do
4792− nodes <- o .: "nodes"
4793− stats <- forM (HM.toList nodes) $ \(fullNID, v) -> do
4794− node <- parseJSON v
4795− parseNodeStats (FullNodeId fullNID) node
4796− cn <- o .: "cluster_name"
4797− return (NodesStats stats cn)
4798−
4799−instance FromJSON NodeBreakerStats where
4800− parseJSON = withObject "NodeBreakerStats" parse
4801− where
4802− parse o = NodeBreakerStats <$> o .: "tripped"
4803− <*> o .: "overhead"
4804− <*> o .: "estimated_size_in_bytes"
4805− <*> o .: "limit_size_in_bytes"
4806−
4807−instance FromJSON NodeHTTPStats where
4808− parseJSON = withObject "NodeHTTPStats" parse
4809− where
4810− parse o = NodeHTTPStats <$> o .: "total_opened"
4811− <*> o .: "current_open"
4812−
4813−instance FromJSON NodeTransportStats where
4814− parseJSON = withObject "NodeTransportStats" parse
4815− where
4816− parse o = NodeTransportStats <$> o .: "tx_size_in_bytes"
4817− <*> o .: "tx_count"
4818− <*> o .: "rx_size_in_bytes"
4819− <*> o .: "rx_count"
4820− <*> o .: "server_open"
4821−
4822−instance FromJSON NodeFSStats where
4823− parseJSON = withObject "NodeFSStats" parse
4824− where
4825− parse o = NodeFSStats <$> o .: "data"
4826− <*> o .: "total"
4827− <*> (posixMS <$> o .: "timestamp")
4828−
4829−instance FromJSON NodeDataPathStats where
4830− parseJSON = withObject "NodeDataPathStats" parse
4831− where
4832− parse o =
4833− NodeDataPathStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
4834− <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
4835− <*> o .:? "disk_io_size_in_bytes"
4836− <*> o .:? "disk_write_size_in_bytes"
4837− <*> o .:? "disk_read_size_in_bytes"
4838− <*> o .:? "disk_io_op"
4839− <*> o .:? "disk_writes"
4840− <*> o .:? "disk_reads"
4841− <*> o .: "available_in_bytes"
4842− <*> o .: "free_in_bytes"
4843− <*> o .: "total_in_bytes"
4844− <*> o .:? "type"
4845− <*> o .: "dev"
4846− <*> o .: "mount"
4847− <*> o .: "path"
4848−
4849−newtype StringlyTypedDouble = StringlyTypedDouble { unStringlyTypedDouble :: Double }
4850−
4851−
4852−instance FromJSON StringlyTypedDouble where
4853− parseJSON = fmap StringlyTypedDouble . parseJSON . unStringlyTypeJSON
4854−
4855−
4856−instance FromJSON NodeFSTotalStats where
4857− parseJSON = withObject "NodeFSTotalStats" parse
4858− where
4859− parse o = NodeFSTotalStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
4860− <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
4861− <*> o .:? "disk_io_size_in_bytes"
4862− <*> o .:? "disk_write_size_in_bytes"
4863− <*> o .:? "disk_read_size_in_bytes"
4864− <*> o .:? "disk_io_op"
4865− <*> o .:? "disk_writes"
4866− <*> o .:? "disk_reads"
4867− <*> o .: "available_in_bytes"
4868− <*> o .: "free_in_bytes"
4869− <*> o .: "total_in_bytes"
4870−
4871−instance FromJSON NodeNetworkStats where
4872− parseJSON = withObject "NodeNetworkStats" parse
4873− where
4874− parse o = do
4875− tcp <- o .: "tcp"
4876− NodeNetworkStats <$> tcp .: "out_rsts"
4877− <*> tcp .: "in_errs"
4878− <*> tcp .: "attempt_fails"
4879− <*> tcp .: "estab_resets"
4880− <*> tcp .: "retrans_segs"
4881− <*> tcp .: "out_segs"
4882− <*> tcp .: "in_segs"
4883− <*> tcp .: "curr_estab"
4884− <*> tcp .: "passive_opens"
4885− <*> tcp .: "active_opens"
4886−
4887−instance FromJSON NodeThreadPoolsStats where
4888− parseJSON = withObject "NodeThreadPoolsStats" parse
4889− where
4890− parse o = NodeThreadPoolsStats <$> o .: "snapshot"
4891− <*> o .: "bulk"
4892− <*> o .: "merge"
4893− <*> o .: "get"
4894− <*> o .: "management"
4895− <*> o .:? "fetch_shard_store"
4896− <*> o .: "optimize"
4897− <*> o .: "flush"
4898− <*> o .: "search"
4899− <*> o .: "warmer"
4900− <*> o .: "generic"
4901− <*> o .: "suggest"
4902− <*> o .: "refresh"
4903− <*> o .: "index"
4904− <*> o .:? "listener"
4905− <*> o .:? "fetch_shard_started"
4906− <*> o .: "percolate"
4907−instance FromJSON NodeThreadPoolStats where
4908− parseJSON = withObject "NodeThreadPoolStats" parse
4909− where
4910− parse o = NodeThreadPoolStats <$> o .: "completed"
4911− <*> o .: "largest"
4912− <*> o .: "rejected"
4913− <*> o .: "active"
4914− <*> o .: "queue"
4915− <*> o .: "threads"
4916−
4917−instance FromJSON NodeJVMStats where
4918− parseJSON = withObject "NodeJVMStats" parse
4919− where
4920− parse o = do
4921− bufferPools <- o .: "buffer_pools"
4922− mapped <- bufferPools .: "mapped"
4923− direct <- bufferPools .: "direct"
4924− gc <- o .: "gc"
4925− collectors <- gc .: "collectors"
4926− oldC <- collectors .: "old"
4927− youngC <- collectors .: "young"
4928− threads <- o .: "threads"
4929− mem <- o .: "mem"
4930− pools <- mem .: "pools"
4931− oldM <- pools .: "old"
4932− survivorM <- pools .: "survivor"
4933− youngM <- pools .: "young"
4934− NodeJVMStats <$> pure mapped
4935− <*> pure direct
4936− <*> pure oldC
4937− <*> pure youngC
4938− <*> threads .: "peak_count"
4939− <*> threads .: "count"
4940− <*> pure oldM
4941− <*> pure survivorM
4942− <*> pure youngM
4943− <*> mem .: "non_heap_committed_in_bytes"
4944− <*> mem .: "non_heap_used_in_bytes"
4945− <*> mem .: "heap_max_in_bytes"
4946− <*> mem .: "heap_committed_in_bytes"
4947− <*> mem .: "heap_used_percent"
4948− <*> mem .: "heap_used_in_bytes"
4949− <*> (unMS <$> o .: "uptime_in_millis")
4950− <*> (posixMS <$> o .: "timestamp")
4951−
4952−instance FromJSON JVMBufferPoolStats where
4953− parseJSON = withObject "JVMBufferPoolStats" parse
4954− where
4955− parse o = JVMBufferPoolStats <$> o .: "total_capacity_in_bytes"
4956− <*> o .: "used_in_bytes"
4957− <*> o .: "count"
4958−
4959−instance FromJSON JVMGCStats where
4960− parseJSON = withObject "JVMGCStats" parse
4961− where
4962− parse o = JVMGCStats <$> (unMS <$> o .: "collection_time_in_millis")
4963− <*> o .: "collection_count"
4964−
4965−instance FromJSON JVMPoolStats where
4966− parseJSON = withObject "JVMPoolStats" parse
4967− where
4968− parse o = JVMPoolStats <$> o .: "peak_max_in_bytes"
4969− <*> o .: "peak_used_in_bytes"
4970− <*> o .: "max_in_bytes"
4971− <*> o .: "used_in_bytes"
4972−
4973−instance FromJSON NodeProcessStats where
4974− parseJSON = withObject "NodeProcessStats" parse
4975− where
4976− parse o = do
4977− mem <- o .: "mem"
4978− cpu <- o .: "cpu"
4979− NodeProcessStats <$> mem .: "total_virtual_in_bytes"
4980− <*> mem .: "share_in_bytes"
4981− <*> mem .: "resident_in_bytes"
4982− <*> (unMS <$> cpu .: "total_in_millis")
4983− <*> (unMS <$> cpu .: "user_in_millis")
4984− <*> (unMS <$> cpu .: "sys_in_millis")
4985− <*> cpu .: "percent"
4986− <*> o .: "open_file_descriptors"
4987− <*> (posixMS <$> o .: "timestamp")
4988−
4989−instance FromJSON NodeOSStats where
4990− parseJSON = withObject "NodeOSStats" parse
4991− where
4992− parse o = do
4993− swap <- o .: "swap"
4994− mem <- o .: "mem"
4995− cpu <- o .: "cpu"
4996− load <- o .:? "load_average"
4997− NodeOSStats <$> swap .: "free_in_bytes"
4998− <*> swap .: "used_in_bytes"
4999− <*> mem .: "actual_used_in_bytes"
5000− <*> mem .: "actual_free_in_bytes"
5001− <*> mem .: "used_percent"
5002− <*> mem .: "free_percent"
5003− <*> mem .: "used_in_bytes"
5004− <*> mem .: "free_in_bytes"
5005− <*> cpu .: "stolen"
5006− <*> cpu .: "usage"
5007− <*> cpu .: "idle"
5008− <*> cpu .: "user"
5009− <*> cpu .: "sys"
5010− <*> pure load
5011− <*> (unMS <$> o .: "uptime_in_millis")
5012− <*> (posixMS <$> o .: "timestamp")
5013−
5014−instance FromJSON LoadAvgs where
5015− parseJSON = withArray "LoadAvgs" parse
5016− where
5017− parse v = case V.toList v of
5018− [one, five, fifteen] -> LoadAvgs <$> parseJSON one
5019− <*> parseJSON five
5020− <*> parseJSON fifteen
5021− _ -> fail "Expecting a triple of Doubles"
5022−
5023−instance FromJSON NodeIndicesStats where
5024− parseJSON = withObject "NodeIndicesStats" parse
5025− where
5026− parse o = do
5027− let (.::) mv k = case mv of
5028− Just v -> Just <$> v .: k
5029− Nothing -> pure Nothing
5030− mRecovery <- o .:? "recovery"
5031− mQueryCache <- o .:? "query_cache"
5032− suggest <- o .: "suggest"
5033− translog <- o .: "translog"
5034− segments <- o .: "segments"
5035− completion <- o .: "completion"
5036− percolate <- o .: "percolate"
5037− fielddata <- o .: "fielddata"
5038− idCache <- o .: "id_cache"
5039− filterCache <- o .: "filter_cache"
5040− warmer <- o .: "warmer"
5041− flush <- o .: "flush"
5042− refresh <- o .: "refresh"
5043− merges <- o .: "merges"
5044− search <- o .: "search"
5045− getStats <- o .: "get"
5046− indexing <- o .: "indexing"
5047− store <- o .: "store"
5048− docs <- o .: "docs"
5049− NodeIndicesStats <$> (fmap unMS <$> mRecovery .:: "throttle_time_in_millis")
5050− <*> mRecovery .:: "current_as_target"
5051− <*> mRecovery .:: "current_as_source"
5052− <*> mQueryCache .:: "miss_count"
5053− <*> mQueryCache .:: "hit_count"
5054− <*> mQueryCache .:: "evictions"
5055− <*> mQueryCache .:: "memory_size_in_bytes"
5056− <*> suggest .: "current"
5057− <*> (unMS <$> suggest .: "time_in_millis")
5058− <*> suggest .: "total"
5059− <*> translog .: "size_in_bytes"
5060− <*> translog .: "operations"
5061− <*> segments .:? "fixed_bit_set_memory_in_bytes"
5062− <*> segments .: "version_map_memory_in_bytes"
5063− <*> segments .:? "index_writer_max_memory_in_bytes"
5064− <*> segments .: "index_writer_memory_in_bytes"
5065− <*> segments .: "memory_in_bytes"
5066− <*> segments .: "count"
5067− <*> completion .: "size_in_bytes"
5068− <*> percolate .: "queries"
5069− <*> percolate .: "memory_size_in_bytes"
5070− <*> percolate .: "current"
5071− <*> (unMS <$> percolate .: "time_in_millis")
5072− <*> percolate .: "total"
5073− <*> fielddata .: "evictions"
5074− <*> fielddata .: "memory_size_in_bytes"
5075− <*> idCache .: "memory_size_in_bytes"
5076− <*> filterCache .: "evictions"
5077− <*> filterCache .: "memory_size_in_bytes"
5078− <*> (unMS <$> warmer .: "total_time_in_millis")
5079− <*> warmer .: "total"
5080− <*> warmer .: "current"
5081− <*> (unMS <$> flush .: "total_time_in_millis")
5082− <*> flush .: "total"
5083− <*> (unMS <$> refresh .: "total_time_in_millis")
5084− <*> refresh .: "total"
5085− <*> merges .: "total_size_in_bytes"
5086− <*> merges .: "total_docs"
5087− <*> (unMS <$> merges .: "total_time_in_millis")
5088− <*> merges .: "total"
5089− <*> merges .: "current_size_in_bytes"
5090− <*> merges .: "current_docs"
5091− <*> merges .: "current"
5092− <*> search .: "fetch_current"
5093− <*> (unMS <$> search .: "fetch_time_in_millis")
5094− <*> search .: "fetch_total"
5095− <*> search .: "query_current"
5096− <*> (unMS <$> search .: "query_time_in_millis")
5097− <*> search .: "query_total"
5098− <*> search .: "open_contexts"
5099− <*> getStats .: "current"
5100− <*> (unMS <$> getStats .: "missing_time_in_millis")
5101− <*> getStats .: "missing_total"
5102− <*> (unMS <$> getStats .: "exists_time_in_millis")
5103− <*> getStats .: "exists_total"
5104− <*> (unMS <$> getStats .: "time_in_millis")
5105− <*> getStats .: "total"
5106− <*> (fmap unMS <$> indexing .:? "throttle_time_in_millis")
5107− <*> indexing .:? "is_throttled"
5108− <*> indexing .:? "noop_update_total"
5109− <*> indexing .: "delete_current"
5110− <*> (unMS <$> indexing .: "delete_time_in_millis")
5111− <*> indexing .: "delete_total"
5112− <*> indexing .: "index_current"
5113− <*> (unMS <$> indexing .: "index_time_in_millis")
5114− <*> indexing .: "index_total"
5115− <*> (unMS <$> store .: "throttle_time_in_millis")
5116− <*> store .: "size_in_bytes"
5117− <*> docs .: "deleted"
5118− <*> docs .: "count"
5119−
5120−instance FromJSON NodeBreakersStats where
5121− parseJSON = withObject "NodeBreakersStats" parse
5122− where
5123− parse o = NodeBreakersStats <$> o .: "parent"
5124− <*> o .: "request"
5125− <*> o .: "fielddata"
5126−
5127−parseNodeStats :: FullNodeId -> Object -> Parser NodeStats
5128−parseNodeStats fnid o = do
5129− NodeStats <$> o .: "name"
5130− <*> pure fnid
5131− <*> o .:? "breakers"
5132− <*> o .: "http"
5133− <*> o .: "transport"
5134− <*> o .: "fs"
5135− <*> o .: "network"
5136− <*> o .: "thread_pool"
5137− <*> o .: "jvm"
5138− <*> o .: "process"
5139− <*> o .: "os"
5140− <*> o .: "indices"
5141−
5142−parseNodeInfo :: FullNodeId -> Object -> Parser NodeInfo
5143−parseNodeInfo nid o =
5144− NodeInfo <$> o .: "http_address"
5145− <*> o .: "build"
5146− <*> o .: "version"
5147− <*> o .: "ip"
5148− <*> o .: "host"
5149− <*> o .: "transport_address"
5150− <*> o .: "name"
5151− <*> pure nid
5152− <*> o .: "plugins"
5153− <*> o .: "http"
5154− <*> o .: "transport"
5155− <*> o .: "network"
5156− <*> o .: "thread_pool"
5157− <*> o .: "jvm"
5158− <*> o .: "process"
5159− <*> o .: "os"
5160− <*> o .: "settings"
5161−
5162−instance FromJSON NodePluginInfo where
5163− parseJSON = withObject "NodePluginInfo" parse
5164− where
5165− parse o = NodePluginInfo <$> o .: "site"
5166− <*> o .: "jvm"
5167− <*> o .: "description"
5168− <*> o .: "version"
5169− <*> o .: "name"
5170−
5171−instance FromJSON NodeHTTPInfo where
5172− parseJSON = withObject "NodeHTTPInfo" parse
5173− where
5174− parse o = NodeHTTPInfo <$> o .: "max_content_length_in_bytes"
5175− <*> parseJSON (Object o)
5176−
5177−instance FromJSON BoundTransportAddress where
5178− parseJSON = withObject "BoundTransportAddress" parse
5179− where
5180− parse o = BoundTransportAddress <$> o .: "publish_address"
5181− <*> o .: "bound_address"
5182−
5183−instance FromJSON NodeOSInfo where
5184− parseJSON = withObject "NodeOSInfo" parse
5185− where
5186− parse o = do
5187− swap <- o .: "swap"
5188− mem <- o .: "mem"
5189− NodeOSInfo <$> swap .: "total_in_bytes"
5190− <*> mem .: "total_in_bytes"
5191− <*> o .: "cpu"
5192− <*> o .: "available_processors"
5193− <*> (unMS <$> o .: "refresh_interval_in_millis")
5194−
5195−
5196−instance FromJSON CPUInfo where
5197− parseJSON = withObject "CPUInfo" parse
5198− where
5199− parse o = CPUInfo <$> o .: "cache_size_in_bytes"
5200− <*> o .: "cores_per_socket"
5201− <*> o .: "total_sockets"
5202− <*> o .: "total_cores"
5203− <*> o .: "mhz"
5204− <*> o .: "model"
5205− <*> o .: "vendor"
5206−
5207−instance FromJSON NodeProcessInfo where
5208− parseJSON = withObject "NodeProcessInfo" parse
5209− where
5210− parse o = NodeProcessInfo <$> o .: "mlockall"
5211− <*> o .: "max_file_descriptors"
5212− <*> o .: "id"
5213− <*> (unMS <$> o .: "refresh_interval_in_millis")
5214−
5215−instance FromJSON NodeJVMInfo where
5216− parseJSON = withObject "NodeJVMInfo" parse
5217− where
5218− parse o = NodeJVMInfo <$> o .: "memory_pools"
5219− <*> o .: "gc_collectors"
5220− <*> o .: "mem"
5221− <*> (posixMS <$> o .: "start_time_in_millis")
5222− <*> o .: "vm_vendor"
5223− <*> o .: "vm_version"
5224− <*> o .: "vm_name"
5225− <*> (unJVMVersion <$> o .: "version")
5226− <*> o .: "pid"
5227−
5228−instance FromJSON JVMVersion where
5229− parseJSON (String t) =
5230− JVMVersion <$> parseJSON (String (T.replace "_" "." t))
5231− parseJSON v = JVMVersion <$> parseJSON v
5232−
5233−instance FromJSON JVMMemoryInfo where
5234− parseJSON = withObject "JVMMemoryInfo" parse
5235− where
5236− parse o = JVMMemoryInfo <$> o .: "direct_max_in_bytes"
5237− <*> o .: "non_heap_max_in_bytes"
5238− <*> o .: "non_heap_init_in_bytes"
5239− <*> o .: "heap_max_in_bytes"
5240− <*> o .: "heap_init_in_bytes"
5241−
5242−instance FromJSON NodeThreadPoolsInfo where
5243− parseJSON = withObject "NodeThreadPoolsInfo" parse
5244− where
5245− parse o = NodeThreadPoolsInfo <$> o .: "refresh"
5246− <*> o .: "management"
5247− <*> o .: "percolate"
5248− <*> o .:? "listener"
5249− <*> o .:? "fetch_shard_started"
5250− <*> o .: "search"
5251− <*> o .: "flush"
5252− <*> o .: "warmer"
5253− <*> o .: "optimize"
5254− <*> o .: "bulk"
5255− <*> o .: "suggest"
5256− <*> o .: "merge"
5257− <*> o .: "snapshot"
5258− <*> o .: "get"
5259− <*> o .:? "fetch_shard_store"
5260− <*> o .: "index"
5261− <*> o .: "generic"
5262−
5263−instance FromJSON NodeThreadPoolInfo where
5264− parseJSON = withObject "NodeThreadPoolInfo" parse
5265− where
5266− parse o = do
5267− ka <- maybe (return Nothing) (fmap Just . parseStringInterval) =<< o .:? "keep_alive"
5268− NodeThreadPoolInfo <$> (parseJSON . unStringlyTypeJSON =<< o .: "queue_size")
5269− <*> pure ka
5270− <*> o .:? "min"
5271− <*> o .:? "max"
5272− <*> o .: "type"
5273−
5274−parseStringInterval :: (Monad m) => String -> m NominalDiffTime
5275−parseStringInterval s = case span isNumber s of
5276− ("", _) -> fail "Invalid interval"
5277− (nS, unitS) -> case (readMay nS, readMay unitS) of
5278− (Just n, Just unit) -> return (fromInteger (n * unitNDT unit))
5279− (Nothing, _) -> fail "Invalid interval number"
5280− (_, Nothing) -> fail "Invalid interval unit"
5281− where
5282− unitNDT Seconds = 1
5283− unitNDT Minutes = 60
5284− unitNDT Hours = 60 * 60
5285− unitNDT Days = 24 * 60 * 60
5286− unitNDT Weeks = 7 * 24 * 60 * 60
5287−
5288−instance FromJSON ThreadPoolSize where
5289− parseJSON v = parseAsNumber v <|> parseAsString v
5290− where
5291− parseAsNumber = parseAsInt <=< parseJSON
5292− parseAsInt (-1) = return ThreadPoolUnbounded
5293− parseAsInt n
5294− | n >= 0 = return (ThreadPoolBounded n)
5295− | otherwise = fail "Thread pool size must be >= -1."
5296− parseAsString = withText "ThreadPoolSize" $ \t ->
5297− case first (readMay . T.unpack) (T.span isNumber t) of
5298− (Just n, "k") -> return (ThreadPoolBounded (n * 1000))
5299− (Just n, "") -> return (ThreadPoolBounded n)
5300− _ -> fail ("Invalid thread pool size " <> T.unpack t)
5301−
5302−instance FromJSON ThreadPoolType where
5303− parseJSON = withText "ThreadPoolType" parse
5304− where
5305− parse "scaling" = return ThreadPoolScaling
5306− parse "fixed" = return ThreadPoolFixed
5307− parse "cached" = return ThreadPoolCached
5308− parse e = fail ("Unexpected thread pool type" <> T.unpack e)
5309−
5310−instance FromJSON NodeTransportInfo where
5311− parseJSON = withObject "NodeTransportInfo" parse
5312− where
5313− parse o = NodeTransportInfo <$> (maybe (return mempty) parseProfiles =<< o .:? "profiles")
5314− <*> parseJSON (Object o)
5315− parseProfiles (Object o) | HM.null o = return []
5316− parseProfiles v@(Array _) = parseJSON v
5317− parseProfiles Null = return []
5318− parseProfiles _ = fail "Could not parse profiles"
5319−
5320−instance FromJSON NodeNetworkInfo where
5321− parseJSON = withObject "NodeNetworkInfo" parse
5322− where
5323− parse o = NodeNetworkInfo <$> o .: "primary_interface"
5324− <*> (unMS <$> o .: "refresh_interval_in_millis")
5325−
5326−
5327−instance FromJSON NodeNetworkInterface where
5328− parseJSON = withObject "NodeNetworkInterface" parse
5329− where
5330− parse o = NodeNetworkInterface <$> o .: "mac_address"
5331− <*> o .: "name"
5332− <*> o .: "address"
5333−
5334−newtype MaybeNA a = MaybeNA { unMaybeNA :: Maybe a }
5335− deriving (Show, Eq)
5336−
5337−instance FromJSON a => FromJSON (MaybeNA a) where
5338− parseJSON (String "NA") = pure $ MaybeNA Nothing
5339− parseJSON o = MaybeNA . Just <$> parseJSON o
5340−
5341−
5342−data Suggest = Suggest { suggestText :: Text
5343− , suggestName :: Text
5344− , suggestType :: SuggestType
5345− }
5346− deriving (Show, Generic, Eq, Read, Typeable)
5347−
5348−instance ToJSON Suggest where
5349− toJSON Suggest{..} = object [ "text" .= suggestText
5350− , suggestName .= suggestType
5351− ]
5352−
5353−instance FromJSON Suggest where
5354− parseJSON (Object o) = do
5355− suggestText' <- o .: "text"
5356− let dropTextList = HM.toList $ HM.filterWithKey (\x _ -> x /= "text") o
5357− suggestName' <- case dropTextList of
5358− [(x, _)] -> return x
5359− _ -> fail "error parsing Suggest field name"
5360− suggestType' <- o .: suggestName'
5361− return $ Suggest suggestText' suggestName' suggestType'
5362− parseJSON x = typeMismatch "Suggest" x
5363−
5364−data SuggestType = SuggestTypePhraseSuggester PhraseSuggester
5365− deriving (Show, Generic, Eq, Read, Typeable)
5366−
5367−instance ToJSON SuggestType where
5368− toJSON (SuggestTypePhraseSuggester x) = object ["phrase" .= x]
5369−
5370−instance FromJSON SuggestType where
5371− parseJSON = withObject "SuggestType" parse
5372− where parse o = phraseSuggester `taggedWith` "phrase"
5373− where taggedWith parser k = parser =<< o .: k
5374− phraseSuggester = pure . SuggestTypePhraseSuggester
5375−
5376−data PhraseSuggester =
5377− PhraseSuggester { phraseSuggesterField :: FieldName
5378− , phraseSuggesterGramSize :: Maybe Int
5379− , phraseSuggesterRealWordErrorLikelihood :: Maybe Int
5380− , phraseSuggesterConfidence :: Maybe Int
5381− , phraseSuggesterMaxErrors :: Maybe Int
5382− , phraseSuggesterSeparator :: Maybe Text
5383− , phraseSuggesterSize :: Maybe Size
5384− , phraseSuggesterAnalyzer :: Maybe Analyzer
5385− , phraseSuggesterShardSize :: Maybe Int
5386− , phraseSuggesterHighlight :: Maybe PhraseSuggesterHighlighter
5387− , phraseSuggesterCollate :: Maybe PhraseSuggesterCollate
5388− , phraseSuggesterCandidateGenerators :: [DirectGenerators]
5389− }
5390− deriving (Show, Generic, Eq, Read, Typeable)
5391−
5392−instance ToJSON PhraseSuggester where
5393− toJSON PhraseSuggester{..} = omitNulls [ "field" .= phraseSuggesterField
5394− , "gram_size" .= phraseSuggesterGramSize
5395− , "real_word_error_likelihood" .= phraseSuggesterRealWordErrorLikelihood
5396− , "confidence" .= phraseSuggesterConfidence
5397− , "max_errors" .= phraseSuggesterMaxErrors
5398− , "separator" .= phraseSuggesterSeparator
5399− , "size" .= phraseSuggesterSize
5400− , "analyzer" .= phraseSuggesterAnalyzer
5401− , "shard_size" .= phraseSuggesterShardSize
5402− , "highlight" .= phraseSuggesterHighlight
5403− , "collate" .= phraseSuggesterCollate
5404− , "direct_generator" .= phraseSuggesterCandidateGenerators
5405− ]
5406−
5407−instance FromJSON PhraseSuggester where
5408− parseJSON = withObject "PhraseSuggester" parse
5409− where parse o = PhraseSuggester
5410− <$> o .: "field"
5411− <*> o .:? "gram_size"
5412− <*> o .:? "real_word_error_likelihood"
5413− <*> o .:? "confidence"
5414− <*> o .:? "max_errors"
5415− <*> o .:? "separator"
5416− <*> o .:? "size"
5417− <*> o .:? "analyzer"
5418− <*> o .:? "shard_size"
5419− <*> o .:? "highlight"
5420− <*> o .:? "collate"
5421− <*> o .:? "direct_generator" .!= []
5422−
5423−mkPhraseSuggester :: FieldName -> PhraseSuggester
5424−mkPhraseSuggester fName =
5425− PhraseSuggester fName Nothing Nothing Nothing Nothing Nothing Nothing
5426− Nothing Nothing Nothing Nothing []
5427−
5428−data PhraseSuggesterHighlighter =
5429− PhraseSuggesterHighlighter { phraseSuggesterHighlighterPreTag :: Text
5430− , phraseSuggesterHighlighterPostTag :: Text
5431− }
5432− deriving (Show, Generic, Eq, Read, Typeable)
5433−
5434−instance ToJSON PhraseSuggesterHighlighter where
5435− toJSON PhraseSuggesterHighlighter{..} =
5436− object [ "pre_tag" .= phraseSuggesterHighlighterPreTag
5437− , "post_tag" .= phraseSuggesterHighlighterPostTag
5438− ]
5439−
5440−instance FromJSON PhraseSuggesterHighlighter where
5441− parseJSON = withObject "PhraseSuggesterHighlighter" parse
5442− where parse o = PhraseSuggesterHighlighter
5443− <$> o .: "pre_tag"
5444− <*> o .: "post_tag"
5445−
5446−data PhraseSuggesterCollate =
5447− PhraseSuggesterCollate { phraseSuggesterCollateTemplateQuery :: TemplateQueryInline
5448− , phraseSuggesterCollatePrune :: Bool
5449− }
5450− deriving (Show, Generic, Eq, Read, Typeable)
5451−
5452−instance ToJSON PhraseSuggesterCollate where
5453− toJSON PhraseSuggesterCollate{..} = object [ "query" .= object
5454− [ "inline" .= (inline phraseSuggesterCollateTemplateQuery)
5455− ]
5456− , "params" .= (params phraseSuggesterCollateTemplateQuery)
5457− , "prune" .= phraseSuggesterCollatePrune
5458− ]
5459−
5460−instance FromJSON PhraseSuggesterCollate where
5461− parseJSON (Object o) = do
5462− query' <- o .: "query"
5463− inline' <- query' .: "inline"
5464− params' <- o .: "params"
5465− prune' <- o .:? "prune" .!= False
5466− return $ PhraseSuggesterCollate (TemplateQueryInline inline' params') prune'
5467− parseJSON x = typeMismatch "PhraseSuggesterCollate" x
5468−
5469−data SuggestOptions =
5470− SuggestOptions { suggestOptionsText :: Text
5471− , suggestOptionsScore :: Double
5472− , suggestOptionsFreq :: Maybe Int
5473− , suggestOptionsHighlighted :: Maybe Text
5474− }
5475− deriving (Eq, Read, Show)
5476−
5477−instance FromJSON SuggestOptions where
5478− parseJSON = withObject "SuggestOptions" parse
5479− where parse o = SuggestOptions
5480− <$> o .: "text"
5481− <*> o .: "score"
5482− <*> o .:? "freq"
5483− <*> o .:? "highlighted"
5484−
5485−data SuggestResponse =
5486− SuggestResponse { suggestResponseText :: Text
5487− , suggestResponseOffset :: Int
5488− , suggestResponseLength :: Int
5489− , suggestResponseOptions :: [SuggestOptions]
5490− }
5491− deriving (Eq, Read, Show)
5492−
5493−instance FromJSON SuggestResponse where
5494− parseJSON = withObject "SuggestResponse" parse
5495− where parse o = SuggestResponse
5496− <$> o .: "text"
5497− <*> o .: "offset"
5498− <*> o .: "length"
5499− <*> o .: "options"
5500−
5501−data NamedSuggestionResponse =
5502− NamedSuggestionResponse { nsrName :: Text
5503− , nsrResponses :: [SuggestResponse]
5504− }
5505− deriving (Eq, Read, Show)
5506−
5507−instance FromJSON NamedSuggestionResponse where
5508− parseJSON (Object o) = do
5509− suggestionName' <- case HM.toList o of
5510− [(x, _)] -> return x
5511− _ -> fail "error parsing NamedSuggestionResponse name"
5512− suggestionResponses' <- o .: suggestionName'
5513− return $ NamedSuggestionResponse suggestionName' suggestionResponses'
5514−
5515− parseJSON x = typeMismatch "NamedSuggestionResponse" x
5516−
5517−data DirectGeneratorSuggestModeTypes = DirectGeneratorSuggestModeMissing
5518− | DirectGeneratorSuggestModePopular
5519− | DirectGeneratorSuggestModeAlways
5520− deriving (Show, Eq, Read, Generic, Typeable)
5521−
5522−instance ToJSON DirectGeneratorSuggestModeTypes where
5523− toJSON DirectGeneratorSuggestModeMissing = "missing"
5524− toJSON DirectGeneratorSuggestModePopular = "popular"
5525− toJSON DirectGeneratorSuggestModeAlways = "always"
5526−
5527−instance FromJSON DirectGeneratorSuggestModeTypes where
5528− parseJSON = withText "DirectGeneratorSuggestModeTypes" parse
5529− where parse "missing" = pure DirectGeneratorSuggestModeMissing
5530− parse "popular" = pure DirectGeneratorSuggestModePopular
5531− parse "always" = pure DirectGeneratorSuggestModeAlways
5532− parse f = fail ("Unexpected DirectGeneratorSuggestModeTypes: " <> show f)
5533−
5534−data DirectGenerators = DirectGenerators
5535− { directGeneratorsField :: FieldName
5536− , directGeneratorsSize :: Maybe Int
5537− , directGeneratorSuggestMode :: DirectGeneratorSuggestModeTypes
5538− , directGeneratorMaxEdits :: Maybe Double
5539− , directGeneratorPrefixLength :: Maybe Int
5540− , directGeneratorMinWordLength :: Maybe Int
5541− , directGeneratorMaxInspections :: Maybe Int
5542− , directGeneratorMinDocFreq :: Maybe Double
5543− , directGeneratorMaxTermFreq :: Maybe Double
5544− , directGeneratorPreFilter :: Maybe Text
5545− , directGeneratorPostFilter :: Maybe Text
5546− }
5547− deriving (Show, Eq, Read, Generic, Typeable)
5548−
5549−
5550−instance ToJSON DirectGenerators where
5551− toJSON DirectGenerators{..} = omitNulls [ "field" .= directGeneratorsField
5552− , "size" .= directGeneratorsSize
5553− , "suggest_mode" .= directGeneratorSuggestMode
5554− , "max_edits" .= directGeneratorMaxEdits
5555− , "prefix_length" .= directGeneratorPrefixLength
5556− , "min_word_length" .= directGeneratorMinWordLength
5557− , "max_inspections" .= directGeneratorMaxInspections
5558− , "min_doc_freq" .= directGeneratorMinDocFreq
5559− , "max_term_freq" .= directGeneratorMaxTermFreq
5560− , "pre_filter" .= directGeneratorPreFilter
5561− , "post_filter" .= directGeneratorPostFilter
5562− ]
5563−
5564−instance FromJSON DirectGenerators where
5565− parseJSON = withObject "DirectGenerators" parse
5566− where parse o = DirectGenerators
5567− <$> o .: "field"
5568− <*> o .:? "size"
5569− <*> o .: "suggest_mode"
5570− <*> o .:? "max_edits"
5571− <*> o .:? "prefix_length"
5572− <*> o .:? "min_word_length"
5573− <*> o .:? "max_inspections"
5574− <*> o .:? "min_doc_freq"
5575− <*> o .:? "max_term_freq"
5576− <*> o .:? "pre_filter"
5577− <*> o .:? "post_filter"
5578−
5579−mkDirectGenerators :: FieldName -> DirectGenerators
5580−mkDirectGenerators fn = DirectGenerators fn Nothing DirectGeneratorSuggestModeMissing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
src/Database/V1/Bloodhound/Types/Internal.hsdeleted−57
@@ -1,57 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−
6−-------------------------------------------------------------------------------
7−-- |
8−-- Module : Database.Bloodhound.Types.Internal
9−-- Copyright : (C) 2014 Chris Allen
10−-- License : BSD-style (see the file LICENSE)
11−-- Maintainer : Chris Allen <cma@bitemyapp.com>
12−-- Stability : provisional
13−-- Portability : DeriveGeneric, RecordWildCards
14−--
15−-- Internal data types for Bloodhound. These types may change without
16−-- notice so import at your own risk.
17−-------------------------------------------------------------------------------
18−module Database.V1.Bloodhound.Types.Internal
19− ( BHEnv(..)
20− , Server(..)
21− , MonadBH(..)
22− ) where
23−
24−
25−import Control.Applicative as A
26−import Control.Monad.Reader
27−import Data.Aeson
28−import Data.Text (Text)
29−import Data.Typeable (Typeable)
30−import GHC.Generics (Generic)
31−import Network.HTTP.Client
32−
33−{-| Common environment for Elasticsearch calls. Connections will be
34− pipelined according to the provided HTTP connection manager.
35−-}
36−data BHEnv = BHEnv { bhServer :: Server
37− , bhManager :: Manager
38− , bhRequestHook :: Request -> IO Request
39− -- ^ Low-level hook that is run before every request is sent. Used to implement custom authentication strategies. Defaults to 'return' with 'mkBHEnv'.
40− }
41−
42−instance (Functor m, Applicative m, MonadIO m) => MonadBH (ReaderT BHEnv m) where
43− getBHEnv = ask
44−
45−{-| 'Server' is used with the client functions to point at the ES instance
46−-}
47−newtype Server = Server Text deriving (Eq, Show, Generic, Typeable, FromJSON)
48−
49−{-| All API calls to Elasticsearch operate within
50− MonadBH
51− . The idea is that it can be easily embedded in your
52− own monad transformer stack. A default instance for a ReaderT and
53− alias 'BH' is provided for the simple case.
54−-}
55−class (Functor m, A.Applicative m, MonadIO m) => MonadBH m where
56− getBHEnv :: m BHEnv
57−
src/Database/V5/Bloodhound/Client.hs+44−18
@@ -5,7 +5,7 @@
55-------------------------------------------------------------------------------
66-- |
77-- Module : Database.Bloodhound.Client
8−-- Copyright : (C) 2014 Chris Allen
8+-- Copyright : (C) 2014, 2018 Chris Allen
99-- License : BSD-style (see the file LICENSE)
1010-- Maintainer : Chris Allen <cma@bitemyapp.com>
1111-- Stability : provisional
@@ -216,6 +216,7 @@ dispatch dMethod url body = do
216216 $ setRequestIgnoreStatus
217217 $ initReq { method = dMethod
218218 , requestHeaders =
219+ -- "application/x-ndjson" for bulk
219220 ("Content-Type", "application/json") : requestHeaders initReq
220221 , requestBody = reqBody }
221222 -- req <- liftIO $ reqHook $ setRequestIgnoreStatus $ initReq { method = dMethod
@@ -316,7 +317,7 @@ instance FromJSON GSRs where
316317 parseJSON = withObject "Collection of GenericSnapshotRepo" parse
317318 where
318319 parse = fmap GSRs . mapM (uncurry go) . HM.toList
319− go rawName = withObject "GenericSnapshotRepo" $ \o -> do
320+ go rawName = withObject "GenericSnapshotRepo" $ \o ->
320321 GenericSnapshotRepo (SnapshotRepoName rawName) <$> o .: "type"
321322 <*> o .: "settings"
322323
@@ -554,16 +555,18 @@ deleteIndex (IndexName indexName) =
554555updateIndexSettings :: MonadBH m => NonEmpty UpdatableIndexSetting -> IndexName -> m Reply
555556updateIndexSettings updates (IndexName indexName) =
556557 bindM2 put url (return body)
557− where url = joinPath [indexName, "_settings"]
558− body = Just (encode jsonBody)
559− jsonBody = Object (deepMerge [u | Object u <- toJSON <$> toList updates])
558+ where
559+ url = joinPath [indexName, "_settings"]
560+ body = Just (encode jsonBody)
561+ jsonBody = Object (deepMerge [u | Object u <- toJSON <$> toList updates])
560562
561563
562564getIndexSettings :: (MonadBH m, MonadThrow m) => IndexName
563565 -> m (Either EsError IndexSettingsSummary)
564−getIndexSettings (IndexName indexName) = do
566+getIndexSettings (IndexName indexName) =
565567 parseEsResponse =<< get =<< url
566− where url = joinPath [indexName, "_settings"]
568+ where
569+ url = joinPath [indexName, "_settings"]
567570
568571-- | 'forceMergeIndex'
569572--
@@ -703,7 +706,7 @@ listIndices =
703706 url = joinPath ["_cat/indices?format=json"]
704707 parse body = maybe (throwM (EsProtocolException body)) return $ do
705708 vals <- decode body
706− forM vals $ \val -> do
709+ forM vals $ \val ->
707710 case val of
708711 Object obj -> do
709712 indexVal <- HM.lookup "index" obj
@@ -860,9 +863,11 @@ deleteDocument (IndexName indexName)
860863-- >>> _ <- runBH' $ bulk stream
861864-- >>> _ <- runBH' $ refreshIndex testIndex
862865bulk :: MonadBH m => V.Vector BulkOperation -> m Reply
863−bulk bulkOps = bindM2 post url (return body)
864− where url = joinPath ["_bulk"]
865− body = Just $ encodeBulkOperations bulkOps
866+bulk bulkOps =
867+ bindM2 post url (return body)
868+ where
869+ url = joinPath ["_bulk"]
870+ body = Just $ encodeBulkOperations bulkOps
866871
867872-- | 'encodeBulkOperations' is a convenience function for dumping a vector of 'BulkOperation'
868873-- into an 'L.ByteString'
@@ -872,12 +877,15 @@ bulk bulkOps = bindM2 post url (return body)
872877-- "\n{\"index\":{\"_type\":\"tweet\",\"_id\":\"2\",\"_index\":\"twitter\"}}\n{\"name\":\"blah\"}\n"
873878encodeBulkOperations :: V.Vector BulkOperation -> L.ByteString
874879encodeBulkOperations stream = collapsed where
875− blobs = fmap encodeBulkOperation stream
876− mashedTaters = mash (mempty :: Builder) blobs
877− collapsed = toLazyByteString $ mappend mashedTaters (byteString "\n")
880+ blobs =
881+ fmap encodeBulkOperation stream
882+ mashedTaters =
883+ mash (mempty :: Builder) blobs
884+ collapsed =
885+ toLazyByteString $ mappend mashedTaters (byteString "\n")
878886
879887mash :: Builder -> V.Vector L.ByteString -> Builder
880−mash = V.foldl' (\b x -> b `mappend` (byteString "\n") `mappend` (lazyByteString x))
888+mash = V.foldl' (\b x -> b <> byteString "\n" <> lazyByteString x)
881889
882890mkBulkStreamValue :: Text -> Text -> Text -> Text -> Value
883891mkBulkStreamValue operation indexName mappingName docId =
@@ -886,6 +894,12 @@ mkBulkStreamValue operation indexName mappingName docId =
886894 , "_type" .= mappingName
887895 , "_id" .= docId]]
888896
897+mkBulkStreamValueAuto :: Text -> Text -> Text -> Value
898+mkBulkStreamValueAuto operation indexName mappingName =
899+ object [operation .=
900+ object [ "_index" .= indexName
901+ , "_type" .= mappingName]]
902+
889903-- | 'encodeBulkOperation' is a convenience function for dumping a single 'BulkOperation'
890904-- into an 'L.ByteString'
891905--
@@ -899,6 +913,18 @@ encodeBulkOperation (BulkIndex (IndexName indexName)
899913 where metadata = mkBulkStreamValue "index" indexName mappingName docId
900914 blob = encode metadata `mappend` "\n" `mappend` encode value
901915
916+encodeBulkOperation (BulkIndexAuto (IndexName indexName)
917+ (MappingName mappingName)
918+ value) = blob
919+ where metadata = mkBulkStreamValueAuto "index" indexName mappingName
920+ blob = encode metadata `mappend` "\n" `mappend` encode value
921+
922+encodeBulkOperation (BulkIndexEncodingAuto (IndexName indexName)
923+ (MappingName mappingName)
924+ encoding) = toLazyByteString blob
925+ where metadata = toEncoding (mkBulkStreamValueAuto "index" indexName mappingName)
926+ blob = fromEncoding metadata <> "\n" <> fromEncoding encoding
927+
902928encodeBulkOperation (BulkCreate (IndexName indexName)
903929 (MappingName mappingName)
904930 (DocId docId) value) = blob
@@ -1099,7 +1125,7 @@ scanSearch indexName mappingName search = do
10991125-- >>> mkSearch (Just query) Nothing
11001126-- Search {queryBody = Just (TermQuery (Term {termField = "user", termValue = "bitemyapp"}) Nothing), filterBody = Nothing, sortBody = Nothing, aggBody = Nothing, highlight = Nothing, trackSortScores = False, from = From 0, size = Size 10, searchType = SearchTypeQueryThenFetch, fields = Nothing, source = Nothing}
11011127mkSearch :: Maybe Query -> Maybe Filter -> Search
1102−mkSearch query filter = Search query filter Nothing Nothing Nothing False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing
1128+mkSearch query filter = Search query filter Nothing Nothing Nothing False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
11031129
11041130-- | 'mkAggregateSearch' is a helper function that defaults everything in a 'Search' except for
11051131-- the 'Query' and the 'Aggregation'.
@@ -1109,7 +1135,7 @@ mkSearch query filter = Search query filter Nothing Nothing Nothing False (From
11091135-- TermsAgg (TermsAggregation {term = Left "user", termInclude = Nothing, termExclude = Nothing, termOrder = Nothing, termMinDocCount = Nothing, termSize = Nothing, termShardSize = Nothing, termCollectMode = Just BreadthFirst, termExecutionHint = Nothing, termAggs = Nothing})
11101136-- >>> let myAggregation = mkAggregateSearch Nothing $ mkAggregations "users" terms
11111137mkAggregateSearch :: Maybe Query -> Aggregations -> Search
1112−mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSearchAggs) Nothing False (From 0) (Size 0) SearchTypeQueryThenFetch Nothing Nothing Nothing
1138+mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSearchAggs) Nothing False (From 0) (Size 0) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
11131139
11141140-- | 'mkHighlightSearch' is a helper function that defaults everything in a 'Search' except for
11151141-- the 'Query' and the 'Aggregation'.
@@ -1118,7 +1144,7 @@ mkAggregateSearch query mkSearchAggs = Search query Nothing Nothing (Just mkSear
11181144-- >>> let testHighlight = Highlights Nothing [FieldHighlight (FieldName "message") Nothing]
11191145-- >>> let search = mkHighlightSearch (Just query) testHighlight
11201146mkHighlightSearch :: Maybe Query -> Highlights -> Search
1121−mkHighlightSearch query searchHighlights = Search query Nothing Nothing Nothing (Just searchHighlights) False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing
1147+mkHighlightSearch query searchHighlights = Search query Nothing Nothing Nothing (Just searchHighlights) False (From 0) (Size 10) SearchTypeQueryThenFetch Nothing Nothing Nothing Nothing
11221148
11231149-- | 'pageSearch' is a helper function that takes a search and assigns the from
11241150-- and size fields for the search. The from parameter defines the offset
src/Database/V5/Bloodhound/Internal/Aggregation.hsnew file+467
@@ -0,0 +1,467 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+module Database.V5.Bloodhound.Internal.Aggregation where
6+
7+import Bloodhound.Import
8+
9+import qualified Data.HashMap.Strict as HM
10+import qualified Data.Map.Strict as M
11+import qualified Data.Text as T
12+
13+import Database.V5.Bloodhound.Internal.Client
14+import Database.V5.Bloodhound.Internal.Highlight (HitHighlight)
15+import Database.V5.Bloodhound.Internal.Newtypes
16+import Database.V5.Bloodhound.Internal.Query
17+import Database.V5.Bloodhound.Internal.Sort
18+
19+type Aggregations = M.Map Text Aggregation
20+
21+emptyAggregations :: Aggregations
22+emptyAggregations = M.empty
23+
24+mkAggregations :: Text -> Aggregation -> Aggregations
25+mkAggregations name aggregation = M.insert name aggregation emptyAggregations
26+
27+data Aggregation = TermsAgg TermsAggregation
28+ | CardinalityAgg CardinalityAggregation
29+ | DateHistogramAgg DateHistogramAggregation
30+ | ValueCountAgg ValueCountAggregation
31+ | FilterAgg FilterAggregation
32+ | DateRangeAgg DateRangeAggregation
33+ | MissingAgg MissingAggregation
34+ | TopHitsAgg TopHitsAggregation
35+ | StatsAgg StatisticsAggregation
36+ deriving (Eq, Show)
37+
38+instance ToJSON Aggregation where
39+ toJSON (TermsAgg (TermsAggregation term include exclude order minDocCount size shardSize collectMode executionHint termAggs)) =
40+ omitNulls ["terms" .= omitNulls [ toJSON' term,
41+ "include" .= include,
42+ "exclude" .= exclude,
43+ "order" .= order,
44+ "min_doc_count" .= minDocCount,
45+ "size" .= size,
46+ "shard_size" .= shardSize,
47+ "collect_mode" .= collectMode,
48+ "execution_hint" .= executionHint
49+ ],
50+ "aggs" .= termAggs ]
51+ where
52+ toJSON' x = case x of { Left y -> "field" .= y; Right y -> "script" .= y }
53+
54+ toJSON (CardinalityAgg (CardinalityAggregation field precisionThreshold)) =
55+ object ["cardinality" .= omitNulls [ "field" .= field,
56+ "precisionThreshold" .= precisionThreshold
57+ ]
58+ ]
59+
60+ toJSON (DateHistogramAgg
61+ (DateHistogramAggregation field interval format
62+ preZone postZone preOffset postOffset dateHistoAggs)) =
63+ omitNulls ["date_histogram" .= omitNulls [ "field" .= field,
64+ "interval" .= interval,
65+ "format" .= format,
66+ "pre_zone" .= preZone,
67+ "post_zone" .= postZone,
68+ "pre_offset" .= preOffset,
69+ "post_offset" .= postOffset
70+ ],
71+ "aggs" .= dateHistoAggs ]
72+ toJSON (ValueCountAgg a) = object ["value_count" .= v]
73+ where v = case a of
74+ (FieldValueCount (FieldName n)) ->
75+ object ["field" .= n]
76+ (ScriptValueCount s) ->
77+ object ["script" .= s]
78+ toJSON (FilterAgg (FilterAggregation filt ags)) =
79+ omitNulls [ "filter" .= filt
80+ , "aggs" .= ags]
81+ toJSON (DateRangeAgg a) = object [ "date_range" .= a
82+ ]
83+ toJSON (MissingAgg (MissingAggregation{..})) =
84+ object ["missing" .= object ["field" .= maField]]
85+
86+ toJSON (TopHitsAgg (TopHitsAggregation mfrom msize msort)) =
87+ omitNulls ["top_hits" .= omitNulls [ "size" .= msize
88+ , "from" .= mfrom
89+ , "sort" .= msort
90+ ]
91+ ]
92+
93+ toJSON (StatsAgg (StatisticsAggregation typ field)) =
94+ object [stType .= omitNulls [ "field" .= field ]]
95+ where
96+ stType | typ == Basic = "stats"
97+ | otherwise = "extended_stats"
98+
99+data TopHitsAggregation = TopHitsAggregation
100+ { taFrom :: Maybe From
101+ , taSize :: Maybe Size
102+ , taSort :: Maybe Sort
103+ } deriving (Eq, Show)
104+
105+data MissingAggregation = MissingAggregation
106+ { maField :: Text
107+ } deriving (Eq, Show)
108+
109+data TermsAggregation = TermsAggregation
110+ { term :: Either Text Text
111+ , termInclude :: Maybe TermInclusion
112+ , termExclude :: Maybe TermInclusion
113+ , termOrder :: Maybe TermOrder
114+ , termMinDocCount :: Maybe Int
115+ , termSize :: Maybe Int
116+ , termShardSize :: Maybe Int
117+ , termCollectMode :: Maybe CollectionMode
118+ , termExecutionHint :: Maybe ExecutionHint
119+ , termAggs :: Maybe Aggregations
120+ } deriving (Eq, Show)
121+
122+data CardinalityAggregation = CardinalityAggregation
123+ { cardinalityField :: FieldName,
124+ precisionThreshold :: Maybe Int
125+ } deriving (Eq, Show)
126+
127+data DateHistogramAggregation = DateHistogramAggregation
128+ { dateField :: FieldName
129+ , dateInterval :: Interval
130+ , dateFormat :: Maybe Text
131+ -- pre and post deprecated in 1.5
132+ , datePreZone :: Maybe Text
133+ , datePostZone :: Maybe Text
134+ , datePreOffset :: Maybe Text
135+ , datePostOffset :: Maybe Text
136+ , dateAggs :: Maybe Aggregations
137+ } deriving (Eq, Show)
138+
139+data DateRangeAggregation = DateRangeAggregation
140+ { draField :: FieldName
141+ , draFormat :: Maybe Text
142+ , draRanges :: NonEmpty DateRangeAggRange
143+ } deriving (Eq, Show)
144+
145+instance ToJSON DateRangeAggregation where
146+ toJSON DateRangeAggregation {..} =
147+ omitNulls [ "field" .= draField
148+ , "format" .= draFormat
149+ , "ranges" .= toList draRanges
150+ ]
151+
152+data DateRangeAggRange =
153+ DateRangeFrom DateMathExpr
154+ | DateRangeTo DateMathExpr
155+ | DateRangeFromAndTo DateMathExpr DateMathExpr
156+ deriving (Eq, Show)
157+
158+instance ToJSON DateRangeAggRange where
159+ toJSON (DateRangeFrom e) = object [ "from" .= e ]
160+ toJSON (DateRangeTo e) = object [ "to" .= e ]
161+ toJSON (DateRangeFromAndTo f t) = object [ "from" .= f, "to" .= t ]
162+
163+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-valuecount-aggregation.html> for more information.
164+data ValueCountAggregation =
165+ FieldValueCount FieldName
166+ | ScriptValueCount Script
167+ deriving (Eq, Show)
168+
169+-- | 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.
170+data FilterAggregation = FilterAggregation
171+ { faFilter :: Filter
172+ , faAggs :: Maybe Aggregations }
173+ deriving (Eq, Show)
174+
175+data StatisticsAggregation = StatisticsAggregation
176+ { statsType :: StatsType
177+ , statsField :: FieldName }
178+ deriving (Eq, Show)
179+
180+data StatsType
181+ = Basic
182+ | Extended
183+ deriving (Eq, Show)
184+
185+mkTermsAggregation :: Text -> TermsAggregation
186+mkTermsAggregation t =
187+ TermsAggregation (Left t)
188+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
189+
190+mkTermsScriptAggregation :: Text -> TermsAggregation
191+mkTermsScriptAggregation t = TermsAggregation (Right t) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
192+
193+mkDateHistogram :: FieldName -> Interval -> DateHistogramAggregation
194+mkDateHistogram t i = DateHistogramAggregation t i Nothing Nothing Nothing Nothing Nothing Nothing
195+
196+mkCardinalityAggregation :: FieldName -> CardinalityAggregation
197+mkCardinalityAggregation t = CardinalityAggregation t Nothing
198+
199+mkStatsAggregation :: FieldName -> StatisticsAggregation
200+mkStatsAggregation = StatisticsAggregation Basic
201+
202+mkExtendedStatsAggregation :: FieldName -> StatisticsAggregation
203+mkExtendedStatsAggregation = StatisticsAggregation Extended
204+
205+type AggregationResults = M.Map Text Value
206+
207+class BucketAggregation a where
208+ key :: a -> BucketValue
209+ docCount :: a -> Int
210+ aggs :: a -> Maybe AggregationResults
211+
212+data Bucket a = Bucket
213+ { buckets :: [a]
214+ } deriving (Read, Show)
215+
216+instance (FromJSON a) => FromJSON (Bucket a) where
217+ parseJSON (Object v) = Bucket <$>
218+ v .: "buckets"
219+ parseJSON _ = mempty
220+
221+data BucketValue = TextValue Text
222+ | ScientificValue Scientific
223+ | BoolValue Bool deriving (Read, Show)
224+
225+instance FromJSON BucketValue where
226+ parseJSON (String t) = return $ TextValue t
227+ parseJSON (Number s) = return $ ScientificValue s
228+ parseJSON (Bool b) = return $ BoolValue b
229+ parseJSON _ = mempty
230+
231+data TermInclusion = TermInclusion Text
232+ | TermPattern Text Text deriving (Eq, Show)
233+
234+instance ToJSON TermInclusion where
235+ toJSON (TermInclusion x) = toJSON x
236+ toJSON (TermPattern pattern flags) =
237+ omitNulls [ "pattern" .= pattern
238+ , "flags" .= flags]
239+
240+data TermOrder = TermOrder
241+ { termSortField :: Text
242+ , termSortOrder :: SortOrder } deriving (Eq, Show)
243+
244+instance ToJSON TermOrder where
245+ toJSON (TermOrder termSortField termSortOrder) =
246+ object [termSortField .= termSortOrder]
247+
248+data CollectionMode = BreadthFirst
249+ | DepthFirst deriving (Eq, Show)
250+
251+instance ToJSON CollectionMode where
252+ toJSON BreadthFirst = "breadth_first"
253+ toJSON DepthFirst = "depth_first"
254+
255+data ExecutionHint = Ordinals
256+ | GlobalOrdinals
257+ | GlobalOrdinalsHash
258+ | GlobalOrdinalsLowCardinality
259+ | Map deriving (Eq, Show)
260+
261+instance ToJSON ExecutionHint where
262+ toJSON Ordinals = "ordinals"
263+ toJSON GlobalOrdinals = "global_ordinals"
264+ toJSON GlobalOrdinalsHash = "global_ordinals_hash"
265+ toJSON GlobalOrdinalsLowCardinality = "global_ordinals_low_cardinality"
266+ toJSON Map = "map"
267+
268+-- | See <https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math> for more information.
269+data DateMathExpr =
270+ DateMathExpr DateMathAnchor [DateMathModifier]
271+ deriving (Eq, Show)
272+
273+instance ToJSON DateMathExpr where
274+ toJSON (DateMathExpr a mods) = String (fmtA a <> mconcat (fmtMod <$> mods))
275+ where fmtA DMNow = "now"
276+ fmtA (DMDate date) = (T.pack $ showGregorian date) <> "||"
277+ fmtMod (AddTime n u) = "+" <> showText n <> fmtU u
278+ fmtMod (SubtractTime n u) = "-" <> showText n <> fmtU u
279+ fmtMod (RoundDownTo u) = "/" <> fmtU u
280+ fmtU DMYear = "y"
281+ fmtU DMMonth = "M"
282+ fmtU DMWeek = "w"
283+ fmtU DMDay = "d"
284+ fmtU DMHour = "h"
285+ fmtU DMMinute = "m"
286+ fmtU DMSecond = "s"
287+
288+-- | Starting point for a date range. This along with the 'DateMathModifiers' gets you the date ES will start from.
289+data DateMathAnchor =
290+ DMNow
291+ | DMDate Day
292+ deriving (Eq, Show)
293+
294+data DateMathModifier =
295+ AddTime Int DateMathUnit
296+ | SubtractTime Int DateMathUnit
297+ | RoundDownTo DateMathUnit
298+ deriving (Eq, Show)
299+
300+data DateMathUnit =
301+ DMYear
302+ | DMMonth
303+ | DMWeek
304+ | DMDay
305+ | DMHour
306+ | DMMinute
307+ | DMSecond
308+ deriving (Eq, Show)
309+
310+data TermsResult = TermsResult
311+ { termKey :: BucketValue
312+ , termsDocCount :: Int
313+ , termsAggs :: Maybe AggregationResults
314+ } deriving (Read, Show)
315+
316+instance FromJSON TermsResult where
317+ parseJSON (Object v) = TermsResult <$>
318+ v .: "key" <*>
319+ v .: "doc_count" <*>
320+ (pure $ getNamedSubAgg v ["key", "doc_count"])
321+ parseJSON _ = mempty
322+
323+instance BucketAggregation TermsResult where
324+ key = termKey
325+ docCount = termsDocCount
326+ aggs = termsAggs
327+
328+data DateHistogramResult = DateHistogramResult
329+ { dateKey :: Int
330+ , dateKeyStr :: Maybe Text
331+ , dateDocCount :: Int
332+ , dateHistogramAggs :: Maybe AggregationResults
333+ } deriving (Show)
334+
335+instance FromJSON DateHistogramResult where
336+ parseJSON (Object v) = DateHistogramResult <$>
337+ v .: "key" <*>
338+ v .:? "key_as_string" <*>
339+ v .: "doc_count" <*>
340+ (pure $ getNamedSubAgg v [ "key"
341+ , "doc_count"
342+ , "key_as_string"
343+ ]
344+ )
345+ parseJSON _ = mempty
346+
347+instance BucketAggregation DateHistogramResult where
348+ key = TextValue . showText . dateKey
349+ docCount = dateDocCount
350+ aggs = dateHistogramAggs
351+
352+data DateRangeResult = DateRangeResult
353+ { dateRangeKey :: Text
354+ , dateRangeFrom :: Maybe UTCTime
355+ , dateRangeFromAsString :: Maybe Text
356+ , dateRangeTo :: Maybe UTCTime
357+ , dateRangeToAsString :: Maybe Text
358+ , dateRangeDocCount :: Int
359+ , dateRangeAggs :: Maybe AggregationResults
360+ } deriving (Eq, Show)
361+
362+instance FromJSON DateRangeResult where
363+ parseJSON = withObject "DateRangeResult" parse
364+ where parse v = DateRangeResult <$>
365+ v .: "key" <*>
366+ (fmap posixMS <$> v .:? "from") <*>
367+ v .:? "from_as_string" <*>
368+ (fmap posixMS <$> v .:? "to") <*>
369+ v .:? "to_as_string" <*>
370+ v .: "doc_count" <*>
371+ (pure $ getNamedSubAgg v [ "key"
372+ , "from"
373+ , "from_as_string"
374+ , "to"
375+ , "to_as_string"
376+ , "doc_count"
377+ ]
378+ )
379+
380+instance BucketAggregation DateRangeResult where
381+ key = TextValue . dateRangeKey
382+ docCount = dateRangeDocCount
383+ aggs = dateRangeAggs
384+
385+toTerms :: Text -> AggregationResults -> Maybe (Bucket TermsResult)
386+toTerms = toAggResult
387+
388+toDateHistogram :: Text -> AggregationResults -> Maybe (Bucket DateHistogramResult)
389+toDateHistogram = toAggResult
390+
391+toMissing :: Text -> AggregationResults -> Maybe MissingResult
392+toMissing = toAggResult
393+
394+toTopHits :: (FromJSON a) => Text -> AggregationResults -> Maybe (TopHitResult a)
395+toTopHits = toAggResult
396+
397+toAggResult :: (FromJSON a) => Text -> AggregationResults -> Maybe a
398+toAggResult t a = M.lookup t a >>= deserialize
399+ where deserialize = parseMaybe parseJSON
400+
401+-- Try to get an AggregationResults when we don't know the
402+-- field name. We filter out the known keys to try to minimize the noise.
403+getNamedSubAgg :: Object -> [Text] -> Maybe AggregationResults
404+getNamedSubAgg o knownKeys = maggRes
405+ where unknownKeys = HM.filterWithKey (\k _ -> k `notElem` knownKeys) o
406+ maggRes
407+ | HM.null unknownKeys = Nothing
408+ | otherwise = Just . M.fromList $ HM.toList unknownKeys
409+
410+data MissingResult = MissingResult
411+ { missingDocCount :: Int
412+ } deriving (Show)
413+
414+instance FromJSON MissingResult where
415+ parseJSON = withObject "MissingResult" parse
416+ where parse v = MissingResult <$> v .: "doc_count"
417+
418+data TopHitResult a = TopHitResult
419+ { tarHits :: (SearchHits a)
420+ } deriving Show
421+
422+instance (FromJSON a) => FromJSON (TopHitResult a) where
423+ parseJSON (Object v) = TopHitResult <$>
424+ v .: "hits"
425+ parseJSON _ = fail "Failure in FromJSON (TopHitResult a)"
426+
427+data SearchHits a =
428+ SearchHits { hitsTotal :: Int
429+ , maxScore :: Score
430+ , hits :: [Hit a] } deriving (Eq, Show)
431+
432+
433+instance (FromJSON a) => FromJSON (SearchHits a) where
434+ parseJSON (Object v) = SearchHits <$>
435+ v .: "total" <*>
436+ v .: "max_score" <*>
437+ v .: "hits"
438+ parseJSON _ = empty
439+
440+instance Semigroup (SearchHits a) where
441+ (SearchHits ta ma ha) <> (SearchHits tb mb hb) =
442+ SearchHits (ta + tb) (max ma mb) (ha <> hb)
443+
444+instance Monoid (SearchHits a) where
445+ mempty = SearchHits 0 Nothing mempty
446+ mappend = (<>)
447+
448+
449+data Hit a =
450+ Hit { hitIndex :: IndexName
451+ , hitType :: MappingName
452+ , hitDocId :: DocId
453+ , hitScore :: Score
454+ , hitSource :: Maybe a
455+ , hitFields :: Maybe HitFields
456+ , hitHighlight :: Maybe HitHighlight } deriving (Eq, Show)
457+
458+instance (FromJSON a) => FromJSON (Hit a) where
459+ parseJSON (Object v) = Hit <$>
460+ v .: "_index" <*>
461+ v .: "_type" <*>
462+ v .: "_id" <*>
463+ v .: "_score" <*>
464+ v .:? "_source" <*>
465+ v .:? "fields" <*>
466+ v .:? "highlight"
467+ parseJSON _ = empty
src/Database/V5/Bloodhound/Internal/Analysis.hsnew file+312
@@ -0,0 +1,312 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.V5.Bloodhound.Internal.Analysis where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.Map.Strict as M
9+import qualified Data.Text as T
10+
11+import Database.V5.Bloodhound.Internal.Newtypes
12+import Database.V5.Bloodhound.Internal.StringlyTyped
13+
14+data Analysis = Analysis
15+ { analysisAnalyzer :: M.Map Text AnalyzerDefinition
16+ , analysisTokenizer :: M.Map Text TokenizerDefinition
17+ , analysisTokenFilter :: M.Map Text TokenFilterDefinition
18+ } deriving (Eq, Show)
19+
20+instance ToJSON Analysis where
21+ toJSON (Analysis analyzer tokenizer tokenFilter) = object
22+ [ "analyzer" .= analyzer
23+ , "tokenizer" .= tokenizer
24+ , "filter" .= tokenFilter
25+ ]
26+
27+instance FromJSON Analysis where
28+ parseJSON = withObject "Analysis" $ \m -> Analysis
29+ <$> m .: "analyzer"
30+ <*> m .:? "tokenizer" .!= M.empty
31+ <*> m .:? "filter" .!= M.empty
32+
33+newtype Tokenizer =
34+ Tokenizer Text
35+ deriving (Eq, Show, ToJSON, FromJSON)
36+
37+data AnalyzerDefinition = AnalyzerDefinition
38+ { analyzerDefinitionTokenizer :: Maybe Tokenizer
39+ , analyzerDefinitionFilter :: [TokenFilter]
40+ } deriving (Eq,Show)
41+
42+instance ToJSON AnalyzerDefinition where
43+ toJSON (AnalyzerDefinition tokenizer tokenFilter) = object $ catMaybes
44+ [ fmap ("tokenizer" .=) tokenizer
45+ , Just $ "filter" .= tokenFilter
46+ ]
47+
48+instance FromJSON AnalyzerDefinition where
49+ parseJSON = withObject "AnalyzerDefinition" $ \m -> AnalyzerDefinition
50+ <$> m .:? "tokenizer"
51+ <*> m .:? "filter" .!= []
52+
53+newtype TokenizerDefinition =
54+ TokenizerDefinitionNgram Ngram
55+ deriving (Eq,Show)
56+
57+instance ToJSON TokenizerDefinition where
58+ toJSON x = case x of
59+ TokenizerDefinitionNgram (Ngram minGram maxGram tokenChars) -> object
60+ [ "type" .= ("ngram" :: Text)
61+ , "min_gram" .= minGram
62+ , "max_gram" .= maxGram
63+ , "token_chars" .= tokenChars
64+ ]
65+
66+instance FromJSON TokenizerDefinition where
67+ parseJSON = withObject "TokenizerDefinition" $ \m -> do
68+ typ <- m .: "type" :: Parser Text
69+ case typ of
70+ "ngram" -> fmap TokenizerDefinitionNgram $ Ngram
71+ <$> fmap unStringlyTypedInt (m .: "min_gram")
72+ <*> fmap unStringlyTypedInt (m .: "max_gram")
73+ <*> m .: "token_chars"
74+ _ -> fail "invalid TokenizerDefinition"
75+
76+data Ngram = Ngram
77+ { ngramMinGram :: Int
78+ , ngramMaxGram :: Int
79+ , ngramTokenChars :: [TokenChar]
80+ } deriving (Eq,Show)
81+
82+data TokenChar =
83+ TokenLetter
84+ | TokenDigit
85+ | TokenWhitespace
86+ | TokenPunctuation
87+ | TokenSymbol
88+ deriving (Eq,Show)
89+
90+instance ToJSON TokenChar where
91+ toJSON t = String $ case t of
92+ TokenLetter -> "letter"
93+ TokenDigit -> "digit"
94+ TokenWhitespace -> "whitespace"
95+ TokenPunctuation -> "punctuation"
96+ TokenSymbol -> "symbol"
97+
98+instance FromJSON TokenChar where
99+ parseJSON = withText "TokenChar" $ \t -> case t of
100+ "letter" -> return TokenLetter
101+ "digit" -> return TokenDigit
102+ "whitespace" -> return TokenWhitespace
103+ "punctuation" -> return TokenPunctuation
104+ "symbol" -> return TokenSymbol
105+ _ -> fail "invalid TokenChar"
106+
107+-- | Token filters are used to create custom analyzers.
108+data TokenFilterDefinition
109+ = TokenFilterDefinitionLowercase (Maybe Language)
110+ | TokenFilterDefinitionUppercase (Maybe Language)
111+ | TokenFilterDefinitionApostrophe
112+ | TokenFilterDefinitionReverse
113+ | TokenFilterDefinitionSnowball Language
114+ | TokenFilterDefinitionShingle Shingle
115+ deriving (Eq, Show)
116+
117+instance ToJSON TokenFilterDefinition where
118+ toJSON x = case x of
119+ TokenFilterDefinitionLowercase mlang -> object $ catMaybes
120+ [ Just $ "type" .= ("lowercase" :: Text)
121+ , fmap (\lang -> "language" .= languageToText lang) mlang
122+ ]
123+ TokenFilterDefinitionUppercase mlang -> object $ catMaybes
124+ [ Just $ "type" .= ("uppercase" :: Text)
125+ , fmap (\lang -> "language" .= languageToText lang) mlang
126+ ]
127+ TokenFilterDefinitionApostrophe -> object
128+ [ "type" .= ("apostrophe" :: Text)
129+ ]
130+ TokenFilterDefinitionReverse -> object
131+ [ "type" .= ("reverse" :: Text)
132+ ]
133+ TokenFilterDefinitionSnowball lang -> object
134+ [ "type" .= ("snowball" :: Text)
135+ , "language" .= languageToText lang
136+ ]
137+ TokenFilterDefinitionShingle s -> object
138+ [ "type" .= ("shingle" :: Text)
139+ , "max_shingle_size" .= shingleMaxSize s
140+ , "min_shingle_size" .= shingleMinSize s
141+ , "output_unigrams" .= shingleOutputUnigrams s
142+ , "output_unigrams_if_no_shingles" .= shingleOutputUnigramsIfNoShingles s
143+ , "token_separator" .= shingleTokenSeparator s
144+ , "filler_token" .= shingleFillerToken s
145+ ]
146+
147+instance FromJSON TokenFilterDefinition where
148+ parseJSON = withObject "TokenFilterDefinition" $ \m -> do
149+ t <- m .: "type"
150+ case (t :: Text) of
151+ "reverse" -> return TokenFilterDefinitionReverse
152+ "apostrophe" -> return TokenFilterDefinitionApostrophe
153+ "lowercase" -> TokenFilterDefinitionLowercase
154+ <$> m .:? "language"
155+ "uppercase" -> TokenFilterDefinitionUppercase
156+ <$> m .:? "language"
157+ "snowball" -> TokenFilterDefinitionSnowball
158+ <$> m .: "language"
159+ "shingle" -> fmap TokenFilterDefinitionShingle $ Shingle
160+ <$> (fmap.fmap) unStringlyTypedInt (m .:? "max_shingle_size") .!= 2
161+ <*> (fmap.fmap) unStringlyTypedInt (m .:? "min_shingle_size") .!= 2
162+ <*> (fmap.fmap) unStringlyTypedBool (m .:? "output_unigrams") .!= True
163+ <*> (fmap.fmap) unStringlyTypedBool (m .:? "output_unigrams_if_no_shingles") .!= False
164+ <*> m .:? "token_separator" .!= " "
165+ <*> m .:? "filler_token" .!= "_"
166+ _ -> fail ("unrecognized token filter type: " ++ T.unpack t)
167+
168+-- | The set of languages that can be passed to various analyzers,
169+-- filters, etc. in ElasticSearch. Most data types in this module
170+-- that have a 'Language' field are actually only actually to
171+-- handle a subset of these languages. Consult the official
172+-- ElasticSearch documentation to see what is actually supported.
173+data Language
174+ = Arabic
175+ | Armenian
176+ | Basque
177+ | Bengali
178+ | Brazilian
179+ | Bulgarian
180+ | Catalan
181+ | Cjk
182+ | Czech
183+ | Danish
184+ | Dutch
185+ | English
186+ | Finnish
187+ | French
188+ | Galician
189+ | German
190+ | German2
191+ | Greek
192+ | Hindi
193+ | Hungarian
194+ | Indonesian
195+ | Irish
196+ | Italian
197+ | Kp
198+ | Latvian
199+ | Lithuanian
200+ | Lovins
201+ | Norwegian
202+ | Persian
203+ | Porter
204+ | Portuguese
205+ | Romanian
206+ | Russian
207+ | Sorani
208+ | Spanish
209+ | Swedish
210+ | Thai
211+ | Turkish
212+ deriving (Eq, Show)
213+
214+instance ToJSON Language where
215+ toJSON = String . languageToText
216+
217+instance FromJSON Language where
218+ parseJSON = withText "Language" $ \t -> case languageFromText t of
219+ Nothing -> fail "not a supported ElasticSearch language"
220+ Just lang -> return lang
221+
222+languageToText :: Language -> Text
223+languageToText x = case x of
224+ Arabic -> "arabic"
225+ Armenian -> "armenian"
226+ Basque -> "basque"
227+ Bengali -> "bengali"
228+ Brazilian -> "brazilian"
229+ Bulgarian -> "bulgarian"
230+ Catalan -> "catalan"
231+ Cjk -> "cjk"
232+ Czech -> "czech"
233+ Danish -> "danish"
234+ Dutch -> "dutch"
235+ English -> "english"
236+ Finnish -> "finnish"
237+ French -> "french"
238+ Galician -> "galician"
239+ German -> "german"
240+ German2 -> "german2"
241+ Greek -> "greek"
242+ Hindi -> "hindi"
243+ Hungarian -> "hungarian"
244+ Indonesian -> "indonesian"
245+ Irish -> "irish"
246+ Italian -> "italian"
247+ Kp -> "kp"
248+ Latvian -> "latvian"
249+ Lithuanian -> "lithuanian"
250+ Lovins -> "lovins"
251+ Norwegian -> "norwegian"
252+ Persian -> "persian"
253+ Porter -> "porter"
254+ Portuguese -> "portuguese"
255+ Romanian -> "romanian"
256+ Russian -> "russian"
257+ Sorani -> "sorani"
258+ Spanish -> "spanish"
259+ Swedish -> "swedish"
260+ Thai -> "thai"
261+ Turkish -> "turkish"
262+
263+languageFromText :: Text -> Maybe Language
264+languageFromText x = case x of
265+ "arabic" -> Just Arabic
266+ "armenian" -> Just Armenian
267+ "basque" -> Just Basque
268+ "bengali" -> Just Bengali
269+ "brazilian" -> Just Brazilian
270+ "bulgarian" -> Just Bulgarian
271+ "catalan" -> Just Catalan
272+ "cjk" -> Just Cjk
273+ "czech" -> Just Czech
274+ "danish" -> Just Danish
275+ "dutch" -> Just Dutch
276+ "english" -> Just English
277+ "finnish" -> Just Finnish
278+ "french" -> Just French
279+ "galician" -> Just Galician
280+ "german" -> Just German
281+ "german2" -> Just German2
282+ "greek" -> Just Greek
283+ "hindi" -> Just Hindi
284+ "hungarian" -> Just Hungarian
285+ "indonesian" -> Just Indonesian
286+ "irish" -> Just Irish
287+ "italian" -> Just Italian
288+ "kp" -> Just Kp
289+ "latvian" -> Just Latvian
290+ "lithuanian" -> Just Lithuanian
291+ "lovins" -> Just Lovins
292+ "norwegian" -> Just Norwegian
293+ "persian" -> Just Persian
294+ "porter" -> Just Porter
295+ "portuguese" -> Just Portuguese
296+ "romanian" -> Just Romanian
297+ "russian" -> Just Russian
298+ "sorani" -> Just Sorani
299+ "spanish" -> Just Spanish
300+ "swedish" -> Just Swedish
301+ "thai" -> Just Thai
302+ "turkish" -> Just Turkish
303+ _ -> Nothing
304+
305+data Shingle = Shingle
306+ { shingleMaxSize :: Int
307+ , shingleMinSize :: Int
308+ , shingleOutputUnigrams :: Bool
309+ , shingleOutputUnigramsIfNoShingles :: Bool
310+ , shingleTokenSeparator :: Text
311+ , shingleFillerToken :: Text
312+ } deriving (Eq, Show)
src/Database/V5/Bloodhound/Internal/Client.hsnew file+2408
@@ -0,0 +1,2408 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE RecordWildCards #-}
6+{-# LANGUAGE UndecidableInstances #-}
7+
8+module Database.V5.Bloodhound.Internal.Client where
9+
10+import Bloodhound.Import
11+
12+import qualified Data.Text as T
13+import qualified Data.Traversable as DT
14+import qualified Data.HashMap.Strict as HM
15+import qualified Data.Vector as V
16+import qualified Data.Version as Vers
17+import GHC.Enum
18+import Network.HTTP.Client
19+import qualified Text.ParserCombinators.ReadP as RP
20+import Text.Read (Read(..))
21+import qualified Text.Read as TR
22+
23+import Database.V5.Bloodhound.Internal.Analysis
24+import Database.V5.Bloodhound.Internal.Newtypes
25+import Database.V5.Bloodhound.Internal.Query
26+import Database.V5.Bloodhound.Internal.StringlyTyped
27+
28+{-| Common environment for Elasticsearch calls. Connections will be
29+ pipelined according to the provided HTTP connection manager.
30+-}
31+data BHEnv = BHEnv { bhServer :: Server
32+ , bhManager :: Manager
33+ , bhRequestHook :: Request -> IO Request
34+ -- ^ Low-level hook that is run before every request is sent. Used to implement custom authentication strategies. Defaults to 'return' with 'mkBHEnv'.
35+ }
36+
37+instance (Functor m, Applicative m, MonadIO m) => MonadBH (ReaderT BHEnv m) where
38+ getBHEnv = ask
39+
40+{-| 'Server' is used with the client functions to point at the ES instance
41+-}
42+newtype Server = Server Text deriving (Eq, Show, FromJSON)
43+
44+{-| All API calls to Elasticsearch operate within
45+ MonadBH
46+ . The idea is that it can be easily embedded in your
47+ own monad transformer stack. A default instance for a ReaderT and
48+ alias 'BH' is provided for the simple case.
49+-}
50+class (Functor m, Applicative m, MonadIO m) => MonadBH m where
51+ getBHEnv :: m BHEnv
52+
53+-- | Create a 'BHEnv' with all optional fields defaulted. HTTP hook
54+-- will be a noop. You can use the exported fields to customize
55+-- it further, e.g.:
56+--
57+-- >> (mkBHEnv myServer myManager) { bhRequestHook = customHook }
58+mkBHEnv :: Server -> Manager -> BHEnv
59+mkBHEnv s m = BHEnv s m return
60+
61+newtype BH m a = BH {
62+ unBH :: ReaderT BHEnv m a
63+ } deriving ( Functor
64+ , Applicative
65+ , Monad
66+ , MonadIO
67+ , MonadState s
68+ , MonadWriter w
69+ , MonadError e
70+ , Alternative
71+ , MonadPlus
72+ , MonadFix
73+ , MonadThrow
74+ , MonadCatch
75+ , MonadMask)
76+
77+instance MonadTrans BH where
78+ lift = BH . lift
79+
80+instance (MonadReader r m) => MonadReader r (BH m) where
81+ ask = lift ask
82+ local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
83+ local f (m r)
84+
85+instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
86+ getBHEnv = BH getBHEnv
87+
88+runBH :: BHEnv -> BH m a -> m a
89+runBH e f = runReaderT (unBH f) e
90+
91+{-| 'Version' is embedded in 'Status' -}
92+data Version = Version { number :: VersionNumber
93+ , build_hash :: BuildHash
94+ , build_date :: UTCTime
95+ , build_snapshot :: Bool
96+ , lucene_version :: VersionNumber }
97+ deriving (Eq, Show)
98+
99+-- | Traditional software versioning number
100+newtype VersionNumber = VersionNumber
101+ { versionNumber :: Vers.Version }
102+ deriving (Eq, Ord, Show)
103+
104+{-| 'Status' is a data type for describing the JSON body returned by
105+ Elasticsearch when you query its status. This was deprecated in 1.2.0.
106+
107+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
108+-}
109+
110+data Status = Status
111+ { name :: Text
112+ , cluster_name :: Text
113+ , cluster_uuid :: Text
114+ , version :: Version
115+ , tagline :: Text }
116+ deriving (Eq, Show)
117+
118+instance FromJSON Status where
119+ parseJSON (Object v) = Status <$>
120+ v .: "name" <*>
121+ v .: "cluster_name" <*>
122+ v .: "cluster_uuid" <*>
123+ v .: "version" <*>
124+ v .: "tagline"
125+ parseJSON _ = empty
126+
127+{-| 'IndexSettings' is used to configure the shards and replicas when
128+ you create an Elasticsearch Index.
129+
130+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
131+-}
132+
133+data IndexSettings = IndexSettings
134+ { indexShards :: ShardCount
135+ , indexReplicas :: ReplicaCount }
136+ deriving (Eq, Show)
137+
138+instance ToJSON IndexSettings where
139+ toJSON (IndexSettings s r) = object ["settings" .=
140+ object ["index" .=
141+ object ["number_of_shards" .= s, "number_of_replicas" .= r]
142+ ]
143+ ]
144+
145+instance FromJSON IndexSettings where
146+ parseJSON = withObject "IndexSettings" parse
147+ where parse o = do s <- o .: "settings"
148+ i <- s .: "index"
149+ IndexSettings <$> i .: "number_of_shards"
150+ <*> i .: "number_of_replicas"
151+
152+{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and
153+ 2 replicas. -}
154+defaultIndexSettings :: IndexSettings
155+defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
156+-- defaultIndexSettings is exported by Database.Bloodhound as well
157+-- no trailing slashes in servers, library handles building the path.
158+
159+
160+{-| 'ForceMergeIndexSettings' is used to configure index optimization. See
161+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html>
162+ for more info.
163+-}
164+data ForceMergeIndexSettings =
165+ ForceMergeIndexSettings { maxNumSegments :: Maybe Int
166+ -- ^ 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.
167+ , onlyExpungeDeletes :: Bool
168+ -- ^ 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.
169+ , flushAfterOptimize :: Bool
170+ -- ^ Should a flush be performed after the optimize.
171+ } deriving (Eq, Show)
172+
173+
174+{-| 'defaultForceMergeIndexSettings' implements the default settings that
175+ ElasticSearch uses for index optimization. 'maxNumSegments' is Nothing,
176+ 'onlyExpungeDeletes' is False, and flushAfterOptimize is True.
177+-}
178+defaultForceMergeIndexSettings :: ForceMergeIndexSettings
179+defaultForceMergeIndexSettings = ForceMergeIndexSettings Nothing False True
180+
181+{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
182+
183+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
184+-}
185+data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
186+ -- ^ The number of replicas each shard has.
187+ | AutoExpandReplicas ReplicaBounds
188+ | BlocksReadOnly Bool
189+ -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
190+ | BlocksRead Bool
191+ -- ^ Set to True to disable read operations against the index.
192+ | BlocksWrite Bool
193+ -- ^ Set to True to disable write operations against the index.
194+ | BlocksMetaData Bool
195+ -- ^ Set to True to disable metadata operations against the index.
196+ | RefreshInterval NominalDiffTime
197+ -- ^ The async refresh interval of a shard
198+ | IndexConcurrency Int
199+ | FailOnMergeFailure Bool
200+ | TranslogFlushThresholdOps Int
201+ -- ^ When to flush on operations.
202+ | TranslogFlushThresholdSize Bytes
203+ -- ^ When to flush based on translog (bytes) size.
204+ | TranslogFlushThresholdPeriod NominalDiffTime
205+ -- ^ When to flush based on a period of not flushing.
206+ | TranslogDisableFlush Bool
207+ -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
208+ | CacheFilterMaxSize (Maybe Bytes)
209+ -- ^ The maximum size of filter cache (per segment in shard).
210+ | CacheFilterExpire (Maybe NominalDiffTime)
211+ -- ^ The expire after access time for filter cache.
212+ | GatewaySnapshotInterval NominalDiffTime
213+ -- ^ The gateway snapshot interval (only applies to shared gateways).
214+ | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
215+ -- ^ A node matching any rule will be allowed to host shards from the index.
216+ | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
217+ -- ^ A node matching any rule will NOT be allowed to host shards from the index.
218+ | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
219+ -- ^ Only nodes matching all rules will be allowed to host shards from the index.
220+ | RoutingAllocationEnable AllocationPolicy
221+ -- ^ Enables shard allocation for a specific index.
222+ | RoutingAllocationShardsPerNode ShardCount
223+ -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
224+ | RecoveryInitialShards InitialShardCount
225+ -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
226+ | GCDeletes NominalDiffTime
227+ | TTLDisablePurge Bool
228+ -- ^ Disables temporarily the purge of expired docs.
229+ | TranslogFSType FSType
230+ | CompressionSetting Compression
231+ | IndexCompoundFormat CompoundFormat
232+ | IndexCompoundOnFlush Bool
233+ | WarmerEnabled Bool
234+ | MappingTotalFieldsLimit Int
235+ | AnalysisSetting Analysis
236+ -- ^ Analysis is not a dynamic setting and can only be performed on a closed index.
237+ deriving (Eq, Show)
238+
239+attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
240+attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
241+ | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
242+
243+parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
244+parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
245+ where parse o = case HM.toList o of
246+ [] -> fail "Expected non-empty list of NodeAttrFilters"
247+ x:xs -> DT.mapM (uncurry parse') (x :| xs)
248+ parse' n = withText "Text" $ \t ->
249+ case T.splitOn "," t of
250+ fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
251+ [] -> fail "Expected non-empty list of filter values"
252+
253+instance ToJSON UpdatableIndexSetting where
254+ toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
255+ toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
256+ toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
257+ toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
258+ toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
259+ toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
260+ toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
261+ toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
262+ toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
263+ toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
264+ toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
265+ toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
266+ toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
267+ toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
268+ toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
269+ toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
270+ toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
271+ toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
272+ toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
273+ toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
274+ toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
275+ toJSON (CompressionSetting x) = oPath ("index" :| ["codec"]) x
276+ toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
277+ toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
278+ toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
279+ toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
280+ toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
281+ toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
282+ toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
283+ toJSON (MappingTotalFieldsLimit x) = oPath ("index" :| ["mapping","total_fields","limit"]) x
284+ toJSON (AnalysisSetting x) = oPath ("index" :| ["analysis"]) x
285+
286+instance FromJSON UpdatableIndexSetting where
287+ parseJSON = withObject "UpdatableIndexSetting" parse
288+ where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
289+ <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
290+ <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
291+ <|> indexConcurrency `taggedAt` ["index", "concurrency"]
292+ <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
293+ <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
294+ <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
295+ <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
296+ <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
297+ <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
298+ <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
299+ <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
300+ <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
301+ <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
302+ <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
303+ <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
304+ <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
305+ <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
306+ <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
307+ <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
308+ <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
309+ <|> compressionSetting `taggedAt` ["index", "codec"]
310+ <|> compoundFormat `taggedAt` ["index", "compound_format"]
311+ <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
312+ <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
313+ <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
314+ <|> blocksRead `taggedAt` ["blocks", "read"]
315+ <|> blocksWrite `taggedAt` ["blocks", "write"]
316+ <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
317+ <|> mappingTotalFieldsLimit `taggedAt` ["index", "mapping", "total_fields", "limit"]
318+ <|> analysisSetting `taggedAt` ["index", "analysis"]
319+ where taggedAt f ks = taggedAt' f (Object o) ks
320+ taggedAt' f v [] =
321+ f =<< (parseJSON v <|> parseJSON (unStringlyTypeJSON v))
322+ taggedAt' f v (k:ks) =
323+ withObject "Object" (\o -> do v' <- o .: k
324+ taggedAt' f v' ks) v
325+ numberOfReplicas = pure . NumberOfReplicas
326+ autoExpandReplicas = pure . AutoExpandReplicas
327+ refreshInterval = pure . RefreshInterval . ndtJSON
328+ indexConcurrency = pure . IndexConcurrency
329+ failOnMergeFailure = pure . FailOnMergeFailure
330+ translogFlushThresholdOps = pure . TranslogFlushThresholdOps
331+ translogFlushThresholdSize = pure . TranslogFlushThresholdSize
332+ translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
333+ translogDisableFlush = pure . TranslogDisableFlush
334+ cacheFilterMaxSize = pure . CacheFilterMaxSize
335+ cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
336+ gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
337+ routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
338+ routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
339+ routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
340+ routingAllocationEnable = pure . RoutingAllocationEnable
341+ routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
342+ recoveryInitialShards = pure . RecoveryInitialShards
343+ gcDeletes = pure . GCDeletes . ndtJSON
344+ ttlDisablePurge = pure . TTLDisablePurge
345+ translogFSType = pure . TranslogFSType
346+ compressionSetting = pure . CompressionSetting
347+ compoundFormat = pure . IndexCompoundFormat
348+ compoundOnFlush = pure . IndexCompoundOnFlush
349+ warmerEnabled = pure . WarmerEnabled
350+ blocksReadOnly = pure . BlocksReadOnly
351+ blocksRead = pure . BlocksRead
352+ blocksWrite = pure . BlocksWrite
353+ blocksMetaData = pure . BlocksMetaData
354+ mappingTotalFieldsLimit = pure . MappingTotalFieldsLimit
355+ analysisSetting = pure . AnalysisSetting
356+
357+data ReplicaBounds = ReplicasBounded Int Int
358+ | ReplicasLowerBounded Int
359+ | ReplicasUnbounded
360+ deriving (Eq, Show)
361+
362+
363+instance ToJSON ReplicaBounds where
364+ toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
365+ toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
366+ toJSON ReplicasUnbounded = Bool False
367+
368+instance FromJSON ReplicaBounds where
369+ parseJSON v = withText "ReplicaBounds" parseText v
370+ <|> withBool "ReplicaBounds" parseBool v
371+ where parseText t = case T.splitOn "-" t of
372+ [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
373+ [a, b] -> ReplicasBounded <$> parseReadText a
374+ <*> parseReadText b
375+ _ -> fail ("Could not parse ReplicaBounds: " <> show t)
376+ parseBool False = pure ReplicasUnbounded
377+ parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
378+
379+data Compression
380+ = CompressionDefault
381+ -- ^ Compress with LZ4
382+ | CompressionBest
383+ -- ^ Compress with DEFLATE. Elastic
384+ -- <https://www.elastic.co/blog/elasticsearch-storage-the-true-story-2.0 blogs>
385+ -- that this can reduce disk use by 15%-25%.
386+ deriving (Eq,Show)
387+
388+instance ToJSON Compression where
389+ toJSON x = case x of
390+ CompressionDefault -> toJSON ("default" :: Text)
391+ CompressionBest -> toJSON ("best_compression" :: Text)
392+
393+instance FromJSON Compression where
394+ parseJSON = withText "Compression" $ \t -> case t of
395+ "default" -> return CompressionDefault
396+ "best_compression" -> return CompressionBest
397+ _ -> fail "invalid compression codec"
398+
399+-- | A measure of bytes used for various configurations. You may want
400+-- to use smart constructors like 'gigabytes' for larger values.
401+--
402+-- >>> gigabytes 9
403+-- Bytes 9000000000
404+--
405+-- >>> megabytes 9
406+-- Bytes 9000000
407+--
408+-- >>> kilobytes 9
409+-- Bytes 9000
410+newtype Bytes =
411+ Bytes Int
412+ deriving (Eq, Show, Ord, ToJSON, FromJSON)
413+
414+gigabytes :: Int -> Bytes
415+gigabytes n = megabytes (1000 * n)
416+
417+
418+megabytes :: Int -> Bytes
419+megabytes n = kilobytes (1000 * n)
420+
421+
422+kilobytes :: Int -> Bytes
423+kilobytes n = Bytes (1000 * n)
424+
425+
426+data FSType = FSSimple
427+ | FSBuffered deriving (Eq, Show)
428+
429+instance ToJSON FSType where
430+ toJSON FSSimple = "simple"
431+ toJSON FSBuffered = "buffered"
432+
433+instance FromJSON FSType where
434+ parseJSON = withText "FSType" parse
435+ where parse "simple" = pure FSSimple
436+ parse "buffered" = pure FSBuffered
437+ parse t = fail ("Invalid FSType: " <> show t)
438+
439+data InitialShardCount = QuorumShards
440+ | QuorumMinus1Shards
441+ | FullShards
442+ | FullMinus1Shards
443+ | ExplicitShards Int
444+ deriving (Eq, Show)
445+
446+instance FromJSON InitialShardCount where
447+ parseJSON v = withText "InitialShardCount" parseText v
448+ <|> ExplicitShards <$> parseJSON v
449+ where parseText "quorum" = pure QuorumShards
450+ parseText "quorum-1" = pure QuorumMinus1Shards
451+ parseText "full" = pure FullShards
452+ parseText "full-1" = pure FullMinus1Shards
453+ parseText _ = mzero
454+
455+instance ToJSON InitialShardCount where
456+ toJSON QuorumShards = String "quorum"
457+ toJSON QuorumMinus1Shards = String "quorum-1"
458+ toJSON FullShards = String "full"
459+ toJSON FullMinus1Shards = String "full-1"
460+ toJSON (ExplicitShards x) = toJSON x
461+
462+data NodeAttrFilter = NodeAttrFilter
463+ { nodeAttrFilterName :: NodeAttrName
464+ , nodeAttrFilterValues :: NonEmpty Text }
465+ deriving (Eq, Ord, Show)
466+
467+newtype NodeAttrName = NodeAttrName Text deriving (Eq, Ord, Show)
468+
469+data CompoundFormat = CompoundFileFormat Bool
470+ | MergeSegmentVsTotalIndex Double
471+ -- ^ percentage between 0 and 1 where 0 is false, 1 is true
472+ deriving (Eq, Show)
473+
474+instance ToJSON CompoundFormat where
475+ toJSON (CompoundFileFormat x) = Bool x
476+ toJSON (MergeSegmentVsTotalIndex x) = toJSON x
477+
478+instance FromJSON CompoundFormat where
479+ parseJSON v = CompoundFileFormat <$> parseJSON v
480+ <|> MergeSegmentVsTotalIndex <$> parseJSON v
481+
482+newtype NominalDiffTimeJSON =
483+ NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
484+
485+instance ToJSON NominalDiffTimeJSON where
486+ toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
487+
488+instance FromJSON NominalDiffTimeJSON where
489+ parseJSON = withText "NominalDiffTime" parse
490+ where parse t = case T.takeEnd 1 t of
491+ "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
492+ _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
493+
494+data IndexSettingsSummary = IndexSettingsSummary
495+ { sSummaryIndexName :: IndexName
496+ , sSummaryFixedSettings :: IndexSettings
497+ , sSummaryUpdateable :: [UpdatableIndexSetting]}
498+ deriving (Eq, Show)
499+
500+parseSettings :: Object -> Parser [UpdatableIndexSetting]
501+parseSettings o = do
502+ o' <- o .: "index"
503+ -- slice the index object into singleton hashmaps and try to parse each
504+ parses <- forM (HM.toList o') $ \(k, v) -> do
505+ -- blocks are now nested into the "index" key, which is not how they're serialized
506+ let atRoot = Object (HM.singleton k v)
507+ let atIndex = Object (HM.singleton "index" atRoot)
508+ optional (parseJSON atRoot <|> parseJSON atIndex)
509+ return (catMaybes parses)
510+
511+instance FromJSON IndexSettingsSummary where
512+ parseJSON = withObject "IndexSettingsSummary" parse
513+ where parse o = case HM.toList o of
514+ [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
515+ <$> parseJSON v
516+ <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
517+ _ -> fail "Expected single-key object with index name"
518+ redundant (NumberOfReplicas _) = True
519+ redundant _ = False
520+
521+{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
522+type Reply = Network.HTTP.Client.Response LByteString
523+
524+{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
525+
526+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
527+-}
528+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show)
529+
530+data FieldType = GeoPointType
531+ | GeoShapeType
532+ | FloatType
533+ | IntegerType
534+ | LongType
535+ | ShortType
536+ | ByteType deriving (Eq, Show)
537+
538+newtype FieldDefinition = FieldDefinition
539+ { fieldType :: FieldType
540+ } deriving (Eq, Show)
541+
542+{-| An 'IndexTemplate' defines a template that will automatically be
543+ applied to new indices created. The templates include both
544+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
545+ controls if the template will be applied to the index created.
546+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
547+
548+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
549+-}
550+data IndexTemplate =
551+ IndexTemplate { templatePattern :: TemplatePattern
552+ , templateSettings :: Maybe IndexSettings
553+ , templateMappings :: [Value]
554+ }
555+
556+instance ToJSON IndexTemplate where
557+ toJSON (IndexTemplate p s m) = merge
558+ (object [ "template" .= p
559+ , "mappings" .= foldl' merge (object []) m
560+ ])
561+ (toJSON s)
562+ where
563+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
564+ merge o Null = o
565+ merge _ _ = undefined
566+
567+data MappingField =
568+ MappingField { mappingFieldName :: FieldName
569+ , fieldDefinition :: FieldDefinition }
570+ deriving (Eq, Show)
571+
572+{-| Support for type reification of 'Mapping's is currently incomplete, for
573+ now the mapping API verbiage expects a 'ToJSON'able blob.
574+
575+ Indexes have mappings, mappings are schemas for the documents contained
576+ in the index. I'd recommend having only one mapping per index, always
577+ having a mapping, and keeping different kinds of documents separated
578+ if possible.
579+-}
580+data Mapping =
581+ Mapping { typeName :: TypeName
582+ , mappingFields :: [MappingField] }
583+ deriving (Eq, Show)
584+
585+data AllocationPolicy = AllocAll
586+ -- ^ Allows shard allocation for all shards.
587+ | AllocPrimaries
588+ -- ^ Allows shard allocation only for primary shards.
589+ | AllocNewPrimaries
590+ -- ^ Allows shard allocation only for primary shards for new indices.
591+ | AllocNone
592+ -- ^ No shard allocation is allowed
593+ deriving (Eq, Show)
594+
595+instance ToJSON AllocationPolicy where
596+ toJSON AllocAll = String "all"
597+ toJSON AllocPrimaries = String "primaries"
598+ toJSON AllocNewPrimaries = String "new_primaries"
599+ toJSON AllocNone = String "none"
600+
601+instance FromJSON AllocationPolicy where
602+ parseJSON = withText "AllocationPolicy" parse
603+ where parse "all" = pure AllocAll
604+ parse "primaries" = pure AllocPrimaries
605+ parse "new_primaries" = pure AllocNewPrimaries
606+ parse "none" = pure AllocNone
607+ parse t = fail ("Invlaid AllocationPolicy: " <> show t)
608+
609+{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
610+ operation index, create, delete, and update. 'BulkIndex' behaves like an
611+ "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
612+ Consult the <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk Bulk API documentation>
613+ for further explanation.
614+ Warning: Bulk operations suffixed with @Auto@ rely on ElasticSearch to
615+ generate the id. Often, people use auto-generated identifiers when
616+ ElasticSearch is the only place that their data is stored. Do not let
617+ ElasticSearch be the only place your data is stored. It does not guarantee
618+ durability, and it may silently discard data.
619+ This <https://github.com/elastic/elasticsearch/issues/10708 issue> is
620+ discussed further on github.
621+-}
622+data BulkOperation =
623+ BulkIndex IndexName MappingName DocId Value
624+ -- ^ Create the document, replacing it if it already exists.
625+ | BulkIndexAuto IndexName MappingName Value
626+ -- ^ Create a document with an autogenerated id.
627+ | BulkIndexEncodingAuto IndexName MappingName Encoding
628+ -- ^ Create a document with an autogenerated id. Use fast JSON encoding.
629+ | BulkCreate IndexName MappingName DocId Value
630+ -- ^ Create a document, failing if it already exists.
631+ | BulkCreateEncoding IndexName MappingName DocId Encoding
632+ -- ^ Create a document, failing if it already exists. Use fast JSON encoding.
633+ | BulkDelete IndexName MappingName DocId
634+ -- ^ Delete the document
635+ | BulkUpdate IndexName MappingName DocId Value
636+ -- ^ Update the document, merging the new value with the existing one.
637+ deriving (Eq, Show)
638+
639+{-| 'EsResult' describes the standard wrapper JSON document that you see in
640+ successful Elasticsearch lookups or lookups that couldn't find the document.
641+-}
642+data EsResult a = EsResult { _index :: Text
643+ , _type :: Text
644+ , _id :: Text
645+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show)
646+
647+{-| 'EsResultFound' contains the document and its metadata inside of an
648+ 'EsResult' when the document was successfully found.
649+-}
650+data EsResultFound a =
651+ EsResultFound { _version :: DocVersion
652+ , _source :: a }
653+ deriving (Eq, Show)
654+
655+instance (FromJSON a) => FromJSON (EsResult a) where
656+ parseJSON jsonVal@(Object v) = do
657+ found <- v .:? "found" .!= False
658+ fr <- if found
659+ then parseJSON jsonVal
660+ else return Nothing
661+ EsResult <$> v .: "_index" <*>
662+ v .: "_type" <*>
663+ v .: "_id" <*>
664+ pure fr
665+ parseJSON _ = empty
666+
667+instance (FromJSON a) => FromJSON (EsResultFound a) where
668+ parseJSON (Object v) = EsResultFound <$>
669+ v .: "_version" <*>
670+ v .: "_source"
671+ parseJSON _ = empty
672+
673+{-| 'EsError' is the generic type that will be returned when there was a
674+ problem. If you can't parse the expected response, its a good idea to
675+ try parsing this.
676+-}
677+data EsError =
678+ EsError { errorStatus :: Int
679+ , errorMessage :: Text }
680+ deriving (Eq, Show)
681+
682+instance FromJSON EsError where
683+ parseJSON (Object v) = EsError <$>
684+ v .: "status" <*>
685+ (v .: "error" <|> (v .: "error" >>= (.: "reason")))
686+ parseJSON _ = empty
687+
688+{-| 'EsProtocolException' will be thrown if Bloodhound cannot parse a response
689+returned by the ElasticSearch server. If you encounter this error, please
690+verify that your domain data types and FromJSON instances are working properly
691+(for example, the 'a' of '[Hit a]' in 'SearchResult.searchHits.hits'). If you're
692+sure that your mappings are correct, then this error may be an indication of an
693+incompatibility between Bloodhound and ElasticSearch. Please open a bug report
694+and be sure to include the exception body.
695+-}
696+newtype EsProtocolException = EsProtocolException
697+ { esProtoExBody :: LByteString
698+ } deriving (Eq, Show)
699+
700+instance Exception EsProtocolException
701+
702+data IndexAlias = IndexAlias { srcIndex :: IndexName
703+ , indexAlias :: IndexAliasName } deriving (Eq, Show)
704+
705+data IndexAliasAction =
706+ AddAlias IndexAlias IndexAliasCreate
707+ | RemoveAlias IndexAlias
708+ deriving (Eq, Show)
709+
710+data IndexAliasCreate =
711+ IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
712+ , aliasCreateFilter :: Maybe Filter}
713+ deriving (Eq, Show)
714+
715+data AliasRouting =
716+ AllAliasRouting RoutingValue
717+ | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
718+ deriving (Eq, Show)
719+
720+newtype SearchAliasRouting =
721+ SearchAliasRouting (NonEmpty RoutingValue)
722+ deriving (Eq, Show)
723+
724+instance ToJSON SearchAliasRouting where
725+ toJSON (SearchAliasRouting rvs) = toJSON (T.intercalate "," (routingValue <$> toList rvs))
726+
727+instance FromJSON SearchAliasRouting where
728+ parseJSON = withText "SearchAliasRouting" parse
729+ where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
730+
731+newtype IndexAliasRouting =
732+ IndexAliasRouting RoutingValue
733+ deriving (Eq, Show, ToJSON, FromJSON)
734+
735+newtype RoutingValue =
736+ RoutingValue { routingValue :: Text }
737+ deriving (Eq, Show, ToJSON, FromJSON)
738+
739+newtype IndexAliasesSummary =
740+ IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] }
741+ deriving (Eq, Show)
742+
743+instance FromJSON IndexAliasesSummary where
744+ parseJSON = withObject "IndexAliasesSummary" parse
745+ where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
746+ go ixn = withObject "index aliases" $ \ia -> do
747+ aliases <- ia .:? "aliases" .!= mempty
748+ forM (HM.toList aliases) $ \(aName, v) -> do
749+ let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
750+ IndexAliasSummary indexAlias <$> parseJSON v
751+
752+
753+instance ToJSON IndexAliasAction where
754+ toJSON (AddAlias ia opts) = object ["add" .= (iaObj <> optsObj)]
755+ where Object iaObj = toJSON ia
756+ Object optsObj = toJSON opts
757+ toJSON (RemoveAlias ia) = object ["remove" .= iaObj]
758+ where Object iaObj = toJSON ia
759+
760+instance ToJSON IndexAlias where
761+ toJSON IndexAlias {..} = object ["index" .= srcIndex
762+ , "alias" .= indexAlias
763+ ]
764+
765+instance ToJSON IndexAliasCreate where
766+ toJSON IndexAliasCreate {..} = Object (filterObj <> routingObj)
767+ where filterObj = maybe mempty (HM.singleton "filter" . toJSON) aliasCreateFilter
768+ Object routingObj = maybe (Object mempty) toJSON aliasCreateRouting
769+
770+instance ToJSON AliasRouting where
771+ toJSON (AllAliasRouting v) = object ["routing" .= v]
772+ toJSON (GranularAliasRouting srch idx) = object (catMaybes prs)
773+ where prs = [("search_routing" .=) <$> srch
774+ ,("index_routing" .=) <$> idx]
775+
776+instance FromJSON AliasRouting where
777+ parseJSON = withObject "AliasRouting" parse
778+ where parse o = parseAll o <|> parseGranular o
779+ parseAll o = AllAliasRouting <$> o .: "routing"
780+ parseGranular o = do
781+ sr <- o .:? "search_routing"
782+ ir <- o .:? "index_routing"
783+ if isNothing sr && isNothing ir
784+ then fail "Both search_routing and index_routing can't be blank"
785+ else return (GranularAliasRouting sr ir)
786+
787+instance FromJSON IndexAliasCreate where
788+ parseJSON v = withObject "IndexAliasCreate" parse v
789+ where parse o = IndexAliasCreate <$> optional (parseJSON v)
790+ <*> o .:? "filter"
791+
792+{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
793+data IndexAliasSummary = IndexAliasSummary
794+ { indexAliasSummaryAlias :: IndexAlias
795+ , indexAliasSummaryCreate :: IndexAliasCreate }
796+ deriving (Eq, Show)
797+
798+{-| 'DocVersion' is an integer version number for a document between 1
799+and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
800+-}
801+newtype DocVersion = DocVersion {
802+ docVersionNumber :: Int
803+ } deriving (Eq, Show, Ord, ToJSON)
804+
805+-- | Smart constructor for in-range doc version
806+mkDocVersion :: Int -> Maybe DocVersion
807+mkDocVersion i
808+ | i >= docVersionNumber minBound
809+ && i <= docVersionNumber maxBound =
810+ Just $ DocVersion i
811+ | otherwise = Nothing
812+
813+instance Bounded DocVersion where
814+ minBound = DocVersion 1
815+ maxBound = DocVersion 9200000000000000000 -- 9.2e+18
816+
817+instance Enum DocVersion where
818+ succ x
819+ | x /= maxBound = DocVersion (succ $ docVersionNumber x)
820+ | otherwise = succError "DocVersion"
821+ pred x
822+ | x /= minBound = DocVersion (pred $ docVersionNumber x)
823+ | otherwise = predError "DocVersion"
824+ toEnum i =
825+ fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
826+ fromEnum = docVersionNumber
827+ enumFrom = boundedEnumFrom
828+ enumFromThen = boundedEnumFromThen
829+
830+instance FromJSON DocVersion where
831+ parseJSON v = do
832+ i <- parseJSON v
833+ maybe (fail "DocVersion out of range") return $ mkDocVersion i
834+
835+{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
836+own version numbers instead of ones from ES.
837+-}
838+newtype ExternalDocVersion = ExternalDocVersion DocVersion
839+ deriving (Eq, Show, Ord, Bounded, Enum, ToJSON)
840+
841+{-| 'VersionControl' is specified when indexing documents as a
842+optimistic concurrency control.
843+-}
844+data VersionControl = NoVersionControl
845+ -- ^ Don't send a version. This is a pure overwrite.
846+ | InternalVersion DocVersion
847+ -- ^ Use the default ES versioning scheme. Only
848+ -- index the document if the version is the same
849+ -- as the one specified. Only applicable to
850+ -- updates, as you should be getting Version from
851+ -- a search result.
852+ | ExternalGT ExternalDocVersion
853+ -- ^ Use your own version numbering. Only index
854+ -- the document if the version is strictly higher
855+ -- OR the document doesn't exist. The given
856+ -- version will be used as the new version number
857+ -- for the stored document. N.B. All updates must
858+ -- increment this number, meaning there is some
859+ -- global, external ordering of updates.
860+ | ExternalGTE ExternalDocVersion
861+ -- ^ Use your own version numbering. Only index
862+ -- the document if the version is equal or higher
863+ -- than the stored version. Will succeed if there
864+ -- is no existing document. The given version will
865+ -- be used as the new version number for the
866+ -- stored document. Use with care, as this could
867+ -- result in data loss.
868+ | ForceVersion ExternalDocVersion
869+ -- ^ The document will always be indexed and the
870+ -- given version will be the new version. This is
871+ -- typically used for correcting errors. Use with
872+ -- care, as this could result in data loss.
873+ deriving (Eq, Show, Ord)
874+
875+{-| 'DocumentParent' is used to specify a parent document.
876+-}
877+newtype DocumentParent = DocumentParent DocId
878+ deriving (Eq, Show)
879+
880+{-| 'IndexDocumentSettings' are special settings supplied when indexing
881+a document. For the best backwards compatiblity when new fields are
882+added, you should probably prefer to start with 'defaultIndexDocumentSettings'
883+-}
884+data IndexDocumentSettings =
885+ IndexDocumentSettings { idsVersionControl :: VersionControl
886+ , idsParent :: Maybe DocumentParent
887+ } deriving (Eq, Show)
888+
889+{-| Reasonable default settings. Chooses no version control and no parent.
890+-}
891+defaultIndexDocumentSettings :: IndexDocumentSettings
892+defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
893+
894+{-| 'IndexSelection' is used for APIs which take a single index, a list of
895+ indexes, or the special @_all@ index.
896+-}
897+--TODO: this does not fully support <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/multi-index.html multi-index syntax>. It wouldn't be too hard to implement but you'd have to add the optional parameters (ignore_unavailable, allow_no_indices, expand_wildcards) to any APIs using it. Also would be a breaking API.
898+data IndexSelection =
899+ IndexList (NonEmpty IndexName)
900+ | AllIndexes
901+ deriving (Eq, Show)
902+
903+{-| 'NodeSelection' is used for most cluster APIs. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes here> for more details.
904+-}
905+data NodeSelection =
906+ LocalNode
907+ -- ^ Whatever node receives this request
908+ | NodeList (NonEmpty NodeSelector)
909+ | AllNodes
910+ deriving (Eq, Show)
911+
912+
913+-- | An exact match or pattern to identify a node. Note that All of
914+-- these options support wildcarding, so your node name, server, attr
915+-- name can all contain * characters to be a fuzzy match.
916+data NodeSelector =
917+ NodeByName NodeName
918+ | NodeByFullNodeId FullNodeId
919+ | NodeByHost Server
920+ -- ^ e.g. 10.0.0.1 or even 10.0.0.*
921+ | NodeByAttribute NodeAttrName Text
922+ -- ^ NodeAttrName can be a pattern, e.g. rack*. The value can too.
923+ deriving (Eq, Show)
924+
925+{-| 'TemplateName' is used to describe which template to query/create/delete
926+-}
927+newtype TemplateName = TemplateName Text deriving (Eq, Show, ToJSON, FromJSON)
928+
929+{-| 'TemplatePattern' represents a pattern which is matched against index names
930+-}
931+newtype TemplatePattern = TemplatePattern Text deriving (Eq, Show, ToJSON, FromJSON)
932+
933+-- | Username type used for HTTP Basic authentication. See 'basicAuthHook'.
934+newtype EsUsername = EsUsername { esUsername :: Text } deriving (Read, Show, Eq)
935+
936+-- | Password type used for HTTP Basic authentication. See 'basicAuthHook'.
937+newtype EsPassword = EsPassword { esPassword :: Text } deriving (Read, Show, Eq)
938+
939+
940+data SnapshotRepoSelection =
941+ SnapshotRepoList (NonEmpty SnapshotRepoPattern)
942+ | AllSnapshotRepos
943+ deriving (Eq, Show)
944+
945+
946+-- | Either specifies an exact repo name or one with globs in it,
947+-- e.g. @RepoPattern "foo*"@ __NOTE__: Patterns are not supported on ES < 1.7
948+data SnapshotRepoPattern =
949+ ExactRepo SnapshotRepoName
950+ | RepoPattern Text
951+ deriving (Eq, Show)
952+
953+-- | The unique name of a snapshot repository.
954+newtype SnapshotRepoName =
955+ SnapshotRepoName { snapshotRepoName :: Text }
956+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
957+
958+
959+-- | A generic representation of a snapshot repo. This is what gets
960+-- sent to and parsed from the server. For repo types enabled by
961+-- plugins that aren't exported by this library, consider making a
962+-- custom type which implements 'SnapshotRepo'. If it is a common repo
963+-- type, consider submitting a pull request to have it included in the
964+-- library proper
965+data GenericSnapshotRepo = GenericSnapshotRepo {
966+ gSnapshotRepoName :: SnapshotRepoName
967+ , gSnapshotRepoType :: SnapshotRepoType
968+ , gSnapshotRepoSettings :: GenericSnapshotRepoSettings
969+ } deriving (Eq, Show)
970+
971+
972+instance SnapshotRepo GenericSnapshotRepo where
973+ toGSnapshotRepo = id
974+ fromGSnapshotRepo = Right
975+
976+
977+newtype SnapshotRepoType =
978+ SnapshotRepoType { snapshotRepoType :: Text }
979+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
980+
981+
982+-- | Opaque representation of snapshot repo settings. Instances of
983+-- 'SnapshotRepo' will produce this.
984+newtype GenericSnapshotRepoSettings =
985+ GenericSnapshotRepoSettings { gSnapshotRepoSettingsObject :: Object }
986+ deriving (Eq, Show, ToJSON)
987+
988+
989+ -- Regardless of whether you send strongly typed json, my version of
990+ -- ES sends back stringly typed json in the settings, e.g. booleans
991+ -- as strings, so we'll try to convert them.
992+instance FromJSON GenericSnapshotRepoSettings where
993+ parseJSON = fmap (GenericSnapshotRepoSettings . fmap unStringlyTypeJSON). parseJSON
994+
995+-- | The result of running 'verifySnapshotRepo'.
996+newtype SnapshotVerification =
997+ SnapshotVerification {
998+ snapshotNodeVerifications :: [SnapshotNodeVerification]
999+ } deriving (Eq, Show)
1000+
1001+
1002+instance FromJSON SnapshotVerification where
1003+ parseJSON = withObject "SnapshotVerification" parse
1004+ where
1005+ parse o = do
1006+ o2 <- o .: "nodes"
1007+ SnapshotVerification <$> mapM (uncurry parse') (HM.toList o2)
1008+ parse' rawFullId = withObject "SnapshotNodeVerification" $ \o ->
1009+ SnapshotNodeVerification (FullNodeId rawFullId) <$> o .: "name"
1010+
1011+
1012+-- | A node that has verified a snapshot
1013+data SnapshotNodeVerification = SnapshotNodeVerification {
1014+ snvFullId :: FullNodeId
1015+ , snvNodeName :: NodeName
1016+ } deriving (Eq, Show)
1017+
1018+
1019+-- | Unique, automatically-generated name assigned to nodes that are
1020+-- usually returned in node-oriented APIs.
1021+newtype FullNodeId = FullNodeId { fullNodeId :: Text }
1022+ deriving (Eq, Ord, Show, FromJSON)
1023+
1024+
1025+-- | A human-readable node name that is supplied by the user in the
1026+-- node config or automatically generated by ElasticSearch.
1027+newtype NodeName = NodeName { nodeName :: Text }
1028+ deriving (Eq, Ord, Show, FromJSON)
1029+
1030+newtype ClusterName = ClusterName { clusterName :: Text }
1031+ deriving (Eq, Ord, Show, FromJSON)
1032+
1033+data NodesInfo = NodesInfo {
1034+ nodesInfo :: [NodeInfo]
1035+ , nodesClusterName :: ClusterName
1036+ } deriving (Eq, Show)
1037+
1038+data NodesStats = NodesStats {
1039+ nodesStats :: [NodeStats]
1040+ , nodesStatsClusterName :: ClusterName
1041+ } deriving (Eq, Show)
1042+
1043+data NodeStats = NodeStats {
1044+ nodeStatsName :: NodeName
1045+ , nodeStatsFullId :: FullNodeId
1046+ , nodeStatsBreakersStats :: Maybe NodeBreakersStats
1047+ , nodeStatsHTTP :: NodeHTTPStats
1048+ , nodeStatsTransport :: NodeTransportStats
1049+ , nodeStatsFS :: NodeFSStats
1050+ , nodeStatsNetwork :: Maybe NodeNetworkStats
1051+ , nodeStatsThreadPool :: NodeThreadPoolsStats
1052+ , nodeStatsJVM :: NodeJVMStats
1053+ , nodeStatsProcess :: NodeProcessStats
1054+ , nodeStatsOS :: NodeOSStats
1055+ , nodeStatsIndices :: NodeIndicesStats
1056+ } deriving (Eq, Show)
1057+
1058+data NodeBreakersStats = NodeBreakersStats {
1059+ nodeStatsParentBreaker :: NodeBreakerStats
1060+ , nodeStatsRequestBreaker :: NodeBreakerStats
1061+ , nodeStatsFieldDataBreaker :: NodeBreakerStats
1062+ } deriving (Eq, Show)
1063+
1064+data NodeBreakerStats = NodeBreakerStats {
1065+ nodeBreakersTripped :: Int
1066+ , nodeBreakersOverhead :: Double
1067+ , nodeBreakersEstSize :: Bytes
1068+ , nodeBreakersLimitSize :: Bytes
1069+ } deriving (Eq, Show)
1070+
1071+data NodeHTTPStats = NodeHTTPStats {
1072+ nodeHTTPTotalOpened :: Int
1073+ , nodeHTTPCurrentOpen :: Int
1074+ } deriving (Eq, Show)
1075+
1076+data NodeTransportStats = NodeTransportStats {
1077+ nodeTransportTXSize :: Bytes
1078+ , nodeTransportCount :: Int
1079+ , nodeTransportRXSize :: Bytes
1080+ , nodeTransportRXCount :: Int
1081+ , nodeTransportServerOpen :: Int
1082+ } deriving (Eq, Show)
1083+
1084+data NodeFSStats = NodeFSStats {
1085+ nodeFSDataPaths :: [NodeDataPathStats]
1086+ , nodeFSTotal :: NodeFSTotalStats
1087+ , nodeFSTimestamp :: UTCTime
1088+ } deriving (Eq, Show)
1089+
1090+data NodeDataPathStats = NodeDataPathStats {
1091+ nodeDataPathDiskServiceTime :: Maybe Double
1092+ , nodeDataPathDiskQueue :: Maybe Double
1093+ , nodeDataPathIOSize :: Maybe Bytes
1094+ , nodeDataPathWriteSize :: Maybe Bytes
1095+ , nodeDataPathReadSize :: Maybe Bytes
1096+ , nodeDataPathIOOps :: Maybe Int
1097+ , nodeDataPathWrites :: Maybe Int
1098+ , nodeDataPathReads :: Maybe Int
1099+ , nodeDataPathAvailable :: Bytes
1100+ , nodeDataPathFree :: Bytes
1101+ , nodeDataPathTotal :: Bytes
1102+ , nodeDataPathType :: Maybe Text
1103+ , nodeDataPathDevice :: Maybe Text
1104+ , nodeDataPathMount :: Text
1105+ , nodeDataPathPath :: Text
1106+ } deriving (Eq, Show)
1107+
1108+data NodeFSTotalStats = NodeFSTotalStats {
1109+ nodeFSTotalDiskServiceTime :: Maybe Double
1110+ , nodeFSTotalDiskQueue :: Maybe Double
1111+ , nodeFSTotalIOSize :: Maybe Bytes
1112+ , nodeFSTotalWriteSize :: Maybe Bytes
1113+ , nodeFSTotalReadSize :: Maybe Bytes
1114+ , nodeFSTotalIOOps :: Maybe Int
1115+ , nodeFSTotalWrites :: Maybe Int
1116+ , nodeFSTotalReads :: Maybe Int
1117+ , nodeFSTotalAvailable :: Bytes
1118+ , nodeFSTotalFree :: Bytes
1119+ , nodeFSTotalTotal :: Bytes
1120+ } deriving (Eq, Show)
1121+
1122+data NodeNetworkStats = NodeNetworkStats {
1123+ nodeNetTCPOutRSTs :: Int
1124+ , nodeNetTCPInErrs :: Int
1125+ , nodeNetTCPAttemptFails :: Int
1126+ , nodeNetTCPEstabResets :: Int
1127+ , nodeNetTCPRetransSegs :: Int
1128+ , nodeNetTCPOutSegs :: Int
1129+ , nodeNetTCPInSegs :: Int
1130+ , nodeNetTCPCurrEstab :: Int
1131+ , nodeNetTCPPassiveOpens :: Int
1132+ , nodeNetTCPActiveOpens :: Int
1133+ } deriving (Eq, Show)
1134+
1135+data NodeThreadPoolsStats = NodeThreadPoolsStats {
1136+ nodeThreadPoolsStatsSnapshot :: NodeThreadPoolStats
1137+ , nodeThreadPoolsStatsBulk :: NodeThreadPoolStats
1138+ , nodeThreadPoolsStatsMerge :: NodeThreadPoolStats
1139+ , nodeThreadPoolsStatsGet :: NodeThreadPoolStats
1140+ , nodeThreadPoolsStatsManagement :: NodeThreadPoolStats
1141+ , nodeThreadPoolsStatsFetchShardStore :: Maybe NodeThreadPoolStats
1142+ , nodeThreadPoolsStatsOptimize :: Maybe NodeThreadPoolStats
1143+ , nodeThreadPoolsStatsFlush :: NodeThreadPoolStats
1144+ , nodeThreadPoolsStatsSearch :: NodeThreadPoolStats
1145+ , nodeThreadPoolsStatsWarmer :: NodeThreadPoolStats
1146+ , nodeThreadPoolsStatsGeneric :: NodeThreadPoolStats
1147+ , nodeThreadPoolsStatsSuggest :: Maybe NodeThreadPoolStats
1148+ , nodeThreadPoolsStatsRefresh :: NodeThreadPoolStats
1149+ , nodeThreadPoolsStatsIndex :: NodeThreadPoolStats
1150+ , nodeThreadPoolsStatsListener :: Maybe NodeThreadPoolStats
1151+ , nodeThreadPoolsStatsFetchShardStarted :: Maybe NodeThreadPoolStats
1152+ , nodeThreadPoolsStatsPercolate :: Maybe NodeThreadPoolStats
1153+ } deriving (Eq, Show)
1154+
1155+data NodeThreadPoolStats = NodeThreadPoolStats {
1156+ nodeThreadPoolCompleted :: Int
1157+ , nodeThreadPoolLargest :: Int
1158+ , nodeThreadPoolRejected :: Int
1159+ , nodeThreadPoolActive :: Int
1160+ , nodeThreadPoolQueue :: Int
1161+ , nodeThreadPoolThreads :: Int
1162+ } deriving (Eq, Show)
1163+
1164+data NodeJVMStats = NodeJVMStats {
1165+ nodeJVMStatsMappedBufferPool :: JVMBufferPoolStats
1166+ , nodeJVMStatsDirectBufferPool :: JVMBufferPoolStats
1167+ , nodeJVMStatsGCOldCollector :: JVMGCStats
1168+ , nodeJVMStatsGCYoungCollector :: JVMGCStats
1169+ , nodeJVMStatsPeakThreadsCount :: Int
1170+ , nodeJVMStatsThreadsCount :: Int
1171+ , nodeJVMStatsOldPool :: JVMPoolStats
1172+ , nodeJVMStatsSurvivorPool :: JVMPoolStats
1173+ , nodeJVMStatsYoungPool :: JVMPoolStats
1174+ , nodeJVMStatsNonHeapCommitted :: Bytes
1175+ , nodeJVMStatsNonHeapUsed :: Bytes
1176+ , nodeJVMStatsHeapMax :: Bytes
1177+ , nodeJVMStatsHeapCommitted :: Bytes
1178+ , nodeJVMStatsHeapUsedPercent :: Int
1179+ , nodeJVMStatsHeapUsed :: Bytes
1180+ , nodeJVMStatsUptime :: NominalDiffTime
1181+ , nodeJVMStatsTimestamp :: UTCTime
1182+ } deriving (Eq, Show)
1183+
1184+data JVMBufferPoolStats = JVMBufferPoolStats {
1185+ jvmBufferPoolStatsTotalCapacity :: Bytes
1186+ , jvmBufferPoolStatsUsed :: Bytes
1187+ , jvmBufferPoolStatsCount :: Int
1188+ } deriving (Eq, Show)
1189+
1190+data JVMGCStats = JVMGCStats {
1191+ jvmGCStatsCollectionTime :: NominalDiffTime
1192+ , jvmGCStatsCollectionCount :: Int
1193+ } deriving (Eq, Show)
1194+
1195+data JVMPoolStats = JVMPoolStats {
1196+ jvmPoolStatsPeakMax :: Bytes
1197+ , jvmPoolStatsPeakUsed :: Bytes
1198+ , jvmPoolStatsMax :: Bytes
1199+ , jvmPoolStatsUsed :: Bytes
1200+ } deriving (Eq, Show)
1201+
1202+data NodeProcessStats = NodeProcessStats {
1203+ nodeProcessTimestamp :: UTCTime
1204+ , nodeProcessOpenFDs :: Int
1205+ , nodeProcessMaxFDs :: Int
1206+ , nodeProcessCPUPercent :: Int
1207+ , nodeProcessCPUTotal :: NominalDiffTime
1208+ , nodeProcessMemTotalVirtual :: Bytes
1209+ } deriving (Eq, Show)
1210+
1211+data NodeOSStats = NodeOSStats {
1212+ nodeOSTimestamp :: UTCTime
1213+ , nodeOSCPUPercent :: Int
1214+ , nodeOSLoad :: Maybe LoadAvgs
1215+ , nodeOSMemTotal :: Bytes
1216+ , nodeOSMemFree :: Bytes
1217+ , nodeOSMemFreePercent :: Int
1218+ , nodeOSMemUsed :: Bytes
1219+ , nodeOSMemUsedPercent :: Int
1220+ , nodeOSSwapTotal :: Bytes
1221+ , nodeOSSwapFree :: Bytes
1222+ , nodeOSSwapUsed :: Bytes
1223+ } deriving (Eq, Show)
1224+
1225+data LoadAvgs = LoadAvgs {
1226+ loadAvg1Min :: Double
1227+ , loadAvg5Min :: Double
1228+ , loadAvg15Min :: Double
1229+ } deriving (Eq, Show)
1230+
1231+data NodeIndicesStats = NodeIndicesStats {
1232+ nodeIndicesStatsRecoveryThrottleTime :: Maybe NominalDiffTime
1233+ , nodeIndicesStatsRecoveryCurrentAsTarget :: Maybe Int
1234+ , nodeIndicesStatsRecoveryCurrentAsSource :: Maybe Int
1235+ , nodeIndicesStatsQueryCacheMisses :: Maybe Int
1236+ , nodeIndicesStatsQueryCacheHits :: Maybe Int
1237+ , nodeIndicesStatsQueryCacheEvictions :: Maybe Int
1238+ , nodeIndicesStatsQueryCacheSize :: Maybe Bytes
1239+ , nodeIndicesStatsSuggestCurrent :: Maybe Int
1240+ , nodeIndicesStatsSuggestTime :: Maybe NominalDiffTime
1241+ , nodeIndicesStatsSuggestTotal :: Maybe Int
1242+ , nodeIndicesStatsTranslogSize :: Bytes
1243+ , nodeIndicesStatsTranslogOps :: Int
1244+ , nodeIndicesStatsSegFixedBitSetMemory :: Maybe Bytes
1245+ , nodeIndicesStatsSegVersionMapMemory :: Bytes
1246+ , nodeIndicesStatsSegIndexWriterMaxMemory :: Maybe Bytes
1247+ , nodeIndicesStatsSegIndexWriterMemory :: Bytes
1248+ , nodeIndicesStatsSegMemory :: Bytes
1249+ , nodeIndicesStatsSegCount :: Int
1250+ , nodeIndicesStatsCompletionSize :: Bytes
1251+ , nodeIndicesStatsPercolateQueries :: Maybe Int
1252+ , nodeIndicesStatsPercolateMemory :: Maybe Bytes
1253+ , nodeIndicesStatsPercolateCurrent :: Maybe Int
1254+ , nodeIndicesStatsPercolateTime :: Maybe NominalDiffTime
1255+ , nodeIndicesStatsPercolateTotal :: Maybe Int
1256+ , nodeIndicesStatsFieldDataEvictions :: Int
1257+ , nodeIndicesStatsFieldDataMemory :: Bytes
1258+ , nodeIndicesStatsWarmerTotalTime :: NominalDiffTime
1259+ , nodeIndicesStatsWarmerTotal :: Int
1260+ , nodeIndicesStatsWarmerCurrent :: Int
1261+ , nodeIndicesStatsFlushTotalTime :: NominalDiffTime
1262+ , nodeIndicesStatsFlushTotal :: Int
1263+ , nodeIndicesStatsRefreshTotalTime :: NominalDiffTime
1264+ , nodeIndicesStatsRefreshTotal :: Int
1265+ , nodeIndicesStatsMergesTotalSize :: Bytes
1266+ , nodeIndicesStatsMergesTotalDocs :: Int
1267+ , nodeIndicesStatsMergesTotalTime :: NominalDiffTime
1268+ , nodeIndicesStatsMergesTotal :: Int
1269+ , nodeIndicesStatsMergesCurrentSize :: Bytes
1270+ , nodeIndicesStatsMergesCurrentDocs :: Int
1271+ , nodeIndicesStatsMergesCurrent :: Int
1272+ , nodeIndicesStatsSearchFetchCurrent :: Int
1273+ , nodeIndicesStatsSearchFetchTime :: NominalDiffTime
1274+ , nodeIndicesStatsSearchFetchTotal :: Int
1275+ , nodeIndicesStatsSearchQueryCurrent :: Int
1276+ , nodeIndicesStatsSearchQueryTime :: NominalDiffTime
1277+ , nodeIndicesStatsSearchQueryTotal :: Int
1278+ , nodeIndicesStatsSearchOpenContexts :: Int
1279+ , nodeIndicesStatsGetCurrent :: Int
1280+ , nodeIndicesStatsGetMissingTime :: NominalDiffTime
1281+ , nodeIndicesStatsGetMissingTotal :: Int
1282+ , nodeIndicesStatsGetExistsTime :: NominalDiffTime
1283+ , nodeIndicesStatsGetExistsTotal :: Int
1284+ , nodeIndicesStatsGetTime :: NominalDiffTime
1285+ , nodeIndicesStatsGetTotal :: Int
1286+ , nodeIndicesStatsIndexingThrottleTime :: Maybe NominalDiffTime
1287+ , nodeIndicesStatsIndexingIsThrottled :: Maybe Bool
1288+ , nodeIndicesStatsIndexingNoopUpdateTotal :: Maybe Int
1289+ , nodeIndicesStatsIndexingDeleteCurrent :: Int
1290+ , nodeIndicesStatsIndexingDeleteTime :: NominalDiffTime
1291+ , nodeIndicesStatsIndexingDeleteTotal :: Int
1292+ , nodeIndicesStatsIndexingIndexCurrent :: Int
1293+ , nodeIndicesStatsIndexingIndexTime :: NominalDiffTime
1294+ , nodeIndicesStatsIndexingTotal :: Int
1295+ , nodeIndicesStatsStoreThrottleTime :: NominalDiffTime
1296+ , nodeIndicesStatsStoreSize :: Bytes
1297+ , nodeIndicesStatsDocsDeleted :: Int
1298+ , nodeIndicesStatsDocsCount :: Int
1299+ } deriving (Eq, Show)
1300+
1301+-- | A quirky address format used throughout ElasticSearch. An example
1302+-- would be inet[/1.1.1.1:9200]. inet may be a placeholder for a
1303+-- <https://en.wikipedia.org/wiki/Fully_qualified_domain_name FQDN>.
1304+newtype EsAddress = EsAddress { esAddress :: Text }
1305+ deriving (Eq, Ord, Show, FromJSON)
1306+
1307+-- | Typically a 7 character hex string.
1308+newtype BuildHash = BuildHash { buildHash :: Text }
1309+ deriving (Eq, Ord, Show, FromJSON, ToJSON)
1310+
1311+newtype PluginName = PluginName { pluginName :: Text }
1312+ deriving (Eq, Ord, Show, FromJSON)
1313+
1314+data NodeInfo = NodeInfo {
1315+ nodeInfoHTTPAddress :: Maybe EsAddress
1316+ , nodeInfoBuild :: BuildHash
1317+ , nodeInfoESVersion :: VersionNumber
1318+ , nodeInfoIP :: Server
1319+ , nodeInfoHost :: Server
1320+ , nodeInfoTransportAddress :: EsAddress
1321+ , nodeInfoName :: NodeName
1322+ , nodeInfoFullId :: FullNodeId
1323+ , nodeInfoPlugins :: [NodePluginInfo]
1324+ , nodeInfoHTTP :: NodeHTTPInfo
1325+ , nodeInfoTransport :: NodeTransportInfo
1326+ , nodeInfoNetwork :: Maybe NodeNetworkInfo
1327+ , nodeInfoThreadPool :: NodeThreadPoolsInfo
1328+ , nodeInfoJVM :: NodeJVMInfo
1329+ , nodeInfoProcess :: NodeProcessInfo
1330+ , nodeInfoOS :: NodeOSInfo
1331+ , nodeInfoSettings :: Object
1332+ -- ^ The members of the settings objects are not consistent,
1333+ -- dependent on plugins, etc.
1334+ } deriving (Eq, Show)
1335+
1336+data NodePluginInfo = NodePluginInfo {
1337+ nodePluginSite :: Maybe Bool
1338+ -- ^ Is this a site plugin?
1339+ , nodePluginJVM :: Maybe Bool
1340+ -- ^ Is this plugin running on the JVM
1341+ , nodePluginDescription :: Text
1342+ , nodePluginVersion :: MaybeNA VersionNumber
1343+ , nodePluginName :: PluginName
1344+ } deriving (Eq, Show)
1345+
1346+data NodeHTTPInfo = NodeHTTPInfo {
1347+ nodeHTTPMaxContentLength :: Bytes
1348+ , nodeHTTPTransportAddress :: BoundTransportAddress
1349+ } deriving (Eq, Show)
1350+
1351+data NodeTransportInfo = NodeTransportInfo {
1352+ nodeTransportProfiles :: [BoundTransportAddress]
1353+ , nodeTransportAddress :: BoundTransportAddress
1354+ } deriving (Eq, Show)
1355+
1356+data BoundTransportAddress = BoundTransportAddress {
1357+ publishAddress :: EsAddress
1358+ , boundAddress :: [EsAddress]
1359+ } deriving (Eq, Show)
1360+
1361+data NodeNetworkInfo = NodeNetworkInfo {
1362+ nodeNetworkPrimaryInterface :: NodeNetworkInterface
1363+ , nodeNetworkRefreshInterval :: NominalDiffTime
1364+ } deriving (Eq, Show)
1365+
1366+newtype MacAddress = MacAddress { macAddress :: Text }
1367+ deriving (Eq, Ord, Show, FromJSON)
1368+
1369+newtype NetworkInterfaceName = NetworkInterfaceName { networkInterfaceName :: Text }
1370+ deriving (Eq, Ord, Show, FromJSON)
1371+
1372+data NodeNetworkInterface = NodeNetworkInterface {
1373+ nodeNetIfaceMacAddress :: MacAddress
1374+ , nodeNetIfaceName :: NetworkInterfaceName
1375+ , nodeNetIfaceAddress :: Server
1376+ } deriving (Eq, Show)
1377+
1378+data NodeThreadPoolsInfo = NodeThreadPoolsInfo {
1379+ nodeThreadPoolsRefresh :: NodeThreadPoolInfo
1380+ , nodeThreadPoolsManagement :: NodeThreadPoolInfo
1381+ , nodeThreadPoolsPercolate :: Maybe NodeThreadPoolInfo
1382+ , nodeThreadPoolsListener :: Maybe NodeThreadPoolInfo
1383+ , nodeThreadPoolsFetchShardStarted :: Maybe NodeThreadPoolInfo
1384+ , nodeThreadPoolsSearch :: NodeThreadPoolInfo
1385+ , nodeThreadPoolsFlush :: NodeThreadPoolInfo
1386+ , nodeThreadPoolsWarmer :: NodeThreadPoolInfo
1387+ , nodeThreadPoolsOptimize :: Maybe NodeThreadPoolInfo
1388+ , nodeThreadPoolsBulk :: NodeThreadPoolInfo
1389+ , nodeThreadPoolsSuggest :: Maybe NodeThreadPoolInfo
1390+ , nodeThreadPoolsMerge :: NodeThreadPoolInfo
1391+ , nodeThreadPoolsSnapshot :: NodeThreadPoolInfo
1392+ , nodeThreadPoolsGet :: NodeThreadPoolInfo
1393+ , nodeThreadPoolsFetchShardStore :: Maybe NodeThreadPoolInfo
1394+ , nodeThreadPoolsIndex :: NodeThreadPoolInfo
1395+ , nodeThreadPoolsGeneric :: NodeThreadPoolInfo
1396+ } deriving (Eq, Show)
1397+
1398+data NodeThreadPoolInfo = NodeThreadPoolInfo {
1399+ nodeThreadPoolQueueSize :: ThreadPoolSize
1400+ , nodeThreadPoolKeepalive :: Maybe NominalDiffTime
1401+ , nodeThreadPoolMin :: Maybe Int
1402+ , nodeThreadPoolMax :: Maybe Int
1403+ , nodeThreadPoolType :: ThreadPoolType
1404+ } deriving (Eq, Show)
1405+
1406+data ThreadPoolSize = ThreadPoolBounded Int
1407+ | ThreadPoolUnbounded
1408+ deriving (Eq, Show)
1409+
1410+data ThreadPoolType = ThreadPoolScaling
1411+ | ThreadPoolFixed
1412+ | ThreadPoolCached
1413+ deriving (Eq, Show)
1414+
1415+data NodeJVMInfo = NodeJVMInfo {
1416+ nodeJVMInfoMemoryPools :: [JVMMemoryPool]
1417+ , nodeJVMInfoMemoryPoolsGCCollectors :: [JVMGCCollector]
1418+ , nodeJVMInfoMemoryInfo :: JVMMemoryInfo
1419+ , nodeJVMInfoStartTime :: UTCTime
1420+ , nodeJVMInfoVMVendor :: Text
1421+ , nodeJVMVMVersion :: VersionNumber
1422+ -- ^ JVM doesn't seme to follow normal version conventions
1423+ , nodeJVMVMName :: Text
1424+ , nodeJVMVersion :: VersionNumber
1425+ , nodeJVMPID :: PID
1426+ } deriving (Eq, Show)
1427+
1428+-- | Handles quirks in the way JVM versions are rendered (1.7.0_101 -> 1.7.0.101)
1429+newtype JVMVersion = JVMVersion { unJVMVersion :: VersionNumber }
1430+
1431+data JVMMemoryInfo = JVMMemoryInfo {
1432+ jvmMemoryInfoDirectMax :: Bytes
1433+ , jvmMemoryInfoNonHeapMax :: Bytes
1434+ , jvmMemoryInfoNonHeapInit :: Bytes
1435+ , jvmMemoryInfoHeapMax :: Bytes
1436+ , jvmMemoryInfoHeapInit :: Bytes
1437+ } deriving (Eq, Show)
1438+
1439+newtype JVMMemoryPool = JVMMemoryPool {
1440+ jvmMemoryPool :: Text
1441+ } deriving (Eq, Show, FromJSON)
1442+
1443+newtype JVMGCCollector = JVMGCCollector {
1444+ jvmGCCollector :: Text
1445+ } deriving (Eq, Show, FromJSON)
1446+
1447+newtype PID = PID {
1448+ pid :: Int
1449+ } deriving (Eq, Show, FromJSON)
1450+
1451+data NodeOSInfo = NodeOSInfo {
1452+ nodeOSRefreshInterval :: NominalDiffTime
1453+ , nodeOSName :: Text
1454+ , nodeOSArch :: Text
1455+ , nodeOSVersion :: VersionNumber
1456+ , nodeOSAvailableProcessors :: Int
1457+ , nodeOSAllocatedProcessors :: Int
1458+ } deriving (Eq, Show)
1459+
1460+data CPUInfo = CPUInfo {
1461+ cpuCacheSize :: Bytes
1462+ , cpuCoresPerSocket :: Int
1463+ , cpuTotalSockets :: Int
1464+ , cpuTotalCores :: Int
1465+ , cpuMHZ :: Int
1466+ , cpuModel :: Text
1467+ , cpuVendor :: Text
1468+ } deriving (Eq, Show)
1469+
1470+data NodeProcessInfo = NodeProcessInfo {
1471+ nodeProcessMLockAll :: Bool
1472+ -- ^ See <https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
1473+ , nodeProcessMaxFileDescriptors :: Maybe Int
1474+ , nodeProcessId :: PID
1475+ , nodeProcessRefreshInterval :: NominalDiffTime
1476+ } deriving (Eq, Show)
1477+
1478+data ShardResult =
1479+ ShardResult { shardTotal :: Int
1480+ , shardsSuccessful :: Int
1481+ , shardsFailed :: Int } deriving (Eq, Show)
1482+
1483+instance FromJSON ShardResult where
1484+ parseJSON (Object v) = ShardResult <$>
1485+ v .: "total" <*>
1486+ v .: "successful" <*>
1487+ v .: "failed"
1488+ parseJSON _ = empty
1489+
1490+data SnapshotState = SnapshotInit
1491+ | SnapshotStarted
1492+ | SnapshotSuccess
1493+ | SnapshotFailed
1494+ | SnapshotAborted
1495+ | SnapshotMissing
1496+ | SnapshotWaiting
1497+ deriving (Eq, Show)
1498+
1499+instance FromJSON SnapshotState where
1500+ parseJSON = withText "SnapshotState" parse
1501+ where
1502+ parse "INIT" = return SnapshotInit
1503+ parse "STARTED" = return SnapshotStarted
1504+ parse "SUCCESS" = return SnapshotSuccess
1505+ parse "FAILED" = return SnapshotFailed
1506+ parse "ABORTED" = return SnapshotAborted
1507+ parse "MISSING" = return SnapshotMissing
1508+ parse "WAITING" = return SnapshotWaiting
1509+ parse t = fail ("Invalid snapshot state " <> T.unpack t)
1510+
1511+
1512+data SnapshotRestoreSettings = SnapshotRestoreSettings {
1513+ snapRestoreWaitForCompletion :: Bool
1514+ -- ^ Should the API call return immediately after initializing
1515+ -- the restore or wait until completed? Note that if this is
1516+ -- enabled, it could wait a long time, so you should adjust your
1517+ -- 'ManagerSettings' accordingly to set long timeouts or
1518+ -- explicitly handle timeouts.
1519+ , snapRestoreIndices :: Maybe IndexSelection
1520+ -- ^ Nothing will restore all indices in the snapshot. Just [] is
1521+ -- permissable and will essentially be a no-op restore.
1522+ , snapRestoreIgnoreUnavailable :: Bool
1523+ -- ^ If set to True, any indices that do not exist will be ignored
1524+ -- during snapshot rather than failing the restore.
1525+ , snapRestoreIncludeGlobalState :: Bool
1526+ -- ^ If set to false, will ignore any global state in the snapshot
1527+ -- and will not restore it.
1528+ , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
1529+ -- ^ A regex pattern for matching indices. Used with
1530+ -- 'snapRestoreRenameReplacement', the restore can reference the
1531+ -- matched index and create a new index name upon restore.
1532+ , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
1533+ -- ^ Expression of how index renames should be constructed.
1534+ , snapRestorePartial :: Bool
1535+ -- ^ If some indices fail to restore, should the process proceed?
1536+ , snapRestoreIncludeAliases :: Bool
1537+ -- ^ Should the restore also restore the aliases captured in the
1538+ -- snapshot.
1539+ , snapRestoreIndexSettingsOverrides :: Maybe RestoreIndexSettings
1540+ -- ^ Settings to apply during the restore process. __NOTE:__ This
1541+ -- option is not supported in ES < 1.5 and should be set to
1542+ -- Nothing in that case.
1543+ , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
1544+ -- ^ This type could be more rich but it isn't clear which
1545+ -- settings are allowed to be ignored during restore, so we're
1546+ -- going with including this feature in a basic form rather than
1547+ -- omitting it. One example here would be
1548+ -- "index.refresh_interval". Any setting specified here will
1549+ -- revert back to the server default during the restore process.
1550+ } deriving (Eq, Show)
1551+
1552+newtype SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings
1553+ { repoUpdateVerify :: Bool
1554+ -- ^ After creation/update, synchronously check that nodes can
1555+ -- write to this repo. Defaults to True. You may use False if you
1556+ -- need a faster response and plan on verifying manually later
1557+ -- with 'verifySnapshotRepo'.
1558+ } deriving (Eq, Show)
1559+
1560+
1561+-- | Reasonable defaults for repo creation/update
1562+--
1563+-- * repoUpdateVerify True
1564+defaultSnapshotRepoUpdateSettings :: SnapshotRepoUpdateSettings
1565+defaultSnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings True
1566+
1567+
1568+-- | A filesystem-based snapshot repo that ships with
1569+-- ElasticSearch. This is an instance of 'SnapshotRepo' so it can be
1570+-- used with 'updateSnapshotRepo'
1571+data FsSnapshotRepo = FsSnapshotRepo {
1572+ fsrName :: SnapshotRepoName
1573+ , fsrLocation :: FilePath
1574+ , fsrCompressMetadata :: Bool
1575+ , fsrChunkSize :: Maybe Bytes
1576+ -- ^ Size by which to split large files during snapshotting.
1577+ , fsrMaxRestoreBytesPerSec :: Maybe Bytes
1578+ -- ^ Throttle node restore rate. If not supplied, defaults to 40mb/sec
1579+ , fsrMaxSnapshotBytesPerSec :: Maybe Bytes
1580+ -- ^ Throttle node snapshot rate. If not supplied, defaults to 40mb/sec
1581+ } deriving (Eq, Show)
1582+
1583+
1584+instance SnapshotRepo FsSnapshotRepo where
1585+ toGSnapshotRepo FsSnapshotRepo {..} =
1586+ GenericSnapshotRepo fsrName fsRepoType (GenericSnapshotRepoSettings settings)
1587+ where
1588+ Object settings = object $ [ "location" .= fsrLocation
1589+ , "compress" .= fsrCompressMetadata
1590+ ] ++ optionalPairs
1591+ optionalPairs = catMaybes [ ("chunk_size" .=) <$> fsrChunkSize
1592+ , ("max_restore_bytes_per_sec" .=) <$> fsrMaxRestoreBytesPerSec
1593+ , ("max_snapshot_bytes_per_sec" .=) <$> fsrMaxSnapshotBytesPerSec
1594+ ]
1595+ fromGSnapshotRepo GenericSnapshotRepo {..}
1596+ | gSnapshotRepoType == fsRepoType = do
1597+ let o = gSnapshotRepoSettingsObject gSnapshotRepoSettings
1598+ parseRepo $
1599+ FsSnapshotRepo gSnapshotRepoName <$> o .: "location"
1600+ <*> o .:? "compress" .!= False
1601+ <*> o .:? "chunk_size"
1602+ <*> o .:? "max_restore_bytes_per_sec"
1603+ <*> o .:? "max_snapshot_bytes_per_sec"
1604+ | otherwise = Left (RepoTypeMismatch fsRepoType gSnapshotRepoType)
1605+
1606+
1607+parseRepo :: Parser a -> Either SnapshotRepoConversionError a
1608+parseRepo parser = case parseEither (const parser) () of
1609+ Left e -> Left (OtherRepoConversionError (T.pack e))
1610+ Right a -> Right a
1611+
1612+
1613+fsRepoType :: SnapshotRepoType
1614+fsRepoType = SnapshotRepoType "fs"
1615+
1616+-- | Law: fromGSnapshotRepo (toGSnapshotRepo r) == Right r
1617+class SnapshotRepo r where
1618+ toGSnapshotRepo :: r -> GenericSnapshotRepo
1619+ fromGSnapshotRepo :: GenericSnapshotRepo -> Either SnapshotRepoConversionError r
1620+
1621+
1622+data SnapshotRepoConversionError = RepoTypeMismatch SnapshotRepoType SnapshotRepoType
1623+ -- ^ Expected type and actual type
1624+ | OtherRepoConversionError Text
1625+ deriving (Show, Eq)
1626+
1627+
1628+instance Exception SnapshotRepoConversionError
1629+
1630+
1631+data SnapshotCreateSettings = SnapshotCreateSettings {
1632+ snapWaitForCompletion :: Bool
1633+ -- ^ Should the API call return immediately after initializing
1634+ -- the snapshot or wait until completed? Note that if this is
1635+ -- enabled it could wait a long time, so you should adjust your
1636+ -- 'ManagerSettings' accordingly to set long timeouts or
1637+ -- explicitly handle timeouts.
1638+ , snapIndices :: Maybe IndexSelection
1639+ -- ^ Nothing will snapshot all indices. Just [] is permissable and
1640+ -- will essentially be a no-op snapshot.
1641+ , snapIgnoreUnavailable :: Bool
1642+ -- ^ If set to True, any matched indices that don't exist will be
1643+ -- ignored. Otherwise it will be an error and fail.
1644+ , snapIncludeGlobalState :: Bool
1645+ , snapPartial :: Bool
1646+ -- ^ If some indices failed to snapshot (e.g. if not all primary
1647+ -- shards are available), should the process proceed?
1648+ } deriving (Eq, Show)
1649+
1650+
1651+-- | Reasonable defaults for snapshot creation
1652+--
1653+-- * snapWaitForCompletion False
1654+-- * snapIndices Nothing
1655+-- * snapIgnoreUnavailable False
1656+-- * snapIncludeGlobalState True
1657+-- * snapPartial False
1658+defaultSnapshotCreateSettings :: SnapshotCreateSettings
1659+defaultSnapshotCreateSettings = SnapshotCreateSettings {
1660+ snapWaitForCompletion = False
1661+ , snapIndices = Nothing
1662+ , snapIgnoreUnavailable = False
1663+ , snapIncludeGlobalState = True
1664+ , snapPartial = False
1665+ }
1666+
1667+
1668+data SnapshotSelection =
1669+ SnapshotList (NonEmpty SnapshotPattern)
1670+ | AllSnapshots
1671+ deriving (Eq, Show)
1672+
1673+
1674+-- | Either specifies an exact snapshot name or one with globs in it,
1675+-- e.g. @SnapPattern "foo*"@ __NOTE__: Patterns are not supported on
1676+-- ES < 1.7
1677+data SnapshotPattern =
1678+ ExactSnap SnapshotName
1679+ | SnapPattern Text
1680+ deriving (Eq, Show)
1681+
1682+
1683+-- | General information about the state of a snapshot. Has some
1684+-- redundancies with 'SnapshotStatus'
1685+data SnapshotInfo = SnapshotInfo {
1686+ snapInfoShards :: ShardResult
1687+ , snapInfoFailures :: [SnapshotShardFailure]
1688+ , snapInfoDuration :: NominalDiffTime
1689+ , snapInfoEndTime :: UTCTime
1690+ , snapInfoStartTime :: UTCTime
1691+ , snapInfoState :: SnapshotState
1692+ , snapInfoIndices :: [IndexName]
1693+ , snapInfoName :: SnapshotName
1694+ } deriving (Eq, Show)
1695+
1696+
1697+instance FromJSON SnapshotInfo where
1698+ parseJSON = withObject "SnapshotInfo" parse
1699+ where
1700+ parse o = SnapshotInfo <$> o .: "shards"
1701+ <*> o .: "failures"
1702+ <*> (unMS <$> o .: "duration_in_millis")
1703+ <*> (posixMS <$> o .: "end_time_in_millis")
1704+ <*> (posixMS <$> o .: "start_time_in_millis")
1705+ <*> o .: "state"
1706+ <*> o .: "indices"
1707+ <*> o .: "snapshot"
1708+
1709+data SnapshotShardFailure = SnapshotShardFailure {
1710+ snapShardFailureIndex :: IndexName
1711+ , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
1712+ , snapShardFailureReason :: Text
1713+ , snapShardFailureShardId :: ShardId
1714+ } deriving (Eq, Show)
1715+
1716+
1717+instance FromJSON SnapshotShardFailure where
1718+ parseJSON = withObject "SnapshotShardFailure" parse
1719+ where
1720+ parse o = SnapshotShardFailure <$> o .: "index"
1721+ <*> o .:? "node_id"
1722+ <*> o .: "reason"
1723+ <*> o .: "shard_id"
1724+
1725+-- | Regex-stype pattern, e.g. "index_(.+)" to match index names
1726+newtype RestoreRenamePattern =
1727+ RestoreRenamePattern { rrPattern :: Text }
1728+ deriving (Eq, Show, Ord, ToJSON)
1729+
1730+
1731+-- | A single token in a index renaming scheme for a restore. These
1732+-- are concatenated into a string before being sent to
1733+-- ElasticSearch. Check out these Java
1734+-- <https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html docs> to find out more if you're into that sort of thing.
1735+data RestoreRenameToken = RRTLit Text
1736+ -- ^ Just a literal string of characters
1737+ | RRSubWholeMatch
1738+ -- ^ Equivalent to $0. The entire matched pattern, not any subgroup
1739+ | RRSubGroup RRGroupRefNum
1740+ -- ^ A specific reference to a group number
1741+ deriving (Eq, Show)
1742+
1743+
1744+-- | A group number for regex matching. Only values from 1-9 are
1745+-- supported. Construct with 'mkRRGroupRefNum'
1746+newtype RRGroupRefNum =
1747+ RRGroupRefNum { rrGroupRefNum :: Int }
1748+ deriving (Eq, Ord, Show)
1749+
1750+instance Bounded RRGroupRefNum where
1751+ minBound = RRGroupRefNum 1
1752+ maxBound = RRGroupRefNum 9
1753+
1754+
1755+-- | Only allows valid group number references (1-9).
1756+mkRRGroupRefNum :: Int -> Maybe RRGroupRefNum
1757+mkRRGroupRefNum i
1758+ | i >= rrGroupRefNum minBound
1759+ && i <= rrGroupRefNum maxBound =
1760+ Just $ RRGroupRefNum i
1761+ | otherwise = Nothing
1762+
1763+-- | Reasonable defaults for snapshot restores
1764+--
1765+-- * snapRestoreWaitForCompletion False
1766+-- * snapRestoreIndices Nothing
1767+-- * snapRestoreIgnoreUnavailable False
1768+-- * snapRestoreIncludeGlobalState True
1769+-- * snapRestoreRenamePattern Nothing
1770+-- * snapRestoreRenameReplacement Nothing
1771+-- * snapRestorePartial False
1772+-- * snapRestoreIncludeAliases True
1773+-- * snapRestoreIndexSettingsOverrides Nothing
1774+-- * snapRestoreIgnoreIndexSettings Nothing
1775+defaultSnapshotRestoreSettings :: SnapshotRestoreSettings
1776+defaultSnapshotRestoreSettings = SnapshotRestoreSettings {
1777+ snapRestoreWaitForCompletion = False
1778+ , snapRestoreIndices = Nothing
1779+ , snapRestoreIgnoreUnavailable = False
1780+ , snapRestoreIncludeGlobalState = True
1781+ , snapRestoreRenamePattern = Nothing
1782+ , snapRestoreRenameReplacement = Nothing
1783+ , snapRestorePartial = False
1784+ , snapRestoreIncludeAliases = True
1785+ , snapRestoreIndexSettingsOverrides = Nothing
1786+ , snapRestoreIgnoreIndexSettings = Nothing
1787+ }
1788+
1789+
1790+-- | Index settings that can be overridden. The docs only mention you
1791+-- can update number of replicas, but there may be more. You
1792+-- definitely cannot override shard count.
1793+newtype RestoreIndexSettings = RestoreIndexSettings
1794+ { restoreOverrideReplicas :: Maybe ReplicaCount
1795+ } deriving (Eq, Show)
1796+
1797+
1798+instance ToJSON RestoreIndexSettings where
1799+ toJSON RestoreIndexSettings {..} = object prs
1800+ where
1801+ prs = catMaybes [("index.number_of_replicas" .=) <$> restoreOverrideReplicas]
1802+
1803+
1804+instance FromJSON NodesInfo where
1805+ parseJSON = withObject "NodesInfo" parse
1806+ where
1807+ parse o = do
1808+ nodes <- o .: "nodes"
1809+ infos <- forM (HM.toList nodes) $ \(fullNID, v) -> do
1810+ node <- parseJSON v
1811+ parseNodeInfo (FullNodeId fullNID) node
1812+ cn <- o .: "cluster_name"
1813+ return (NodesInfo infos cn)
1814+
1815+instance FromJSON NodesStats where
1816+ parseJSON = withObject "NodesStats" parse
1817+ where
1818+ parse o = do
1819+ nodes <- o .: "nodes"
1820+ stats <- forM (HM.toList nodes) $ \(fullNID, v) -> do
1821+ node <- parseJSON v
1822+ parseNodeStats (FullNodeId fullNID) node
1823+ cn <- o .: "cluster_name"
1824+ return (NodesStats stats cn)
1825+
1826+instance FromJSON NodeBreakerStats where
1827+ parseJSON = withObject "NodeBreakerStats" parse
1828+ where
1829+ parse o = NodeBreakerStats <$> o .: "tripped"
1830+ <*> o .: "overhead"
1831+ <*> o .: "estimated_size_in_bytes"
1832+ <*> o .: "limit_size_in_bytes"
1833+
1834+instance FromJSON NodeHTTPStats where
1835+ parseJSON = withObject "NodeHTTPStats" parse
1836+ where
1837+ parse o = NodeHTTPStats <$> o .: "total_opened"
1838+ <*> o .: "current_open"
1839+
1840+instance FromJSON NodeTransportStats where
1841+ parseJSON = withObject "NodeTransportStats" parse
1842+ where
1843+ parse o = NodeTransportStats <$> o .: "tx_size_in_bytes"
1844+ <*> o .: "tx_count"
1845+ <*> o .: "rx_size_in_bytes"
1846+ <*> o .: "rx_count"
1847+ <*> o .: "server_open"
1848+
1849+instance FromJSON NodeFSStats where
1850+ parseJSON = withObject "NodeFSStats" parse
1851+ where
1852+ parse o = NodeFSStats <$> o .: "data"
1853+ <*> o .: "total"
1854+ <*> (posixMS <$> o .: "timestamp")
1855+
1856+instance FromJSON NodeDataPathStats where
1857+ parseJSON = withObject "NodeDataPathStats" parse
1858+ where
1859+ parse o =
1860+ NodeDataPathStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
1861+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
1862+ <*> o .:? "disk_io_size_in_bytes"
1863+ <*> o .:? "disk_write_size_in_bytes"
1864+ <*> o .:? "disk_read_size_in_bytes"
1865+ <*> o .:? "disk_io_op"
1866+ <*> o .:? "disk_writes"
1867+ <*> o .:? "disk_reads"
1868+ <*> o .: "available_in_bytes"
1869+ <*> o .: "free_in_bytes"
1870+ <*> o .: "total_in_bytes"
1871+ <*> o .:? "type"
1872+ <*> o .:? "dev"
1873+ <*> o .: "mount"
1874+ <*> o .: "path"
1875+
1876+instance FromJSON NodeFSTotalStats where
1877+ parseJSON = withObject "NodeFSTotalStats" parse
1878+ where
1879+ parse o = NodeFSTotalStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
1880+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
1881+ <*> o .:? "disk_io_size_in_bytes"
1882+ <*> o .:? "disk_write_size_in_bytes"
1883+ <*> o .:? "disk_read_size_in_bytes"
1884+ <*> o .:? "disk_io_op"
1885+ <*> o .:? "disk_writes"
1886+ <*> o .:? "disk_reads"
1887+ <*> o .: "available_in_bytes"
1888+ <*> o .: "free_in_bytes"
1889+ <*> o .: "total_in_bytes"
1890+
1891+instance FromJSON NodeNetworkStats where
1892+ parseJSON = withObject "NodeNetworkStats" parse
1893+ where
1894+ parse o = do
1895+ tcp <- o .: "tcp"
1896+ NodeNetworkStats <$> tcp .: "out_rsts"
1897+ <*> tcp .: "in_errs"
1898+ <*> tcp .: "attempt_fails"
1899+ <*> tcp .: "estab_resets"
1900+ <*> tcp .: "retrans_segs"
1901+ <*> tcp .: "out_segs"
1902+ <*> tcp .: "in_segs"
1903+ <*> tcp .: "curr_estab"
1904+ <*> tcp .: "passive_opens"
1905+ <*> tcp .: "active_opens"
1906+
1907+instance FromJSON NodeThreadPoolsStats where
1908+ parseJSON = withObject "NodeThreadPoolsStats" parse
1909+ where
1910+ parse o = NodeThreadPoolsStats <$> o .: "snapshot"
1911+ <*> o .: "bulk"
1912+ <*> o .: "force_merge"
1913+ <*> o .: "get"
1914+ <*> o .: "management"
1915+ <*> o .:? "fetch_shard_store"
1916+ <*> o .:? "optimize"
1917+ <*> o .: "flush"
1918+ <*> o .: "search"
1919+ <*> o .: "warmer"
1920+ <*> o .: "generic"
1921+ <*> o .:? "suggest"
1922+ <*> o .: "refresh"
1923+ <*> o .: "index"
1924+ <*> o .:? "listener"
1925+ <*> o .:? "fetch_shard_started"
1926+ <*> o .:? "percolate"
1927+instance FromJSON NodeThreadPoolStats where
1928+ parseJSON = withObject "NodeThreadPoolStats" parse
1929+ where
1930+ parse o = NodeThreadPoolStats <$> o .: "completed"
1931+ <*> o .: "largest"
1932+ <*> o .: "rejected"
1933+ <*> o .: "active"
1934+ <*> o .: "queue"
1935+ <*> o .: "threads"
1936+
1937+instance FromJSON NodeJVMStats where
1938+ parseJSON = withObject "NodeJVMStats" parse
1939+ where
1940+ parse o = do
1941+ bufferPools <- o .: "buffer_pools"
1942+ mapped <- bufferPools .: "mapped"
1943+ direct <- bufferPools .: "direct"
1944+ gc <- o .: "gc"
1945+ collectors <- gc .: "collectors"
1946+ oldC <- collectors .: "old"
1947+ youngC <- collectors .: "young"
1948+ threads <- o .: "threads"
1949+ mem <- o .: "mem"
1950+ pools <- mem .: "pools"
1951+ oldM <- pools .: "old"
1952+ survivorM <- pools .: "survivor"
1953+ youngM <- pools .: "young"
1954+ NodeJVMStats <$> pure mapped
1955+ <*> pure direct
1956+ <*> pure oldC
1957+ <*> pure youngC
1958+ <*> threads .: "peak_count"
1959+ <*> threads .: "count"
1960+ <*> pure oldM
1961+ <*> pure survivorM
1962+ <*> pure youngM
1963+ <*> mem .: "non_heap_committed_in_bytes"
1964+ <*> mem .: "non_heap_used_in_bytes"
1965+ <*> mem .: "heap_max_in_bytes"
1966+ <*> mem .: "heap_committed_in_bytes"
1967+ <*> mem .: "heap_used_percent"
1968+ <*> mem .: "heap_used_in_bytes"
1969+ <*> (unMS <$> o .: "uptime_in_millis")
1970+ <*> (posixMS <$> o .: "timestamp")
1971+
1972+instance FromJSON JVMBufferPoolStats where
1973+ parseJSON = withObject "JVMBufferPoolStats" parse
1974+ where
1975+ parse o = JVMBufferPoolStats <$> o .: "total_capacity_in_bytes"
1976+ <*> o .: "used_in_bytes"
1977+ <*> o .: "count"
1978+
1979+instance FromJSON JVMGCStats where
1980+ parseJSON = withObject "JVMGCStats" parse
1981+ where
1982+ parse o = JVMGCStats <$> (unMS <$> o .: "collection_time_in_millis")
1983+ <*> o .: "collection_count"
1984+
1985+instance FromJSON JVMPoolStats where
1986+ parseJSON = withObject "JVMPoolStats" parse
1987+ where
1988+ parse o = JVMPoolStats <$> o .: "peak_max_in_bytes"
1989+ <*> o .: "peak_used_in_bytes"
1990+ <*> o .: "max_in_bytes"
1991+ <*> o .: "used_in_bytes"
1992+
1993+instance FromJSON NodeProcessStats where
1994+ parseJSON = withObject "NodeProcessStats" parse
1995+ where
1996+ parse o = do
1997+ mem <- o .: "mem"
1998+ cpu <- o .: "cpu"
1999+ NodeProcessStats <$> (posixMS <$> o .: "timestamp")
2000+ <*> o .: "open_file_descriptors"
2001+ <*> o .: "max_file_descriptors"
2002+ <*> cpu .: "percent"
2003+ <*> (unMS <$> cpu .: "total_in_millis")
2004+ <*> mem .: "total_virtual_in_bytes"
2005+
2006+instance FromJSON NodeOSStats where
2007+ parseJSON = withObject "NodeOSStats" parse
2008+ where
2009+ parse o = do
2010+ swap <- o .: "swap"
2011+ mem <- o .: "mem"
2012+ cpu <- o .: "cpu"
2013+ load <- o .:? "load_average"
2014+ NodeOSStats <$> (posixMS <$> o .: "timestamp")
2015+ <*> cpu .: "percent"
2016+ <*> pure load
2017+ <*> mem .: "total_in_bytes"
2018+ <*> mem .: "free_in_bytes"
2019+ <*> mem .: "free_percent"
2020+ <*> mem .: "used_in_bytes"
2021+ <*> mem .: "used_percent"
2022+ <*> swap .: "total_in_bytes"
2023+ <*> swap .: "free_in_bytes"
2024+ <*> swap .: "used_in_bytes"
2025+
2026+instance FromJSON LoadAvgs where
2027+ parseJSON = withArray "LoadAvgs" parse
2028+ where
2029+ parse v = case V.toList v of
2030+ [one, five, fifteen] -> LoadAvgs <$> parseJSON one
2031+ <*> parseJSON five
2032+ <*> parseJSON fifteen
2033+ _ -> fail "Expecting a triple of Doubles"
2034+
2035+instance FromJSON NodeIndicesStats where
2036+ parseJSON = withObject "NodeIndicesStats" parse
2037+ where
2038+ parse o = do
2039+ let (.::) mv k = case mv of
2040+ Just v -> Just <$> v .: k
2041+ Nothing -> pure Nothing
2042+ mRecovery <- o .:? "recovery"
2043+ mQueryCache <- o .:? "query_cache"
2044+ mSuggest <- o .:? "suggest"
2045+ translog <- o .: "translog"
2046+ segments <- o .: "segments"
2047+ completion <- o .: "completion"
2048+ mPercolate <- o .:? "percolate"
2049+ fielddata <- o .: "fielddata"
2050+ warmer <- o .: "warmer"
2051+ flush <- o .: "flush"
2052+ refresh <- o .: "refresh"
2053+ merges <- o .: "merges"
2054+ search <- o .: "search"
2055+ getStats <- o .: "get"
2056+ indexing <- o .: "indexing"
2057+ store <- o .: "store"
2058+ docs <- o .: "docs"
2059+ NodeIndicesStats <$> (fmap unMS <$> mRecovery .:: "throttle_time_in_millis")
2060+ <*> mRecovery .:: "current_as_target"
2061+ <*> mRecovery .:: "current_as_source"
2062+ <*> mQueryCache .:: "miss_count"
2063+ <*> mQueryCache .:: "hit_count"
2064+ <*> mQueryCache .:: "evictions"
2065+ <*> mQueryCache .:: "memory_size_in_bytes"
2066+ <*> mSuggest .:: "current"
2067+ <*> (fmap unMS <$> mSuggest .:: "time_in_millis")
2068+ <*> mSuggest .:: "total"
2069+ <*> translog .: "size_in_bytes"
2070+ <*> translog .: "operations"
2071+ <*> segments .:? "fixed_bit_set_memory_in_bytes"
2072+ <*> segments .: "version_map_memory_in_bytes"
2073+ <*> segments .:? "index_writer_max_memory_in_bytes"
2074+ <*> segments .: "index_writer_memory_in_bytes"
2075+ <*> segments .: "memory_in_bytes"
2076+ <*> segments .: "count"
2077+ <*> completion .: "size_in_bytes"
2078+ <*> mPercolate .:: "queries"
2079+ <*> mPercolate .:: "memory_size_in_bytes"
2080+ <*> mPercolate .:: "current"
2081+ <*> (fmap unMS <$> mPercolate .:: "time_in_millis")
2082+ <*> mPercolate .:: "total"
2083+ <*> fielddata .: "evictions"
2084+ <*> fielddata .: "memory_size_in_bytes"
2085+ <*> (unMS <$> warmer .: "total_time_in_millis")
2086+ <*> warmer .: "total"
2087+ <*> warmer .: "current"
2088+ <*> (unMS <$> flush .: "total_time_in_millis")
2089+ <*> flush .: "total"
2090+ <*> (unMS <$> refresh .: "total_time_in_millis")
2091+ <*> refresh .: "total"
2092+ <*> merges .: "total_size_in_bytes"
2093+ <*> merges .: "total_docs"
2094+ <*> (unMS <$> merges .: "total_time_in_millis")
2095+ <*> merges .: "total"
2096+ <*> merges .: "current_size_in_bytes"
2097+ <*> merges .: "current_docs"
2098+ <*> merges .: "current"
2099+ <*> search .: "fetch_current"
2100+ <*> (unMS <$> search .: "fetch_time_in_millis")
2101+ <*> search .: "fetch_total"
2102+ <*> search .: "query_current"
2103+ <*> (unMS <$> search .: "query_time_in_millis")
2104+ <*> search .: "query_total"
2105+ <*> search .: "open_contexts"
2106+ <*> getStats .: "current"
2107+ <*> (unMS <$> getStats .: "missing_time_in_millis")
2108+ <*> getStats .: "missing_total"
2109+ <*> (unMS <$> getStats .: "exists_time_in_millis")
2110+ <*> getStats .: "exists_total"
2111+ <*> (unMS <$> getStats .: "time_in_millis")
2112+ <*> getStats .: "total"
2113+ <*> (fmap unMS <$> indexing .:? "throttle_time_in_millis")
2114+ <*> indexing .:? "is_throttled"
2115+ <*> indexing .:? "noop_update_total"
2116+ <*> indexing .: "delete_current"
2117+ <*> (unMS <$> indexing .: "delete_time_in_millis")
2118+ <*> indexing .: "delete_total"
2119+ <*> indexing .: "index_current"
2120+ <*> (unMS <$> indexing .: "index_time_in_millis")
2121+ <*> indexing .: "index_total"
2122+ <*> (unMS <$> store .: "throttle_time_in_millis")
2123+ <*> store .: "size_in_bytes"
2124+ <*> docs .: "deleted"
2125+ <*> docs .: "count"
2126+
2127+instance FromJSON NodeBreakersStats where
2128+ parseJSON = withObject "NodeBreakersStats" parse
2129+ where
2130+ parse o = NodeBreakersStats <$> o .: "parent"
2131+ <*> o .: "request"
2132+ <*> o .: "fielddata"
2133+
2134+parseNodeStats :: FullNodeId -> Object -> Parser NodeStats
2135+parseNodeStats fnid o =
2136+ NodeStats <$> o .: "name"
2137+ <*> pure fnid
2138+ <*> o .:? "breakers"
2139+ <*> o .: "http"
2140+ <*> o .: "transport"
2141+ <*> o .: "fs"
2142+ <*> o .:? "network"
2143+ <*> o .: "thread_pool"
2144+ <*> o .: "jvm"
2145+ <*> o .: "process"
2146+ <*> o .: "os"
2147+ <*> o .: "indices"
2148+
2149+parseNodeInfo :: FullNodeId -> Object -> Parser NodeInfo
2150+parseNodeInfo nid o =
2151+ NodeInfo <$> o .:? "http_address"
2152+ <*> o .: "build_hash"
2153+ <*> o .: "version"
2154+ <*> o .: "ip"
2155+ <*> o .: "host"
2156+ <*> o .: "transport_address"
2157+ <*> o .: "name"
2158+ <*> pure nid
2159+ <*> o .: "plugins"
2160+ <*> o .: "http"
2161+ <*> o .: "transport"
2162+ <*> o .:? "network"
2163+ <*> o .: "thread_pool"
2164+ <*> o .: "jvm"
2165+ <*> o .: "process"
2166+ <*> o .: "os"
2167+ <*> o .: "settings"
2168+
2169+instance FromJSON NodePluginInfo where
2170+ parseJSON = withObject "NodePluginInfo" parse
2171+ where
2172+ parse o = NodePluginInfo <$> o .:? "site"
2173+ <*> o .:? "jvm"
2174+ <*> o .: "description"
2175+ <*> o .: "version"
2176+ <*> o .: "name"
2177+
2178+instance FromJSON NodeHTTPInfo where
2179+ parseJSON = withObject "NodeHTTPInfo" parse
2180+ where
2181+ parse o = NodeHTTPInfo <$> o .: "max_content_length_in_bytes"
2182+ <*> parseJSON (Object o)
2183+
2184+instance FromJSON BoundTransportAddress where
2185+ parseJSON = withObject "BoundTransportAddress" parse
2186+ where
2187+ parse o = BoundTransportAddress <$> o .: "publish_address"
2188+ <*> o .: "bound_address"
2189+
2190+instance FromJSON NodeOSInfo where
2191+ parseJSON = withObject "NodeOSInfo" parse
2192+ where
2193+ parse o =
2194+ NodeOSInfo <$> (unMS <$> o .: "refresh_interval_in_millis")
2195+ <*> o .: "name"
2196+ <*> o .: "arch"
2197+ <*> o .: "version"
2198+ <*> o .: "available_processors"
2199+ <*> o .: "allocated_processors"
2200+
2201+
2202+instance FromJSON CPUInfo where
2203+ parseJSON = withObject "CPUInfo" parse
2204+ where
2205+ parse o = CPUInfo <$> o .: "cache_size_in_bytes"
2206+ <*> o .: "cores_per_socket"
2207+ <*> o .: "total_sockets"
2208+ <*> o .: "total_cores"
2209+ <*> o .: "mhz"
2210+ <*> o .: "model"
2211+ <*> o .: "vendor"
2212+
2213+instance FromJSON NodeProcessInfo where
2214+ parseJSON = withObject "NodeProcessInfo" parse
2215+ where
2216+ parse o = NodeProcessInfo <$> o .: "mlockall"
2217+ <*> o .:? "max_file_descriptors"
2218+ <*> o .: "id"
2219+ <*> (unMS <$> o .: "refresh_interval_in_millis")
2220+
2221+instance FromJSON NodeJVMInfo where
2222+ parseJSON = withObject "NodeJVMInfo" parse
2223+ where
2224+ parse o = NodeJVMInfo <$> o .: "memory_pools"
2225+ <*> o .: "gc_collectors"
2226+ <*> o .: "mem"
2227+ <*> (posixMS <$> o .: "start_time_in_millis")
2228+ <*> o .: "vm_vendor"
2229+ <*> o .: "vm_version"
2230+ <*> o .: "vm_name"
2231+ <*> (unJVMVersion <$> o .: "version")
2232+ <*> o .: "pid"
2233+
2234+instance FromJSON JVMVersion where
2235+ parseJSON (String t) =
2236+ JVMVersion <$> parseJSON (String (T.replace "_" "." t))
2237+ parseJSON v = JVMVersion <$> parseJSON v
2238+
2239+instance FromJSON JVMMemoryInfo where
2240+ parseJSON = withObject "JVMMemoryInfo" parse
2241+ where
2242+ parse o = JVMMemoryInfo <$> o .: "direct_max_in_bytes"
2243+ <*> o .: "non_heap_max_in_bytes"
2244+ <*> o .: "non_heap_init_in_bytes"
2245+ <*> o .: "heap_max_in_bytes"
2246+ <*> o .: "heap_init_in_bytes"
2247+
2248+instance FromJSON NodeThreadPoolsInfo where
2249+ parseJSON = withObject "NodeThreadPoolsInfo" parse
2250+ where
2251+ parse o = NodeThreadPoolsInfo <$> o .: "refresh"
2252+ <*> o .: "management"
2253+ <*> o .:? "percolate"
2254+ <*> o .:? "listener"
2255+ <*> o .:? "fetch_shard_started"
2256+ <*> o .: "search"
2257+ <*> o .: "flush"
2258+ <*> o .: "warmer"
2259+ <*> o .:? "optimize"
2260+ <*> o .: "bulk"
2261+ <*> o .:? "suggest"
2262+ <*> o .: "force_merge"
2263+ <*> o .: "snapshot"
2264+ <*> o .: "get"
2265+ <*> o .:? "fetch_shard_store"
2266+ <*> o .: "index"
2267+ <*> o .: "generic"
2268+
2269+instance FromJSON NodeThreadPoolInfo where
2270+ parseJSON = withObject "NodeThreadPoolInfo" parse
2271+ where
2272+ parse o = do
2273+ ka <- maybe (return Nothing) (fmap Just . parseStringInterval) =<< o .:? "keep_alive"
2274+ NodeThreadPoolInfo <$> (parseJSON . unStringlyTypeJSON =<< o .: "queue_size")
2275+ <*> pure ka
2276+ <*> o .:? "min"
2277+ <*> o .:? "max"
2278+ <*> o .: "type"
2279+
2280+data TimeInterval = Weeks
2281+ | Days
2282+ | Hours
2283+ | Minutes
2284+ | Seconds deriving Eq
2285+
2286+instance Show TimeInterval where
2287+ show Weeks = "w"
2288+ show Days = "d"
2289+ show Hours = "h"
2290+ show Minutes = "m"
2291+ show Seconds = "s"
2292+
2293+instance Read TimeInterval where
2294+ readPrec = f =<< TR.get
2295+ where
2296+ f 'w' = return Weeks
2297+ f 'd' = return Days
2298+ f 'h' = return Hours
2299+ f 'm' = return Minutes
2300+ f 's' = return Seconds
2301+ f _ = fail "TimeInterval expected one of w, d, h, m, s"
2302+
2303+data Interval = Year
2304+ | Quarter
2305+ | Month
2306+ | Week
2307+ | Day
2308+ | Hour
2309+ | Minute
2310+ | Second deriving (Eq, Show)
2311+
2312+instance ToJSON Interval where
2313+ toJSON Year = "year"
2314+ toJSON Quarter = "quarter"
2315+ toJSON Month = "month"
2316+ toJSON Week = "week"
2317+ toJSON Day = "day"
2318+ toJSON Hour = "hour"
2319+ toJSON Minute = "minute"
2320+ toJSON Second = "second"
2321+
2322+parseStringInterval :: (Monad m) => String -> m NominalDiffTime
2323+parseStringInterval s = case span isNumber s of
2324+ ("", _) -> fail "Invalid interval"
2325+ (nS, unitS) -> case (readMay nS, readMay unitS) of
2326+ (Just n, Just unit) -> return (fromInteger (n * unitNDT unit))
2327+ (Nothing, _) -> fail "Invalid interval number"
2328+ (_, Nothing) -> fail "Invalid interval unit"
2329+ where
2330+ unitNDT Seconds = 1
2331+ unitNDT Minutes = 60
2332+ unitNDT Hours = 60 * 60
2333+ unitNDT Days = 24 * 60 * 60
2334+ unitNDT Weeks = 7 * 24 * 60 * 60
2335+
2336+instance FromJSON ThreadPoolSize where
2337+ parseJSON v = parseAsNumber v <|> parseAsString v
2338+ where
2339+ parseAsNumber = parseAsInt <=< parseJSON
2340+ parseAsInt (-1) = return ThreadPoolUnbounded
2341+ parseAsInt n
2342+ | n >= 0 = return (ThreadPoolBounded n)
2343+ | otherwise = fail "Thread pool size must be >= -1."
2344+ parseAsString = withText "ThreadPoolSize" $ \t ->
2345+ case first (readMay . T.unpack) (T.span isNumber t) of
2346+ (Just n, "k") -> return (ThreadPoolBounded (n * 1000))
2347+ (Just n, "") -> return (ThreadPoolBounded n)
2348+ _ -> fail ("Invalid thread pool size " <> T.unpack t)
2349+
2350+instance FromJSON ThreadPoolType where
2351+ parseJSON = withText "ThreadPoolType" parse
2352+ where
2353+ parse "scaling" = return ThreadPoolScaling
2354+ parse "fixed" = return ThreadPoolFixed
2355+ parse "cached" = return ThreadPoolCached
2356+ parse e = fail ("Unexpected thread pool type" <> T.unpack e)
2357+
2358+instance FromJSON NodeTransportInfo where
2359+ parseJSON = withObject "NodeTransportInfo" parse
2360+ where
2361+ parse o = NodeTransportInfo <$> (maybe (return mempty) parseProfiles =<< o .:? "profiles")
2362+ <*> parseJSON (Object o)
2363+ parseProfiles (Object o) | HM.null o = return []
2364+ parseProfiles v@(Array _) = parseJSON v
2365+ parseProfiles Null = return []
2366+ parseProfiles _ = fail "Could not parse profiles"
2367+
2368+instance FromJSON NodeNetworkInfo where
2369+ parseJSON = withObject "NodeNetworkInfo" parse
2370+ where
2371+ parse o = NodeNetworkInfo <$> o .: "primary_interface"
2372+ <*> (unMS <$> o .: "refresh_interval_in_millis")
2373+
2374+
2375+instance FromJSON NodeNetworkInterface where
2376+ parseJSON = withObject "NodeNetworkInterface" parse
2377+ where
2378+ parse o = NodeNetworkInterface <$> o .: "mac_address"
2379+ <*> o .: "name"
2380+ <*> o .: "address"
2381+
2382+
2383+instance ToJSON Version where
2384+ toJSON Version {..} = object ["number" .= number
2385+ ,"build_hash" .= build_hash
2386+ ,"build_date" .= build_date
2387+ ,"build_snapshot" .= build_snapshot
2388+ ,"lucene_version" .= lucene_version]
2389+
2390+instance FromJSON Version where
2391+ parseJSON = withObject "Version" parse
2392+ where parse o = Version
2393+ <$> o .: "number"
2394+ <*> o .: "build_hash"
2395+ <*> o .: "build_date"
2396+ <*> o .: "build_snapshot"
2397+ <*> o .: "lucene_version"
2398+
2399+instance ToJSON VersionNumber where
2400+ toJSON = toJSON . Vers.showVersion . versionNumber
2401+
2402+instance FromJSON VersionNumber where
2403+ parseJSON = withText "VersionNumber" (parse . T.unpack)
2404+ where
2405+ parse s = case filter (null . snd)(RP.readP_to_S Vers.parseVersion s) of
2406+ [(v, _)] -> pure (VersionNumber v)
2407+ [] -> fail ("Invalid version string " ++ s)
2408+ xs -> fail ("Ambiguous version string " ++ s ++ " (" ++ intercalate ", " (Vers.showVersion . fst <$> xs) ++ ")")
src/Database/V5/Bloodhound/Internal/Highlight.hsnew file+164
@@ -0,0 +1,164 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.V5.Bloodhound.Internal.Highlight where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.Map.Strict as M
9+
10+import Database.V5.Bloodhound.Internal.Newtypes
11+import Database.V5.Bloodhound.Internal.Query
12+
13+type HitHighlight = M.Map Text [Text]
14+
15+data Highlights = Highlights
16+ { globalsettings :: Maybe HighlightSettings
17+ , highlightFields :: [FieldHighlight]
18+ } deriving (Eq, Show)
19+
20+instance ToJSON Highlights where
21+ toJSON (Highlights global fields) =
22+ omitNulls (("fields" .= fields)
23+ : highlightSettingsPairs global)
24+
25+data FieldHighlight =
26+ FieldHighlight FieldName (Maybe HighlightSettings)
27+ deriving (Eq, Show)
28+
29+
30+instance ToJSON FieldHighlight where
31+ toJSON (FieldHighlight (FieldName fName) (Just fSettings)) =
32+ object [ fName .= fSettings ]
33+ toJSON (FieldHighlight (FieldName fName) Nothing) =
34+ object [ fName .= emptyObject ]
35+
36+data HighlightSettings =
37+ Plain PlainHighlight
38+ | Postings PostingsHighlight
39+ | FastVector FastVectorHighlight
40+ deriving (Eq, Show)
41+
42+
43+instance ToJSON HighlightSettings where
44+ toJSON hs = omitNulls (highlightSettingsPairs (Just hs))
45+
46+data PlainHighlight =
47+ PlainHighlight { plainCommon :: Maybe CommonHighlight
48+ , plainNonPost :: Maybe NonPostings }
49+ deriving (Eq, Show)
50+
51+ -- This requires that index_options are set to 'offset' in the mapping.
52+data PostingsHighlight =
53+ PostingsHighlight (Maybe CommonHighlight)
54+ deriving (Eq, Show)
55+
56+-- This requires that term_vector is set to 'with_positions_offsets' in the mapping.
57+data FastVectorHighlight = FastVectorHighlight
58+ { fvCommon :: Maybe CommonHighlight
59+ , fvNonPostSettings :: Maybe NonPostings
60+ , boundaryChars :: Maybe Text
61+ , boundaryMaxScan :: Maybe Int
62+ , fragmentOffset :: Maybe Int
63+ , matchedFields :: [Text]
64+ , phraseLimit :: Maybe Int
65+ } deriving (Eq, Show)
66+
67+data CommonHighlight = CommonHighlight
68+ { order :: Maybe Text
69+ , forceSource :: Maybe Bool
70+ , tag :: Maybe HighlightTag
71+ , encoder :: Maybe HighlightEncoder
72+ , noMatchSize :: Maybe Int
73+ , highlightQuery :: Maybe Query
74+ , requireFieldMatch :: Maybe Bool
75+ } deriving (Eq, Show)
76+
77+-- Settings that are only applicable to FastVector and Plain highlighters.
78+data NonPostings =
79+ NonPostings { fragmentSize :: Maybe Int
80+ , numberOfFragments :: Maybe Int
81+ } deriving (Eq, Show)
82+
83+data HighlightEncoder = DefaultEncoder
84+ | HTMLEncoder
85+ deriving (Eq, Show)
86+
87+instance ToJSON HighlightEncoder where
88+ toJSON DefaultEncoder = String "default"
89+ toJSON HTMLEncoder = String "html"
90+
91+-- NOTE: Should the tags use some kind of HTML type, rather than Text?
92+data HighlightTag =
93+ TagSchema Text
94+ -- Only uses more than the first value in the lists if fvh
95+ | CustomTags ([Text], [Text])
96+ deriving (Eq, Show)
97+
98+highlightSettingsPairs :: Maybe HighlightSettings -> [Pair]
99+highlightSettingsPairs Nothing = []
100+highlightSettingsPairs (Just (Plain plh)) = plainHighPairs (Just plh)
101+highlightSettingsPairs (Just (Postings ph)) = postHighPairs (Just ph)
102+highlightSettingsPairs (Just (FastVector fvh)) = fastVectorHighPairs (Just fvh)
103+
104+
105+plainHighPairs :: Maybe PlainHighlight -> [Pair]
106+plainHighPairs Nothing = []
107+plainHighPairs (Just (PlainHighlight plCom plNonPost)) =
108+ [ "type" .= String "plain"]
109+ ++ commonHighlightPairs plCom
110+ ++ nonPostingsToPairs plNonPost
111+
112+postHighPairs :: Maybe PostingsHighlight -> [Pair]
113+postHighPairs Nothing = []
114+postHighPairs (Just (PostingsHighlight pCom)) =
115+ ("type" .= String "postings")
116+ : commonHighlightPairs pCom
117+
118+fastVectorHighPairs :: Maybe FastVectorHighlight -> [Pair]
119+fastVectorHighPairs Nothing = []
120+fastVectorHighPairs
121+ (Just
122+ (FastVectorHighlight fvCom fvNonPostSettings' fvBoundChars
123+ fvBoundMaxScan fvFragOff fvMatchedFields
124+ fvPhraseLim)) =
125+ [ "type" .= String "fvh"
126+ , "boundary_chars" .= fvBoundChars
127+ , "boundary_max_scan" .= fvBoundMaxScan
128+ , "fragment_offset" .= fvFragOff
129+ , "matched_fields" .= fvMatchedFields
130+ , "phraseLimit" .= fvPhraseLim]
131+ ++ commonHighlightPairs fvCom
132+ ++ nonPostingsToPairs fvNonPostSettings'
133+
134+commonHighlightPairs :: Maybe CommonHighlight -> [Pair]
135+commonHighlightPairs Nothing = []
136+commonHighlightPairs (Just (CommonHighlight chScore chForceSource
137+ chTag chEncoder chNoMatchSize
138+ chHighlightQuery chRequireFieldMatch)) =
139+ [ "order" .= chScore
140+ , "force_source" .= chForceSource
141+ , "encoder" .= chEncoder
142+ , "no_match_size" .= chNoMatchSize
143+ , "highlight_query" .= chHighlightQuery
144+ , "require_fieldMatch" .= chRequireFieldMatch
145+ ]
146+ ++ highlightTagToPairs chTag
147+
148+
149+nonPostingsToPairs :: Maybe NonPostings -> [Pair]
150+nonPostingsToPairs Nothing = []
151+nonPostingsToPairs (Just (NonPostings npFragSize npNumOfFrags)) =
152+ [ "fragment_size" .= npFragSize
153+ , "number_of_fragments" .= npNumOfFrags
154+ ]
155+
156+highlightTagToPairs :: Maybe HighlightTag -> [Pair]
157+highlightTagToPairs (Just (TagSchema _)) =
158+ [ "scheme" .= String "default"
159+ ]
160+highlightTagToPairs (Just (CustomTags (pre, post))) =
161+ [ "pre_tags" .= pre
162+ , "post_tags" .= post
163+ ]
164+highlightTagToPairs Nothing = []
src/Database/V5/Bloodhound/Internal/Newtypes.hsnew file+234
@@ -0,0 +1,234 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Database.V5.Bloodhound.Internal.Newtypes where
5+
6+import Bloodhound.Import
7+
8+import qualified Data.Map.Strict as M
9+
10+newtype From = From Int deriving (Eq, Show, ToJSON)
11+newtype Size = Size Int deriving (Eq, Show, ToJSON, FromJSON)
12+
13+-- Used with scripts
14+newtype HitFields =
15+ HitFields (M.Map Text [Value])
16+ deriving (Eq, Show)
17+
18+instance FromJSON HitFields where
19+ parseJSON x
20+ = HitFields <$> parseJSON x
21+
22+-- Slight misnomer.
23+type Score = Maybe Double
24+
25+{-| 'MappingName' is part of mappings which are how ES describes and schematizes
26+ the data in the indices.
27+-}
28+newtype MappingName =
29+ MappingName Text
30+ deriving (Eq, Show, ToJSON, FromJSON)
31+
32+newtype ShardId = ShardId { shardId :: Int }
33+ deriving (Eq, Show, FromJSON)
34+
35+{-| 'DocId' is a generic wrapper value for expressing unique Document IDs.
36+ Can be set by the user or created by ES itself. Often used in client
37+ functions for poking at specific documents.
38+-}
39+newtype DocId =
40+ DocId Text
41+ deriving (Eq, Show, ToJSON, FromJSON)
42+
43+{-| 'FieldName' is used all over the place wherever a specific field within
44+ a document needs to be specified, usually in 'Query's or 'Filter's.
45+-}
46+newtype FieldName =
47+ FieldName Text
48+ deriving (Eq, Read, Show, ToJSON, FromJSON)
49+
50+newtype TypeName =
51+ TypeName Text
52+ deriving (Eq, Show, ToJSON, FromJSON)
53+
54+{-| 'QueryString' is used to wrap query text bodies, be they human written or not.
55+-}
56+newtype QueryString =
57+ QueryString Text
58+ deriving (Eq, Show, ToJSON, FromJSON)
59+
60+
61+-- {-| 'Script' is often used in place of 'FieldName' to specify more
62+-- complex ways of extracting a value from a document.
63+-- -}
64+-- newtype Script =
65+-- Script { scriptText :: Text }
66+-- deriving (Eq, Show)
67+
68+{-| 'CacheName' is used in 'RegexpFilter' for describing the
69+ 'CacheKey' keyed caching behavior.
70+-}
71+newtype CacheName =
72+ CacheName Text
73+ deriving (Eq, Show, FromJSON, ToJSON)
74+
75+{-| 'CacheKey' is used in 'RegexpFilter' to key regex caching.
76+-}
77+newtype CacheKey =
78+ CacheKey Text deriving (Eq, Show, FromJSON, ToJSON)
79+newtype Existence =
80+ Existence Bool deriving (Eq, Show, FromJSON, ToJSON)
81+newtype NullValue =
82+ NullValue Bool deriving (Eq, Show, FromJSON, ToJSON)
83+newtype CutoffFrequency =
84+ CutoffFrequency Double deriving (Eq, Show, FromJSON, ToJSON)
85+newtype Analyzer =
86+ Analyzer Text deriving (Eq, Show, FromJSON, ToJSON)
87+newtype MaxExpansions =
88+ MaxExpansions Int deriving (Eq, Show, FromJSON, ToJSON)
89+
90+{-| 'Lenient', if set to true, will cause format based failures to be
91+ ignored. I don't know what the bloody default is, Elasticsearch
92+ documentation didn't say what it was. Let me know if you figure it out.
93+-}
94+newtype Lenient =
95+ Lenient Bool deriving (Eq, Show, FromJSON, ToJSON)
96+newtype Tiebreaker =
97+ Tiebreaker Double deriving (Eq, Show, FromJSON, ToJSON)
98+
99+{-| 'MinimumMatch' controls how many should clauses in the bool query should
100+ match. Can be an absolute value (2) or a percentage (30%) or a
101+ combination of both.
102+-}
103+newtype MinimumMatch =
104+ MinimumMatch Int deriving (Eq, Show, FromJSON, ToJSON)
105+newtype DisableCoord =
106+ DisableCoord Bool deriving (Eq, Show, FromJSON, ToJSON)
107+newtype IgnoreTermFrequency =
108+ IgnoreTermFrequency Bool deriving (Eq, Show, FromJSON, ToJSON)
109+newtype MinimumTermFrequency =
110+ MinimumTermFrequency Int deriving (Eq, Show, FromJSON, ToJSON)
111+newtype MaxQueryTerms =
112+ MaxQueryTerms Int deriving (Eq, Show, FromJSON, ToJSON)
113+newtype Fuzziness =
114+ Fuzziness Double deriving (Eq, Show, FromJSON, ToJSON)
115+
116+{-| 'PrefixLength' is the prefix length used in queries, defaults to 0. -}
117+newtype PrefixLength =
118+ PrefixLength Int deriving (Eq, Show, FromJSON, ToJSON)
119+newtype PercentMatch =
120+ PercentMatch Double deriving (Eq, Show, FromJSON, ToJSON)
121+newtype StopWord =
122+ StopWord Text deriving (Eq, Show, FromJSON, ToJSON)
123+newtype QueryPath =
124+ QueryPath Text deriving (Eq, Show, FromJSON, ToJSON)
125+
126+{-| Allowing a wildcard at the beginning of a word (eg "*ing") is particularly
127+ heavy, because all terms in the index need to be examined, just in case
128+ they match. Leading wildcards can be disabled by setting
129+ 'AllowLeadingWildcard' to false. -}
130+newtype AllowLeadingWildcard =
131+ AllowLeadingWildcard Bool deriving (Eq, Show, FromJSON, ToJSON)
132+newtype LowercaseExpanded =
133+ LowercaseExpanded Bool deriving (Eq, Show, FromJSON, ToJSON)
134+newtype EnablePositionIncrements =
135+ EnablePositionIncrements Bool deriving (Eq, Show, FromJSON, ToJSON)
136+
137+{-| By default, wildcard terms in a query are not analyzed.
138+ Setting 'AnalyzeWildcard' to true enables best-effort analysis.
139+-}
140+newtype AnalyzeWildcard = AnalyzeWildcard Bool deriving (Eq, Show, FromJSON, ToJSON)
141+
142+{-| 'GeneratePhraseQueries' defaults to false.
143+-}
144+newtype GeneratePhraseQueries =
145+ GeneratePhraseQueries Bool deriving (Eq, Show, FromJSON, ToJSON)
146+
147+{-| 'Locale' is used for string conversions - defaults to ROOT.
148+-}
149+newtype Locale = Locale Text deriving (Eq, Show, FromJSON, ToJSON)
150+newtype MaxWordLength = MaxWordLength Int deriving (Eq, Show, FromJSON, ToJSON)
151+newtype MinWordLength = MinWordLength Int deriving (Eq, Show, FromJSON, ToJSON)
152+
153+{-| 'PhraseSlop' sets the default slop for phrases, 0 means exact
154+ phrase matches. Default is 0.
155+-}
156+newtype PhraseSlop = PhraseSlop Int deriving (Eq, Show, FromJSON, ToJSON)
157+newtype MinDocFrequency = MinDocFrequency Int deriving (Eq, Show, FromJSON, ToJSON)
158+newtype MaxDocFrequency = MaxDocFrequency Int deriving (Eq, Show, FromJSON, ToJSON)
159+
160+-- | Newtype wrapper to parse ES's concerning tendency to in some APIs return a floating point number of milliseconds since epoch ಠ_ಠ
161+newtype POSIXMS = POSIXMS { posixMS :: UTCTime }
162+
163+instance FromJSON POSIXMS where
164+ parseJSON = withScientific "POSIXMS" (return . parse)
165+ where parse n =
166+ let n' = truncate n :: Integer
167+ in POSIXMS (posixSecondsToUTCTime (fromInteger (n' `div` 1000)))
168+
169+newtype Boost =
170+ Boost Double
171+ deriving (Eq, Show, ToJSON, FromJSON)
172+
173+newtype BoostTerms =
174+ BoostTerms Double
175+ deriving (Eq, Show, ToJSON, FromJSON)
176+
177+{-| 'ReplicaCount' is part of 'IndexSettings' -}
178+newtype ReplicaCount =
179+ ReplicaCount Int
180+ deriving (Eq, Show, ToJSON)
181+
182+{-| 'ShardCount' is part of 'IndexSettings' -}
183+newtype ShardCount =
184+ ShardCount Int
185+ deriving (Eq, Show, ToJSON)
186+
187+-- This insanity is because ES *sometimes* returns Replica/Shard counts as strings
188+instance FromJSON ReplicaCount where
189+ parseJSON v = parseAsInt v
190+ <|> parseAsString v
191+ where parseAsInt = fmap ReplicaCount . parseJSON
192+ parseAsString = withText "ReplicaCount" (fmap ReplicaCount . parseReadText)
193+
194+instance FromJSON ShardCount where
195+ parseJSON v = parseAsInt v
196+ <|> parseAsString v
197+ where parseAsInt = fmap ShardCount . parseJSON
198+ parseAsString = withText "ShardCount" (fmap ShardCount . parseReadText)
199+
200+{-| 'IndexName' is used to describe which index to query/create/delete -}
201+newtype IndexName =
202+ IndexName Text
203+ deriving (Eq, Show, ToJSON, FromJSON)
204+
205+newtype IndexAliasName =
206+ IndexAliasName { indexAliasName :: IndexName }
207+ deriving (Eq, Show, ToJSON)
208+
209+newtype MaybeNA a = MaybeNA { unMaybeNA :: Maybe a }
210+ deriving (Show, Eq)
211+
212+instance FromJSON a => FromJSON (MaybeNA a) where
213+ parseJSON (String "NA") = pure $ MaybeNA Nothing
214+ parseJSON o = MaybeNA . Just <$> parseJSON o
215+
216+newtype SnapshotName =
217+ SnapshotName { snapshotName :: Text }
218+ deriving (Eq, Show, ToJSON, FromJSON)
219+
220+-- | Milliseconds
221+newtype MS = MS NominalDiffTime
222+
223+-- keeps the unexported constructor warnings at bay
224+unMS :: MS -> NominalDiffTime
225+unMS (MS t) = t
226+
227+
228+instance FromJSON MS where
229+ parseJSON = withScientific "MS" (return . MS . parse)
230+ where
231+ parse n = fromInteger (truncate n * 1000)
232+
233+newtype TokenFilter =
234+ TokenFilter Text deriving (Eq, Show, FromJSON, ToJSON)
src/Database/V5/Bloodhound/Internal/Query.hsnew file+1597
@@ -0,0 +1,1597 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+module Database.V5.Bloodhound.Internal.Query
6+ ( module X
7+ , module Database.V5.Bloodhound.Internal.Query
8+ ) where
9+
10+import Bloodhound.Import
11+
12+import Data.Char (isNumber)
13+import qualified Data.HashMap.Strict as HM
14+import Data.List (nub)
15+import qualified Data.Text as T
16+
17+import Database.Bloodhound.Common.Script as X
18+import Database.V5.Bloodhound.Internal.Newtypes
19+
20+data Query =
21+ TermQuery Term (Maybe Boost)
22+ | TermsQuery Text (NonEmpty Text)
23+ | QueryMatchQuery MatchQuery
24+ | QueryMultiMatchQuery MultiMatchQuery
25+ | QueryBoolQuery BoolQuery
26+ | QueryBoostingQuery BoostingQuery
27+ | QueryCommonTermsQuery CommonTermsQuery
28+ | ConstantScoreQuery Query Boost
29+ | QueryFunctionScoreQuery FunctionScoreQuery
30+ | QueryDisMaxQuery DisMaxQuery
31+ | QueryFuzzyLikeThisQuery FuzzyLikeThisQuery
32+ | QueryFuzzyLikeFieldQuery FuzzyLikeFieldQuery
33+ | QueryFuzzyQuery FuzzyQuery
34+ | QueryHasChildQuery HasChildQuery
35+ | QueryHasParentQuery HasParentQuery
36+ | IdsQuery MappingName [DocId]
37+ | QueryIndicesQuery IndicesQuery
38+ | MatchAllQuery (Maybe Boost)
39+ | QueryMoreLikeThisQuery MoreLikeThisQuery
40+ | QueryMoreLikeThisFieldQuery MoreLikeThisFieldQuery
41+ | QueryNestedQuery NestedQuery
42+ | QueryPrefixQuery PrefixQuery
43+ | QueryQueryStringQuery QueryStringQuery
44+ | QuerySimpleQueryStringQuery SimpleQueryStringQuery
45+ | QueryRangeQuery RangeQuery
46+ | QueryRegexpQuery RegexpQuery
47+ | QueryExistsQuery FieldName
48+ | QueryTemplateQueryInline TemplateQueryInline
49+ | QueryMatchNoneQuery
50+ deriving (Eq, Show)
51+
52+instance ToJSON Query where
53+ toJSON (TermQuery (Term termQueryField termQueryValue) boost) =
54+ object [ "term" .=
55+ object [termQueryField .= object merged]]
56+ where
57+ base = [ "value" .= termQueryValue ]
58+ boosted = maybe [] (return . ("boost" .=)) boost
59+ merged = mappend base boosted
60+
61+ toJSON (TermsQuery fieldName terms) =
62+ object [ "terms" .= object conjoined ]
63+ where conjoined = [fieldName .= terms]
64+
65+ toJSON (IdsQuery idsQueryMappingName docIds) =
66+ object [ "ids" .= object conjoined ]
67+ where conjoined = [ "type" .= idsQueryMappingName
68+ , "values" .= fmap toJSON docIds ]
69+
70+ toJSON (QueryQueryStringQuery qQueryStringQuery) =
71+ object [ "query_string" .= qQueryStringQuery ]
72+
73+ toJSON (QueryMatchQuery matchQuery) =
74+ object [ "match" .= matchQuery ]
75+
76+ toJSON (QueryMultiMatchQuery multiMatchQuery) =
77+ toJSON multiMatchQuery
78+
79+ toJSON (QueryBoolQuery boolQuery) =
80+ object [ "bool" .= boolQuery ]
81+
82+ toJSON (QueryBoostingQuery boostingQuery) =
83+ object [ "boosting" .= boostingQuery ]
84+
85+ toJSON (QueryCommonTermsQuery commonTermsQuery) =
86+ object [ "common" .= commonTermsQuery ]
87+
88+ toJSON (ConstantScoreQuery query boost) =
89+ object ["constant_score" .= object ["query" .= query
90+ , "boost" .= boost]]
91+
92+ toJSON (QueryFunctionScoreQuery functionScoreQuery) =
93+ object [ "function_score" .= functionScoreQuery ]
94+
95+ toJSON (QueryDisMaxQuery disMaxQuery) =
96+ object [ "dis_max" .= disMaxQuery ]
97+
98+ toJSON (QueryFuzzyLikeThisQuery fuzzyQuery) =
99+ object [ "fuzzy_like_this" .= fuzzyQuery ]
100+
101+ toJSON (QueryFuzzyLikeFieldQuery fuzzyFieldQuery) =
102+ object [ "fuzzy_like_this_field" .= fuzzyFieldQuery ]
103+
104+ toJSON (QueryFuzzyQuery fuzzyQuery) =
105+ object [ "fuzzy" .= fuzzyQuery ]
106+
107+ toJSON (QueryHasChildQuery childQuery) =
108+ object [ "has_child" .= childQuery ]
109+
110+ toJSON (QueryHasParentQuery parentQuery) =
111+ object [ "has_parent" .= parentQuery ]
112+
113+ toJSON (QueryIndicesQuery qIndicesQuery) =
114+ object [ "indices" .= qIndicesQuery ]
115+
116+ toJSON (MatchAllQuery boost) =
117+ object [ "match_all" .= omitNulls [ "boost" .= boost ] ]
118+
119+ toJSON (QueryMoreLikeThisQuery query) =
120+ object [ "more_like_this" .= query ]
121+
122+ toJSON (QueryMoreLikeThisFieldQuery query) =
123+ object [ "more_like_this_field" .= query ]
124+
125+ toJSON (QueryNestedQuery query) =
126+ object [ "nested" .= query ]
127+
128+ toJSON (QueryPrefixQuery query) =
129+ object [ "prefix" .= query ]
130+
131+ toJSON (QueryRangeQuery query) =
132+ object [ "range" .= query ]
133+
134+ toJSON (QueryRegexpQuery query) =
135+ object [ "regexp" .= query ]
136+
137+ toJSON (QuerySimpleQueryStringQuery query) =
138+ object [ "simple_query_string" .= query ]
139+
140+ toJSON (QueryExistsQuery (FieldName fieldName)) =
141+ object ["exists" .= object
142+ ["field" .= fieldName]
143+ ]
144+ toJSON QueryMatchNoneQuery =
145+ object ["match_none" .= object []]
146+
147+ toJSON (QueryTemplateQueryInline templateQuery) =
148+ object [ "template" .= templateQuery ]
149+
150+instance FromJSON Query where
151+ parseJSON v = withObject "Query" parse v
152+ where parse o = termQuery `taggedWith` "term"
153+ <|> termsQuery `taggedWith` "terms"
154+ <|> idsQuery `taggedWith` "ids"
155+ <|> queryQueryStringQuery `taggedWith` "query_string"
156+ <|> queryMatchQuery `taggedWith` "match"
157+ <|> queryMultiMatchQuery
158+ <|> queryBoolQuery `taggedWith` "bool"
159+ <|> queryBoostingQuery `taggedWith` "boosting"
160+ <|> queryCommonTermsQuery `taggedWith` "common"
161+ <|> constantScoreQuery `taggedWith` "constant_score"
162+ <|> queryFunctionScoreQuery `taggedWith` "function_score"
163+ <|> queryDisMaxQuery `taggedWith` "dis_max"
164+ <|> queryFuzzyLikeThisQuery `taggedWith` "fuzzy_like_this"
165+ <|> queryFuzzyLikeFieldQuery `taggedWith` "fuzzy_like_this_field"
166+ <|> queryFuzzyQuery `taggedWith` "fuzzy"
167+ <|> queryHasChildQuery `taggedWith` "has_child"
168+ <|> queryHasParentQuery `taggedWith` "has_parent"
169+ <|> queryIndicesQuery `taggedWith` "indices"
170+ <|> matchAllQuery `taggedWith` "match_all"
171+ <|> queryMoreLikeThisQuery `taggedWith` "more_like_this"
172+ <|> queryMoreLikeThisFieldQuery `taggedWith` "more_like_this_field"
173+ <|> queryNestedQuery `taggedWith` "nested"
174+ <|> queryPrefixQuery `taggedWith` "prefix"
175+ <|> queryRangeQuery `taggedWith` "range"
176+ <|> queryRegexpQuery `taggedWith` "regexp"
177+ <|> querySimpleQueryStringQuery `taggedWith` "simple_query_string"
178+ <|> queryTemplateQueryInline `taggedWith` "template"
179+ where taggedWith parser k = parser =<< o .: k
180+ termQuery = fieldTagged $ \(FieldName fn) o ->
181+ TermQuery <$> (Term fn <$> o .: "value") <*> o .:? "boost"
182+ termsQuery o = case HM.toList o of
183+ [(fn, vs)] -> do vals <- parseJSON vs
184+ case vals of
185+ x:xs -> return (TermsQuery fn (x :| xs))
186+ _ -> fail "Expected non empty list of values"
187+ _ -> fail "Expected object with 1 field-named key"
188+ idsQuery o = IdsQuery <$> o .: "type"
189+ <*> o .: "values"
190+ queryQueryStringQuery = pure . QueryQueryStringQuery
191+ queryMatchQuery = pure . QueryMatchQuery
192+ queryMultiMatchQuery = QueryMultiMatchQuery <$> parseJSON v
193+ queryBoolQuery = pure . QueryBoolQuery
194+ queryBoostingQuery = pure . QueryBoostingQuery
195+ queryCommonTermsQuery = pure . QueryCommonTermsQuery
196+ constantScoreQuery o = case HM.lookup "query" o of
197+ Just x -> ConstantScoreQuery <$> parseJSON x
198+ <*> o .: "boost"
199+ _ -> fail "Does not appear to be a ConstantScoreQuery"
200+ queryFunctionScoreQuery = pure . QueryFunctionScoreQuery
201+ queryDisMaxQuery = pure . QueryDisMaxQuery
202+ queryFuzzyLikeThisQuery = pure . QueryFuzzyLikeThisQuery
203+ queryFuzzyLikeFieldQuery = pure . QueryFuzzyLikeFieldQuery
204+ queryFuzzyQuery = pure . QueryFuzzyQuery
205+ queryHasChildQuery = pure . QueryHasChildQuery
206+ queryHasParentQuery = pure . QueryHasParentQuery
207+ queryIndicesQuery = pure . QueryIndicesQuery
208+ matchAllQuery o = MatchAllQuery <$> o .:? "boost"
209+ queryMoreLikeThisQuery = pure . QueryMoreLikeThisQuery
210+ queryMoreLikeThisFieldQuery = pure . QueryMoreLikeThisFieldQuery
211+ queryNestedQuery = pure . QueryNestedQuery
212+ queryPrefixQuery = pure . QueryPrefixQuery
213+ queryRangeQuery = pure . QueryRangeQuery
214+ queryRegexpQuery = pure . QueryRegexpQuery
215+ querySimpleQueryStringQuery = pure . QuerySimpleQueryStringQuery
216+ -- queryExistsQuery o = QueryExistsQuery <$> o .: "field"
217+ queryTemplateQueryInline = pure . QueryTemplateQueryInline
218+
219+-- | As of Elastic 2.0, 'Filters' are just 'Queries' housed in a
220+-- Bool Query, and flagged in a different context.
221+newtype Filter = Filter { unFilter :: Query }
222+ deriving (Eq, Show)
223+
224+instance ToJSON Filter where
225+ toJSON = toJSON . unFilter
226+
227+instance FromJSON Filter where
228+ parseJSON v = Filter <$> parseJSON v
229+
230+data RegexpQuery =
231+ RegexpQuery { regexpQueryField :: FieldName
232+ , regexpQuery :: Regexp
233+ , regexpQueryFlags :: RegexpFlags
234+ , regexpQueryBoost :: Maybe Boost
235+ } deriving (Eq, Show)
236+
237+instance ToJSON RegexpQuery where
238+ toJSON (RegexpQuery (FieldName rqQueryField)
239+ (Regexp regexpQueryQuery) rqQueryFlags
240+ rqQueryBoost) =
241+ object [ rqQueryField .= omitNulls base ]
242+ where base = [ "value" .= regexpQueryQuery
243+ , "flags" .= rqQueryFlags
244+ , "boost" .= rqQueryBoost ]
245+
246+instance FromJSON RegexpQuery where
247+ parseJSON = withObject "RegexpQuery" parse
248+ where parse = fieldTagged $ \fn o ->
249+ RegexpQuery fn
250+ <$> o .: "value"
251+ <*> o .: "flags"
252+ <*> o .:? "boost"
253+
254+data RangeQuery =
255+ RangeQuery { rangeQueryField :: FieldName
256+ , rangeQueryRange :: RangeValue
257+ , rangeQueryBoost :: Boost } deriving (Eq, Show)
258+
259+instance ToJSON RangeQuery where
260+ toJSON (RangeQuery (FieldName fieldName) range boost) =
261+ object [ fieldName .= object conjoined ]
262+ where
263+ conjoined = ("boost" .= boost) : rangeValueToPair range
264+
265+instance FromJSON RangeQuery where
266+ parseJSON = withObject "RangeQuery" parse
267+ where parse = fieldTagged $ \fn o ->
268+ RangeQuery fn
269+ <$> parseJSON (Object o)
270+ <*> o .: "boost"
271+
272+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
273+mkRangeQuery f r = RangeQuery f r (Boost 1.0)
274+
275+data SimpleQueryStringQuery =
276+ SimpleQueryStringQuery
277+ { simpleQueryStringQuery :: QueryString
278+ , simpleQueryStringField :: Maybe FieldOrFields
279+ , simpleQueryStringOperator :: Maybe BooleanOperator
280+ , simpleQueryStringAnalyzer :: Maybe Analyzer
281+ , simpleQueryStringFlags :: Maybe (NonEmpty SimpleQueryFlag)
282+ , simpleQueryStringLowercaseExpanded :: Maybe LowercaseExpanded
283+ , simpleQueryStringLocale :: Maybe Locale
284+ } deriving (Eq, Show)
285+
286+
287+instance ToJSON SimpleQueryStringQuery where
288+ toJSON SimpleQueryStringQuery {..} =
289+ omitNulls (base ++ maybeAdd)
290+ where base = [ "query" .= simpleQueryStringQuery ]
291+ maybeAdd = [ "fields" .= simpleQueryStringField
292+ , "default_operator" .= simpleQueryStringOperator
293+ , "analyzer" .= simpleQueryStringAnalyzer
294+ , "flags" .= simpleQueryStringFlags
295+ , "lowercase_expanded_terms" .= simpleQueryStringLowercaseExpanded
296+ , "locale" .= simpleQueryStringLocale ]
297+
298+instance FromJSON SimpleQueryStringQuery where
299+ parseJSON = withObject "SimpleQueryStringQuery" parse
300+ where parse o = SimpleQueryStringQuery <$> o .: "query"
301+ <*> o .:? "fields"
302+ <*> o .:? "default_operator"
303+ <*> o .:? "analyzer"
304+ <*> (parseFlags <$> o .:? "flags")
305+ <*> o .:? "lowercase_expanded_terms"
306+ <*> o .:? "locale"
307+ parseFlags (Just (x:xs)) = Just (x :| xs)
308+ parseFlags _ = Nothing
309+
310+data SimpleQueryFlag =
311+ SimpleQueryAll
312+ | SimpleQueryNone
313+ | SimpleQueryAnd
314+ | SimpleQueryOr
315+ | SimpleQueryPrefix
316+ | SimpleQueryPhrase
317+ | SimpleQueryPrecedence
318+ | SimpleQueryEscape
319+ | SimpleQueryWhitespace
320+ | SimpleQueryFuzzy
321+ | SimpleQueryNear
322+ | SimpleQuerySlop deriving (Eq, Show)
323+
324+instance ToJSON SimpleQueryFlag where
325+ toJSON SimpleQueryAll = "ALL"
326+ toJSON SimpleQueryNone = "NONE"
327+ toJSON SimpleQueryAnd = "AND"
328+ toJSON SimpleQueryOr = "OR"
329+ toJSON SimpleQueryPrefix = "PREFIX"
330+ toJSON SimpleQueryPhrase = "PHRASE"
331+ toJSON SimpleQueryPrecedence = "PRECEDENCE"
332+ toJSON SimpleQueryEscape = "ESCAPE"
333+ toJSON SimpleQueryWhitespace = "WHITESPACE"
334+ toJSON SimpleQueryFuzzy = "FUZZY"
335+ toJSON SimpleQueryNear = "NEAR"
336+ toJSON SimpleQuerySlop = "SLOP"
337+
338+instance FromJSON SimpleQueryFlag where
339+ parseJSON = withText "SimpleQueryFlag" parse
340+ where parse "ALL" = pure SimpleQueryAll
341+ parse "NONE" = pure SimpleQueryNone
342+ parse "AND" = pure SimpleQueryAnd
343+ parse "OR" = pure SimpleQueryOr
344+ parse "PREFIX" = pure SimpleQueryPrefix
345+ parse "PHRASE" = pure SimpleQueryPhrase
346+ parse "PRECEDENCE" = pure SimpleQueryPrecedence
347+ parse "ESCAPE" = pure SimpleQueryEscape
348+ parse "WHITESPACE" = pure SimpleQueryWhitespace
349+ parse "FUZZY" = pure SimpleQueryFuzzy
350+ parse "NEAR" = pure SimpleQueryNear
351+ parse "SLOP" = pure SimpleQuerySlop
352+ parse f = fail ("Unexpected SimpleQueryFlag: " <> show f)
353+
354+-- use_dis_max and tie_breaker when fields are plural?
355+data QueryStringQuery =
356+ QueryStringQuery
357+ { queryStringQuery :: QueryString
358+ , queryStringDefaultField :: Maybe FieldName
359+ , queryStringOperator :: Maybe BooleanOperator
360+ , queryStringAnalyzer :: Maybe Analyzer
361+ , queryStringAllowLeadingWildcard :: Maybe AllowLeadingWildcard
362+ , queryStringLowercaseExpanded :: Maybe LowercaseExpanded
363+ , queryStringEnablePositionIncrements :: Maybe EnablePositionIncrements
364+ , queryStringFuzzyMaxExpansions :: Maybe MaxExpansions
365+ , queryStringFuzziness :: Maybe Fuzziness
366+ , queryStringFuzzyPrefixLength :: Maybe PrefixLength
367+ , queryStringPhraseSlop :: Maybe PhraseSlop
368+ , queryStringBoost :: Maybe Boost
369+ , queryStringAnalyzeWildcard :: Maybe AnalyzeWildcard
370+ , queryStringGeneratePhraseQueries :: Maybe GeneratePhraseQueries
371+ , queryStringMinimumShouldMatch :: Maybe MinimumMatch
372+ , queryStringLenient :: Maybe Lenient
373+ , queryStringLocale :: Maybe Locale
374+ } deriving (Eq, Show)
375+
376+
377+instance ToJSON QueryStringQuery where
378+ toJSON (QueryStringQuery qsQueryString
379+ qsDefaultField qsOperator
380+ qsAnalyzer qsAllowWildcard
381+ qsLowercaseExpanded qsEnablePositionIncrements
382+ qsFuzzyMaxExpansions qsFuzziness
383+ qsFuzzyPrefixLength qsPhraseSlop
384+ qsBoost qsAnalyzeWildcard
385+ qsGeneratePhraseQueries qsMinimumShouldMatch
386+ qsLenient qsLocale) =
387+ omitNulls base
388+ where
389+ base = [ "query" .= qsQueryString
390+ , "default_field" .= qsDefaultField
391+ , "default_operator" .= qsOperator
392+ , "analyzer" .= qsAnalyzer
393+ , "allow_leading_wildcard" .= qsAllowWildcard
394+ , "lowercase_expanded_terms" .= qsLowercaseExpanded
395+ , "enable_position_increments" .= qsEnablePositionIncrements
396+ , "fuzzy_max_expansions" .= qsFuzzyMaxExpansions
397+ , "fuzziness" .= qsFuzziness
398+ , "fuzzy_prefix_length" .= qsFuzzyPrefixLength
399+ , "phrase_slop" .= qsPhraseSlop
400+ , "boost" .= qsBoost
401+ , "analyze_wildcard" .= qsAnalyzeWildcard
402+ , "auto_generate_phrase_queries" .= qsGeneratePhraseQueries
403+ , "minimum_should_match" .= qsMinimumShouldMatch
404+ , "lenient" .= qsLenient
405+ , "locale" .= qsLocale ]
406+
407+instance FromJSON QueryStringQuery where
408+ parseJSON = withObject "QueryStringQuery" parse
409+ where parse o = QueryStringQuery
410+ <$> o .: "query"
411+ <*> o .:? "default_field"
412+ <*> o .:? "default_operator"
413+ <*> o .:? "analyzer"
414+ <*> o .:? "allow_leading_wildcard"
415+ <*> o .:? "lowercase_expanded_terms"
416+ <*> o .:? "enable_position_increments"
417+ <*> o .:? "fuzzy_max_expansions"
418+ <*> o .:? "fuzziness"
419+ <*> o .:? "fuzzy_prefix_length"
420+ <*> o .:? "phrase_slop"
421+ <*> o .:? "boost"
422+ <*> o .:? "analyze_wildcard"
423+ <*> o .:? "auto_generate_phrase_queries"
424+ <*> o .:? "minimum_should_match"
425+ <*> o .:? "lenient"
426+ <*> o .:? "locale"
427+
428+mkQueryStringQuery :: QueryString -> QueryStringQuery
429+mkQueryStringQuery qs =
430+ QueryStringQuery qs Nothing Nothing
431+ Nothing Nothing Nothing Nothing
432+ Nothing Nothing Nothing Nothing
433+ Nothing Nothing Nothing Nothing
434+ Nothing Nothing
435+
436+data FieldOrFields = FofField FieldName
437+ | FofFields (NonEmpty FieldName) deriving (Eq, Show)
438+
439+instance ToJSON FieldOrFields where
440+ toJSON (FofField fieldName) =
441+ toJSON fieldName
442+ toJSON (FofFields fieldNames) =
443+ toJSON fieldNames
444+
445+instance FromJSON FieldOrFields where
446+ parseJSON v = FofField <$> parseJSON v
447+ <|> FofFields <$> (parseNEJSON =<< parseJSON v)
448+
449+data PrefixQuery =
450+ PrefixQuery
451+ { prefixQueryField :: FieldName
452+ , prefixQueryPrefixValue :: Text
453+ , prefixQueryBoost :: Maybe Boost } deriving (Eq, Show)
454+
455+instance ToJSON PrefixQuery where
456+ toJSON (PrefixQuery (FieldName fieldName) queryValue boost) =
457+ object [ fieldName .= omitNulls base ]
458+ where base = [ "value" .= queryValue
459+ , "boost" .= boost ]
460+
461+instance FromJSON PrefixQuery where
462+ parseJSON = withObject "PrefixQuery" parse
463+ where parse = fieldTagged $ \fn o ->
464+ PrefixQuery fn
465+ <$> o .: "value"
466+ <*> o .:? "boost"
467+
468+data NestedQuery =
469+ NestedQuery
470+ { nestedQueryPath :: QueryPath
471+ , nestedQueryScoreType :: ScoreType
472+ , nestedQuery :: Query } deriving (Eq, Show)
473+
474+instance ToJSON NestedQuery where
475+ toJSON (NestedQuery nqPath nqScoreType nqQuery) =
476+ object [ "path" .= nqPath
477+ , "score_mode" .= nqScoreType
478+ , "query" .= nqQuery ]
479+
480+instance FromJSON NestedQuery where
481+ parseJSON = withObject "NestedQuery" parse
482+ where parse o = NestedQuery
483+ <$> o .: "path"
484+ <*> o .: "score_mode"
485+ <*> o .: "query"
486+
487+data MoreLikeThisFieldQuery =
488+ MoreLikeThisFieldQuery
489+ { moreLikeThisFieldText :: Text
490+ , moreLikeThisFieldFields :: FieldName
491+ -- default 0.3 (30%)
492+ , moreLikeThisFieldPercentMatch :: Maybe PercentMatch
493+ , moreLikeThisFieldMinimumTermFreq :: Maybe MinimumTermFrequency
494+ , moreLikeThisFieldMaxQueryTerms :: Maybe MaxQueryTerms
495+ , moreLikeThisFieldStopWords :: Maybe (NonEmpty StopWord)
496+ , moreLikeThisFieldMinDocFrequency :: Maybe MinDocFrequency
497+ , moreLikeThisFieldMaxDocFrequency :: Maybe MaxDocFrequency
498+ , moreLikeThisFieldMinWordLength :: Maybe MinWordLength
499+ , moreLikeThisFieldMaxWordLength :: Maybe MaxWordLength
500+ , moreLikeThisFieldBoostTerms :: Maybe BoostTerms
501+ , moreLikeThisFieldBoost :: Maybe Boost
502+ , moreLikeThisFieldAnalyzer :: Maybe Analyzer
503+ } deriving (Eq, Show)
504+
505+
506+instance ToJSON MoreLikeThisFieldQuery where
507+ toJSON (MoreLikeThisFieldQuery text (FieldName fieldName)
508+ percent mtf mqt stopwords mindf maxdf
509+ minwl maxwl boostTerms boost analyzer) =
510+ object [ fieldName .= omitNulls base ]
511+ where base = [ "like_text" .= text
512+ , "percent_terms_to_match" .= percent
513+ , "min_term_freq" .= mtf
514+ , "max_query_terms" .= mqt
515+ , "stop_words" .= stopwords
516+ , "min_doc_freq" .= mindf
517+ , "max_doc_freq" .= maxdf
518+ , "min_word_length" .= minwl
519+ , "max_word_length" .= maxwl
520+ , "boost_terms" .= boostTerms
521+ , "boost" .= boost
522+ , "analyzer" .= analyzer ]
523+
524+instance FromJSON MoreLikeThisFieldQuery where
525+ parseJSON = withObject "MoreLikeThisFieldQuery" parse
526+ where parse = fieldTagged $ \fn o ->
527+ MoreLikeThisFieldQuery
528+ <$> o .: "like_text"
529+ <*> pure fn
530+ <*> o .:? "percent_terms_to_match"
531+ <*> o .:? "min_term_freq"
532+ <*> o .:? "max_query_terms"
533+ -- <*> (optionalNE =<< o .:? "stop_words")
534+ <*> o .:? "stop_words"
535+ <*> o .:? "min_doc_freq"
536+ <*> o .:? "max_doc_freq"
537+ <*> o .:? "min_word_length"
538+ <*> o .:? "max_word_length"
539+ <*> o .:? "boost_terms"
540+ <*> o .:? "boost"
541+ <*> o .:? "analyzer"
542+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
543+
544+data MoreLikeThisQuery =
545+ MoreLikeThisQuery
546+ { moreLikeThisText :: Text
547+ , moreLikeThisFields :: Maybe (NonEmpty FieldName)
548+ -- default 0.3 (30%)
549+ , moreLikeThisPercentMatch :: Maybe PercentMatch
550+ , moreLikeThisMinimumTermFreq :: Maybe MinimumTermFrequency
551+ , moreLikeThisMaxQueryTerms :: Maybe MaxQueryTerms
552+ , moreLikeThisStopWords :: Maybe (NonEmpty StopWord)
553+ , moreLikeThisMinDocFrequency :: Maybe MinDocFrequency
554+ , moreLikeThisMaxDocFrequency :: Maybe MaxDocFrequency
555+ , moreLikeThisMinWordLength :: Maybe MinWordLength
556+ , moreLikeThisMaxWordLength :: Maybe MaxWordLength
557+ , moreLikeThisBoostTerms :: Maybe BoostTerms
558+ , moreLikeThisBoost :: Maybe Boost
559+ , moreLikeThisAnalyzer :: Maybe Analyzer
560+ } deriving (Eq, Show)
561+
562+
563+instance ToJSON MoreLikeThisQuery where
564+ toJSON (MoreLikeThisQuery text fields percent
565+ mtf mqt stopwords mindf maxdf
566+ minwl maxwl boostTerms boost analyzer) =
567+ omitNulls base
568+ where base = [ "like_text" .= text
569+ , "fields" .= fields
570+ , "percent_terms_to_match" .= percent
571+ , "min_term_freq" .= mtf
572+ , "max_query_terms" .= mqt
573+ , "stop_words" .= stopwords
574+ , "min_doc_freq" .= mindf
575+ , "max_doc_freq" .= maxdf
576+ , "min_word_length" .= minwl
577+ , "max_word_length" .= maxwl
578+ , "boost_terms" .= boostTerms
579+ , "boost" .= boost
580+ , "analyzer" .= analyzer ]
581+
582+instance FromJSON MoreLikeThisQuery where
583+ parseJSON = withObject "MoreLikeThisQuery" parse
584+ where parse o = MoreLikeThisQuery
585+ <$> o .: "like_text"
586+ -- <*> (optionalNE =<< o .:? "fields")
587+ <*> o .:? "fields"
588+ <*> o .:? "percent_terms_to_match"
589+ <*> o .:? "min_term_freq"
590+ <*> o .:? "max_query_terms"
591+ -- <*> (optionalNE =<< o .:? "stop_words")
592+ <*> o .:? "stop_words"
593+ <*> o .:? "min_doc_freq"
594+ <*> o .:? "max_doc_freq"
595+ <*> o .:? "min_word_length"
596+ <*> o .:? "max_word_length"
597+ <*> o .:? "boost_terms"
598+ <*> o .:? "boost"
599+ <*> o .:? "analyzer"
600+ -- optionalNE = maybe (pure Nothing) (fmap Just . parseNEJSON)
601+
602+data IndicesQuery =
603+ IndicesQuery
604+ { indicesQueryIndices :: [IndexName]
605+ , indicesQuery :: Query
606+ -- default "all"
607+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show)
608+
609+
610+instance ToJSON IndicesQuery where
611+ toJSON (IndicesQuery indices query noMatch) =
612+ omitNulls [ "indices" .= indices
613+ , "no_match_query" .= noMatch
614+ , "query" .= query ]
615+
616+instance FromJSON IndicesQuery where
617+ parseJSON = withObject "IndicesQuery" parse
618+ where parse o = IndicesQuery
619+ <$> o .:? "indices" .!= []
620+ <*> o .: "query"
621+ <*> o .:? "no_match_query"
622+
623+data HasParentQuery =
624+ HasParentQuery
625+ { hasParentQueryType :: TypeName
626+ , hasParentQuery :: Query
627+ , hasParentQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
628+
629+instance ToJSON HasParentQuery where
630+ toJSON (HasParentQuery queryType query scoreType) =
631+ omitNulls [ "parent_type" .= queryType
632+ , "score_type" .= scoreType
633+ , "query" .= query ]
634+
635+instance FromJSON HasParentQuery where
636+ parseJSON = withObject "HasParentQuery" parse
637+ where parse o = HasParentQuery
638+ <$> o .: "parent_type"
639+ <*> o .: "query"
640+ <*> o .:? "score_type"
641+
642+data HasChildQuery =
643+ HasChildQuery
644+ { hasChildQueryType :: TypeName
645+ , hasChildQuery :: Query
646+ , hasChildQueryScoreType :: Maybe ScoreType } deriving (Eq, Show)
647+
648+instance ToJSON HasChildQuery where
649+ toJSON (HasChildQuery queryType query scoreType) =
650+ omitNulls [ "query" .= query
651+ , "score_type" .= scoreType
652+ , "type" .= queryType ]
653+
654+instance FromJSON HasChildQuery where
655+ parseJSON = withObject "HasChildQuery" parse
656+ where parse o = HasChildQuery
657+ <$> o .: "type"
658+ <*> o .: "query"
659+ <*> o .:? "score_type"
660+
661+data ScoreType =
662+ ScoreTypeMax
663+ | ScoreTypeSum
664+ | ScoreTypeAvg
665+ | ScoreTypeNone deriving (Eq, Show)
666+
667+instance ToJSON ScoreType where
668+ toJSON ScoreTypeMax = "max"
669+ toJSON ScoreTypeAvg = "avg"
670+ toJSON ScoreTypeSum = "sum"
671+ toJSON ScoreTypeNone = "none"
672+
673+instance FromJSON ScoreType where
674+ parseJSON = withText "ScoreType" parse
675+ where parse "max" = pure ScoreTypeMax
676+ parse "avg" = pure ScoreTypeAvg
677+ parse "sum" = pure ScoreTypeSum
678+ parse "none" = pure ScoreTypeNone
679+ parse t = fail ("Unexpected ScoreType: " <> show t)
680+
681+data FuzzyQuery =
682+ FuzzyQuery { fuzzyQueryField :: FieldName
683+ , fuzzyQueryValue :: Text
684+ , fuzzyQueryPrefixLength :: PrefixLength
685+ , fuzzyQueryMaxExpansions :: MaxExpansions
686+ , fuzzyQueryFuzziness :: Fuzziness
687+ , fuzzyQueryBoost :: Maybe Boost
688+ } deriving (Eq, Show)
689+
690+
691+instance ToJSON FuzzyQuery where
692+ toJSON (FuzzyQuery (FieldName fieldName) queryText
693+ prefixLength maxEx fuzziness boost) =
694+ object [ fieldName .= omitNulls base ]
695+ where base = [ "value" .= queryText
696+ , "fuzziness" .= fuzziness
697+ , "prefix_length" .= prefixLength
698+ , "boost" .= boost
699+ , "max_expansions" .= maxEx ]
700+
701+instance FromJSON FuzzyQuery where
702+ parseJSON = withObject "FuzzyQuery" parse
703+ where parse = fieldTagged $ \fn o ->
704+ FuzzyQuery fn
705+ <$> o .: "value"
706+ <*> o .: "prefix_length"
707+ <*> o .: "max_expansions"
708+ <*> o .: "fuzziness"
709+ <*> o .:? "boost"
710+
711+data FuzzyLikeFieldQuery =
712+ FuzzyLikeFieldQuery
713+ { fuzzyLikeField :: FieldName
714+ -- anaphora is good for the soul.
715+ , fuzzyLikeFieldText :: Text
716+ , fuzzyLikeFieldMaxQueryTerms :: MaxQueryTerms
717+ , fuzzyLikeFieldIgnoreTermFrequency :: IgnoreTermFrequency
718+ , fuzzyLikeFieldFuzziness :: Fuzziness
719+ , fuzzyLikeFieldPrefixLength :: PrefixLength
720+ , fuzzyLikeFieldBoost :: Boost
721+ , fuzzyLikeFieldAnalyzer :: Maybe Analyzer
722+ } deriving (Eq, Show)
723+
724+
725+instance ToJSON FuzzyLikeFieldQuery where
726+ toJSON (FuzzyLikeFieldQuery (FieldName fieldName)
727+ fieldText maxTerms ignoreFreq fuzziness prefixLength
728+ boost analyzer) =
729+ object [ fieldName .=
730+ omitNulls [ "like_text" .= fieldText
731+ , "max_query_terms" .= maxTerms
732+ , "ignore_tf" .= ignoreFreq
733+ , "fuzziness" .= fuzziness
734+ , "prefix_length" .= prefixLength
735+ , "analyzer" .= analyzer
736+ , "boost" .= boost ]]
737+
738+instance FromJSON FuzzyLikeFieldQuery where
739+ parseJSON = withObject "FuzzyLikeFieldQuery" parse
740+ where parse = fieldTagged $ \fn o ->
741+ FuzzyLikeFieldQuery fn
742+ <$> o .: "like_text"
743+ <*> o .: "max_query_terms"
744+ <*> o .: "ignore_tf"
745+ <*> o .: "fuzziness"
746+ <*> o .: "prefix_length"
747+ <*> o .: "boost"
748+ <*> o .:? "analyzer"
749+
750+data FuzzyLikeThisQuery =
751+ FuzzyLikeThisQuery
752+ { fuzzyLikeFields :: [FieldName]
753+ , fuzzyLikeText :: Text
754+ , fuzzyLikeMaxQueryTerms :: MaxQueryTerms
755+ , fuzzyLikeIgnoreTermFrequency :: IgnoreTermFrequency
756+ , fuzzyLikeFuzziness :: Fuzziness
757+ , fuzzyLikePrefixLength :: PrefixLength
758+ , fuzzyLikeBoost :: Boost
759+ , fuzzyLikeAnalyzer :: Maybe Analyzer
760+ } deriving (Eq, Show)
761+
762+
763+instance ToJSON FuzzyLikeThisQuery where
764+ toJSON (FuzzyLikeThisQuery fields text maxTerms
765+ ignoreFreq fuzziness prefixLength boost analyzer) =
766+ omitNulls base
767+ where base = [ "fields" .= fields
768+ , "like_text" .= text
769+ , "max_query_terms" .= maxTerms
770+ , "ignore_tf" .= ignoreFreq
771+ , "fuzziness" .= fuzziness
772+ , "prefix_length" .= prefixLength
773+ , "analyzer" .= analyzer
774+ , "boost" .= boost ]
775+
776+instance FromJSON FuzzyLikeThisQuery where
777+ parseJSON = withObject "FuzzyLikeThisQuery" parse
778+ where parse o = FuzzyLikeThisQuery
779+ <$> o .:? "fields" .!= []
780+ <*> o .: "like_text"
781+ <*> o .: "max_query_terms"
782+ <*> o .: "ignore_tf"
783+ <*> o .: "fuzziness"
784+ <*> o .: "prefix_length"
785+ <*> o .: "boost"
786+ <*> o .:? "analyzer"
787+
788+data DisMaxQuery =
789+ DisMaxQuery { disMaxQueries :: [Query]
790+ -- default 0.0
791+ , disMaxTiebreaker :: Tiebreaker
792+ , disMaxBoost :: Maybe Boost
793+ } deriving (Eq, Show)
794+
795+
796+instance ToJSON DisMaxQuery where
797+ toJSON (DisMaxQuery queries tiebreaker boost) =
798+ omitNulls base
799+ where base = [ "queries" .= queries
800+ , "boost" .= boost
801+ , "tie_breaker" .= tiebreaker ]
802+
803+instance FromJSON DisMaxQuery where
804+ parseJSON = withObject "DisMaxQuery" parse
805+ where parse o = DisMaxQuery
806+ <$> o .:? "queries" .!= []
807+ <*> o .: "tie_breaker"
808+ <*> o .:? "boost"
809+
810+data MatchQuery = MatchQuery
811+ { matchQueryField :: FieldName
812+ , matchQueryQueryString :: QueryString
813+ , matchQueryOperator :: BooleanOperator
814+ , matchQueryZeroTerms :: ZeroTermsQuery
815+ , matchQueryCutoffFrequency :: Maybe CutoffFrequency
816+ , matchQueryMatchType :: Maybe MatchQueryType
817+ , matchQueryAnalyzer :: Maybe Analyzer
818+ , matchQueryMaxExpansions :: Maybe MaxExpansions
819+ , matchQueryLenient :: Maybe Lenient
820+ , matchQueryBoost :: Maybe Boost
821+ , matchQueryMinimumShouldMatch :: Maybe Text
822+ } deriving (Eq, Show)
823+
824+
825+instance ToJSON MatchQuery where
826+ toJSON (MatchQuery (FieldName fieldName)
827+ (QueryString mqQueryString) booleanOperator
828+ zeroTermsQuery cutoffFrequency matchQueryType
829+ analyzer maxExpansions lenient boost
830+ minShouldMatch
831+ ) =
832+ object [ fieldName .= omitNulls base ]
833+ where base = [ "query" .= mqQueryString
834+ , "operator" .= booleanOperator
835+ , "zero_terms_query" .= zeroTermsQuery
836+ , "cutoff_frequency" .= cutoffFrequency
837+ , "type" .= matchQueryType
838+ , "analyzer" .= analyzer
839+ , "max_expansions" .= maxExpansions
840+ , "lenient" .= lenient
841+ , "boost" .= boost
842+ , "minimum_should_match" .= minShouldMatch
843+ ]
844+
845+instance FromJSON MatchQuery where
846+ parseJSON = withObject "MatchQuery" parse
847+ where parse = fieldTagged $ \fn o ->
848+ MatchQuery fn
849+ <$> o .: "query"
850+ <*> o .: "operator"
851+ <*> o .: "zero_terms_query"
852+ <*> o .:? "cutoff_frequency"
853+ <*> o .:? "type"
854+ <*> o .:? "analyzer"
855+ <*> o .:? "max_expansions"
856+ <*> o .:? "lenient"
857+ <*> o .:? "boost"
858+ <*> o .:? "minimum_should_match"
859+
860+{-| 'mkMatchQuery' is a convenience function that defaults the less common parameters,
861+ enabling you to provide only the 'FieldName' and 'QueryString' to make a 'MatchQuery'
862+-}
863+mkMatchQuery :: FieldName -> QueryString -> MatchQuery
864+mkMatchQuery field query = MatchQuery field query Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing Nothing
865+
866+data MatchQueryType =
867+ MatchPhrase
868+ | MatchPhrasePrefix deriving (Eq, Show)
869+
870+instance ToJSON MatchQueryType where
871+ toJSON MatchPhrase = "phrase"
872+ toJSON MatchPhrasePrefix = "phrase_prefix"
873+
874+instance FromJSON MatchQueryType where
875+ parseJSON = withText "MatchQueryType" parse
876+ where parse "phrase" = pure MatchPhrase
877+ parse "phrase_prefix" = pure MatchPhrasePrefix
878+ parse t = fail ("Unexpected MatchQueryType: " <> show t)
879+
880+data MultiMatchQuery = MultiMatchQuery
881+ { multiMatchQueryFields :: [FieldName]
882+ , multiMatchQueryString :: QueryString
883+ , multiMatchQueryOperator :: BooleanOperator
884+ , multiMatchQueryZeroTerms :: ZeroTermsQuery
885+ , multiMatchQueryTiebreaker :: Maybe Tiebreaker
886+ , multiMatchQueryType :: Maybe MultiMatchQueryType
887+ , multiMatchQueryCutoffFrequency :: Maybe CutoffFrequency
888+ , multiMatchQueryAnalyzer :: Maybe Analyzer
889+ , multiMatchQueryMaxExpansions :: Maybe MaxExpansions
890+ , multiMatchQueryLenient :: Maybe Lenient
891+ } deriving (Eq, Show)
892+
893+instance ToJSON MultiMatchQuery where
894+ toJSON (MultiMatchQuery fields (QueryString query) boolOp
895+ ztQ tb mmqt cf analyzer maxEx lenient) =
896+ object ["multi_match" .= omitNulls base]
897+ where base = [ "fields" .= fmap toJSON fields
898+ , "query" .= query
899+ , "operator" .= boolOp
900+ , "zero_terms_query" .= ztQ
901+ , "tie_breaker" .= tb
902+ , "type" .= mmqt
903+ , "cutoff_frequency" .= cf
904+ , "analyzer" .= analyzer
905+ , "max_expansions" .= maxEx
906+ , "lenient" .= lenient ]
907+
908+instance FromJSON MultiMatchQuery where
909+ parseJSON = withObject "MultiMatchQuery" parse
910+ where parse raw = do o <- raw .: "multi_match"
911+ MultiMatchQuery
912+ <$> o .:? "fields" .!= []
913+ <*> o .: "query"
914+ <*> o .: "operator"
915+ <*> o .: "zero_terms_query"
916+ <*> o .:? "tie_breaker"
917+ <*> o .:? "type"
918+ <*> o .:? "cutoff_frequency"
919+ <*> o .:? "analyzer"
920+ <*> o .:? "max_expansions"
921+ <*> o .:? "lenient"
922+
923+{-| 'mkMultiMatchQuery' is a convenience function that defaults the less common parameters,
924+ enabling you to provide only the list of 'FieldName's and 'QueryString' to
925+ make a 'MultiMatchQuery'.
926+-}
927+
928+mkMultiMatchQuery :: [FieldName] -> QueryString -> MultiMatchQuery
929+mkMultiMatchQuery matchFields query =
930+ MultiMatchQuery matchFields query
931+ Or ZeroTermsNone Nothing Nothing Nothing Nothing Nothing Nothing
932+
933+data MultiMatchQueryType =
934+ MultiMatchBestFields
935+ | MultiMatchMostFields
936+ | MultiMatchCrossFields
937+ | MultiMatchPhrase
938+ | MultiMatchPhrasePrefix deriving (Eq, Show)
939+
940+instance ToJSON MultiMatchQueryType where
941+ toJSON MultiMatchBestFields = "best_fields"
942+ toJSON MultiMatchMostFields = "most_fields"
943+ toJSON MultiMatchCrossFields = "cross_fields"
944+ toJSON MultiMatchPhrase = "phrase"
945+ toJSON MultiMatchPhrasePrefix = "phrase_prefix"
946+
947+instance FromJSON MultiMatchQueryType where
948+ parseJSON = withText "MultiMatchPhrasePrefix" parse
949+ where parse "best_fields" = pure MultiMatchBestFields
950+ parse "most_fields" = pure MultiMatchMostFields
951+ parse "cross_fields" = pure MultiMatchCrossFields
952+ parse "phrase" = pure MultiMatchPhrase
953+ parse "phrase_prefix" = pure MultiMatchPhrasePrefix
954+ parse t = fail ("Unexpected MultiMatchPhrasePrefix: " <> show t)
955+
956+data BoolQuery =
957+ BoolQuery { boolQueryMustMatch :: [Query]
958+ , boolQueryFilter :: [Filter]
959+ , boolQueryMustNotMatch :: [Query]
960+ , boolQueryShouldMatch :: [Query]
961+ , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
962+ , boolQueryBoost :: Maybe Boost
963+ , boolQueryDisableCoord :: Maybe DisableCoord
964+ } deriving (Eq, Show)
965+
966+
967+instance ToJSON BoolQuery where
968+ toJSON (BoolQuery mustM filterM' notM shouldM bqMin boost disableCoord) =
969+ omitNulls base
970+ where base = [ "must" .= mustM
971+ , "filter" .= filterM'
972+ , "must_not" .= notM
973+ , "should" .= shouldM
974+ , "minimum_should_match" .= bqMin
975+ , "boost" .= boost
976+ , "disable_coord" .= disableCoord ]
977+
978+instance FromJSON BoolQuery where
979+ parseJSON = withObject "BoolQuery" parse
980+ where parse o = BoolQuery
981+ <$> o .:? "must" .!= []
982+ <*> o .:? "filter" .!= []
983+ <*> o .:? "must_not" .!= []
984+ <*> o .:? "should" .!= []
985+ <*> o .:? "minimum_should_match"
986+ <*> o .:? "boost"
987+ <*> o .:? "disable_coord"
988+
989+mkBoolQuery :: [Query] -> [Filter] -> [Query] -> [Query] -> BoolQuery
990+mkBoolQuery must filt mustNot should =
991+ BoolQuery must filt mustNot should Nothing Nothing Nothing
992+
993+data BoostingQuery =
994+ BoostingQuery { positiveQuery :: Query
995+ , negativeQuery :: Query
996+ , negativeBoost :: Boost } deriving (Eq, Show)
997+
998+instance ToJSON BoostingQuery where
999+ toJSON (BoostingQuery bqPositiveQuery bqNegativeQuery bqNegativeBoost) =
1000+ object [ "positive" .= bqPositiveQuery
1001+ , "negative" .= bqNegativeQuery
1002+ , "negative_boost" .= bqNegativeBoost ]
1003+
1004+instance FromJSON BoostingQuery where
1005+ parseJSON = withObject "BoostingQuery" parse
1006+ where parse o = BoostingQuery
1007+ <$> o .: "positive"
1008+ <*> o .: "negative"
1009+ <*> o .: "negative_boost"
1010+
1011+data CommonTermsQuery =
1012+ CommonTermsQuery { commonField :: FieldName
1013+ , commonQuery :: QueryString
1014+ , commonCutoffFrequency :: CutoffFrequency
1015+ , commonLowFreqOperator :: BooleanOperator
1016+ , commonHighFreqOperator :: BooleanOperator
1017+ , commonMinimumShouldMatch :: Maybe CommonMinimumMatch
1018+ , commonBoost :: Maybe Boost
1019+ , commonAnalyzer :: Maybe Analyzer
1020+ , commonDisableCoord :: Maybe DisableCoord
1021+ } deriving (Eq, Show)
1022+
1023+
1024+instance ToJSON CommonTermsQuery where
1025+ toJSON (CommonTermsQuery (FieldName fieldName)
1026+ (QueryString query) cf lfo hfo msm
1027+ boost analyzer disableCoord) =
1028+ object [fieldName .= omitNulls base ]
1029+ where base = [ "query" .= query
1030+ , "cutoff_frequency" .= cf
1031+ , "low_freq_operator" .= lfo
1032+ , "minimum_should_match" .= msm
1033+ , "boost" .= boost
1034+ , "analyzer" .= analyzer
1035+ , "disable_coord" .= disableCoord
1036+ , "high_freq_operator" .= hfo ]
1037+
1038+instance FromJSON CommonTermsQuery where
1039+ parseJSON = withObject "CommonTermsQuery" parse
1040+ where parse = fieldTagged $ \fn o ->
1041+ CommonTermsQuery fn
1042+ <$> o .: "query"
1043+ <*> o .: "cutoff_frequency"
1044+ <*> o .: "low_freq_operator"
1045+ <*> o .: "high_freq_operator"
1046+ <*> o .:? "minimum_should_match"
1047+ <*> o .:? "boost"
1048+ <*> o .:? "analyzer"
1049+ <*> o .:? "disable_coord"
1050+
1051+data CommonMinimumMatch =
1052+ CommonMinimumMatchHighLow MinimumMatchHighLow
1053+ | CommonMinimumMatch MinimumMatch
1054+ deriving (Eq, Show)
1055+
1056+
1057+instance ToJSON CommonMinimumMatch where
1058+ toJSON (CommonMinimumMatch mm) = toJSON mm
1059+ toJSON (CommonMinimumMatchHighLow (MinimumMatchHighLow lowF highF)) =
1060+ object [ "low_freq" .= lowF
1061+ , "high_freq" .= highF ]
1062+
1063+instance FromJSON CommonMinimumMatch where
1064+ parseJSON v = parseMinimum v
1065+ <|> parseMinimumHighLow v
1066+ where parseMinimum = fmap CommonMinimumMatch . parseJSON
1067+ parseMinimumHighLow = fmap CommonMinimumMatchHighLow . withObject "CommonMinimumMatchHighLow" (\o ->
1068+ MinimumMatchHighLow
1069+ <$> o .: "low_freq"
1070+ <*> o .: "high_freq")
1071+
1072+data MinimumMatchHighLow =
1073+ MinimumMatchHighLow { lowFreq :: MinimumMatch
1074+ , highFreq :: MinimumMatch } deriving (Eq, Show)
1075+
1076+data ZeroTermsQuery =
1077+ ZeroTermsNone
1078+ | ZeroTermsAll deriving (Eq, Show)
1079+
1080+instance ToJSON ZeroTermsQuery where
1081+ toJSON ZeroTermsNone = String "none"
1082+ toJSON ZeroTermsAll = String "all"
1083+
1084+instance FromJSON ZeroTermsQuery where
1085+ parseJSON = withText "ZeroTermsQuery" parse
1086+ where parse "none" = pure ZeroTermsNone
1087+ parse "all" = pure ZeroTermsAll
1088+ parse q = fail ("Unexpected ZeroTermsQuery: " <> show q)
1089+
1090+data RangeExecution = RangeExecutionIndex
1091+ | RangeExecutionFielddata deriving (Eq, Show)
1092+
1093+-- index for smaller ranges, fielddata for longer ranges
1094+instance ToJSON RangeExecution where
1095+ toJSON RangeExecutionIndex = "index"
1096+ toJSON RangeExecutionFielddata = "fielddata"
1097+
1098+
1099+instance FromJSON RangeExecution where
1100+ parseJSON = withText "RangeExecution" parse
1101+ where parse "index" = pure RangeExecutionIndex
1102+ parse "fielddata" = pure RangeExecutionFielddata
1103+ parse t = error ("Unrecognized RangeExecution " <> show t)
1104+
1105+newtype Regexp = Regexp Text deriving (Eq, Show, FromJSON)
1106+
1107+data RegexpFlags = AllRegexpFlags
1108+ | NoRegexpFlags
1109+ | SomeRegexpFlags (NonEmpty RegexpFlag) deriving (Eq, Show)
1110+
1111+instance ToJSON RegexpFlags where
1112+ toJSON AllRegexpFlags = String "ALL"
1113+ toJSON NoRegexpFlags = String "NONE"
1114+ toJSON (SomeRegexpFlags (h :| fs)) = String $ T.intercalate "|" flagStrs
1115+ where flagStrs = map flagStr . nub $ h:fs
1116+ flagStr AnyString = "ANYSTRING"
1117+ flagStr Automaton = "AUTOMATON"
1118+ flagStr Complement = "COMPLEMENT"
1119+ flagStr Empty = "EMPTY"
1120+ flagStr Intersection = "INTERSECTION"
1121+ flagStr Interval = "INTERVAL"
1122+
1123+instance FromJSON RegexpFlags where
1124+ parseJSON = withText "RegexpFlags" parse
1125+ where parse "ALL" = pure AllRegexpFlags
1126+ parse "NONE" = pure NoRegexpFlags
1127+ parse t = SomeRegexpFlags <$> parseNEJSON (String <$> T.splitOn "|" t)
1128+
1129+data RegexpFlag = AnyString
1130+ | Automaton
1131+ | Complement
1132+ | Empty
1133+ | Intersection
1134+ | Interval deriving (Eq, Show)
1135+
1136+instance FromJSON RegexpFlag where
1137+ parseJSON = withText "RegexpFlag" parse
1138+ where parse "ANYSTRING" = pure AnyString
1139+ parse "AUTOMATON" = pure Automaton
1140+ parse "COMPLEMENT" = pure Complement
1141+ parse "EMPTY" = pure Empty
1142+ parse "INTERSECTION" = pure Intersection
1143+ parse "INTERVAL" = pure Interval
1144+ parse f = fail ("Unknown RegexpFlag: " <> show f)
1145+
1146+newtype LessThan = LessThan Double deriving (Eq, Show)
1147+newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
1148+newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
1149+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
1150+
1151+newtype LessThanD = LessThanD UTCTime deriving (Eq, Show)
1152+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show)
1153+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show)
1154+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show)
1155+
1156+data RangeValue = RangeDateLte LessThanEqD
1157+ | RangeDateLt LessThanD
1158+ | RangeDateGte GreaterThanEqD
1159+ | RangeDateGt GreaterThanD
1160+ | RangeDateGtLt GreaterThanD LessThanD
1161+ | RangeDateGteLte GreaterThanEqD LessThanEqD
1162+ | RangeDateGteLt GreaterThanEqD LessThanD
1163+ | RangeDateGtLte GreaterThanD LessThanEqD
1164+ | RangeDoubleLte LessThanEq
1165+ | RangeDoubleLt LessThan
1166+ | RangeDoubleGte GreaterThanEq
1167+ | RangeDoubleGt GreaterThan
1168+ | RangeDoubleGtLt GreaterThan LessThan
1169+ | RangeDoubleGteLte GreaterThanEq LessThanEq
1170+ | RangeDoubleGteLt GreaterThanEq LessThan
1171+ | RangeDoubleGtLte GreaterThan LessThanEq
1172+ deriving (Eq, Show)
1173+
1174+
1175+parseRangeValue :: ( FromJSON t4
1176+ , FromJSON t3
1177+ , FromJSON t2
1178+ , FromJSON t1
1179+ )
1180+ => (t3 -> t5)
1181+ -> (t1 -> t6)
1182+ -> (t4 -> t7)
1183+ -> (t2 -> t8)
1184+ -> (t5 -> t6 -> b)
1185+ -> (t7 -> t6 -> b)
1186+ -> (t5 -> t8 -> b)
1187+ -> (t7 -> t8 -> b)
1188+ -> (t5 -> b)
1189+ -> (t6 -> b)
1190+ -> (t7 -> b)
1191+ -> (t8 -> b)
1192+ -> Parser b
1193+ -> Object
1194+ -> Parser b
1195+parseRangeValue mkGt mkLt mkGte mkLte
1196+ fGtLt fGteLt fGtLte fGteLte
1197+ fGt fLt fGte fLte nada o = do
1198+ lt <- o .:? "lt"
1199+ lte <- o .:? "lte"
1200+ gt <- o .:? "gt"
1201+ gte <- o .:? "gte"
1202+ case (lt, lte, gt, gte) of
1203+ (Just a, _, Just b, _) ->
1204+ return (fGtLt (mkGt b) (mkLt a))
1205+ (Just a, _, _, Just b) ->
1206+ return (fGteLt (mkGte b) (mkLt a))
1207+ (_, Just a, Just b, _) ->
1208+ return (fGtLte (mkGt b) (mkLte a))
1209+ (_, Just a, _, Just b) ->
1210+ return (fGteLte (mkGte b) (mkLte a))
1211+ (_, _, Just a, _) ->
1212+ return (fGt (mkGt a))
1213+ (Just a, _, _, _) ->
1214+ return (fLt (mkLt a))
1215+ (_, _, _, Just a) ->
1216+ return (fGte (mkGte a))
1217+ (_, Just a, _, _) ->
1218+ return (fLte (mkLte a))
1219+ (Nothing, Nothing, Nothing, Nothing) ->
1220+ nada
1221+
1222+
1223+instance FromJSON RangeValue where
1224+ parseJSON = withObject "RangeValue" parse
1225+ where parse o = parseDate o
1226+ <|> parseDouble o
1227+ parseDate o =
1228+ parseRangeValue
1229+ GreaterThanD LessThanD
1230+ GreaterThanEqD LessThanEqD
1231+ RangeDateGtLt RangeDateGteLt
1232+ RangeDateGtLte RangeDateGteLte
1233+ RangeDateGt RangeDateLt
1234+ RangeDateGte RangeDateLte
1235+ mzero o
1236+ parseDouble o =
1237+ parseRangeValue
1238+ GreaterThan LessThan
1239+ GreaterThanEq LessThanEq
1240+ RangeDoubleGtLt RangeDoubleGteLt
1241+ RangeDoubleGtLte RangeDoubleGteLte
1242+ RangeDoubleGt RangeDoubleLt
1243+ RangeDoubleGte RangeDoubleLte
1244+ mzero o
1245+
1246+rangeValueToPair :: RangeValue -> [Pair]
1247+rangeValueToPair rv = case rv of
1248+ RangeDateLte (LessThanEqD t) -> ["lte" .= t]
1249+ RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
1250+ RangeDateLt (LessThanD t) -> ["lt" .= t]
1251+ RangeDateGt (GreaterThanD t) -> ["gt" .= t]
1252+ RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
1253+ RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
1254+ RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
1255+ RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
1256+ RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
1257+ RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
1258+ RangeDoubleLt (LessThan t) -> ["lt" .= t]
1259+ RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
1260+ RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
1261+ RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
1262+ RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
1263+ RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
1264+
1265+data Term = Term { termField :: Text
1266+ , termValue :: Text } deriving (Eq, Show)
1267+
1268+instance ToJSON Term where
1269+ toJSON (Term field value) = object ["term" .= object
1270+ [field .= value]]
1271+
1272+instance FromJSON Term where
1273+ parseJSON = withObject "Term" parse
1274+ where parse o = do termObj <- o .: "term"
1275+ case HM.toList termObj of
1276+ [(fn, v)] -> Term fn <$> parseJSON v
1277+ _ -> fail "Expected object with 1 field-named key"
1278+
1279+data BoolMatch = MustMatch Term Cache
1280+ | MustNotMatch Term Cache
1281+ | ShouldMatch [Term] Cache deriving (Eq, Show)
1282+
1283+
1284+instance ToJSON BoolMatch where
1285+ toJSON (MustMatch term cache) = object ["must" .= term,
1286+ "_cache" .= cache]
1287+ toJSON (MustNotMatch term cache) = object ["must_not" .= term,
1288+ "_cache" .= cache]
1289+ toJSON (ShouldMatch terms cache) = object ["should" .= fmap toJSON terms,
1290+ "_cache" .= cache]
1291+
1292+instance FromJSON BoolMatch where
1293+ parseJSON = withObject "BoolMatch" parse
1294+ where parse o = mustMatch `taggedWith` "must"
1295+ <|> mustNotMatch `taggedWith` "must_not"
1296+ <|> shouldMatch `taggedWith` "should"
1297+ where taggedWith parser k = parser =<< o .: k
1298+ mustMatch t = MustMatch t <$> o .:? "_cache" .!= defaultCache
1299+ mustNotMatch t = MustNotMatch t <$> o .:? "_cache" .!= defaultCache
1300+ shouldMatch t = ShouldMatch t <$> o .:? "_cache" .!= defaultCache
1301+
1302+-- "memory" or "indexed"
1303+data GeoFilterType = GeoFilterMemory
1304+ | GeoFilterIndexed deriving (Eq, Show)
1305+
1306+instance ToJSON GeoFilterType where
1307+ toJSON GeoFilterMemory = String "memory"
1308+ toJSON GeoFilterIndexed = String "indexed"
1309+
1310+instance FromJSON GeoFilterType where
1311+ parseJSON = withText "GeoFilterType" parse
1312+ where parse "memory" = pure GeoFilterMemory
1313+ parse "indexed" = pure GeoFilterIndexed
1314+ parse t = fail ("Unrecognized GeoFilterType: " <> show t)
1315+
1316+data LatLon = LatLon { lat :: Double
1317+ , lon :: Double } deriving (Eq, Show)
1318+
1319+instance ToJSON LatLon where
1320+ toJSON (LatLon lLat lLon) =
1321+ object ["lat" .= lLat
1322+ , "lon" .= lLon]
1323+
1324+instance FromJSON LatLon where
1325+ parseJSON = withObject "LatLon" parse
1326+ where parse o = LatLon <$> o .: "lat"
1327+ <*> o .: "lon"
1328+
1329+data GeoBoundingBox =
1330+ GeoBoundingBox { topLeft :: LatLon
1331+ , bottomRight :: LatLon } deriving (Eq, Show)
1332+
1333+instance ToJSON GeoBoundingBox where
1334+ toJSON (GeoBoundingBox gbbTopLeft gbbBottomRight) =
1335+ object ["top_left" .= gbbTopLeft
1336+ , "bottom_right" .= gbbBottomRight]
1337+
1338+instance FromJSON GeoBoundingBox where
1339+ parseJSON = withObject "GeoBoundingBox" parse
1340+ where parse o = GeoBoundingBox
1341+ <$> o .: "top_left"
1342+ <*> o .: "bottom_right"
1343+
1344+data GeoBoundingBoxConstraint =
1345+ GeoBoundingBoxConstraint { geoBBField :: FieldName
1346+ , constraintBox :: GeoBoundingBox
1347+ , bbConstraintcache :: Cache
1348+ , geoType :: GeoFilterType
1349+ } deriving (Eq, Show)
1350+
1351+instance ToJSON GeoBoundingBoxConstraint where
1352+ toJSON (GeoBoundingBoxConstraint
1353+ (FieldName gbbcGeoBBField) gbbcConstraintBox cache type') =
1354+ object [gbbcGeoBBField .= gbbcConstraintBox
1355+ , "_cache" .= cache
1356+ , "type" .= type']
1357+
1358+instance FromJSON GeoBoundingBoxConstraint where
1359+ parseJSON = withObject "GeoBoundingBoxConstraint" parse
1360+ where parse o = case HM.toList (deleteSeveral ["type", "_cache"] o) of
1361+ [(fn, v)] -> GeoBoundingBoxConstraint (FieldName fn)
1362+ <$> parseJSON v
1363+ <*> o .:? "_cache" .!= defaultCache
1364+ <*> o .: "type"
1365+ _ -> fail "Could not find field name for GeoBoundingBoxConstraint"
1366+
1367+data GeoPoint =
1368+ GeoPoint { geoField :: FieldName
1369+ , latLon :: LatLon} deriving (Eq, Show)
1370+
1371+instance ToJSON GeoPoint where
1372+ toJSON (GeoPoint (FieldName geoPointField) geoPointLatLon) =
1373+ object [ geoPointField .= geoPointLatLon ]
1374+
1375+data DistanceUnit = Miles
1376+ | Yards
1377+ | Feet
1378+ | Inches
1379+ | Kilometers
1380+ | Meters
1381+ | Centimeters
1382+ | Millimeters
1383+ | NauticalMiles deriving (Eq, Show)
1384+
1385+instance ToJSON DistanceUnit where
1386+ toJSON Miles = String "mi"
1387+ toJSON Yards = String "yd"
1388+ toJSON Feet = String "ft"
1389+ toJSON Inches = String "in"
1390+ toJSON Kilometers = String "km"
1391+ toJSON Meters = String "m"
1392+ toJSON Centimeters = String "cm"
1393+ toJSON Millimeters = String "mm"
1394+ toJSON NauticalMiles = String "nmi"
1395+
1396+instance FromJSON DistanceUnit where
1397+ parseJSON = withText "DistanceUnit" parse
1398+ where parse "mi" = pure Miles
1399+ parse "yd" = pure Yards
1400+ parse "ft" = pure Feet
1401+ parse "in" = pure Inches
1402+ parse "km" = pure Kilometers
1403+ parse "m" = pure Meters
1404+ parse "cm" = pure Centimeters
1405+ parse "mm" = pure Millimeters
1406+ parse "nmi" = pure NauticalMiles
1407+ parse u = fail ("Unrecognized DistanceUnit: " <> show u)
1408+
1409+data DistanceType = Arc
1410+ | SloppyArc -- doesn't exist <1.0
1411+ | Plane deriving (Eq, Show)
1412+
1413+instance ToJSON DistanceType where
1414+ toJSON Arc = String "arc"
1415+ toJSON SloppyArc = String "sloppy_arc"
1416+ toJSON Plane = String "plane"
1417+
1418+instance FromJSON DistanceType where
1419+ parseJSON = withText "DistanceType" parse
1420+ where parse "arc" = pure Arc
1421+ parse "sloppy_arc" = pure SloppyArc
1422+ parse "plane" = pure Plane
1423+ parse t = fail ("Unrecognized DistanceType: " <> show t)
1424+
1425+data OptimizeBbox = OptimizeGeoFilterType GeoFilterType
1426+ | NoOptimizeBbox deriving (Eq, Show)
1427+
1428+
1429+instance ToJSON OptimizeBbox where
1430+ toJSON NoOptimizeBbox = String "none"
1431+ toJSON (OptimizeGeoFilterType gft) = toJSON gft
1432+
1433+instance FromJSON OptimizeBbox where
1434+ parseJSON v = withText "NoOptimizeBbox" parseNoOptimize v
1435+ <|> parseOptimize v
1436+ where parseNoOptimize "none" = pure NoOptimizeBbox
1437+ parseNoOptimize _ = mzero
1438+ parseOptimize = fmap OptimizeGeoFilterType . parseJSON
1439+
1440+data Distance =
1441+ Distance { coefficient :: Double
1442+ , unit :: DistanceUnit } deriving (Eq, Show)
1443+
1444+
1445+instance ToJSON Distance where
1446+ toJSON (Distance dCoefficient dUnit) =
1447+ String boltedTogether where
1448+ coefText = showText dCoefficient
1449+ (String unitText) = toJSON dUnit
1450+ boltedTogether = mappend coefText unitText
1451+
1452+instance FromJSON Distance where
1453+ parseJSON = withText "Distance" parse
1454+ where parse t = Distance <$> parseCoeff nT
1455+ <*> parseJSON (String unitT)
1456+ where (nT, unitT) = T.span validForNumber t
1457+ -- may be a better way to do this
1458+ validForNumber '-' = True
1459+ validForNumber '.' = True
1460+ validForNumber 'e' = True
1461+ validForNumber c = isNumber c
1462+ parseCoeff "" = fail "Empty string cannot be parsed as number"
1463+ parseCoeff s = return (read (T.unpack s))
1464+
1465+data DistanceRange =
1466+ DistanceRange { distanceFrom :: Distance
1467+ , distanceTo :: Distance } deriving (Eq, Show)
1468+
1469+type TemplateQueryKey = Text
1470+type TemplateQueryValue = Text
1471+
1472+newtype TemplateQueryKeyValuePairs =
1473+ TemplateQueryKeyValuePairs (HM.HashMap TemplateQueryKey TemplateQueryValue)
1474+ deriving (Eq, Show)
1475+
1476+instance ToJSON TemplateQueryKeyValuePairs where
1477+ toJSON (TemplateQueryKeyValuePairs x) =
1478+ Object $ HM.map toJSON x
1479+
1480+instance FromJSON TemplateQueryKeyValuePairs where
1481+ parseJSON (Object o) =
1482+ pure . TemplateQueryKeyValuePairs $ HM.mapMaybe getValue o
1483+ where getValue (String x) = Just x
1484+ getValue _ = Nothing
1485+ parseJSON _ =
1486+ fail "error parsing TemplateQueryKeyValuePairs"
1487+
1488+data TemplateQueryInline =
1489+ TemplateQueryInline { inline :: Query
1490+ , params :: TemplateQueryKeyValuePairs
1491+ }
1492+ deriving (Eq, Show)
1493+
1494+instance ToJSON TemplateQueryInline where
1495+ toJSON TemplateQueryInline{..} = object [ "inline" .= inline
1496+ , "params" .= params
1497+ ]
1498+
1499+instance FromJSON TemplateQueryInline where
1500+ parseJSON = withObject "TemplateQueryInline" parse
1501+ where parse o = TemplateQueryInline
1502+ <$> o .: "inline"
1503+ <*> o .: "params"
1504+
1505+{-| 'BooleanOperator' is the usual And/Or operators with an ES compatible
1506+ JSON encoding baked in. Used all over the place.
1507+-}
1508+data BooleanOperator = And | Or deriving (Eq, Show)
1509+
1510+instance ToJSON BooleanOperator where
1511+ toJSON And = String "and"
1512+ toJSON Or = String "or"
1513+
1514+instance FromJSON BooleanOperator where
1515+ parseJSON = withText "BooleanOperator" parse
1516+ where parse "and" = pure And
1517+ parse "or" = pure Or
1518+ parse o = fail ("Unexpected BooleanOperator: " <> show o)
1519+
1520+{-| 'Cache' is for telling ES whether it should cache a 'Filter' not.
1521+ 'Query's cannot be cached.
1522+-}
1523+type Cache = Bool -- caching on/off
1524+defaultCache :: Cache
1525+defaultCache = False
1526+
1527+
1528+data FunctionScoreQuery =
1529+ FunctionScoreQuery { functionScoreQuery :: Maybe Query
1530+ , functionScoreBoost :: Maybe Boost
1531+ , functionScoreFunctions :: FunctionScoreFunctions
1532+ , functionScoreMaxBoost :: Maybe Boost
1533+ , functionScoreBoostMode :: Maybe BoostMode
1534+ , functionScoreMinScore :: Score
1535+ , functionScoreScoreMode :: Maybe ScoreMode
1536+ } deriving (Eq, Show)
1537+
1538+instance ToJSON FunctionScoreQuery where
1539+ toJSON (FunctionScoreQuery query boost fns maxBoost boostMode minScore scoreMode) =
1540+ omitNulls base
1541+ where base = functionScoreFunctionsPair fns :
1542+ [ "query" .= query
1543+ , "boost" .= boost
1544+ , "max_boost" .= maxBoost
1545+ , "boost_mode" .= boostMode
1546+ , "min_score" .= minScore
1547+ , "score_mode" .= scoreMode ]
1548+
1549+instance FromJSON FunctionScoreQuery where
1550+ parseJSON = withObject "FunctionScoreQuery" parse
1551+ where parse o = FunctionScoreQuery
1552+ <$> o .:? "query"
1553+ <*> o .:? "boost"
1554+ <*> (singleFunction o
1555+ <|> multipleFunctions `taggedWith` "functions")
1556+ <*> o .:? "max_boost"
1557+ <*> o .:? "boost_mode"
1558+ <*> o .:? "min_score"
1559+ <*> o .:? "score_mode"
1560+ where taggedWith parser k = parser =<< o .: k
1561+ singleFunction = fmap FunctionScoreSingle . parseFunctionScoreFunction
1562+ multipleFunctions = pure . FunctionScoreMultiple
1563+
1564+data FunctionScoreFunctions =
1565+ FunctionScoreSingle FunctionScoreFunction
1566+ | FunctionScoreMultiple (NonEmpty ComponentFunctionScoreFunction) deriving (Eq, Show)
1567+
1568+data ComponentFunctionScoreFunction =
1569+ ComponentFunctionScoreFunction { componentScoreFunctionFilter :: Maybe Filter
1570+ , componentScoreFunction :: FunctionScoreFunction
1571+ , componentScoreFunctionWeight :: Maybe Weight
1572+ } deriving (Eq, Show)
1573+
1574+instance ToJSON ComponentFunctionScoreFunction where
1575+ toJSON (ComponentFunctionScoreFunction filter' fn weight) =
1576+ omitNulls base
1577+ where base = functionScoreFunctionPair fn :
1578+ [ "filter" .= filter'
1579+ , "weight" .= weight ]
1580+
1581+instance FromJSON ComponentFunctionScoreFunction where
1582+ parseJSON = withObject "ComponentFunctionScoreFunction" parse
1583+ where parse o = ComponentFunctionScoreFunction
1584+ <$> o .:? "filter"
1585+ <*> parseFunctionScoreFunction o
1586+ <*> o .:? "weight"
1587+
1588+functionScoreFunctionsPair :: FunctionScoreFunctions -> (Text, Value)
1589+functionScoreFunctionsPair (FunctionScoreSingle fn)
1590+ = functionScoreFunctionPair fn
1591+functionScoreFunctionsPair (FunctionScoreMultiple componentFns) =
1592+ ("functions", toJSON componentFns)
1593+
1594+fieldTagged :: Monad m => (FieldName -> Object -> m a) -> Object -> m a
1595+fieldTagged f o = case HM.toList o of
1596+ [(k, Object o')] -> f (FieldName k) o'
1597+ _ -> fail "Expected object with 1 field-named key"
src/Database/V5/Bloodhound/Internal/Sort.hsnew file+107
@@ -0,0 +1,107 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+module Database.V5.Bloodhound.Internal.Sort where
6+
7+import Bloodhound.Import
8+
9+import Database.V5.Bloodhound.Internal.Newtypes
10+import Database.V5.Bloodhound.Internal.Query
11+
12+{-| 'SortMode' prescribes how to handle sorting array/multi-valued fields.
13+
14+http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_sort_mode_option
15+-}
16+data SortMode = SortMin
17+ | SortMax
18+ | SortSum
19+ | SortAvg deriving (Eq, Show)
20+
21+instance ToJSON SortMode where
22+ toJSON SortMin = String "min"
23+ toJSON SortMax = String "max"
24+ toJSON SortSum = String "sum"
25+ toJSON SortAvg = String "avg"
26+
27+{-| 'mkSort' defaults everything but the 'FieldName' and the 'SortOrder' so
28+ that you can concisely describe the usual kind of 'SortSpec's you want.
29+-}
30+mkSort :: FieldName -> SortOrder -> DefaultSort
31+mkSort fieldName sOrder = DefaultSort fieldName sOrder Nothing Nothing Nothing Nothing
32+
33+{-| 'Sort' is a synonym for a list of 'SortSpec's. Sort behavior is order
34+ dependent with later sorts acting as tie-breakers for earlier sorts.
35+-}
36+type Sort = [SortSpec]
37+
38+{-| The two main kinds of 'SortSpec' are 'DefaultSortSpec' and
39+ 'GeoDistanceSortSpec'. The latter takes a 'SortOrder', 'GeoPoint', and
40+ 'DistanceUnit' to express "nearness" to a single geographical point as a
41+ sort specification.
42+
43+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
44+-}
45+data SortSpec =
46+ DefaultSortSpec DefaultSort
47+ | GeoDistanceSortSpec SortOrder GeoPoint DistanceUnit
48+ deriving (Eq, Show)
49+
50+instance ToJSON SortSpec where
51+ toJSON (DefaultSortSpec
52+ (DefaultSort (FieldName dsSortFieldName) dsSortOrder dsIgnoreUnmapped
53+ dsSortMode dsMissingSort dsNestedFilter)) =
54+ object [dsSortFieldName .= omitNulls base] where
55+ base = [ "order" .= dsSortOrder
56+ , "unmapped_type" .= dsIgnoreUnmapped
57+ , "mode" .= dsSortMode
58+ , "missing" .= dsMissingSort
59+ , "nested_filter" .= dsNestedFilter ]
60+
61+ toJSON (GeoDistanceSortSpec gdsSortOrder (GeoPoint (FieldName field) gdsLatLon) units) =
62+ object [ "unit" .= units
63+ , field .= gdsLatLon
64+ , "order" .= gdsSortOrder ]
65+
66+{-| 'DefaultSort' is usually the kind of 'SortSpec' you'll want. There's a
67+ 'mkSort' convenience function for when you want to specify only the most
68+ common parameters.
69+
70+ The `ignoreUnmapped`, when `Just` field is used to set the elastic 'unmapped_type'
71+
72+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
73+-}
74+data DefaultSort =
75+ DefaultSort { sortFieldName :: FieldName
76+ , sortOrder :: SortOrder
77+ -- default False
78+ , ignoreUnmapped :: Maybe Text
79+ , sortMode :: Maybe SortMode
80+ , missingSort :: Maybe Missing
81+ , nestedFilter :: Maybe Filter } deriving (Eq, Show)
82+
83+{-| 'SortOrder' is 'Ascending' or 'Descending', as you might expect. These get
84+ encoded into "asc" or "desc" when turned into JSON.
85+
86+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#search-request-sort>
87+-}
88+data SortOrder = Ascending
89+ | Descending deriving (Eq, Show)
90+
91+instance ToJSON SortOrder where
92+ toJSON Ascending = String "asc"
93+ toJSON Descending = String "desc"
94+
95+{-| 'Missing' prescribes how to handle missing fields. A missing field can be
96+ sorted last, first, or using a custom value as a substitute.
97+
98+<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_missing_values>
99+-}
100+data Missing = LastMissing
101+ | FirstMissing
102+ | CustomMissing Text deriving (Eq, Show)
103+
104+instance ToJSON Missing where
105+ toJSON LastMissing = String "_last"
106+ toJSON FirstMissing = String "_first"
107+ toJSON (CustomMissing txt) = String txt
src/Database/V5/Bloodhound/Internal/StringlyTyped.hsnew file+51
@@ -0,0 +1,51 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Database.V5.Bloodhound.Internal.StringlyTyped where
4+
5+import Bloodhound.Import
6+
7+import qualified Data.Text as T
8+
9+
10+-- This whole module is a sin bucket to deal with Elasticsearch badness.
11+newtype StringlyTypedDouble = StringlyTypedDouble
12+ { unStringlyTypedDouble :: Double }
13+
14+instance FromJSON StringlyTypedDouble where
15+ parseJSON =
16+ fmap StringlyTypedDouble
17+ . parseJSON
18+ . unStringlyTypeJSON
19+
20+newtype StringlyTypedInt = StringlyTypedInt
21+ { unStringlyTypedInt :: Int }
22+
23+instance FromJSON StringlyTypedInt where
24+ parseJSON =
25+ fmap StringlyTypedInt
26+ . parseJSON
27+ . unStringlyTypeJSON
28+
29+newtype StringlyTypedBool = StringlyTypedBool { unStringlyTypedBool :: Bool }
30+
31+instance FromJSON StringlyTypedBool where
32+ parseJSON =
33+ fmap StringlyTypedBool
34+ . parseJSON
35+ . unStringlyTypeJSON
36+
37+-- | For some reason in several settings APIs, all leaf values get returned
38+-- as strings. This function attempts to recover from this for all
39+-- non-recursive JSON types. If nothing can be done, the value is left alone.
40+unStringlyTypeJSON :: Value -> Value
41+unStringlyTypeJSON (String "true") =
42+ Bool True
43+unStringlyTypeJSON (String "false") =
44+ Bool False
45+unStringlyTypeJSON (String "null") =
46+ Null
47+unStringlyTypeJSON v@(String t) =
48+ case readMay (T.unpack t) of
49+ Just n -> Number n
50+ Nothing -> v
51+unStringlyTypeJSON v = v
src/Database/V5/Bloodhound/Internal/Suggest.hsnew file+265
@@ -0,0 +1,265 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE RecordWildCards #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+module Database.V5.Bloodhound.Internal.Suggest where
6+
7+import Bloodhound.Import
8+
9+import qualified Data.HashMap.Strict as HM
10+
11+import Database.V5.Bloodhound.Internal.Newtypes
12+import Database.V5.Bloodhound.Internal.Query (TemplateQueryInline(..), params)
13+
14+data Suggest = Suggest
15+ { suggestText :: Text
16+ , suggestName :: Text
17+ , suggestType :: SuggestType
18+ } deriving (Eq, Show)
19+
20+instance ToJSON Suggest where
21+ toJSON Suggest{..} =
22+ object [ "text" .= suggestText
23+ , suggestName .= suggestType
24+ ]
25+
26+instance FromJSON Suggest where
27+ parseJSON (Object o) = do
28+ suggestText' <- o .: "text"
29+ let dropTextList =
30+ HM.toList
31+ $ HM.filterWithKey (\x _ -> x /= "text") o
32+ suggestName' <-
33+ case dropTextList of
34+ [(x, _)] -> return x
35+ _ -> fail "error parsing Suggest field name"
36+ suggestType' <- o .: suggestName'
37+ return $ Suggest suggestText' suggestName' suggestType'
38+ parseJSON x = typeMismatch "Suggest" x
39+
40+data SuggestType =
41+ SuggestTypePhraseSuggester PhraseSuggester
42+ deriving (Eq, Show)
43+
44+instance ToJSON SuggestType where
45+ toJSON (SuggestTypePhraseSuggester x) =
46+ object [ "phrase" .= x ]
47+
48+instance FromJSON SuggestType where
49+ parseJSON = withObject "SuggestType" parse
50+ where parse o = phraseSuggester `taggedWith` "phrase"
51+ where taggedWith parser k = parser =<< o .: k
52+ phraseSuggester = pure . SuggestTypePhraseSuggester
53+
54+data PhraseSuggester = PhraseSuggester
55+ { phraseSuggesterField :: FieldName
56+ , phraseSuggesterGramSize :: Maybe Int
57+ , phraseSuggesterRealWordErrorLikelihood :: Maybe Int
58+ , phraseSuggesterConfidence :: Maybe Int
59+ , phraseSuggesterMaxErrors :: Maybe Int
60+ , phraseSuggesterSeparator :: Maybe Text
61+ , phraseSuggesterSize :: Maybe Size
62+ , phraseSuggesterAnalyzer :: Maybe Analyzer
63+ , phraseSuggesterShardSize :: Maybe Int
64+ , phraseSuggesterHighlight :: Maybe PhraseSuggesterHighlighter
65+ , phraseSuggesterCollate :: Maybe PhraseSuggesterCollate
66+ , phraseSuggesterCandidateGenerators :: [DirectGenerators]
67+ } deriving (Eq, Show)
68+
69+instance ToJSON PhraseSuggester where
70+ toJSON PhraseSuggester{..} =
71+ omitNulls [ "field" .= phraseSuggesterField
72+ , "gram_size" .= phraseSuggesterGramSize
73+ , "real_word_error_likelihood" .=
74+ phraseSuggesterRealWordErrorLikelihood
75+ , "confidence" .= phraseSuggesterConfidence
76+ , "max_errors" .= phraseSuggesterMaxErrors
77+ , "separator" .= phraseSuggesterSeparator
78+ , "size" .= phraseSuggesterSize
79+ , "analyzer" .= phraseSuggesterAnalyzer
80+ , "shard_size" .= phraseSuggesterShardSize
81+ , "highlight" .= phraseSuggesterHighlight
82+ , "collate" .= phraseSuggesterCollate
83+ , "direct_generator" .=
84+ phraseSuggesterCandidateGenerators
85+ ]
86+
87+instance FromJSON PhraseSuggester where
88+ parseJSON = withObject "PhraseSuggester" parse
89+ where parse o = PhraseSuggester
90+ <$> o .: "field"
91+ <*> o .:? "gram_size"
92+ <*> o .:? "real_word_error_likelihood"
93+ <*> o .:? "confidence"
94+ <*> o .:? "max_errors"
95+ <*> o .:? "separator"
96+ <*> o .:? "size"
97+ <*> o .:? "analyzer"
98+ <*> o .:? "shard_size"
99+ <*> o .:? "highlight"
100+ <*> o .:? "collate"
101+ <*> o .:? "direct_generator" .!= []
102+
103+mkPhraseSuggester :: FieldName -> PhraseSuggester
104+mkPhraseSuggester fName =
105+ PhraseSuggester fName Nothing Nothing Nothing Nothing Nothing Nothing
106+ Nothing Nothing Nothing Nothing []
107+
108+data PhraseSuggesterHighlighter =
109+ PhraseSuggesterHighlighter { phraseSuggesterHighlighterPreTag :: Text
110+ , phraseSuggesterHighlighterPostTag :: Text
111+ }
112+ deriving (Eq, Show)
113+
114+instance ToJSON PhraseSuggesterHighlighter where
115+ toJSON PhraseSuggesterHighlighter{..} =
116+ object [ "pre_tag" .= phraseSuggesterHighlighterPreTag
117+ , "post_tag" .= phraseSuggesterHighlighterPostTag
118+ ]
119+
120+instance FromJSON PhraseSuggesterHighlighter where
121+ parseJSON = withObject "PhraseSuggesterHighlighter" parse
122+ where parse o = PhraseSuggesterHighlighter
123+ <$> o .: "pre_tag"
124+ <*> o .: "post_tag"
125+
126+data PhraseSuggesterCollate = PhraseSuggesterCollate
127+ { phraseSuggesterCollateTemplateQuery :: TemplateQueryInline
128+ , phraseSuggesterCollatePrune :: Bool
129+ } deriving (Eq, Show)
130+
131+instance ToJSON PhraseSuggesterCollate where
132+ toJSON PhraseSuggesterCollate{..} =
133+ object [ "query" .= object
134+ [ "inline" .= (inline phraseSuggesterCollateTemplateQuery)
135+ ]
136+ , "params" .= (params phraseSuggesterCollateTemplateQuery)
137+ , "prune" .= phraseSuggesterCollatePrune
138+ ]
139+
140+instance FromJSON PhraseSuggesterCollate where
141+ parseJSON (Object o) = do
142+ query' <- o .: "query"
143+ inline' <- query' .: "inline"
144+ params' <- o .: "params"
145+ prune' <- o .:? "prune" .!= False
146+ return $ PhraseSuggesterCollate
147+ (TemplateQueryInline inline' params') prune'
148+ parseJSON x = typeMismatch "PhraseSuggesterCollate" x
149+
150+data SuggestOptions =
151+ SuggestOptions { suggestOptionsText :: Text
152+ , suggestOptionsScore :: Double
153+ , suggestOptionsFreq :: Maybe Int
154+ , suggestOptionsHighlighted :: Maybe Text
155+ }
156+ deriving (Eq, Read, Show)
157+
158+instance FromJSON SuggestOptions where
159+ parseJSON = withObject "SuggestOptions" parse
160+ where
161+ parse o = SuggestOptions
162+ <$> o .: "text"
163+ <*> o .: "score"
164+ <*> o .:? "freq"
165+ <*> o .:? "highlighted"
166+
167+data SuggestResponse =
168+ SuggestResponse { suggestResponseText :: Text
169+ , suggestResponseOffset :: Int
170+ , suggestResponseLength :: Int
171+ , suggestResponseOptions :: [SuggestOptions]
172+ }
173+ deriving (Eq, Read, Show)
174+
175+instance FromJSON SuggestResponse where
176+ parseJSON = withObject "SuggestResponse" parse
177+ where parse o = SuggestResponse
178+ <$> o .: "text"
179+ <*> o .: "offset"
180+ <*> o .: "length"
181+ <*> o .: "options"
182+
183+data NamedSuggestionResponse = NamedSuggestionResponse
184+ { nsrName :: Text
185+ , nsrResponses :: [SuggestResponse]
186+ } deriving (Eq, Read, Show)
187+
188+instance FromJSON NamedSuggestionResponse where
189+ parseJSON (Object o) = do
190+ suggestionName' <- case HM.toList o of
191+ [(x, _)] -> return x
192+ _ -> fail "error parsing NamedSuggestionResponse name"
193+ suggestionResponses' <- o .: suggestionName'
194+ return $ NamedSuggestionResponse suggestionName' suggestionResponses'
195+
196+ parseJSON x = typeMismatch "NamedSuggestionResponse" x
197+
198+data DirectGeneratorSuggestModeTypes = DirectGeneratorSuggestModeMissing
199+ | DirectGeneratorSuggestModePopular
200+ | DirectGeneratorSuggestModeAlways
201+ deriving (Eq, Show)
202+
203+instance ToJSON DirectGeneratorSuggestModeTypes where
204+ toJSON DirectGeneratorSuggestModeMissing = "missing"
205+ toJSON DirectGeneratorSuggestModePopular = "popular"
206+ toJSON DirectGeneratorSuggestModeAlways = "always"
207+
208+instance FromJSON DirectGeneratorSuggestModeTypes where
209+ parseJSON = withText "DirectGeneratorSuggestModeTypes" parse
210+ where
211+ parse "missing" =
212+ pure DirectGeneratorSuggestModeMissing
213+ parse "popular" =
214+ pure DirectGeneratorSuggestModePopular
215+ parse "always" =
216+ pure DirectGeneratorSuggestModeAlways
217+ parse f =
218+ fail ("Unexpected DirectGeneratorSuggestModeTypes: " <> show f)
219+
220+data DirectGenerators = DirectGenerators
221+ { directGeneratorsField :: FieldName
222+ , directGeneratorsSize :: Maybe Int
223+ , directGeneratorSuggestMode :: DirectGeneratorSuggestModeTypes
224+ , directGeneratorMaxEdits :: Maybe Double
225+ , directGeneratorPrefixLength :: Maybe Int
226+ , directGeneratorMinWordLength :: Maybe Int
227+ , directGeneratorMaxInspections :: Maybe Int
228+ , directGeneratorMinDocFreq :: Maybe Double
229+ , directGeneratorMaxTermFreq :: Maybe Double
230+ , directGeneratorPreFilter :: Maybe Text
231+ , directGeneratorPostFilter :: Maybe Text
232+ } deriving (Eq, Show)
233+
234+instance ToJSON DirectGenerators where
235+ toJSON DirectGenerators{..} =
236+ omitNulls [ "field" .= directGeneratorsField
237+ , "size" .= directGeneratorsSize
238+ , "suggest_mode" .= directGeneratorSuggestMode
239+ , "max_edits" .= directGeneratorMaxEdits
240+ , "prefix_length" .= directGeneratorPrefixLength
241+ , "min_word_length" .= directGeneratorMinWordLength
242+ , "max_inspections" .= directGeneratorMaxInspections
243+ , "min_doc_freq" .= directGeneratorMinDocFreq
244+ , "max_term_freq" .= directGeneratorMaxTermFreq
245+ , "pre_filter" .= directGeneratorPreFilter
246+ , "post_filter" .= directGeneratorPostFilter
247+ ]
248+
249+instance FromJSON DirectGenerators where
250+ parseJSON = withObject "DirectGenerators" parse
251+ where parse o = DirectGenerators
252+ <$> o .: "field"
253+ <*> o .:? "size"
254+ <*> o .: "suggest_mode"
255+ <*> o .:? "max_edits"
256+ <*> o .:? "prefix_length"
257+ <*> o .:? "min_word_length"
258+ <*> o .:? "max_inspections"
259+ <*> o .:? "min_doc_freq"
260+ <*> o .:? "max_term_freq"
261+ <*> o .:? "pre_filter"
262+ <*> o .:? "post_filter"
263+
264+mkDirectGenerators :: FieldName -> DirectGenerators
265+mkDirectGenerators fn = DirectGenerators fn Nothing DirectGeneratorSuggestModeMissing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
src/Database/V5/Bloodhound/Types.hs+95−5272

This diff is too big to show here.

src/Database/V5/Bloodhound/Types/Internal.hsdeleted−57

This diff is too big to show here.

stack-7.10.yaml+3

This diff is too big to show here.

stack-7.8.yaml+3

This diff is too big to show here.

stack-8.0.yaml+1

This diff is too big to show here.

stack-8.2.yaml+1

This diff is too big to show here.

tests/V1/Test/Aggregation.hsnew file+132

This diff is too big to show here.

tests/V1/Test/ApproxEq.hsnew file+63

This diff is too big to show here.

tests/V1/Test/BulkAPI.hsnew file+77

This diff is too big to show here.

tests/V1/Test/Common.hsnew file+289

This diff is too big to show here.

tests/V1/Test/Documents.hsnew file+52

This diff is too big to show here.

tests/V1/Test/Generators.hsnew file+432

This diff is too big to show here.

tests/V1/Test/Highlights.hsnew file+32

This diff is too big to show here.

tests/V1/Test/Import.hsnew file+58

This diff is too big to show here.

tests/V1/Test/Indices.hsnew file+78

This diff is too big to show here.

tests/V1/Test/JSON.hsnew file+202

This diff is too big to show here.

tests/V1/Test/Query.hsnew file+115

This diff is too big to show here.

tests/V1/Test/Snapshots.hsnew file+201

This diff is too big to show here.

tests/V1/Test/Sorting.hsnew file+22

This diff is too big to show here.

tests/V1/Test/SourceFiltering.hsnew file+38

This diff is too big to show here.

tests/V1/Test/Suggest.hsnew file+22

This diff is too big to show here.

tests/V1/Test/Templates.hsnew file+26

This diff is too big to show here.

tests/V1/tests.hs+37−1886

This diff is too big to show here.

tests/V5/Test/Aggregation.hsnew file+147

This diff is too big to show here.

tests/V5/Test/ApproxEq.hsnew file+63

This diff is too big to show here.

tests/V5/Test/BulkAPI.hsnew file+95

This diff is too big to show here.

tests/V5/Test/Common.hsnew file+292

This diff is too big to show here.

tests/V5/Test/Documents.hsnew file+51

This diff is too big to show here.

tests/V5/Test/Generators.hsnew file+536

This diff is too big to show here.

tests/V5/Test/Highlights.hsnew file+32

This diff is too big to show here.

tests/V5/Test/Import.hsnew file+58

This diff is too big to show here.

tests/V5/Test/Indices.hsnew file+152

This diff is too big to show here.

tests/V5/Test/JSON.hsnew file+213

This diff is too big to show here.

tests/V5/Test/Query.hsnew file+115

This diff is too big to show here.

tests/V5/Test/Script.hsnew file+36

This diff is too big to show here.

tests/V5/Test/Snapshots.hsnew file+201

This diff is too big to show here.

tests/V5/Test/Sorting.hsnew file+22

This diff is too big to show here.

tests/V5/Test/SourceFiltering.hsnew file+38

This diff is too big to show here.

tests/V5/Test/Suggest.hsnew file+23

This diff is too big to show here.

tests/V5/Test/Templates.hsnew file+26

This diff is too big to show here.

tests/V5/tests.hs+41−1773

This diff is too big to show here.