git.iko.soy/typeable/bloodhound
on master help sign in
Merge pull request #25 from unthingable/moar-range

add Double/Text typed ranges

commit20ed65b52e65db878ae918f7aad829ae52199c6c authorChris Allen <cma@bitemyapp.com> dateSun 8 Feb 2015, 16:10 (12 years ago) parent6e68a225435f9c
3 files changed+98−72
README.md+23−9 src/Database/Bloodhound/Types.hs+57−61 tests/tests.hs+18−2
README.md+23−9
@@ -321,7 +321,7 @@ let sndOp = BulkIndex testIndex
321321-- Some explanation, the final "Value" type that BulkIndex,
322322-- BulkCreate, and BulkUpdate accept is the actual document
323323-- data that your operation applies to. BulkDelete doesn't
324−-- take a value because it's just deleting whatever DocId
324+-- take a value because it's just deleting whatever DocId
325325-- you pass.
326326
327327-- list of bulk operations
@@ -636,31 +636,46 @@ IdsFilter (MappingName "tweet") [DocId "1"]
636636
637637#### Range Filter
638638
639−##### Full Range
640−
641639``` {.haskell}
642640
643641-- RangeFilter :: FieldName
644−-- -> Either HalfRange Range
642+-- -> RangeValue
645643-- -> RangeExecution
646644-- -> Cache -> Filter
647645
648646let filter = RangeFilter (FieldName "age")
649− (Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0)))
647+ (RangeGtLt (GreaterThan 1000.0) (LessThan 100000.0))
650648 RangeExecutionIndex False
651649
652650```
653651
654−##### Half Range
655−
656652``` {.haskell}
657653
658654let filter = RangeFilter (FieldName "age")
659− (Left (HalfRangeLt (LessThan 100000.0)))
655+ (RangeLte (LessThanEq 100000.0))
660656 RangeExecutionIndex False
661657
662658```
663659
660+##### Date Ranges
661+
662+Date ranges are expressed in UTCTime. Date ranges use the same range bound constructors as numerics, except that they end in "D".
663+
664+Note that compatibility with ES is tested only down to seconds.
665+
666+``` {.haskell}
667+
668+let filter = RangeFilter (FieldName "postDate")
669+ (RangeDateGtLte
670+ (GreaterThanD (UTCTime
671+ (ModifiedJulianDay 55000)
672+ (secondsToDiffTime 9)))
673+ (LessThanEqD (UTCTime
674+ (ModifiedJulianDay 55000)
675+ (secondsToDiffTime 11))))
676+ RangeExecutionIndex False
677+```
678+
664679#### Regexp Filter
665680
666681``` {.haskell}
@@ -928,4 +943,3 @@ Photo Origin
928943============
929944
930945Photo from HA! Designs: <https://www.flickr.com/photos/hadesigns/>
931−
src/Database/Bloodhound/Types.hs+57−61
@@ -24,9 +24,7 @@
2424module Database.Bloodhound.Types
2525 ( defaultCache
2626 , defaultIndexSettings
27− , halfRangeToKV
2827 , mkSort
29− , rangeToKV
3028 , showText
3129 , unpackId
3230 , mkMatchQuery
@@ -69,13 +67,16 @@ module Database.Bloodhound.Types
6967 , DistanceRange(..)
7068 , OptimizeBbox(..)
7169 , LatLon(..)
72− , Range(..)
73− , HalfRange(..)
70+ , RangeValue(..)
7471 , RangeExecution(..)
7572 , LessThan(..)
7673 , LessThanEq(..)
7774 , GreaterThan(..)
7875 , GreaterThanEq(..)
76+ , LessThanD(..)
77+ , LessThanEqD(..)
78+ , GreaterThanD(..)
79+ , GreaterThanEqD(..)
7980 , Regexp(..)
8081 , RegexpFlags(..)
8182 , RegexpFlag(..)
@@ -636,10 +637,10 @@ data RegexpQuery =
636637
637638data RangeQuery =
638639 RangeQuery { rangeQueryField :: FieldName
639− , rangeQueryRange :: Either HalfRange Range
640+ , rangeQueryRange :: RangeValue
640641 , rangeQueryBoost :: Boost } deriving (Eq, Show)
641642
642−mkRangeQuery :: FieldName -> Either HalfRange Range -> RangeQuery
643+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
643644mkRangeQuery f r = RangeQuery f r (Boost 1.0)
644645
645646data SimpleQueryStringQuery =
@@ -922,7 +923,7 @@ data Filter = AndFilter [Filter] Cache
922923 | LimitFilter Int
923924 | MissingFilter FieldName Existence NullValue
924925 | PrefixFilter FieldName PrefixValue Cache
925− | RangeFilter FieldName (Either HalfRange Range) RangeExecution Cache
926+ | RangeFilter FieldName RangeValue RangeExecution Cache
926927 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
927928 | TermFilter Term Cache
928929 deriving (Eq, Show)
@@ -930,22 +931,6 @@ data Filter = AndFilter [Filter] Cache
930931data ZeroTermsQuery = ZeroTermsNone
931932 | ZeroTermsAll deriving (Eq, Show)
932933
933−-- lt, lte | gt, gte
934−newtype LessThan = LessThan Double deriving (Eq, Show)
935−newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
936−newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
937−newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
938−
939−data HalfRange = HalfRangeLt LessThan
940− | HalfRangeLte LessThanEq
941− | HalfRangeGt GreaterThan
942− | HalfRangeGte GreaterThanEq deriving (Eq, Show)
943−
944−data Range = RangeLtGt LessThan GreaterThan
945− | RangeLtGte LessThan GreaterThanEq
946− | RangeLteGt LessThanEq GreaterThan
947− | RangeLteGte LessThanEq GreaterThanEq deriving (Eq, Show)
948−
949934data RangeExecution = RangeExecutionIndex
950935 | RangeExecutionFielddata deriving (Eq, Show)
951936
@@ -962,19 +947,52 @@ data RegexpFlag = AnyString
962947 | Intersection
963948 | Interval deriving (Eq, Show)
964949
965−halfRangeToKV :: HalfRange -> (Text, Double)
966−halfRangeToKV (HalfRangeLt (LessThan n)) = ("lt", n)
967−halfRangeToKV (HalfRangeLte (LessThanEq n)) = ("lte", n)
968−halfRangeToKV (HalfRangeGt (GreaterThan n)) = ("gt", n)
969−halfRangeToKV (HalfRangeGte (GreaterThanEq n)) = ("gte", n)
970−
971−rangeToKV :: Range -> (Text, Double, Text, Double)
972−rangeToKV (RangeLtGt (LessThan m) (GreaterThan n)) = ("lt", m, "gt", n)
973−rangeToKV (RangeLtGte (LessThan m) (GreaterThanEq n)) = ("lt", m, "gte", n)
974−rangeToKV (RangeLteGt (LessThanEq m) (GreaterThan n)) = ("lte", m, "gt", n)
975−rangeToKV (RangeLteGte (LessThanEq m) (GreaterThanEq n)) = ("lte", m, "gte", n)
950+newtype LessThan = LessThan Double deriving (Eq, Show)
951+newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
952+newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
953+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
976954
977−-- phew. Coulda used Agda style case breaking there but, you know, whatever. :)
955+newtype LessThanD = LessThanD UTCTime deriving (Eq, Show)
956+newtype LessThanEqD = LessThanEqD UTCTime deriving (Eq, Show)
957+newtype GreaterThanD = GreaterThanD UTCTime deriving (Eq, Show)
958+newtype GreaterThanEqD = GreaterThanEqD UTCTime deriving (Eq, Show)
959+
960+data RangeValue = RangeDateLte LessThanEqD
961+ | RangeDateLt LessThanD
962+ | RangeDateGte GreaterThanEqD
963+ | RangeDateGt GreaterThanD
964+ | RangeDateGtLt GreaterThanD LessThanD
965+ | RangeDateGteLte GreaterThanEqD LessThanEqD
966+ | RangeDateGteLt GreaterThanEqD LessThanD
967+ | RangeDateGtLte GreaterThanD LessThanEqD
968+ | RangeDoubleLte LessThanEq
969+ | RangeDoubleLt LessThan
970+ | RangeDoubleGte GreaterThanEq
971+ | RangeDoubleGt GreaterThan
972+ | RangeDoubleGtLt GreaterThan LessThan
973+ | RangeDoubleGteLte GreaterThanEq LessThanEq
974+ | RangeDoubleGteLt GreaterThanEq LessThan
975+ | RangeDoubleGtLte GreaterThan LessThanEq
976+ deriving (Eq, Show)
977+
978+rangeValueToPair :: RangeValue -> [Pair]
979+rangeValueToPair rv = case rv of
980+ RangeDateLte (LessThanEqD t) -> ["lte" .= t]
981+ RangeDateGte (GreaterThanEqD t) -> ["gte" .= t]
982+ RangeDateLt (LessThanD t) -> ["lt" .= t]
983+ RangeDateGt (GreaterThanD t) -> ["gt" .= t]
984+ RangeDateGteLte (GreaterThanEqD l) (LessThanEqD g) -> ["gte" .= l, "lte" .= g]
985+ RangeDateGtLte (GreaterThanD l) (LessThanEqD g) -> ["gt" .= l, "lte" .= g]
986+ RangeDateGteLt (GreaterThanEqD l) (LessThanD g) -> ["gte" .= l, "lt" .= g]
987+ RangeDateGtLt (GreaterThanD l) (LessThanD g) -> ["gt" .= l, "lt" .= g]
988+ RangeDoubleLte (LessThanEq t) -> ["lte" .= t]
989+ RangeDoubleGte (GreaterThanEq t) -> ["gte" .= t]
990+ RangeDoubleLt (LessThan t) -> ["lt" .= t]
991+ RangeDoubleGt (GreaterThan t) -> ["gt" .= t]
992+ RangeDoubleGteLte (GreaterThanEq l) (LessThanEq g) -> ["gte" .= l, "lte" .= g]
993+ RangeDoubleGtLte (GreaterThan l) (LessThanEq g) -> ["gt" .= l, "lte" .= g]
994+ RangeDoubleGteLt (GreaterThanEq l) (LessThan g) -> ["gte" .= l, "lt" .= g]
995+ RangeDoubleGtLt (GreaterThan l) (LessThan g) -> ["gt" .= l, "lt" .= g]
978996
979997data Term = Term { termField :: Text
980998 , termValue :: Text } deriving (Eq, Show)
@@ -1338,24 +1356,11 @@ instance ToJSON Filter where
13381356 object [fieldName .= fieldValue
13391357 , "_cache" .= cache]]
13401358
1341− toJSON (RangeFilter (FieldName fieldName) (Left halfRange) rangeExecution cache) =
1342− object ["range" .=
1343− object [fieldName .=
1344− object [rqFKey .= rqFVal]
1345− , "execution" .= toJSON rangeExecution
1346− , "_cache" .= cache]]
1347− where
1348− (rqFKey, rqFVal) = halfRangeToKV halfRange
1349−
1350− toJSON (RangeFilter (FieldName fieldName) (Right range) rangeExecution cache) =
1359+ toJSON (RangeFilter (FieldName fieldName) rangeValue rangeExecution cache) =
13511360 object ["range" .=
1352− object [fieldName .=
1353− object [lessKey .= lessVal
1354− , greaterKey .= greaterVal]
1361+ object [ fieldName .= object (rangeValueToPair rangeValue)
13551362 , "execution" .= toJSON rangeExecution
13561363 , "_cache" .= cache]]
1357− where
1358− (lessKey, lessVal, greaterKey, greaterVal) = rangeToKV range
13591364
13601365 toJSON (RegexpFilter (FieldName fieldName)
13611366 (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
@@ -1549,18 +1554,9 @@ instance ToJSON QueryStringQuery where
15491554
15501555
15511556instance ToJSON RangeQuery where
1552− toJSON (RangeQuery (FieldName fieldName) (Right range) boost) =
1553− object [ fieldName .= conjoined ]
1554− where conjoined = [ "boost" .= toJSON boost
1555− , lessKey .= lessVal
1556− , greaterKey .= greaterVal ]
1557− (lessKey, lessVal, greaterKey, greaterVal) = rangeToKV range
1558−
1559− toJSON (RangeQuery (FieldName fieldName) (Left halfRange) boost) =
1557+ toJSON (RangeQuery (FieldName fieldName) range boost) =
15601558 object [ fieldName .= conjoined ]
1561− where conjoined = [ "boost" .= toJSON boost
1562− , rqKey .= rqVal ]
1563− (rqKey, rqVal) = halfRangeToKV halfRange
1559+ where conjoined = [ "boost" .= toJSON boost ] ++ (rangeValueToPair range)
15641560
15651561
15661562instance ToJSON PrefixQuery where
tests/tests.hs+18−2
@@ -432,15 +432,31 @@ main = hspec $ do
432432 myTweet <- searchTweet search
433433 myTweet `shouldBe` Right exampleTweet
434434
435− it "returns document for range filter" $ do
435+ it "returns document for Double range filter" $ do
436436 _ <- insertData
437437 let filter = RangeFilter (FieldName "age")
438− (Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0)))
438+ (RangeDoubleGtLt (GreaterThan 1000.0) (LessThan 100000.0))
439439 RangeExecutionIndex False
440440 let search = mkSearch Nothing (Just filter)
441441 myTweet <- searchTweet search
442442 myTweet `shouldBe` Right exampleTweet
443443
444+ it "returns document for UTCTime date filter" $ do
445+ _ <- insertData
446+ let filter = RangeFilter (FieldName "postDate")
447+ (RangeDateGtLt
448+ (GreaterThanD (UTCTime
449+ (ModifiedJulianDay 55000)
450+ (secondsToDiffTime 9)))
451+ (LessThanD (UTCTime
452+ (ModifiedJulianDay 55000)
453+ (secondsToDiffTime 11))))
454+ RangeExecutionIndex False
455+ let search = mkSearch Nothing (Just filter)
456+ myTweet <- searchTweet search
457+ myTweet `shouldBe` Right exampleTweet
458+
459+
444460 it "returns document for regexp filter" $ do
445461 _ <- insertData
446462 let filter = RegexpFilter (FieldName "user") (Regexp "bite.*app")