bloodhound: Elasticsearch and OpenSearch client library for Haskell

[ bsd3, database, library, search ] [ Propose Tags ] [ Report a vulnerability ]

Elasticsearch and OpenSearch client and query DSL for Haskell. Provides per-backend, type-safe modules for Elasticsearch 789 and OpenSearch 123, a shared Common layer, and runtime dynamic backend dispatch.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.0.0, 0.1.0.1, 0.1.0.2, 0.2.0.0, 0.3.0.0, 0.4.0.0, 0.4.0.1, 0.4.0.2, 0.5.0.0, 0.5.0.1, 0.6.0.0, 0.6.0.1, 0.7.0.0, 0.7.0.1, 0.8.0.0, 0.9.0.0, 0.10.0.0, 0.11.0.0, 0.11.1.0, 0.12.0.0, 0.12.1.0, 0.13.0.0, 0.14.0.0, 0.15.0.0, 0.15.0.1, 0.15.0.2, 0.16.0.0, 0.18.0.0, 0.19.0.0, 0.19.1.0, 0.20.0.0, 0.20.0.1, 0.20.0.2, 0.21.0.0, 0.22.0.0, 0.23.0.0, 0.23.0.1, 0.24.0.0, 0.25.0.0, 0.26.0.0, 1.0.0.0 (info)
Change log changelog.md
Dependencies aeson (>=2.0 && <3), base (>=4.14 && <5), blaze-builder (>=0.1 && <1), bytestring (>=0.10.0 && <1), conduit (>=1.3 && <2), containers (>=0.5.0.0 && <1), exceptions (>=0.1 && <1), hashable (>=1 && <2), http-client (>=0.4.30 && <1), http-types (>=0.8 && <1), microlens (>=0.4 && <1), mtl (>=1.0 && <3), network-uri (>=2.6 && <3), optics-core (>=0.4 && <0.5), resourcet (>=1.2 && <2), scientific (>=0.3.0.0 && <1), template-haskell (>=2.10 && <3), text (>=0.11 && <3), time (>=1.4 && <2), unordered-containers (>=0.1 && <1), vector (>=0.10.9 && <1), versions (>=5.0.2 && <7) [details]
Tested with ghc ==9.8.4, ghc ==9.10.3, ghc ==9.12.4, ghc ==9.14.1
License BSD-3-Clause
Copyright 2018 Chris Allen
Author Chris Allen
Maintainer foss@difolco.dev
Uploaded by gdifolco at 2026-06-30T19:19:52Z
Category Database, Search
Home page https://github.com/bitemyapp/bloodhound.git#readme
Bug tracker https://github.com/bitemyapp/bloodhound.git/issues
Source repo head: git clone https://github.com/bitemyapp/bloodhound.git
Distributions LTSHaskell:0.23.0.1, Stackage:1.0.0.0
Reverse Dependencies 5 direct, 3 indirect [details]
Downloads 27880 total (193 in the last 30 days)
Rating 2.0 (votes: 2) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-06-30 [all 1 reports]

Readme for bloodhound-1.0.0.0

[back to package description]

Bloodhound compatbuild Haskell nix ormolu Hackage

Bloodhound (dog)

Elasticsearch and OpenSearch client and query DSL for Haskell

Why?

Search doesn't have to be hard. Let the dog do it.

Endorsements

"Bloodhound makes Elasticsearch almost tolerable!" - Almost-gruntled user

"ES is a nightmare but Bloodhound at least makes it tolerable." - Same user, later opinion.

Version compatibility

Bloodhound supports and is continuously tested against the following Elasticsearch and OpenSearch releases (see the compat workflow and the CI runs):

Backend CI-tested version
Elasticsearch 7.17.25
Elasticsearch 8.19.16
Elasticsearch 9.4.2
OpenSearch 1.3.19
OpenSearch 2.19.5
OpenSearch 3.7.0

Backends

Each backend is exposed as its own type-safe module set, so only the endpoints that actually exist on that version are available:

  • Database.Bloodhound.ElasticSearch7.*
  • Database.Bloodhound.ElasticSearch8.*
  • Database.Bloodhound.ElasticSearch9.*
  • Database.Bloodhound.OpenSearch1.*
  • Database.Bloodhound.OpenSearch2.*
  • Database.Bloodhound.OpenSearch3.*

A shared Database.Bloodhound.Common.* layer carries the cross-backend surface, and Database.Bloodhound.Dynamic.* dispatches to the right backend at runtime (detected from the server). Use StaticBH backend m a for type-safe, backend-specific code.

