2014-04-10 09:25:17 +04:00
|
|
|
{-# LANGUAGE DeriveGeneric #-}
|
2014-06-15 21:56:47 +04:00
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
2014-04-07 03:29:46 +04:00
|
|
|
|
2014-04-05 07:55:53 +04:00
|
|
|
module Main where
|
|
|
|
|
2014-06-15 21:56:47 +04:00
|
|
|
import Control.Applicative
|
2014-04-15 11:50:32 +04:00
|
|
|
import Database.Bloodhound
|
2014-04-07 03:29:46 +04:00
|
|
|
import Data.Aeson
|
2014-06-15 21:56:47 +04:00
|
|
|
import Data.List (nub)
|
|
|
|
import Data.List.NonEmpty (NonEmpty(..))
|
2014-04-07 04:53:53 +04:00
|
|
|
import Data.Time.Calendar (Day(..))
|
|
|
|
import Data.Time.Clock (secondsToDiffTime, UTCTime(..))
|
2014-04-07 03:29:46 +04:00
|
|
|
import Data.Text (Text)
|
2014-06-15 21:56:47 +04:00
|
|
|
import qualified Data.Text as T
|
2014-04-07 03:29:46 +04:00
|
|
|
import GHC.Generics (Generic)
|
2014-05-19 23:59:40 +04:00
|
|
|
import Network.HTTP.Client
|
2014-04-05 07:55:53 +04:00
|
|
|
import qualified Network.HTTP.Types.Status as NHTS
|
2014-04-27 09:49:44 +04:00
|
|
|
import Prelude hiding (filter, putStrLn)
|
2014-04-05 07:55:53 +04:00
|
|
|
import Test.Hspec
|
2014-06-15 21:56:47 +04:00
|
|
|
import Test.Hspec.QuickCheck (prop)
|
|
|
|
import Test.QuickCheck
|
2014-04-05 07:55:53 +04:00
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
testServer :: Server
|
2014-04-11 01:39:10 +04:00
|
|
|
testServer = Server "http://localhost:9200"
|
2014-04-27 09:49:44 +04:00
|
|
|
testIndex :: IndexName
|
2014-05-15 19:43:46 +04:00
|
|
|
testIndex = IndexName "bloodhound-tests-twitter-1"
|
2014-04-27 09:49:44 +04:00
|
|
|
testMapping :: MappingName
|
2014-04-12 08:02:13 +04:00
|
|
|
testMapping = MappingName "tweet"
|
2014-04-11 04:54:40 +04:00
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
validateStatus :: Response body -> Int -> Expectation
|
2014-04-11 04:54:40 +04:00
|
|
|
validateStatus resp expected =
|
|
|
|
(NHTS.statusCode $ responseStatus resp)
|
|
|
|
`shouldBe` (expected :: Int)
|
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
createExampleIndex :: IO Reply
|
2014-04-07 03:29:46 +04:00
|
|
|
createExampleIndex = createIndex testServer defaultIndexSettings testIndex
|
2014-04-27 09:49:44 +04:00
|
|
|
deleteExampleIndex :: IO Reply
|
2014-04-07 03:29:46 +04:00
|
|
|
deleteExampleIndex = deleteIndex testServer testIndex
|
|
|
|
|
2014-04-11 01:39:10 +04:00
|
|
|
data Location = Location { lat :: Double
|
|
|
|
, lon :: Double } deriving (Eq, Generic, Show)
|
|
|
|
|
2014-04-10 09:25:17 +04:00
|
|
|
data Tweet = Tweet { user :: Text
|
2014-04-07 03:29:46 +04:00
|
|
|
, postDate :: UTCTime
|
2014-04-11 01:39:10 +04:00
|
|
|
, message :: Text
|
2014-04-11 09:48:29 +04:00
|
|
|
, age :: Int
|
2014-04-11 01:39:10 +04:00
|
|
|
, location :: Location }
|
2014-04-07 03:29:46 +04:00
|
|
|
deriving (Eq, Generic, Show)
|
|
|
|
|
2014-04-11 01:39:10 +04:00
|
|
|
instance ToJSON Tweet
|
2014-04-07 03:29:46 +04:00
|
|
|
instance FromJSON Tweet
|
2014-04-11 01:39:10 +04:00
|
|
|
instance ToJSON Location
|
|
|
|
instance FromJSON Location
|
|
|
|
|
|
|
|
data TweetMapping = TweetMapping deriving (Eq, Show)
|
|
|
|
|
|
|
|
instance ToJSON TweetMapping where
|
|
|
|
toJSON TweetMapping =
|
|
|
|
object ["tweet" .=
|
2014-06-15 06:27:27 +04:00
|
|
|
object ["properties" .=
|
|
|
|
object ["location" .= object ["type" .= ("geo_point" :: Text)]]]]
|
2014-04-07 03:29:46 +04:00
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
exampleTweet :: Tweet
|
2014-04-07 04:53:53 +04:00
|
|
|
exampleTweet = Tweet { user = "bitemyapp"
|
2014-04-07 07:51:30 +04:00
|
|
|
, postDate = UTCTime
|
|
|
|
(ModifiedJulianDay 55000)
|
2014-04-07 04:53:53 +04:00
|
|
|
(secondsToDiffTime 10)
|
2014-04-11 01:39:10 +04:00
|
|
|
, message = "Use haskell!"
|
2014-04-11 09:48:29 +04:00
|
|
|
, age = 10000
|
2014-04-11 01:39:10 +04:00
|
|
|
, location = Location 40.12 (-71.34) }
|
2014-04-05 07:55:53 +04:00
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
otherTweet :: Tweet
|
2014-04-15 12:10:47 +04:00
|
|
|
otherTweet = Tweet { user = "notmyapp"
|
|
|
|
, postDate = UTCTime
|
|
|
|
(ModifiedJulianDay 55000)
|
|
|
|
(secondsToDiffTime 11)
|
|
|
|
, message = "Use haskell!"
|
|
|
|
, age = 1000
|
|
|
|
, location = Location 40.12 (-71.34) }
|
|
|
|
|
2014-04-10 09:25:17 +04:00
|
|
|
insertData :: IO ()
|
|
|
|
insertData = do
|
|
|
|
_ <- deleteExampleIndex
|
2014-04-27 09:49:44 +04:00
|
|
|
_ <- createExampleIndex
|
2014-06-15 08:38:27 +04:00
|
|
|
_ <- putMapping testServer testIndex testMapping TweetMapping
|
2014-04-27 09:49:44 +04:00
|
|
|
_ <- indexDocument testServer testIndex testMapping exampleTweet (DocId "1")
|
2014-04-11 01:39:10 +04:00
|
|
|
_ <- refreshIndex testServer testIndex
|
2014-04-10 09:25:17 +04:00
|
|
|
return ()
|
|
|
|
|
2014-04-15 12:10:47 +04:00
|
|
|
insertOther :: IO ()
|
|
|
|
insertOther = do
|
2014-04-27 09:49:44 +04:00
|
|
|
_ <- indexDocument testServer testIndex testMapping otherTweet (DocId "2")
|
2014-04-15 12:10:47 +04:00
|
|
|
_ <- refreshIndex testServer testIndex
|
|
|
|
return ()
|
|
|
|
|
2014-04-11 01:39:10 +04:00
|
|
|
searchTweet :: Search -> IO (Either String Tweet)
|
|
|
|
searchTweet search = do
|
2014-04-11 01:49:15 +04:00
|
|
|
reply <- searchByIndex testServer testIndex search
|
2014-04-10 09:25:17 +04:00
|
|
|
let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
|
|
|
|
let myTweet = fmap (hitSource . head . hits . searchHits) result
|
|
|
|
return myTweet
|
|
|
|
|
2014-04-11 04:15:10 +04:00
|
|
|
searchExpectNoResults :: Search -> IO ()
|
|
|
|
searchExpectNoResults search = do
|
|
|
|
reply <- searchByIndex testServer testIndex search
|
|
|
|
let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
|
|
|
|
let emptyHits = fmap (hits . searchHits) result
|
|
|
|
emptyHits `shouldBe` Right []
|
|
|
|
|
2014-04-12 11:56:33 +04:00
|
|
|
data BulkTest = BulkTest { name :: Text } deriving (Eq, Generic, Show)
|
|
|
|
instance FromJSON BulkTest
|
|
|
|
instance ToJSON BulkTest
|
|
|
|
|
2014-06-15 21:56:47 +04:00
|
|
|
noDuplicates :: Eq a => [a] -> Bool
|
|
|
|
noDuplicates xs = nub xs == xs
|
|
|
|
|
|
|
|
instance Arbitrary RegexpFlags where
|
|
|
|
arbitrary = oneof [ pure AllRegexpFlags
|
|
|
|
, pure NoRegexpFlags
|
|
|
|
, SomeRegexpFlags <$> arbitrary
|
|
|
|
]
|
|
|
|
|
|
|
|
instance Arbitrary a => Arbitrary (NonEmpty a) where
|
|
|
|
arbitrary = liftA2 (:|) arbitrary arbitrary
|
|
|
|
|
|
|
|
instance Arbitrary RegexpFlag where
|
|
|
|
arbitrary = oneof [ pure AnyString
|
|
|
|
, pure Automaton
|
|
|
|
, pure Complement
|
|
|
|
, pure Empty
|
|
|
|
, pure Intersection
|
|
|
|
, pure Interval
|
|
|
|
]
|
|
|
|
|
2014-04-05 07:55:53 +04:00
|
|
|
main :: IO ()
|
|
|
|
main = hspec $ do
|
2014-04-11 04:54:40 +04:00
|
|
|
|
2014-04-10 10:41:21 +04:00
|
|
|
describe "index create/delete API" $ do
|
|
|
|
it "creates and then deletes the requested index" $ do
|
|
|
|
-- priming state.
|
|
|
|
_ <- deleteExampleIndex
|
|
|
|
resp <- createExampleIndex
|
|
|
|
deleteResp <- deleteExampleIndex
|
|
|
|
validateStatus resp 200
|
|
|
|
validateStatus deleteResp 200
|
2014-04-10 09:25:17 +04:00
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
|
2014-04-07 03:29:46 +04:00
|
|
|
describe "document API" $ do
|
|
|
|
it "indexes, gets, and then deletes the generated document" $ do
|
2014-04-10 10:41:21 +04:00
|
|
|
_ <- insertData
|
2014-04-12 08:02:13 +04:00
|
|
|
docInserted <- getDocument testServer testIndex testMapping (DocId "1")
|
2014-04-11 04:54:40 +04:00
|
|
|
let newTweet = eitherDecode
|
|
|
|
(responseBody docInserted) :: Either String (EsResult Tweet)
|
2014-04-10 09:25:17 +04:00
|
|
|
fmap _source newTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
|
2014-04-12 11:56:33 +04:00
|
|
|
describe "bulk API" $ do
|
|
|
|
it "inserts all documents we request" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let firstTest = BulkTest "blah"
|
|
|
|
let secondTest = BulkTest "bloo"
|
2014-05-15 19:43:46 +04:00
|
|
|
let firstDoc = BulkIndex testIndex
|
2014-09-19 04:20:03 +04:00
|
|
|
testMapping (DocId "2") (toJSON firstTest)
|
2014-05-15 19:43:46 +04:00
|
|
|
let secondDoc = BulkCreate testIndex
|
2014-09-19 04:20:03 +04:00
|
|
|
testMapping (DocId "3") (toJSON secondTest)
|
2014-04-12 11:56:33 +04:00
|
|
|
let stream = [firstDoc, secondDoc]
|
2014-04-27 09:49:44 +04:00
|
|
|
_ <- bulk testServer stream
|
2014-04-12 11:56:33 +04:00
|
|
|
_ <- refreshIndex testServer testIndex
|
|
|
|
fDoc <- getDocument testServer testIndex testMapping (DocId "2")
|
|
|
|
sDoc <- getDocument testServer testIndex testMapping (DocId "3")
|
|
|
|
let maybeFirst = eitherDecode $ responseBody fDoc :: Either String (EsResult BulkTest)
|
|
|
|
let maybeSecond = eitherDecode $ responseBody sDoc :: Either String (EsResult BulkTest)
|
|
|
|
fmap _source maybeFirst `shouldBe` Right firstTest
|
|
|
|
fmap _source maybeSecond `shouldBe` Right secondTest
|
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
|
2014-04-12 06:58:38 +04:00
|
|
|
describe "query API" $ do
|
|
|
|
it "returns document for term query and identity filter" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let query = TermQuery (Term "user" "bitemyapp") Nothing
|
|
|
|
let filter = IdentityFilter <&&> IdentityFilter
|
|
|
|
let search = mkSearch (Just query) (Just filter)
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
it "returns document for match query" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let query = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
|
|
|
|
let search = mkSearch (Just query) Nothing
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 13:57:34 +04:00
|
|
|
it "returns document for multi-match query" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let fields = [FieldName "user", FieldName "message"]
|
|
|
|
let query = QueryMultiMatchQuery $ mkMultiMatchQuery fields (QueryString "bitemyapp")
|
|
|
|
let search = mkSearch (Just query) Nothing
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 15:00:08 +04:00
|
|
|
it "returns document for bool query" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let innerQuery = QueryMatchQuery $
|
|
|
|
mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
|
|
|
|
let query = QueryBoolQuery $
|
|
|
|
mkBoolQuery (Just innerQuery) Nothing Nothing
|
|
|
|
let search = mkSearch (Just query) Nothing
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 16:22:12 +04:00
|
|
|
it "returns document for boosting query" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let posQuery = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
|
|
|
|
let negQuery = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "notmyapp")
|
|
|
|
let query = QueryBoostingQuery $ BoostingQuery posQuery negQuery (Boost 0.2)
|
|
|
|
let search = mkSearch (Just query) Nothing
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-27 22:25:50 +04:00
|
|
|
it "returns document for common terms query" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let query = QueryCommonTermsQuery $
|
|
|
|
CommonTermsQuery (FieldName "user")
|
|
|
|
(QueryString "bitemyapp")
|
|
|
|
(CutoffFrequency 0.0001)
|
|
|
|
Or Or Nothing Nothing Nothing Nothing
|
|
|
|
let search = mkSearch (Just query) Nothing
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
|
|
|
|
2014-04-15 12:10:47 +04:00
|
|
|
describe "sorting" $ do
|
|
|
|
it "returns documents in the right order" $ do
|
|
|
|
_ <- insertData
|
|
|
|
_ <- insertOther
|
|
|
|
let sortSpec = DefaultSortSpec $ mkSort (FieldName "age") Ascending
|
|
|
|
let search = Search Nothing
|
|
|
|
(Just IdentityFilter) (Just [sortSpec])
|
|
|
|
False 0 10
|
|
|
|
reply <- searchByIndex testServer testIndex search
|
|
|
|
let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
|
|
|
|
let myTweet = fmap (hitSource . head . hits . searchHits) result
|
|
|
|
myTweet `shouldBe` Right otherTweet
|
|
|
|
|
2014-04-27 09:49:44 +04:00
|
|
|
|
2014-04-11 01:39:10 +04:00
|
|
|
describe "filtering API" $ do
|
|
|
|
it "returns document for composed boolmatch and identity" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let queryFilter = BoolFilter (MustMatch (Term "user" "bitemyapp") False)
|
|
|
|
<&&> IdentityFilter
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just queryFilter)
|
2014-04-11 01:39:10 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-07-01 19:01:32 +04:00
|
|
|
it "returns document for term filter" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let termFilter = TermFilter (Term "user" "bitemyapp") False
|
|
|
|
let search = mkSearch Nothing (Just termFilter)
|
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "returns document for existential filter" $ do
|
2014-04-11 01:39:10 +04:00
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just (ExistsFilter (FieldName "user")))
|
2014-04-11 01:39:10 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "returns document for geo boundingbox filter" $ do
|
2014-04-10 09:25:17 +04:00
|
|
|
_ <- insertData
|
2014-04-11 01:39:10 +04:00
|
|
|
let box = GeoBoundingBox (LatLon 40.73 (-74.1)) (LatLon 40.10 (-71.12))
|
2014-08-15 22:39:09 +04:00
|
|
|
let bbConstraint = GeoBoundingBoxConstraint (FieldName "tweet.location") box False GeoFilterMemory
|
|
|
|
let geoFilter = GeoBoundingBoxFilter bbConstraint
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 01:39:10 +04:00
|
|
|
myTweet <- searchTweet search
|
2014-04-10 09:25:17 +04:00
|
|
|
myTweet `shouldBe` Right exampleTweet
|
2014-04-11 01:49:15 +04:00
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "doesn't return document for nonsensical boundingbox filter" $ do
|
2014-04-11 01:49:15 +04:00
|
|
|
_ <- insertData
|
|
|
|
let box = GeoBoundingBox (LatLon 0.73 (-4.1)) (LatLon 0.10 (-1.12))
|
2014-08-15 22:39:09 +04:00
|
|
|
let bbConstraint = GeoBoundingBoxConstraint (FieldName "tweet.location") box False GeoFilterMemory
|
|
|
|
let geoFilter = GeoBoundingBoxFilter bbConstraint
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:15:10 +04:00
|
|
|
searchExpectNoResults search
|
2014-04-11 04:01:42 +04:00
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "returns document for geo distance filter" $ do
|
2014-04-11 04:01:42 +04:00
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
|
2014-04-11 04:01:42 +04:00
|
|
|
let distance = Distance 10.0 Miles
|
|
|
|
let optimizeBbox = OptimizeGeoFilterType GeoFilterMemory
|
2014-04-11 04:15:10 +04:00
|
|
|
let geoFilter = GeoDistanceFilter geoPoint distance SloppyArc optimizeBbox False
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:01:42 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
2014-04-11 04:15:10 +04:00
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "returns document for geo distance range filter" $ do
|
2014-04-11 04:15:10 +04:00
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
|
2014-04-11 04:15:10 +04:00
|
|
|
let distanceRange = DistanceRange (Distance 0.0 Miles) (Distance 10.0 Miles)
|
|
|
|
let geoFilter = GeoDistanceRangeFilter geoPoint distanceRange
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:15:10 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
2014-04-11 04:54:40 +04:00
|
|
|
it "doesn't return document for wild geo distance range filter" $ do
|
2014-04-11 04:15:10 +04:00
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
|
2014-04-11 04:15:10 +04:00
|
|
|
let distanceRange = DistanceRange (Distance 100.0 Miles) (Distance 1000.0 Miles)
|
|
|
|
let geoFilter = GeoDistanceRangeFilter geoPoint distanceRange
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:15:10 +04:00
|
|
|
searchExpectNoResults search
|
2014-04-11 04:54:40 +04:00
|
|
|
|
|
|
|
it "returns document for geo polygon filter" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let points = [LatLon 40.0 (-70.00),
|
|
|
|
LatLon 40.0 (-72.00),
|
|
|
|
LatLon 41.0 (-70.00),
|
|
|
|
LatLon 41.0 (-72.00)]
|
2014-04-12 03:29:10 +04:00
|
|
|
let geoFilter = GeoPolygonFilter (FieldName "tweet.location") points
|
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:54:40 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
|
|
|
|
|
|
|
it "doesn't return document for bad geo polygon filter" $ do
|
|
|
|
_ <- insertData
|
|
|
|
let points = [LatLon 40.0 (-70.00),
|
|
|
|
LatLon 40.0 (-71.00),
|
|
|
|
LatLon 41.0 (-70.00),
|
|
|
|
LatLon 41.0 (-71.00)]
|
2014-04-12 03:29:10 +04:00
|
|
|
let geoFilter = GeoPolygonFilter (FieldName "tweet.location") points
|
|
|
|
let search = mkSearch Nothing (Just geoFilter)
|
2014-04-11 04:54:40 +04:00
|
|
|
searchExpectNoResults search
|
2014-04-11 05:04:24 +04:00
|
|
|
|
|
|
|
it "returns document for ids filter" $ do
|
|
|
|
_ <- insertData
|
2014-04-12 08:02:13 +04:00
|
|
|
let filter = IdsFilter (MappingName "tweet") [DocId "1"]
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just filter)
|
2014-04-11 05:04:24 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
2014-04-11 09:48:29 +04:00
|
|
|
|
|
|
|
it "returns document for range filter" $ do
|
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let filter = RangeFilter (FieldName "age")
|
2014-04-11 09:48:29 +04:00
|
|
|
(Right (RangeLtGt (LessThan 100000.0) (GreaterThan 1000.0)))
|
|
|
|
RangeExecutionIndex False
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just filter)
|
2014-04-11 09:48:29 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
2014-04-11 10:08:36 +04:00
|
|
|
|
|
|
|
it "returns document for regexp filter" $ do
|
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let filter = RegexpFilter (FieldName "user") (Regexp "bite.*app")
|
2014-06-15 21:56:47 +04:00
|
|
|
AllRegexpFlags (CacheName "test") False (CacheKey "key")
|
2014-04-12 03:29:10 +04:00
|
|
|
let search = mkSearch Nothing (Just filter)
|
2014-04-11 10:08:36 +04:00
|
|
|
myTweet <- searchTweet search
|
|
|
|
myTweet `shouldBe` Right exampleTweet
|
2014-04-11 10:10:06 +04:00
|
|
|
|
|
|
|
it "doesn't return document for non-matching regexp filter" $ do
|
|
|
|
_ <- insertData
|
2014-04-12 03:29:10 +04:00
|
|
|
let filter = RegexpFilter (FieldName "user")
|
2014-06-15 21:56:47 +04:00
|
|
|
(Regexp "boy") AllRegexpFlags
|
2014-04-12 03:29:10 +04:00
|
|
|
(CacheName "test") False (CacheKey "key")
|
|
|
|
let search = mkSearch Nothing (Just filter)
|
2014-04-11 10:10:06 +04:00
|
|
|
searchExpectNoResults search
|
2014-08-15 23:41:03 +04:00
|
|
|
|
|
|
|
|
2014-06-15 21:56:47 +04:00
|
|
|
describe "ToJSON RegexpFlags" $ do
|
|
|
|
it "generates the correct JSON for AllRegexpFlags" $
|
|
|
|
toJSON AllRegexpFlags `shouldBe` String "ALL"
|
|
|
|
|
|
|
|
it "generates the correct JSON for NoRegexpFlags" $
|
|
|
|
toJSON NoRegexpFlags `shouldBe` String "NONE"
|
|
|
|
|
|
|
|
it "generates the correct JSON for SomeRegexpFlags" $
|
|
|
|
let flags = AnyString :| [ Automaton
|
|
|
|
, Complement
|
|
|
|
, Empty
|
|
|
|
, Intersection
|
|
|
|
, Interval ]
|
|
|
|
in toJSON (SomeRegexpFlags flags) `shouldBe` String "ANYSTRING|AUTOMATON|COMPLEMENT|EMPTY|INTERSECTION|INTERVAL"
|
|
|
|
|
|
|
|
prop "removes duplicates from flags" $ \(flags :: RegexpFlags) ->
|
|
|
|
let String str = toJSON flags
|
|
|
|
flagStrs = T.splitOn "|" str
|
|
|
|
in noDuplicates flagStrs
|