From d42111ce5a45a239cede0f92579e3ab8df7290ab Mon Sep 17 00:00:00 2001 From: Chris Allen Date: Fri, 11 Apr 2014 00:48:29 -0500 Subject: [PATCH] range filters seem to be behaving themselves --- Database/Bloodhound/Client.hs | 92 ++++++++++++++++++++++++++++++++++- README.org | 12 +++++ tests/tests.hs | 11 +++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/Database/Bloodhound/Client.hs b/Database/Bloodhound/Client.hs index edff2ad..540f8c1 100644 --- a/Database/Bloodhound/Client.hs +++ b/Database/Bloodhound/Client.hs @@ -38,6 +38,13 @@ module Database.Bloodhound.Client , DistanceRange(..) , OptimizeBbox(..) , LatLon(..) + , Range(..) + , HalfRange(..) + , RangeExecution(..) + , LessThan(..) + , LessThanEq(..) + , GreaterThan(..) + , GreaterThanEq(..) ) where @@ -366,8 +373,13 @@ type FieldName = Text type Cache = Bool -- caching on/off defaultCache = False +type Existence = Bool +type NullValue = Bool +type PrefixValue = Text + data Filter = AndFilter [Filter] Cache - | OrFilter [Filter] Cache + | OrFilter [Filter] Cache + | NotFilter Filter Cache | IdentityFilter | BoolFilter BoolMatch | ExistsFilter FieldName -- always cached @@ -376,6 +388,10 @@ data Filter = AndFilter [Filter] Cache | GeoDistanceRangeFilter GeoPoint DistanceRange | GeoPolygonFilter FieldName [LatLon] | IdsFilter MappingName [DocumentID] + | LimitFilter Int + | MissingFilter FieldName Existence NullValue + | PrefixFilter FieldName PrefixValue Cache + | RangeFilter FieldName (Either HalfRange Range) RangeExecution Cache deriving (Eq, Show) class Monoid a => Seminearring a where @@ -403,6 +419,11 @@ instance ToJSON Filter where object ["or" .= fmap toJSON filters , "_cache" .= cache] + toJSON (NotFilter filter cache) = + object ["not" .= + object ["filter" .= toJSON filter + , "_cache" .= cache]] + toJSON (IdentityFilter) = object ["match_all" .= object []] @@ -443,6 +464,39 @@ instance ToJSON Filter where object ["type" .= mappingName , "values" .= fmap T.pack values]] + toJSON (LimitFilter limit) = + object ["limit" .= object ["value" .= limit]] + + toJSON (MissingFilter fieldName existence nullValue) = + object ["missing" .= + object ["field" .= fieldName + , "existence" .= existence + , "null_value" .= nullValue]] + + toJSON (PrefixFilter fieldName fieldValue cache) = + object ["prefix" .= + object [fieldName .= fieldValue + , "_cache" .= cache]] + + toJSON (RangeFilter fieldName (Left halfRange) rangeExecution cache) = + object ["range" .= + object [fieldName .= + object [key .= val] + , "execution" .= toJSON rangeExecution + , "_cache" .= cache]] + where + (key, val) = halfRangeToKV halfRange + + toJSON (RangeFilter fieldName (Right range) rangeExecution cache) = + object ["range" .= + object [fieldName .= + object [lessKey .= lessVal + , greaterKey .= greaterVal] + , "execution" .= toJSON rangeExecution + , "_cache" .= cache]] + where + (lessKey, lessVal, greaterKey, greaterVal) = rangeToKV range + instance ToJSON GeoPoint where toJSON (GeoPoint geoField latLon) = object [geoField .= toJSON latLon] @@ -496,6 +550,42 @@ instance ToJSON LatLon where object ["lat" .= lat , "lon" .= lon] +-- lt, lte | gt, gte +newtype LessThan = LessThan Double deriving (Eq, Show) +newtype LessThanEq = LessThanEq Double deriving (Eq, Show) +newtype GreaterThan = GreaterThan Double deriving (Eq, Show) +newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show) + +data HalfRange = HalfRangeLt LessThan + | HalfRangeLte LessThanEq + | HalfRangeGt GreaterThan + | HalfRangeGte GreaterThanEq deriving (Eq, Show) + +data Range = RangeLtGt LessThan GreaterThan + | RangeLtGte LessThan GreaterThanEq + | RangeLteGt LessThanEq GreaterThan + | RangeLteGte LessThanEq GreaterThanEq deriving (Eq, Show) + +halfRangeToKV :: HalfRange -> (Text, Double) +halfRangeToKV (HalfRangeLt (LessThan n)) = ("lt", n) +halfRangeToKV (HalfRangeLte (LessThanEq n)) = ("lte", n) +halfRangeToKV (HalfRangeGt (GreaterThan n)) = ("gt", n) +halfRangeToKV (HalfRangeGte (GreaterThanEq n)) = ("gte", n) + +rangeToKV :: Range -> (Text, Double, Text, Double) +rangeToKV (RangeLtGt (LessThan m) (GreaterThan n)) = ("lt", m, "gt", n) +rangeToKV (RangeLtGte (LessThan m) (GreaterThanEq n)) = ("lt", m, "gte", n) +rangeToKV (RangeLteGt (LessThanEq m) (GreaterThan n)) = ("lte", m, "gt", n) +rangeToKV (RangeLteGte (LessThanEq m) (GreaterThanEq n)) = ("lte", m, "gte", n) + +data RangeExecution = RangeExecutionIndex + | RangeExecutionFielddata deriving (Eq, Show) + +-- index for smaller ranges, fielddata for longer ranges +instance ToJSON RangeExecution where + toJSON RangeExecutionIndex = "index" + toJSON RangeExecutionFielddata = "fielddata" + data Term = Term { termField :: Text , termValue :: Text } deriving (Eq, Show) diff --git a/README.org b/README.org index 764c71f..1352e4e 100644 --- a/README.org +++ b/README.org @@ -4,6 +4,10 @@ Elasticsearch client and query DSL for Haskell +** Examples + +nope.jpg + ** Possible future functionality *** GeoShapeFilter @@ -22,6 +26,14 @@ http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl- http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-has-parent-filter.html +*** Indices Filter + +http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-indices-filter.html + +*** Query Filter + +http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-query-filter.html + *** Runtime checking for cycles in data structures check for n > 1 occurrences in DFS: diff --git a/tests/tests.hs b/tests/tests.hs index 0364306..094021c 100644 --- a/tests/tests.hs +++ b/tests/tests.hs @@ -32,6 +32,7 @@ data Location = Location { lat :: Double data Tweet = Tweet { user :: Text , postDate :: UTCTime , message :: Text + , age :: Int , location :: Location } deriving (Eq, Generic, Show) @@ -53,6 +54,7 @@ exampleTweet = Tweet { user = "bitemyapp" (ModifiedJulianDay 55000) (secondsToDiffTime 10) , message = "Use haskell!" + , age = 10000 , location = Location 40.12 (-71.34) } insertData :: IO () @@ -185,3 +187,12 @@ main = hspec $ do let search = Search Nothing (Just filter) myTweet <- searchTweet search myTweet `shouldBe` Right exampleTweet + + it "returns document for range filter" $ do + _ <- insertData + let filter = RangeFilter "age" + (Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0))) + RangeExecutionIndex False + let search = Search Nothing (Just filter) + myTweet <- searchTweet search + myTweet `shouldBe` Right exampleTweet