From e639c77aa2aea8f225f2245952b084e33189d96e Mon Sep 17 00:00:00 2001 From: Eduardo Jorge Date: Mon, 8 Jul 2019 19:25:58 +0100 Subject: [PATCH] Add websearch_to_tsquery support (#1339) --- CHANGELOG.md | 2 ++ src/PostgREST/Types.hs | 3 ++- test/Feature/AndOrParamsSpec.hs | 21 +++++++++++++--- test/Feature/QuerySpec.hs | 43 ++++++++++++++++++++++++++++++--- test/Feature/RpcSpec.hs | 7 +++++- test/Main.hs | 4 +-- 6 files changed, 69 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d482f6145..18198a2c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- #1243, Add websearch_to_tsquery support - @herulume + ### Added ### Fixed diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index 426370da2..516f9eba6 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -287,7 +287,8 @@ ftsOperators :: M.HashMap Operator SqlFragment ftsOperators = M.fromList [ ("fts", "@@ to_tsquery"), ("plfts", "@@ plainto_tsquery"), - ("phfts", "@@ phraseto_tsquery") + ("phfts", "@@ phraseto_tsquery"), + ("wfts", "@@ websearch_to_tsquery") ] data OpExpr = OpExpr Bool Operation deriving (Eq, Show) diff --git a/test/Feature/AndOrParamsSpec.hs b/test/Feature/AndOrParamsSpec.hs index e68a78811..9193b91b4 100644 --- a/test/Feature/AndOrParamsSpec.hs +++ b/test/Feature/AndOrParamsSpec.hs @@ -7,12 +7,12 @@ import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Protolude hiding (get) +import PostgREST.Types (PgVersion, pgVersion112) +import Protolude hiding (get) import SpecHelper - -spec :: SpecWith Application -spec = +spec :: PgVersion -> SpecWith Application +spec actualPgVersion = describe "and/or params used for complex boolean logic" $ do context "used with GET" $ do context "or param" $ do @@ -80,6 +80,19 @@ spec = {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }, {"text_search_vector": "'art':4 'spass':5 'unmog':7"} ]|] { matchHeaders = [matchContentTypeJson] } + + when (actualPgVersion >= pgVersion112) $ + it "can handle wfts (websearch_to_tsquery)" $ + get "/tsearch?or=(text_search_vector.plfts(german).Art,text_search_vector.plfts(french).amusant,text_search_vector.not.wfts(english).impossible)" + `shouldRespondWith` + [json|[ + {"text_search_vector": "'also':2 'fun':3 'possibl':8" }, + {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }, + {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }, + {"text_search_vector": "'art':4 'spass':5 'unmog':7" } + ]|] + { matchHeaders = [matchContentTypeJson] } + it "can handle cs and cd" $ get "/entities?or=(arr.cs.{1,2,3},arr.cd.{1})&select=id" `shouldRespondWith` [json|[{ "id": 1 },{ "id": 3 }]|] { matchHeaders = [matchContentTypeJson] } diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index f9e918f0e..9528a63c6 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -10,11 +10,12 @@ import Test.Hspec.Wai.JSON import Text.Heredoc -import Protolude hiding (get) +import PostgREST.Types (PgVersion, pgVersion112) +import Protolude hiding (get) import SpecHelper -spec :: SpecWith Application -spec = do +spec :: PgVersion -> SpecWith Application +spec actualPgVersion = do describe "Querying a table with a column called count" $ it "should not confuse count column with pg_catalog.count aggregate" $ @@ -119,6 +120,29 @@ spec = do [json| [ {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] { matchHeaders = [matchContentTypeJson] } + when (actualPgVersion >= pgVersion112) $ do + it "finds matches with websearch_to_tsquery" $ + get "/tsearch?text_search_vector=wfts.The%20Fat%20Rats" `shouldRespondWith` + [json| [ {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] + { matchHeaders = [matchContentTypeJson] } + + it "can use boolean operators(and, or, -) in websearch_to_tsquery" $ do + get "/tsearch?text_search_vector=wfts.fun%20and%20possible" + `shouldRespondWith` + [json| [ {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] |] + { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=wfts.impossible%20or%20possible" + `shouldRespondWith` + [json| [ + {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}, + {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] + |] + { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=wfts.fun%20and%20-possible" + `shouldRespondWith` + [json| [ {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}] |] + { matchHeaders = [matchContentTypeJson] } + it "finds matches with different dictionaries" $ do get "/tsearch?text_search_vector=fts(french).amusant" `shouldRespondWith` [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] @@ -127,6 +151,12 @@ spec = do [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] { matchHeaders = [matchContentTypeJson] } + when (actualPgVersion >= pgVersion112) $ + get "/tsearch?text_search_vector=wfts(french).amusant%20impossible" + `shouldRespondWith` + [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] + { matchHeaders = [matchContentTypeJson] } + it "can be negated with not operator" $ do get "/tsearch?text_search_vector=not.fts.impossible%7Cfat%7Cfun" `shouldRespondWith` [json| [ @@ -145,6 +175,13 @@ spec = do {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] { matchHeaders = [matchContentTypeJson] } + when (actualPgVersion >= pgVersion112) $ + get "/tsearch?text_search_vector=not.wfts(english).impossible%20or%20fat%20or%20fun" + `shouldRespondWith` + [json| [ + {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, + {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] + { matchHeaders = [matchContentTypeJson] } it "matches with computed column" $ get "/items?always_true=eq.true&order=id.asc" `shouldRespondWith` diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index a5466856a..e4c3125e7 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -12,7 +12,8 @@ import Test.Hspec.Wai.JSON import Text.Heredoc import PostgREST.Types (PgVersion, pgVersion100, pgVersion109, - pgVersion110, pgVersion114, pgVersion95) + pgVersion110, pgVersion112, pgVersion114, + pgVersion95) import Protolude hiding (get) import SpecHelper @@ -531,6 +532,10 @@ spec actualPgVersion = get "/rpc/get_tsearch?text_search_vector=not.fts(english).fun%7Crat" `shouldRespondWith` [json|[{"text_search_vector":"'amus':5 'fair':7 'impossibl':9 'peu':4"},{"text_search_vector":"'art':4 'spass':5 'unmog':7"}]|] { matchHeaders = [matchContentTypeJson] } + when (actualPgVersion >= pgVersion112) $ + get "/rpc/get_tsearch?text_search_vector=wfts.impossible" `shouldRespondWith` + [json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|] + { matchHeaders = [matchContentTypeJson] } it "should work with an argument of custom type in public schema" $ get "/rpc/test_arg?my_arg=something" `shouldRespondWith` diff --git a/test/Main.hs b/test/Main.hs index 152f38077..32e7cac03 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -91,12 +91,12 @@ main = do , ("Feature.DeleteSpec" , Feature.DeleteSpec.spec) , ("Feature.InsertSpec" , Feature.InsertSpec.spec actualPgVersion) , ("Feature.JsonOperatorSpec" , Feature.JsonOperatorSpec.spec actualPgVersion) - , ("Feature.QuerySpec" , Feature.QuerySpec.spec) + , ("Feature.QuerySpec" , Feature.QuerySpec.spec actualPgVersion) , ("Feature.RpcSpec" , Feature.RpcSpec.spec actualPgVersion) , ("Feature.RangeSpec" , Feature.RangeSpec.spec) , ("Feature.SingularSpec" , Feature.SingularSpec.spec) , ("Feature.StructureSpec" , Feature.StructureSpec.spec) - , ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec) + , ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec actualPgVersion) ] ++ extraSpecs hspec $ do