1. 11
  1.  

  2. 6

    I solved this problem by lifting their query DSL into types and making it (as much as I could, anyhow) impossible to construct an invalid query: https://github.com/bitemyapp/bloodhound

    1. 2

      My problem was less invalid queries and more that code which built queries to ES from user’s input wasn’t very clear. In fact, it was hard to read and hard to update.

      So I think types may help, but the overall approach is what’s more important.

      1. 2

        more that code which built queries to ES from user’s input wasn’t very clear. In fact, it was hard to read and hard to update.

        Yes, that’s why I wrote Bloodhound. I know people that don’t use Haskell that still use Bloodhound anyway to generate complicated queries, using the Haskell code as a nicer, more maintainable template in effect. Look at the tests for example:

        https://github.com/bitemyapp/bloodhound/blob/master/tests/Test/Query.hs#L24-L27

        I mentioned invalid queries because that’s the harder problem to solve. Just making something that’ll at least tidy up the API is the first step. Tightening it up so you eliminate opportunities for users to make query structures that don’t make sense is where it starts to really come together.

        https://github.com/bitemyapp/bloodhound/blob/master/src/Database/Bloodhound/Internal/Query.hs#L513-L529

        The types are the interface that make it self-documenting and easier to maintain. I’ve been using ES off and on since pre-1.0 and I hated the string blob templates that I had in Python before. After I learned Haskell, I had the idea that you could use types to reify the query DSL into an interface. And I was right, it works great.

        1. 2

          Sorry, I fail to see how types are more maintainable than just plain maps. This thing:

                let query = TermQuery (Term "user" "bitemyapp") Nothing
          

          is not better than just {:term {"user" "bitemyapp"}}. I’d argue it’s worse since you have to know the mapping rather than just writing this stuff directly.

          What I’m talking about is one step higher: it’s a design of a query builder. Not of a query itself, this thing is awful and ElasticSearch receives a lot of heat online for its query language design, and not for nothing. But building those queries has nothing to do with types. Invalid queries were not my problem.

          1. 4

            is not better than just {:term {“user” “bitemyapp”}}.

            It is better because Elasticsearch changes their API with some regularity and with types when we update the type in Bloodhound to match the new API structure, you’ll get a list of type errors everywhere in your code where you need to fix it before your stuff will work. You can get migrations done a lot faster. This isn’t hypothetical: we have production users that love this. This is also a facility of types in general.

            You probably aren’t aware but I was a Clojure user before Haskell and maintained some libraries like Korma. I know what it’s like to maintain production Clojure code.