git.iko.soy/typeable/bloodhound
on master help sign in
Merge pull request #25 from unthingable/moar-range
20ed65bChris Allen
README.md+23−9
@@ -321,7 +321,7 @@ let sndOp = BulkIndex testIndex
321-- Some explanation, the final "Value" type that BulkIndex,
321-- Some explanation, the final "Value" type that BulkIndex,
322-- BulkCreate, and BulkUpdate accept is the actual document
322-- BulkCreate, and BulkUpdate accept is the actual document
323-- data that your operation applies to. BulkDelete doesn't
323-- 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
325-- you pass.
325-- you pass.
326
326
327-- list of bulk operations
327-- list of bulk operations
@@ -636,31 +636,46 @@ IdsFilter (MappingName "tweet") [DocId "1"]
636
636
637#### Range Filter
637#### Range Filter
638
638
639−##### Full Range
640−
641``` {.haskell}
639``` {.haskell}
642
640
643-- RangeFilter :: FieldName
641-- RangeFilter :: FieldName
644−-- -> Either HalfRange Range
642+-- -> RangeValue
645-- -> RangeExecution
643-- -> RangeExecution
646-- -> Cache -> Filter
644-- -> Cache -> Filter
647
645
648let filter = RangeFilter (FieldName "age")
646let filter = RangeFilter (FieldName "age")
649− (Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0)))
647+ (RangeGtLt (GreaterThan 1000.0) (LessThan 100000.0))
650 RangeExecutionIndex False
648 RangeExecutionIndex False
651
649
652```
650```
653
651
654−##### Half Range
655−
656``` {.haskell}
652``` {.haskell}
657
653
658let filter = RangeFilter (FieldName "age")
654let filter = RangeFilter (FieldName "age")
659− (Left (HalfRangeLt (LessThan 100000.0)))
655+ (RangeLte (LessThanEq 100000.0))
660 RangeExecutionIndex False
656 RangeExecutionIndex False
661
657
662```
658```
663
659
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+
664#### Regexp Filter
679#### Regexp Filter
665
680
666``` {.haskell}
681``` {.haskell}
@@ -928,4 +943,3 @@ Photo Origin
928============
943============
929
944
930Photo from HA! Designs: <https://www.flickr.com/photos/hadesigns/>
945Photo from HA! Designs: <https://www.flickr.com/photos/hadesigns/>
931−
src/Database/Bloodhound/Types.hs+57−61
@@ -24,9 +24,7 @@
24module Database.Bloodhound.Types
24module Database.Bloodhound.Types
25 ( defaultCache
25 ( defaultCache
26 , defaultIndexSettings
26 , defaultIndexSettings
27− , halfRangeToKV
28 , mkSort
27 , mkSort
29− , rangeToKV
30 , showText
28 , showText
31 , unpackId
29 , unpackId
32 , mkMatchQuery
30 , mkMatchQuery
@@ -69,13 +67,16 @@ module Database.Bloodhound.Types
69 , DistanceRange(..)
67 , DistanceRange(..)
70 , OptimizeBbox(..)
68 , OptimizeBbox(..)
71 , LatLon(..)
69 , LatLon(..)
72− , Range(..)
70+ , RangeValue(..)
73− , HalfRange(..)
74 , RangeExecution(..)
71 , RangeExecution(..)
75 , LessThan(..)
72 , LessThan(..)
76 , LessThanEq(..)
73 , LessThanEq(..)
77 , GreaterThan(..)
74 , GreaterThan(..)
78 , GreaterThanEq(..)
75 , GreaterThanEq(..)
76+ , LessThanD(..)
77+ , LessThanEqD(..)
78+ , GreaterThanD(..)
79+ , GreaterThanEqD(..)
79 , Regexp(..)
80 , Regexp(..)
80 , RegexpFlags(..)
81 , RegexpFlags(..)
81 , RegexpFlag(..)
82 , RegexpFlag(..)
@@ -636,10 +637,10 @@ data RegexpQuery =
636
637
637data RangeQuery =
638data RangeQuery =
638 RangeQuery { rangeQueryField :: FieldName
639 RangeQuery { rangeQueryField :: FieldName
639− , rangeQueryRange :: Either HalfRange Range
640+ , rangeQueryRange :: RangeValue
640 , rangeQueryBoost :: Boost } deriving (Eq, Show)
641 , rangeQueryBoost :: Boost } deriving (Eq, Show)
641
642
642−mkRangeQuery :: FieldName -> Either HalfRange Range -> RangeQuery
643+mkRangeQuery :: FieldName -> RangeValue -> RangeQuery
643mkRangeQuery f r = RangeQuery f r (Boost 1.0)
644mkRangeQuery f r = RangeQuery f r (Boost 1.0)
644
645
645data SimpleQueryStringQuery =
646data SimpleQueryStringQuery =
@@ -922,7 +923,7 @@ data Filter = AndFilter [Filter] Cache
922 | LimitFilter Int
923 | LimitFilter Int
923 | MissingFilter FieldName Existence NullValue
924 | MissingFilter FieldName Existence NullValue
924 | PrefixFilter FieldName PrefixValue Cache
925 | PrefixFilter FieldName PrefixValue Cache
925− | RangeFilter FieldName (Either HalfRange Range) RangeExecution Cache
926+ | RangeFilter FieldName RangeValue RangeExecution Cache
926 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
927 | RegexpFilter FieldName Regexp RegexpFlags CacheName Cache CacheKey
927 | TermFilter Term Cache
928 | TermFilter Term Cache
928 deriving (Eq, Show)
929 deriving (Eq, Show)
@@ -930,22 +931,6 @@ data Filter = AndFilter [Filter] Cache
930data ZeroTermsQuery = ZeroTermsNone
931data ZeroTermsQuery = ZeroTermsNone
931 | ZeroTermsAll deriving (Eq, Show)
932 | ZeroTermsAll deriving (Eq, Show)
932
933
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−
949data RangeExecution = RangeExecutionIndex
934data RangeExecution = RangeExecutionIndex
950 | RangeExecutionFielddata deriving (Eq, Show)
935 | RangeExecutionFielddata deriving (Eq, Show)
951
936
@@ -962,19 +947,52 @@ data RegexpFlag = AnyString
962 | Intersection
947 | Intersection
963 | Interval deriving (Eq, Show)
948 | Interval deriving (Eq, Show)
964
949
965−halfRangeToKV :: HalfRange -> (Text, Double)
950+newtype LessThan = LessThan Double deriving (Eq, Show)
966−halfRangeToKV (HalfRangeLt (LessThan n)) = ("lt", n)
951+newtype LessThanEq = LessThanEq Double deriving (Eq, Show)
967−halfRangeToKV (HalfRangeLte (LessThanEq n)) = ("lte", n)
952+newtype GreaterThan = GreaterThan Double deriving (Eq, Show)
968−halfRangeToKV (HalfRangeGt (GreaterThan n)) = ("gt", n)
953+newtype GreaterThanEq = GreaterThanEq Double deriving (Eq, Show)
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)
976
954
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]
978
996
979data Term = Term { termField :: Text
997data Term = Term { termField :: Text
980 , termValue :: Text } deriving (Eq, Show)
998 , termValue :: Text } deriving (Eq, Show)
@@ -1338,24 +1356,11 @@ instance ToJSON Filter where
1338 object [fieldName .= fieldValue
1356 object [fieldName .= fieldValue
1339 , "_cache" .= cache]]
1357 , "_cache" .= cache]]
1340
1358
1341− toJSON (RangeFilter (FieldName fieldName) (Left halfRange) rangeExecution cache) =
1359+ toJSON (RangeFilter (FieldName fieldName) rangeValue 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) =
1351 object ["range" .=
1360 object ["range" .=
1352− object [fieldName .=
1361+ object [ fieldName .= object (rangeValueToPair rangeValue)
1353− object [lessKey .= lessVal
1354− , greaterKey .= greaterVal]
1355 , "execution" .= toJSON rangeExecution
1362 , "execution" .= toJSON rangeExecution
1356 , "_cache" .= cache]]
1363 , "_cache" .= cache]]
1357− where
1358− (lessKey, lessVal, greaterKey, greaterVal) = rangeToKV range
1359
1364
1360 toJSON (RegexpFilter (FieldName fieldName)
1365 toJSON (RegexpFilter (FieldName fieldName)
1361 (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
1366 (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
@@ -1549,18 +1554,9 @@ instance ToJSON QueryStringQuery where
1549
1554
1550
1555
1551instance ToJSON RangeQuery where
1556instance ToJSON RangeQuery where
1552− toJSON (RangeQuery (FieldName fieldName) (Right range) boost) =
1557+ toJSON (RangeQuery (FieldName fieldName) 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) =
1560 object [ fieldName .= conjoined ]
1558 object [ fieldName .= conjoined ]
1561− where conjoined = [ "boost" .= toJSON boost
1559+ where conjoined = [ "boost" .= toJSON boost ] ++ (rangeValueToPair range)
1562− , rqKey .= rqVal ]
1563− (rqKey, rqVal) = halfRangeToKV halfRange
1564
1560
1565
1561
1566instance ToJSON PrefixQuery where
1562instance ToJSON PrefixQuery where
tests/tests.hs+18−2
@@ -432,15 +432,31 @@ main = hspec $ do
432 myTweet <- searchTweet search
432 myTweet <- searchTweet search
433 myTweet `shouldBe` Right exampleTweet
433 myTweet `shouldBe` Right exampleTweet
434
434
435− it "returns document for range filter" $ do
435+ it "returns document for Double range filter" $ do
436 _ <- insertData
436 _ <- insertData
437 let filter = RangeFilter (FieldName "age")
437 let filter = RangeFilter (FieldName "age")
438− (Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0)))
438+ (RangeDoubleGtLt (GreaterThan 1000.0) (LessThan 100000.0))
439 RangeExecutionIndex False
439 RangeExecutionIndex False
440 let search = mkSearch Nothing (Just filter)
440 let search = mkSearch Nothing (Just filter)
441 myTweet <- searchTweet search
441 myTweet <- searchTweet search
442 myTweet `shouldBe` Right exampleTweet
442 myTweet `shouldBe` Right exampleTweet
443
443
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+
444 it "returns document for regexp filter" $ do
460 it "returns document for regexp filter" $ do
445 _ <- insertData
461 _ <- insertData
446 let filter = RegexpFilter (FieldName "user") (Regexp "bite.*app")
462 let filter = RegexpFilter (FieldName "user") (Regexp "bite.*app")