diff --git a/CHANGELOG.md b/CHANGELOG.md index 35caa3f1c..aedf40a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #1917, Add error codes with the `"PGRST"` prefix to the error response body to differentiate PostgREST errors from PostgreSQL errors - @laurenceisla - #1917, Normalize the error response body by always having the `detail` and `hint` error fields with a `null` value if they are empty - @laurenceisla - #2176, Errors raised with `SQLSTATE` now include the message and the code in the response body - @laurenceisla + - #2236, Support POSIX regular expression operators for row filtering - @enote-kane ### Fixed diff --git a/src/PostgREST/Query/SqlFragment.hs b/src/PostgREST/Query/SqlFragment.hs index 0c275f9ff..39ca830b1 100644 --- a/src/PostgREST/Query/SqlFragment.hs +++ b/src/PostgREST/Query/SqlFragment.hs @@ -93,6 +93,8 @@ singleValOperator = \case OpNotExtendsRight -> "&<" OpNotExtendsLeft -> "&>" OpAdjacent -> "-|-" + OpMatch -> "~" + OpIMatch -> "~*" ftsOperator :: FtsOperator -> SqlFragment ftsOperator = \case diff --git a/src/PostgREST/Request/QueryParams.hs b/src/PostgREST/Request/QueryParams.hs index 71e2c9202..23bcddeb5 100644 --- a/src/PostgREST/Request/QueryParams.hs +++ b/src/PostgREST/Request/QueryParams.hs @@ -208,23 +208,25 @@ parse qs = operator :: Text -> Maybe SimpleOperator operator = \case - "eq" -> Just OpEqual - "gte" -> Just OpGreaterThanEqual - "gt" -> Just OpGreaterThan - "lte" -> Just OpLessThanEqual - "lt" -> Just OpLessThan - "neq" -> Just OpNotEqual - "like" -> Just OpLike - "ilike" -> Just OpILike - "cs" -> Just OpContains - "cd" -> Just OpContained - "ov" -> Just OpOverlap - "sl" -> Just OpStrictlyLeft - "sr" -> Just OpStrictlyRight - "nxr" -> Just OpNotExtendsRight - "nxl" -> Just OpNotExtendsLeft - "adj" -> Just OpAdjacent - _ -> Nothing + "eq" -> Just OpEqual + "gte" -> Just OpGreaterThanEqual + "gt" -> Just OpGreaterThan + "lte" -> Just OpLessThanEqual + "lt" -> Just OpLessThan + "neq" -> Just OpNotEqual + "like" -> Just OpLike + "ilike" -> Just OpILike + "cs" -> Just OpContains + "cd" -> Just OpContained + "ov" -> Just OpOverlap + "sl" -> Just OpStrictlyLeft + "sr" -> Just OpStrictlyRight + "nxr" -> Just OpNotExtendsRight + "nxl" -> Just OpNotExtendsLeft + "adj" -> Just OpAdjacent + "match" -> Just OpMatch + "imatch" -> Just OpIMatch + _ -> Nothing ftsOperator :: Text -> Maybe FtsOperator ftsOperator = \case diff --git a/src/PostgREST/Request/Types.hs b/src/PostgREST/Request/Types.hs index 4506aab84..de0feb486 100644 --- a/src/PostgREST/Request/Types.hs +++ b/src/PostgREST/Request/Types.hs @@ -273,6 +273,8 @@ data SimpleOperator | OpNotExtendsRight | OpNotExtendsLeft | OpAdjacent + | OpMatch + | OpIMatch deriving Eq -- | Operators for full text search operators diff --git a/test/spec/Feature/Query/QuerySpec.hs b/test/spec/Feature/Query/QuerySpec.hs index 4f30b2bf8..e9f6a0591 100644 --- a/test/spec/Feature/Query/QuerySpec.hs +++ b/test/spec/Feature/Query/QuerySpec.hs @@ -129,6 +129,29 @@ spec actualPgVersion = do it "matches with ilike using not operator" $ get "/simple_pk?k=not.ilike.xy*&order=extra.asc" `shouldRespondWith` "[]" + it "matches with ~" $ do + get "/simple_pk?k=match.yx$" `shouldRespondWith` + [json|[{"k":"xyyx","extra":"u"}]|] + get "/simple_pk?k=match.^xy" `shouldRespondWith` + [json|[{"k":"xyyx","extra":"u"}]|] + get "/simple_pk?k=match.YY" `shouldRespondWith` + [json|[{"k":"xYYx","extra":"v"}]|] + + it "matches with ~ using not operator" $ + get "/simple_pk?k=not.match.yx$" `shouldRespondWith` + [json|[{"k":"xYYx","extra":"v"}]|] + + it "matches with ~*" $ do + get "/simple_pk?k=imatch.^xy&order=extra.asc" `shouldRespondWith` + [json|[{"k":"xyyx","extra":"u"},{"k":"xYYx","extra":"v"}]|] + { matchHeaders = [matchContentTypeJson] } + get "/simple_pk?k=imatch..*YY.*&order=extra.asc" `shouldRespondWith` + [json|[{"k":"xyyx","extra":"u"},{"k":"xYYx","extra":"v"}]|] + { matchHeaders = [matchContentTypeJson] } + + it "matches with ~* using not operator" $ + get "/simple_pk?k=not.imatch.^xy&order=extra.asc" `shouldRespondWith` "[]" + describe "Full text search operator" $ do it "finds matches with to_tsquery" $ get "/tsearch?text_search_vector=fts.impossible" `shouldRespondWith`