Stability

Bloodhound is stable for production use. I will strive to avoid breaking API compatibility from here on forward, but dramatic features like a type-safe, fully integrated mapping API may require breaking things in the future.

Testing

The Bloodhound project uses Github workflows using Cabal to test for regressions and compatibility. A convenient development environment is provided by Nix and a Makefile, though the project can be built with only Cabal.

To run the tests:

  1. Get into the Nix environment by running nix develop (or nix-shell for a non-flake setup)
  2. Start Elasticsearch defined by docker-compose.yml: make compose
  3. Run the tests with Cabal: cabal test

The second step can be left out if ElasticSearch (or OpenSearch) is started manually.

Contributions

Any contribution is welcomed, for consistency reason ormolu is used.

Hackage page and Haddock documentation

http://hackage.haskell.org/package/bloodhound

Elasticsearch Tutorial

It's not using Bloodhound, but if you need an introduction to or overview of Elasticsearch and how to use it, you can use this screencast.

Examples

See the examples directory for example code.

Index a document

indexDocument testIndex defaultIndexDocumentSettings exampleTweet (DocId "1")
{-
IndexedDocument
  { idxDocIndex = "twitter"
  , idxDocType = "_doc"
  , idxDocId = "1"
  , idxDocVersion = 3
  , idxDocResult = "updated"
  , idxDocShards =
      ShardResult
        { shardTotal = 1
        , shardsSuccessful = 1
        , shardsSkipped = 0
        , shardsFailed = 0
        }
  , idxDocSeqNo = 2
  , idxDocPrimaryTerm = 1
  }
-}

Fetch documents

let query = TermQuery (Term "user" "bitemyapp") boost
let search = mkSearch (Just query) boost
searchByIndex @_ @Tweet testIndex search
{-
SearchResult
    { took = 1
    , timedOut = False
    , shards =
            ShardResult
                { shardTotal = 1
                , shardsSuccessful = 1
                , shardsSkipped = 0
                , shardsFailed = 0
                }
    , searchHits =
            SearchHits
                { hitsTotal = HitsTotal { value = 2 , relation = HTR_EQ }
                , maxScore = Just 0.18232156
                , hits =
                        [ Hit
                                { hitIndex = IndexName "twitter"
                                , hitDocId = DocId "1"
                                , hitScore = Just 0.18232156
                                , hitSource =
                                        Just
                                            Tweet
                                                { user = "bitemyapp"
                                                , postDate = 2009-06-18 00:00:10 UTC
                                                , message = "Use haskell!"
                                                , age = 10000
                                                , location = LatLon { lat = 40.12 , lon = -71.3 }
                                                }
                                , hitSort = Nothing
                                , hitFields = Nothing
                                , hitHighlight = Nothing
                                , hitInnerHits = Nothing
                                }
                        , Hit
                                { hitIndex = IndexName "twitter"
                                , hitDocId = DocId "2"
                                , hitScore = Just 0.18232156
                                , hitSource =
                                        Just
                                            Tweet
                                                { user = "bitemyapp"
                                                , postDate = 2009-06-18 00:00:10 UTC
                                                , message = "Use haskell!"
                                                , age = 10000
                                                , location = LatLon { lat = 40.12 , lon = -71.3 }
                                                }
                                , hitSort = Nothing
                                , hitFields = Nothing
                                , hitHighlight = Nothing
                                , hitInnerHits = Nothing
                                }
                        ]
                }
    , aggregations = Nothing
    , scrollId = Nothing
    , suggest = Nothing
    , pitId = Nothing
    }
-}

Contributors

Possible future functionality

Span Queries

Beginning here: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-span-first-query.html

Node discovery and failover

Might require TCP support.

Support for TCP access to Elasticsearch

Pretend to be a transport client?

Bulk cluster-join merge

Might require making a lucene index on disk with the appropriate format.

GeoShapeQuery

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-query.html

Script based sorting

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#_script_based_sorting

Runtime checking for cycles in data structures

check for n > 1 occurrences in DFS:

http://hackage.haskell.org/package/stable-maps-0.0.5/docs/System-Mem-StableName-Dynamic.html

http://hackage.haskell.org/package/stable-maps-0.0.5/docs/System-Mem-StableName-Dynamic-Map.html

Photo Origin

Photo from HA! Designs: https://www.flickr.com/photos/hadesigns/