diff --git a/CHANGELOG.md b/CHANGELOG.md index d6cc0a8a1..b286a86f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +### Fixed +- Filter columns on embedded parent items - @ruslantalpa + ## [0.3.0.0] - 2015-11-24 ### Fixed diff --git a/src/PostgREST/ApiRequest.hs b/src/PostgREST/ApiRequest.hs index ce228b948..10eb324c2 100644 --- a/src/PostgREST/ApiRequest.hs +++ b/src/PostgREST/ApiRequest.hs @@ -20,6 +20,7 @@ import PostgREST.RangeQuery (NonnegRange, rangeRequested) import PostgREST.Types (QualifiedIdentifier (..), Schema, Payload(..), UniformObjects(..)) +import Data.Ranged.Ranges (singletonRange) type RequestBody = BL.ByteString @@ -114,7 +115,7 @@ userApiRequest schema req reqBody = ApiRequest { iAction = action - , iRange = if singular then Nothing else rangeRequested hdrs + , iRange = if singular then Just (singletonRange 0) else rangeRequested hdrs , iTarget = target , iAccepts = pickContentType $ lookupHeader "accept" , iPayload = relevantPayload diff --git a/src/PostgREST/QueryBuilder.hs b/src/PostgREST/QueryBuilder.hs index e73ab758f..9b9481ea9 100644 --- a/src/PostgREST/QueryBuilder.hs +++ b/src/PostgREST/QueryBuilder.hs @@ -34,7 +34,6 @@ import qualified Data.Aeson as JSON import PostgREST.RangeQuery (NonnegRange, rangeLimit, rangeOffset) import Control.Error (note, fromMaybe, mapMaybe) -import Data.Maybe (isNothing) import Control.Monad (join) import qualified Data.HashMap.Strict as HM import Data.List (find) @@ -55,8 +54,6 @@ import Data.Scientific ( FPFormat (..) ) import Prelude hiding (unwords) -import Data.Ranged.Ranges (singletonRange) - type PStmt = H.Stmt P.Postgres instance Monoid PStmt where mappend (B.Stmt query params prep) (B.Stmt query' params' prep') = @@ -74,7 +71,7 @@ createReadStatement selectQuery range isSingle countTable asCsv = if asCsv then asCsvF else if isSingle then asJsonSingleF else asJsonF - ] selectStarF (if isNothing range && isSingle then Just $ singletonRange 0 else range) + ] selectStarF range ) V.empty True createWriteStatement :: SqlQuery -> SqlQuery -> Bool -> Bool -> @@ -127,10 +124,9 @@ addJoinConditions schema (Node (query, (n, r)) forest) = _ -> Left "unknown relation" where -- add parentTable and parentJoinConditions to the query - updatedQuery = foldr (flip addCond) (query{from = parentTables ++ from query}) parentJoinConditions + updatedQuery = foldr (flip addCond) query parentJoinConditions where parentJoinConditions = map (getJoinConditions . snd) parents - parentTables = map fst parents parents = mapMaybe (getParents . rootLabel) forest getParents (_, (tbl, Just rel@(Relation{relType=Parent}))) = Just (tbl, rel) getParents _ = Nothing @@ -198,14 +194,14 @@ requestToQuery schema (DbRead (Node (Select colSelects tbls conditions ord, (mai qi = QualifiedIdentifier (tblSchema mainTbl) mainTbl toQi t = QualifiedIdentifier (tblSchema t) t query = unwords [ - ("WITH " <> intercalate ", " withs) `emptyOnNull` withs, + ("WITH " <> intercalate ", " (map fst withs)) `emptyOnNull` withs, "SELECT ", intercalate ", " (map (pgFmtSelectItem qi) colSelects ++ selects), - "FROM ", intercalate ", " (map (fromQi . toQi) tbls), + "FROM ", intercalate ", " (map (fromQi . toQi) tbls ++ map snd withs), ("WHERE " <> intercalate " AND " ( map (pgFmtCondition qi ) conditions )) `emptyOnNull` conditions, orderF (fromMaybe [] ord) ] (withs, selects) = foldr getQueryParts ([],[]) forest - getQueryParts :: Tree ReadNode -> ([SqlFragment], [SqlFragment]) -> ([SqlFragment], [SqlFragment]) + getQueryParts :: Tree ReadNode -> ([(SqlFragment, Text)], [SqlFragment]) -> ([(SqlFragment,Text)], [SqlFragment]) getQueryParts (Node n@(_, (table, Just (Relation {relType=Child}))) forst) (w,s) = (w,sel:s) where sel = "(" @@ -216,7 +212,7 @@ requestToQuery schema (DbRead (Node (Select colSelects tbls conditions ord, (mai getQueryParts (Node n@(_, (table, Just (Relation {relType=Parent}))) forst) (w,s) = (wit:w,sel:s) where sel = "row_to_json(" <> table <> ".*) AS "<>table --TODO must be singular - wit = table <> " AS ( " <> subquery <> " )" + wit = (table <> " AS ( " <> subquery <> " )", table) where subquery = requestToQuery schema (DbRead (Node n forst)) getQueryParts (Node n@(_, (table, Just (Relation {relType=Many}))) forst) (w,s) = (w,sel:s) where @@ -331,7 +327,7 @@ getJoinConditions (Relation t cols ft fcs typ lt lc1 lc2) = Parent -> zipWith (toFilter tN ftN) cols fcs Many -> zipWith (toFilter tN ltN) cols (fromMaybe [] lc1) ++ zipWith (toFilter ftN ltN) fcs (fromMaybe [] lc2) where - s = tableSchema t + s = if typ == Parent then "" else tableSchema t tN = tableName t ftN = tableName ft ltN = fromMaybe "" (tableName <$> lt) diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index d00bc7e4c..f4bca6c20 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -198,6 +198,10 @@ spec = get "/projects?id=eq.1&select=id, name, clients{*}, tasks{id, name}" `shouldRespondWith` "[{\"id\":1,\"name\":\"Windows 7\",\"clients\":{\"id\":1,\"name\":\"Microsoft\"},\"tasks\":[{\"id\":1,\"name\":\"Design w7\"},{\"id\":2,\"name\":\"Code w7\"}]}]" + it "requesting parents and filtering parent columns" $ + get "/projects?id=eq.1&select=id, name, clients{id}" `shouldRespondWith` + "[{\"id\":1,\"name\":\"Windows 7\",\"clients\":{\"id\":1}}]" + it "requesting children 2 levels" $ get "/clients?id=eq.1&select=id,projects{id,tasks{id}}" `shouldRespondWith` "[{\"id\":1,\"projects\":[{\"id\":1,\"tasks\":[{\"id\":1},{\"id\":2}]},{\"id\":2,\"tasks\":[{\"id\":3},{\"id\":4}]}]}]"