git.iko.soy/typeable/bloodhound
on master help sign in
Merging with master
70f70e4Christopher Guiney
.travis.yml+1−1
@@ -25,7 +25,7 @@ before_install:
25 - sudo apt-get install happy-1.19.3
25 - sudo apt-get install happy-1.19.3
26 - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.3/bin:$PATH
26 - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.3/bin:$PATH
27 - wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-$ESVER.deb
27 - wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-$ESVER.deb
28− - sudo dpkg -i elasticsearch-$ESVER.deb
28+ - sudo dpkg --force-all -i elasticsearch-$ESVER.deb
29 - sudo service elasticsearch start
29 - sudo service elasticsearch start
30
30
31install:
31install:
Database/Bloodhound/Types.hs+43−40
@@ -1,4 +1,4 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
3
3
4{-| Data types for describing actions and data structures performed to interact
4{-| Data types for describing actions and data structures performed to interact
@@ -28,6 +28,7 @@ module Database.Bloodhound.Types
28 , mkDateHistogram
28 , mkDateHistogram
29 , toTerms
29 , toTerms
30 , toDateHistogram
30 , toDateHistogram
31+ , omitNulls
31 , Version(..)
32 , Version(..)
32 , Status(..)
33 , Status(..)
33 , Existence(..)
34 , Existence(..)
@@ -165,22 +166,23 @@ module Database.Bloodhound.Types
165 , DateHistogramResult
166 , DateHistogramResult
166 ) where
167 ) where
167
168
168−import Control.Applicative
169+import Control.Applicative
169−import Data.Aeson
170+import Data.Aeson
170−import Data.Aeson.Types (parseMaybe)
171+import Data.Aeson.Types (parseMaybe)
171−import qualified Data.ByteString.Lazy.Char8 as L
172+import qualified Data.ByteString.Lazy.Char8 as L
172−import Data.List (nub)
173+import Data.List (nub)
173−import Data.List.NonEmpty (NonEmpty(..))
174+import Data.List.NonEmpty (NonEmpty (..))
174−import Data.Monoid
175+import Data.Monoid
175−import Data.Text (Text)
176+import Data.Text (Text)
176−import qualified Data.Map.Strict as M
177+import qualified Data.Text as T
177−import qualified Data.Text as T
178+import qualified Data.Map.Strict as M
178−import Data.Time.Clock (UTCTime)
179+import Data.Time.Clock (UTCTime)
180+import qualified Data.Vector as V
179−import GHC.Generics (Generic)
181+import GHC.Generics (Generic)
180−import Network.HTTP.Client
182+import Network.HTTP.Client
181−import qualified Network.HTTP.Types.Method as NHTM
183+import qualified Network.HTTP.Types.Method as NHTM
182−
184+
183−import Database.Bloodhound.Types.Class
185+import Database.Bloodhound.Types.Class
184
186
185
187
186{-| 'Version' is embedded in 'Status' -}
188{-| 'Version' is embedded in 'Status' -}
@@ -238,8 +240,8 @@ data FieldDefinition =
238 FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
240 FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
239
241
240data MappingField =
242data MappingField =
241− MappingField { mappingFieldName :: FieldName
243+ MappingField { mappingFieldName :: FieldName
242− , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
244+ , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
243
245
244{-| Support for type reification of 'Mapping's is currently incomplete, for
246{-| Support for type reification of 'Mapping's is currently incomplete, for
245 now the mapping API verbiage expects a 'ToJSON'able blob.
247 now the mapping API verbiage expects a 'ToJSON'able blob.
@@ -497,11 +499,11 @@ type Size = Int
497data Search = Search { queryBody :: Maybe Query
499data Search = Search { queryBody :: Maybe Query
498 , filterBody :: Maybe Filter
500 , filterBody :: Maybe Filter
499 , sortBody :: Maybe Sort
501 , sortBody :: Maybe Sort
500− , aggBody :: Maybe Aggregations
502+ , aggBody :: Maybe Aggregations
501 -- default False
503 -- default False
502 , trackSortScores :: TrackSortScores
504 , trackSortScores :: TrackSortScores
503− , from :: From
505+ , from :: From
504− , size :: Size } deriving (Eq, Show)
506+ , size :: Size } deriving (Eq, Show)
505
507
506data Query =
508data Query =
507 TermQuery Term (Maybe Boost)
509 TermQuery Term (Maybe Boost)
@@ -534,10 +536,10 @@ data Query =
534 deriving (Eq, Show)
536 deriving (Eq, Show)
535
537
536data RegexpQuery =
538data RegexpQuery =
537− RegexpQuery { regexpQueryField :: FieldName
539+ RegexpQuery { regexpQueryField :: FieldName
538− , regexpQuery :: Regexp
540+ , regexpQuery :: Regexp
539− , regexpQueryFlags :: RegexpFlags
541+ , regexpQueryFlags :: RegexpFlags
540− , regexpQueryBoost :: Maybe Boost
542+ , regexpQueryBoost :: Maybe Boost
541 } deriving (Eq, Show)
543 } deriving (Eq, Show)
542
544
543data RangeQuery =
545data RangeQuery =
@@ -657,9 +659,9 @@ data MoreLikeThisQuery =
657data IndicesQuery =
659data IndicesQuery =
658 IndicesQuery
660 IndicesQuery
659 { indicesQueryIndices :: [IndexName]
661 { indicesQueryIndices :: [IndexName]
660− , indicesQuery :: Query
662+ , indicesQuery :: Query
661 -- default "all"
663 -- default "all"
662− , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show)
664+ , indicesQueryNoMatch :: Maybe Query } deriving (Eq, Show)
663
665
664data HasParentQuery =
666data HasParentQuery =
665 HasParentQuery
667 HasParentQuery
@@ -776,15 +778,15 @@ data MultiMatchQueryType =
776 | MultiMatchPhrasePrefix deriving (Eq, Show)
778 | MultiMatchPhrasePrefix deriving (Eq, Show)
777
779
778data BoolQuery =
780data BoolQuery =
779− BoolQuery { boolQueryMustMatch :: Maybe Query
781+ BoolQuery { boolQueryMustMatch :: [Query]
780− , boolQueryMustNotMatch :: Maybe Query
782+ , boolQueryMustNotMatch :: [Query]
781− , boolQueryShouldMatch :: Maybe [Query]
783+ , boolQueryShouldMatch :: [Query]
782 , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
784 , boolQueryMinimumShouldMatch :: Maybe MinimumMatch
783 , boolQueryBoost :: Maybe Boost
785 , boolQueryBoost :: Maybe Boost
784 , boolQueryDisableCoord :: Maybe DisableCoord
786 , boolQueryDisableCoord :: Maybe DisableCoord
785 } deriving (Eq, Show)
787 } deriving (Eq, Show)
786
788
787−mkBoolQuery :: Maybe Query -> Maybe Query -> Maybe [Query] -> BoolQuery
789+mkBoolQuery :: [Query] -> [Query] -> [Query] -> BoolQuery
788mkBoolQuery must mustNot should =
790mkBoolQuery must mustNot should =
789 BoolQuery must mustNot should Nothing Nothing Nothing
791 BoolQuery must mustNot should Nothing Nothing Nothing
790
792
@@ -811,7 +813,7 @@ data CommonMinimumMatch =
811 deriving (Eq, Show)
813 deriving (Eq, Show)
812
814
813data MinimumMatchHighLow =
815data MinimumMatchHighLow =
814− MinimumMatchHighLow { lowFreq :: MinimumMatch
816+ MinimumMatchHighLow { lowFreq :: MinimumMatch
815 , highFreq :: MinimumMatch } deriving (Eq, Show)
817 , highFreq :: MinimumMatch } deriving (Eq, Show)
816
818
817data Filter = AndFilter [Filter] Cache
819data Filter = AndFilter [Filter] Cache
@@ -951,11 +953,11 @@ data FromJSON a => SearchHits a =
951 , hits :: [Hit a] } deriving (Eq, Show)
953 , hits :: [Hit a] } deriving (Eq, Show)
952
954
953data FromJSON a => Hit a =
955data FromJSON a => Hit a =
954− Hit { hitIndex :: IndexName
956+ Hit { hitIndex :: IndexName
955− , hitType :: MappingName
957+ , hitType :: MappingName
956− , hitDocId :: DocId
958+ , hitDocId :: DocId
957− , hitScore :: Score
959+ , hitScore :: Score
958− , hitSource :: a } deriving (Eq, Show)
960+ , hitSource :: a } deriving (Eq, Show)
959
961
960data ShardResult =
962data ShardResult =
961 ShardResult { shardTotal :: Int
963 ShardResult { shardTotal :: Int
@@ -1370,8 +1372,9 @@ instance ToJSON Query where
1370
1372
1371omitNulls :: [(Text, Value)] -> Value
1373omitNulls :: [(Text, Value)] -> Value
1372omitNulls = object . filter notNull where
1374omitNulls = object . filter notNull where
1373− notNull (_, Null) = False
1375+ notNull (_, Null) = False
1376+ notNull (_, (Array a)) = (not . V.null) a
1374− notNull _ = True
1377+ notNull _ = True
1375
1378
1376
1379
1377instance ToJSON SimpleQueryStringQuery where
1380instance ToJSON SimpleQueryStringQuery where
README.org+5−1
@@ -13,6 +13,10 @@
13
13
14Search doesn't have to be hard. Let the dog do it.
14Search doesn't have to be hard. Let the dog do it.
15
15
16+** Endorsements
17+
18+"Bloodhound makes Elasticsearch almost tolerable!" - Almost-gruntled user
19+
16** Version compatibility
20** Version compatibility
17
21
18Elasticsearch >= 1.0 is recommended. Bloodhound mostly works with 0.9.x, but I don't recommend it if you expect everything to work. As of Bloodhound 0.3 all >=1.0 versions of Elasticsearch work.
22Elasticsearch >= 1.0 is recommended. Bloodhound mostly works with 0.9.x, but I don't recommend it if you expect everything to work. As of Bloodhound 0.3 all >=1.0 versions of Elasticsearch work.
@@ -382,7 +386,7 @@ let search = mkSearch (Just query) Nothing
382let innerQuery = QueryMatchQuery $
386let innerQuery = QueryMatchQuery $
383 mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
387 mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
384let query = QueryBoolQuery $
388let query = QueryBoolQuery $
385− mkBoolQuery (Just innerQuery) Nothing Nothing
389+ mkBoolQuery [innerQuery] [] []
386let search = mkSearch (Just query) Nothing
390let search = mkSearch (Just query) Nothing
387
391
388#+END_SRC
392#+END_SRC
bloodhound.cabal+6−3
@@ -1,5 +1,5 @@
1name: bloodhound
1name: bloodhound
2−version: 0.3.0.0
2+version: 0.4.0.0
3synopsis: ElasticSearch client library for Haskell
3synopsis: ElasticSearch client library for Haskell
4description: ElasticSearch made awesome for Haskell hackers
4description: ElasticSearch made awesome for Haskell hackers
5homepage: https://github.com/bitemyapp/bloodhound
5homepage: https://github.com/bitemyapp/bloodhound
@@ -32,7 +32,8 @@ library
32 semigroups >= 0.15,
32 semigroups >= 0.15,
33 time >= 1.4,
33 time >= 1.4,
34 text >= 0.11,
34 text >= 0.11,
35− http-types >= 0.8
35+ http-types >= 0.8,
36+ vector >= 0.10.11
36 default-language: Haskell2010
37 default-language: Haskell2010
37
38
38test-suite tests
39test-suite tests
@@ -51,5 +52,7 @@ test-suite tests
51 time >= 1.4,
52 time >= 1.4,
52 aeson >= 0.7,
53 aeson >= 0.7,
53 semigroups >= 0.15,
54 semigroups >= 0.15,
54− QuickCheck
55+ QuickCheck,
56+ vector >= 0.10.11,
57+ unordered-containers >=0.2.5.0
55 default-language: Haskell2010
58 default-language: Haskell2010
changelog → changelog.mdrenamed+19−3
@@ -1,6 +1,22 @@
1−** 0.2.x
1+0.4.0.0
2+===================
3+
4+* omitNulls changed to exclude empty lists and null values
5+
6+* BoolQuery must/mustNot/Should changed from Maybe (Query|[Query]) to [Query]
7+
8+* Added vector dependency so we can check for V.null/V.empty on JSON arrays
9+
10+0.3.0.0
11+===================
12+
13+* Status "ok" changed from Bool to Maybe Bool
14+
15+* Elasticsearch 1.3.x compatibility fixed with changes to geo bounding boxes
16+
17+* CI coverage expanded to 1.0.x -> 1.3.x
2
18
3−0.2.0.0 --> 0.2.0.1
19+0.2.0.1
4===================
20===================
5
21
6* Killed off maybeJson/mField/catMaybes in favor of omitNulls
22* Killed off maybeJson/mField/catMaybes in favor of omitNulls
@@ -11,7 +27,7 @@
11
27
12* Added note about current supported Elasticsearch version.
28* Added note about current supported Elasticsearch version.
13
29
14−0.1.0.2 --> 0.2.0.0
30+0.2.0.0
15===================
31===================
16
32
17* Added TermFilter
33* Added TermFilter
tests/tests.hs+42−23
@@ -1,29 +1,29 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3
3
4module Main where
4module Main where
5
5
6−import Control.Applicative
6+import Control.Applicative
7−import Control.Monad
7+import Control.Monad
8−import Database.Bloodhound
9−import Data.Aeson
8+import Data.Aeson
10−import Data.Aeson.Types (parseMaybe)
9+import Data.Aeson.Types (parseMaybe)
10+import Data.HashMap.Strict (fromList)
11−import Data.List (nub, elemIndex)
11+import Data.List (nub)
12−import Data.List.NonEmpty (NonEmpty(..))
12+import Data.List.NonEmpty (NonEmpty (..))
13+import qualified Data.Map.Strict as M
14+import Data.Text (Text)
15+import qualified Data.Text as T
13−import Data.Time.Calendar (Day(..))
16+import Data.Time.Calendar (Day (..))
14−import Data.Time.Clock (secondsToDiffTime, UTCTime(..))
17+import Data.Time.Clock (UTCTime (..), secondsToDiffTime)
15−import Data.Text (Text)
16−import qualified Data.Text as T
18+import qualified Data.Vector as V
19+import Database.Bloodhound
17−import GHC.Generics (Generic)
20+import GHC.Generics (Generic)
18−import Network.HTTP.Client
21+import Network.HTTP.Client
19import qualified Network.HTTP.Types.Status as NHTS
22import qualified Network.HTTP.Types.Status as NHTS
20−import Prelude hiding (filter, putStrLn)
23+import Prelude hiding (filter, putStrLn)
21−import Test.Hspec
24+import Test.Hspec
22−import Test.Hspec.QuickCheck (prop)
25+import Test.Hspec.QuickCheck (prop)
23−import Test.QuickCheck
26+import Test.QuickCheck
24−import qualified Data.Map.Strict as M
25−
26−import System.IO
27
27
28testServer :: Server
28testServer :: Server
29testServer = Server "http://localhost:9200"
29testServer = Server "http://localhost:9200"
@@ -279,7 +279,7 @@ main = hspec $ do
279 let innerQuery = QueryMatchQuery $
279 let innerQuery = QueryMatchQuery $
280 mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
280 mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
281 let query = QueryBoolQuery $
281 let query = QueryBoolQuery $
282− mkBoolQuery (Just innerQuery) Nothing Nothing
282+ mkBoolQuery [innerQuery] [] []
283 let search = mkSearch (Just query) Nothing
283 let search = mkSearch (Just query) Nothing
284 myTweet <- searchTweet search
284 myTweet <- searchTweet search
285 myTweet `shouldBe` Right exampleTweet
285 myTweet `shouldBe` Right exampleTweet
@@ -503,5 +503,24 @@ main = hspec $ do
503 let String str = toJSON flags
503 let String str = toJSON flags
504 flagStrs = T.splitOn "|" str
504 flagStrs = T.splitOn "|" str
505 in noDuplicates flagStrs
505 in noDuplicates flagStrs
506−
507
506
507+ describe "omitNulls" $ do
508+ it "checks that omitNulls drops list elements when it should" $
509+ let dropped = omitNulls $ [ "test1" .= (toJSON ([] :: [Int]))
510+ , "test2" .= (toJSON ("some value" :: Text))]
511+ in dropped `shouldBe` Object (fromList [("test2", String "some value")])
512+
513+ it "checks that omitNulls doesn't drop list elements when it shouldn't" $
514+ let notDropped = omitNulls $ [ "test1" .= (toJSON ([1] :: [Int]))
515+ , "test2" .= (toJSON ("some value" :: Text))]
516+ in notDropped `shouldBe` Object (fromList [ ("test1", Array (V.fromList [Number 1.0]))
517+ , ("test2", String "some value")])
518+ it "checks that omitNulls drops non list elements when it should" $
519+ let dropped = omitNulls $ [ "test1" .= (toJSON Null)
520+ , "test2" .= (toJSON ("some value" :: Text))]
521+ in dropped `shouldBe` Object (fromList [("test2", String "some value")])
522+ it "checks that omitNulls doesn't drop non list elements when it shouldn't" $
523+ let notDropped = omitNulls $ [ "test1" .= (toJSON (1 :: Int))
524+ , "test2" .= (toJSON ("some value" :: Text))]
525+ in notDropped `shouldBe` Object (fromList [ ("test1", Number 1.0)
526+ , ("test2", String "some value")])