bloodhound-1.0.0.0: src/Database/Bloodhound/ElasticSearch7/Client.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-deprecations #-}
-- |
-- Module : Database.Bloodhound.ElasticSearch7.Client
-- Description : Elasticsearch 7 client (re-exports Common + ES7-specific APIs)
--
-- The Elasticsearch 7 client. Re-exports the version-agnostic "Database.Bloodhound.Common.Client"
-- surface and adds ES7-specific features: point in time (PIT), data streams,
-- EQL search, and the terms enum API.
module Database.Bloodhound.ElasticSearch7.Client
( module Reexport,
pitSearch,
pitSearchWith,
openPointInTime,
openPointInTimeWith,
openPointInTimeWithBody,
closePointInTime,
getDataStreams,
getDataStreamStats,
createDataStream,
dataStreamExists,
deleteDataStream,
migrateToDataStream,
promoteDataStream,
modifyDataStream,
eqlSearch,
eqlSearchWith,
getEQLSearch,
getEQLStatus,
deleteEQLSearch,
getTermsEnum,
getTermsEnumWith,
TermValue (..),
TermsEnumRequest (..),
defaultTermsEnumRequest,
TermsEnumOptions (..),
defaultTermsEnumOptions,
termsEnumOptionsParams,
TermsEnumResponse (..),
SyncedFlushResult (..),
SyncedFlushShardGroup (..),
SyncedFlushFailure (..),
SyncedFlushOptions (..),
defaultSyncedFlushOptions,
syncedFlushOptionsParams,
syncedFlushIndex,
syncedFlushIndexWith,
freezeIndex,
unfreezeIndex,
)
where
import Control.Monad
import Data.Aeson
import Data.Monoid
import Data.Text (Text)
import Database.Bloodhound.Client.Cluster
import Database.Bloodhound.Common.Client as Reexport
import Database.Bloodhound.ElasticSearch7.Requests qualified as Requests
import Database.Bloodhound.ElasticSearch7.Types
import Prelude hiding (filter, head)
-- | 'pitSearch' uses the point in time (PIT) API of elastic, for a given
-- 'IndexName'. Requires Elasticsearch >=7.10. The supplied 'KeepAlive'
-- duration is forwarded to every PIT open\/extend call. Note that this will
-- consume the entire search result set and will be doing O(n) list appends so
-- this may not be suitable for large result sets. In that case, the point in
-- time API should be used directly with `openPointInTime` and
-- `closePointInTime`.
--
-- This is a convenience wrapper around 'pitSearchWith' that lifts the
-- single 'IndexName' into an 'IndexPattern'. To search across multiple
-- comma-joined indices or a wildcard pattern, use 'pitSearchWith'
-- directly with an 'IndexPattern'.
--
-- Note that 'pitSearch' utilizes the 'search_after' parameter under the hood,
-- which requires a non-empty 'sortBody' field in the provided 'Search' value.
-- Otherwise, 'pitSearch' will fail to return all matching documents.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
pitSearch ::
forall a m.
(FromJSON a, MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
KeepAlive ->
Search ->
m [Hit a]
pitSearch indexName = pitSearchWith (IndexPattern (unIndexName indexName))
-- | 'pitSearchWith' is the parameterized variant of 'pitSearch': it
-- takes an 'IndexPattern' as the target, which may be a single index
-- name, a comma-separated list of indices, or a wildcard pattern
-- (e.g. @\"logs-*\"@, @\"logs-2024,logs-2025\"@). Otherwise behaves
-- exactly like 'pitSearch'. See 'openPointInTimeWith' for the target
-- rendering.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
pitSearchWith ::
forall a m.
(FromJSON a, MonadBH m, WithBackend ElasticSearch7 m) =>
IndexPattern ->
KeepAlive ->
Search ->
m [Hit a]
pitSearchWith indexPattern keepAlive search = do
openResp <- openPointInTimeWith indexPattern keepAlive defaultPITOptions
case openResp of
Left e -> throwEsError e
Right OpenPointInTimeResponse {..} -> do
let searchPIT = search {pointInTime = Just (PointInTime oPitId (Just keepAlive))}
hits <- pitAccumulator searchPIT []
closeResp <- closePointInTime (ClosePointInTime oPitId)
case closeResp of
Left _ -> return []
Right (ClosePointInTimeResponse False _) ->
error "failed to close point in time (PIT)"
Right (ClosePointInTimeResponse True _) -> return hits
where
pitAccumulator :: Search -> [Hit a] -> m [Hit a]
pitAccumulator search' oldHits = do
resp <- tryPerformBHRequest $ Requests.searchAll search'
case resp of
Left _ -> return []
Right searchResult -> case hits (searchHits searchResult) of
[] -> return oldHits
newHits -> case (hitSort $ last newHits, pitId searchResult) of
(Nothing, Nothing) ->
error "no point in time (PIT) ID or last sort value"
(Just _, Nothing) -> error "no point in time (PIT) ID"
(Nothing, _) -> return (oldHits <> newHits)
(Just lastSort, Just pitId') -> do
let newSearch =
search'
{ pointInTime = Just (PointInTime pitId' (Just keepAlive)),
searchAfterKey = Just lastSort
}
pitAccumulator newSearch (oldHits <> newHits)
-- | 'openPointInTime' opens a point in time for an index given an 'IndexName'
-- and a 'KeepAlive' duration (e.g. @keepAliveMinutes 1@). Note that the point
-- in time should be closed with 'closePointInTime' as soon as it is no longer
-- needed.
--
-- Equivalent to 'openPointInTimeWith' with
-- 'Database.Bloodhound.Internal.Versions.Common.Types.PointInTime.defaultPITOptions'
-- (no optional URI parameters).
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
openPointInTime ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
KeepAlive ->
m (ParsedEsResponse OpenPointInTimeResponse)
openPointInTime indexName keepAlive = performBHRequest $ Requests.openPointInTime indexName keepAlive
-- | 'openPointInTimeWith' is the parameterized variant of
-- 'openPointInTime': it takes an 'IndexPattern' target (single name,
-- comma-list, or wildcard) and forwards the supplied 'PITOptions' as
-- query-string parameters in addition to the mandatory @keep_alive@.
-- See 'Database.Bloodhound.ElasticSearch7.Requests.openPointInTimeWith'
-- for which parameters are emitted.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
openPointInTimeWith ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexPattern ->
KeepAlive ->
PITOptions ->
m (ParsedEsResponse OpenPointInTimeResponse)
openPointInTimeWith indexPattern keepAlive opts =
performBHRequest $ Requests.openPointInTimeWith indexPattern keepAlive opts
-- | 'openPointInTimeWithBody' is the body-carrying variant of
-- 'openPointInTimeWith': it sends the supplied 'OpenPointInTimeBody' as
-- the JSON request body (the only way to supply an @index_filter@
-- query), in addition to the 'PITOptions' URI parameters and the
-- mandatory @keep_alive@. The 'IndexPattern' target follows the same
-- rules as 'openPointInTimeWith'. See
-- 'Database.Bloodhound.ElasticSearch7.Requests.openPointInTimeWithBody'.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
openPointInTimeWithBody ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexPattern ->
KeepAlive ->
OpenPointInTimeBody ->
PITOptions ->
m (ParsedEsResponse OpenPointInTimeResponse)
openPointInTimeWithBody indexPattern keepAlive body opts =
performBHRequest $ Requests.openPointInTimeWithBody indexPattern keepAlive body opts
-- | 'closePointInTime' closes a point in time given a 'ClosePointInTime'.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html>.
closePointInTime ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
ClosePointInTime ->
m (ParsedEsResponse ClosePointInTimeResponse)
closePointInTime q = performBHRequest $ Requests.closePointInTime q
-- | 'getDataStreams' lists data streams. The wire format is identical
-- across ES 7.x, 8.x and 9.x, so this implementation is shared via the
-- ES8 and ES9 client modules.
--
-- * @'Nothing'@ or @'Just' []@ → @GET /_data_stream@ returns every data
-- stream on the cluster.
-- * @'Just' names@ → @GET /_data_stream\/{a,b,c}@ returns
-- only the named data streams.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#get-data-streams>.
getDataStreams ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
Maybe [DataStreamName] ->
m [DataStreamInfo]
getDataStreams = performBHRequest . Requests.getDataStreams
-- | 'getDataStreamStats' returns statistics about data streams
-- (document counts, store sizes, shard counts, etc.). The wire format
-- is identical across ES 7.x, 8.x and 9.x, so this implementation is
-- shared via the ES8 and ES9 client modules.
--
-- * @'Nothing'@ or @'Just' []@ → @GET /_data_stream/_stats@ returns
-- statistics for every data stream on the cluster.
-- * @'Just' names@ → @GET /_data_stream\/{a,b,c}/_stats@
-- returns statistics for only the named data streams.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#get-data-stream-stats>.
getDataStreamStats ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
Maybe [DataStreamName] ->
m DataStreamStats
getDataStreamStats = performBHRequest . Requests.getDataStreamStats
-- | 'createDataStream' creates a data stream with the given name. The
-- stream's configuration is derived from the matching index template
-- (which must already exist and declare a @data_stream@ object). The
-- wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#create-data-stream>.
createDataStream ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
DataStreamName ->
m Acknowledged
createDataStream = performBHRequest . Requests.createDataStream
-- | 'dataStreamExists' checks whether a data stream with the given
-- name exists. Returns 'True' if the stream exists, 'False'
-- otherwise (any non-2xx status, including @404@, decodes to
-- 'False'; no 'EsError' is raised). The wire format is identical
-- across ES 7.x, 8.x and 9.x, so this implementation is shared via
-- the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html>.
dataStreamExists ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
DataStreamName ->
m Bool
dataStreamExists = performBHRequest . Requests.dataStreamExists
-- | 'deleteDataStream' deletes a data stream and its backing indices.
-- Deleting a non-existent stream surfaces as an 'EsError'. The wire
-- format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#delete-data-stream>.
deleteDataStream ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
DataStreamName ->
m Acknowledged
deleteDataStream = performBHRequest . Requests.deleteDataStream
-- | 'migrateToDataStream' converts an existing alias (whose write index
-- is a hidden or data-stream-compatible backing index) into a data
-- stream. The alias and its write index must already be set up
-- correctly; an ineligible alias surfaces as an 'EsError'. The wire
-- format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#migrate-to-data-stream>.
migrateToDataStream ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexAliasName ->
m Acknowledged
migrateToDataStream = performBHRequest . Requests.migrateToDataStream
-- | 'promoteDataStream' promotes a frozen or cloned data stream back to a
-- regular data stream (disaster recovery). A stream that is not
-- currently frozen surfaces as an 'EsError'. The wire format is
-- identical to ES 7.x, 8.x and 9.x, so this implementation is shared via
-- the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#promote-data-stream>.
promoteDataStream ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
DataStreamName ->
m Acknowledged
promoteDataStream = performBHRequest . Requests.promoteDataStream
-- | 'modifyDataStream' changes the backing indices of one or more data
-- streams (batched add\/remove backing index). Referencing a
-- non-existent stream or an ineligible index surfaces as an 'EsError'.
-- The wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- See
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/data-stream-apis.html#modify-data-stream>.
modifyDataStream ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
ModifyDataStreamRequest ->
m Acknowledged
modifyDataStream = performBHRequest . Requests.modifyDataStream
-- | 'eqlSearch' submits an EQL (Event Query Language) search via
-- @POST /{index}/_eql/search@ (Elasticsearch >= 7.10). The result is
-- parameterized by the document-source type @a@.
--
-- The wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/eql-search-api.html>.
eqlSearch ::
(FromJSON a, MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
EQLRequest ->
m (ParsedEsResponse (EQLResult a))
eqlSearch indexName req = performBHRequest $ Requests.eqlSearch indexName req
-- | 'eqlSearchWith' is the fully-parameterised form of 'eqlSearch'. Every
-- URI parameter accepted by @/{index}/_eql/search@ is exposed via
-- 'EQLSearchOptions'; the 'EQLRequest' body carries the @query@ and every
-- other body parameter. 'defaultEQLSearchOptions' makes this
-- byte-for-byte identical to the simple form (modulo the extra body
-- fields you set).
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/eql-search-api.html>.
eqlSearchWith ::
(FromJSON a, MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
EQLSearchOptions ->
EQLRequest ->
m (ParsedEsResponse (EQLResult a))
eqlSearchWith indexName opts req =
performBHRequest $ Requests.eqlSearchWith indexName opts req
-- | 'getEQLSearch' fetches the deferred results of an async EQL search
-- via @GET /_eql/search\/{id}@ (Elasticsearch >= 7.10). The result is
-- parameterized by the document-source type @a@. The optional 'Text'
-- sets @wait_for_completion_timeout@ as a query parameter; 'Nothing'
-- returns immediately with the current state (useful for status-only
-- polling), a duration string such as @"5s"@ long-polls for completion.
--
-- For more information see
-- <https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-eql-get>.
getEQLSearch ::
(FromJSON a, MonadBH m, WithBackend ElasticSearch7 m) =>
EQLSearchId ->
Maybe Text ->
m (ParsedEsResponse (EQLResult a))
getEQLSearch sid = performBHRequest . Requests.getEQLSearch sid
-- | 'getEQLStatus' returns only the status of an async EQL search via
-- @GET /_eql/search/status\/{id}@ (Elasticsearch >= 7.10) without
-- fetching the results. The result is an 'EQLStatus' carrying
-- 'eqlStatusIsRunning', 'eqlStatusIsPartial', and (once complete)
-- 'eqlStatusCompletionStatus'.
--
-- The wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- For more information see
-- <https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-eql-get-status>.
getEQLStatus ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
EQLSearchId ->
m EQLStatus
getEQLStatus = performBHRequest . Requests.getEQLStatus
-- | 'deleteEQLSearch' deletes an async EQL search result and its
-- context by id via @DELETE /_eql/search/{id}@ (Elasticsearch >= 7.10)
-- and returns 'Acknowledged' on success.
--
-- The wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- For more information see
-- <https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-eql-delete>.
deleteEQLSearch ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
EQLSearchId ->
m Acknowledged
deleteEQLSearch = performBHRequest . Requests.deleteEQLSearch
-- | 'getTermsEnum' returns a list of terms in a field for auto-complete
-- use cases via @GET \/{index}\/_terms_enum@ (Elasticsearch >= 7.10).
-- Pass a non-empty index list to target; the optional 'Text' is the
-- @string@ prefix to match (auto-complete seed).
--
-- The wire format is identical across ES 7.x, 8.x and 9.x, so this
-- implementation is shared via the ES8 and ES9 client modules.
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-terms-enum.html>.
getTermsEnum ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
Maybe [IndexName] ->
FieldName ->
Maybe Text ->
m TermsEnumResponse
getTermsEnum mIndices field mString =
performBHRequest $ Requests.getTermsEnum mIndices field mString
-- | 'getTermsEnumWith' is the fully-parameterised form of
-- 'getTermsEnum'. Every URI parameter accepted by
-- @/{index}/_terms_enum@ is exposed via 'TermsEnumOptions'; the
-- 'TermsEnumRequest' body carries the @field@, the optional @string@
-- prefix and every other body parameter. 'defaultTermsEnumOptions'
-- makes this byte-for-byte identical to the simple form (modulo the
-- extra body fields you set).
--
-- For more information see
-- <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-terms-enum.html>.
getTermsEnumWith ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
Maybe [IndexName] ->
TermsEnumOptions ->
TermsEnumRequest ->
m TermsEnumResponse
getTermsEnumWith mIndices opts req =
performBHRequest $ Requests.getTermsEnumWith mIndices opts req
-- | 'syncedFlushIndex' performs a synced flush on an index. Maps to the
-- deprecated @POST \/{index}/_flush/synced@ endpoint (deprecated in
-- 7.6, removed in 8.0). Prefer
-- 'Database.Bloodhound.Common.Client.flushIndex' for new code.
--
-- Equivalent to
-- @'syncedFlushIndexWith' 'defaultSyncedFlushOptions'@.
--
-- @since 0.26.0.0
{-# DEPRECATED syncedFlushIndex "Synced flush was deprecated in ES 7.6 and removed in 8.0. Use flushIndex instead. See <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/indices-synced-flush-api.html>" #-}
syncedFlushIndex ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
m SyncedFlushResult
syncedFlushIndex = syncedFlushIndexWith defaultSyncedFlushOptions
-- | 'syncedFlushIndexWith' is the fully-parameterised form of
-- 'syncedFlushIndex'.
--
-- @since 0.26.0.0
{-# DEPRECATED syncedFlushIndexWith "Synced flush was deprecated in ES 7.6 and removed in 8.0. Use flushIndex instead. See <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/indices-synced-flush-api.html>" #-}
syncedFlushIndexWith ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
SyncedFlushOptions ->
IndexName ->
m SyncedFlushResult
syncedFlushIndexWith opts indexName =
performBHRequest $ Requests.syncedFlushIndexWith opts indexName
-- | 'freezeIndex' freezes an index. Maps to the deprecated
-- @POST \/{index}/_freeze@ endpoint (deprecated in 7.14, removed in
-- 8.0). Avoid this in new code.
--
-- @since 0.26.0.0
{-# DEPRECATED freezeIndex "Freeze index was deprecated in ES 7.14 and removed in 8.0. See <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/index-modules-block.html>" #-}
freezeIndex ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
m ShardsResult
freezeIndex indexName = performBHRequest $ Requests.freezeIndex indexName
-- | 'unfreezeIndex' unfreezes an index. Maps to the deprecated
-- @POST \/{index}/_unfreeze@ endpoint (deprecated in 7.14, removed in
-- 8.0). Avoid this in new code.
--
-- @since 0.26.0.0
{-# DEPRECATED unfreezeIndex "Unfreeze index was deprecated in ES 7.14 and removed in 8.0. See <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/index-modules-block.html>" #-}
unfreezeIndex ::
(MonadBH m, WithBackend ElasticSearch7 m) =>
IndexName ->
m ShardsResult
unfreezeIndex indexName = performBHRequest $ Requests.unfreezeIndex indexName