这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion docs/graphql/manual/api-reference/query.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,32 @@ Argument

.. parsed-literal::

WhereExp_ | OrderByExp_ | PaginationExp_
DistinctOnExp_ | WhereExp_ | OrderByExp_ | PaginationExp_


.. _DistinctOnExp:

DistinctOnExp
************

.. parsed-literal::

distinct_on: [ TableSelectColumnEnum_ ]

TableSelectColumnEnum
"""""""""""""""""""""

.. code-block:: graphql

#example table_select_column enum for "article" table
enum article_select_column {
id
title
content
author_id
is_published
}


.. _WhereExp:

Expand Down
63 changes: 63 additions & 0 deletions docs/graphql/manual/queries/distinct-queries.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Distinct queries
================

You can fetch distinct columns using ``distinct_on`` argument. Initial ``order_by`` columns must
match ``distinct_on`` columns. Learn more about ``order_by`` :doc:`here <sorting>`.

.. code-block:: graphql

employee (
distinct_on: [employee_select_column]
order_by: [employee_order_by]
): [employee]!

#select column enum type for "employee" table
enum employee_select_column {
id
name
department
salary
}

For example, fetch highest salaried employee from each department:

.. graphiql::
:view_only:
:query:
query {
employee(
distinct_on: [department]
order_by: [{department: asc}, {salary: desc}]
){
id
name
department
salary
}
}
:response:
{
"data": {
"employee": [
{
"id": 5,
"name": "Kamila",
"department": "Engineering",
"salary": 4325
},
{
"id": 4,
"name": "Damien",
"department": "Product",
"salary": 3124
},
{
"id": 7,
"name": "Rickard",
"department": "Services",
"salary": 2223
}
]
}
}

2 changes: 2 additions & 0 deletions docs/graphql/manual/queries/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ types, ``query_root`` and ``mutation_root`` respectively. For example, the auto-
.. code-block:: graphql

author (
distinct_on: [author_select_column]
where: author_bool_exp
limit: Int
offset: Int
Expand All @@ -33,6 +34,7 @@ based on a typical author/article schema for reference.
simple-object-queries
nested-object-queries
aggregation-queries
distinct-queries
query-filters
sorting
pagination
Expand Down
20 changes: 19 additions & 1 deletion server/src-lib/Hasura/GraphQL/Resolve/Select.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fromSelSet f fldTy flds =
fieldAsPath :: (MonadError QErr m) => Field -> m a -> m a
fieldAsPath = nameAsPath . _fName


parseTableArgs
:: (MonadError QErr m, MonadReader r m, Has FieldMap r, Has OrdByCtx r)
=> ((PGColType, PGColValue) -> m S.SQLExp)
Expand All @@ -88,7 +89,24 @@ parseTableArgs f args = do
let ordByExpM = NE.nonEmpty =<< ordByExpML
limitExpM <- withArgM args "limit" parseLimit
offsetExpM <- withArgM args "offset" $ asPGColVal >=> f
return $ RS.TableArgs whereExpM ordByExpM limitExpM offsetExpM
distOnColsML <- withArgM args "distinct_on" parseColumns
let distOnColsM = NE.nonEmpty =<< distOnColsML
mapM_ (validateDistOn ordByExpM) distOnColsM
return $ RS.TableArgs whereExpM ordByExpM limitExpM offsetExpM distOnColsM
where
validateDistOn Nothing _ = return ()
validateDistOn (Just ordBys) cols = withPathK "args" $ do
let colsLen = length cols
initOrdBys = take colsLen $ toList ordBys
initOrdByCols = flip mapMaybe initOrdBys $ \ob ->
case obiColumn ob of
RS.AOCPG ci -> Just $ pgiName ci
_ -> Nothing
isValid = (colsLen == length initOrdByCols)
&& all (`elem` initOrdByCols) (toList cols)

unless isValid $ throwVE
"\"distinct_on\" columns must match initial \"order_by\" columns"

fromField
:: (MonadError QErr m, MonadReader r m, Has FieldMap r, Has OrdByCtx r)
Expand Down
4 changes: 4 additions & 0 deletions server/src-lib/Hasura/GraphQL/Schema.hs
Original file line number Diff line number Diff line change
Expand Up @@ -290,19 +290,23 @@ mkPGColFld (PGColInfo colName colTy isNullable) =
-- where: table_bool_exp
-- limit: Int
-- offset: Int
-- distinct_on: [table_select_column!]
mkSelArgs :: QualifiedTable -> [InpValInfo]
mkSelArgs tn =
[ InpValInfo (Just whereDesc) "where" $ G.toGT $ mkBoolExpTy tn
, InpValInfo (Just limitDesc) "limit" $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just offsetDesc) "offset" $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just orderByDesc) "order_by" $ G.toGT $ G.toLT $ G.toNT $
mkOrdByTy tn
, InpValInfo (Just distinctDesc) "distinct_on" $ G.toGT $ G.toLT $
G.toNT $ mkSelColumnInpTy tn
]
where
whereDesc = "filter the rows returned"
limitDesc = "limit the nuber of rows returned"
offsetDesc = "skip the first n rows. Use only with order_by"
orderByDesc = "sort the rows by one or more columns"
distinctDesc = "distinct select on columns"

fromInpValL :: [InpValInfo] -> Map.HashMap G.Name InpValInfo
fromInpValL = mapFromL _iviName
Expand Down
3 changes: 2 additions & 1 deletion server/src-lib/Hasura/Prelude.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import Data.Bool as M (bool)
import Data.Either as M (lefts, partitionEithers, rights)
import Data.Foldable as M (toList)
import Data.Hashable as M (Hashable)
import Data.List as M (find, foldl', group, sort, sortBy)
import Data.List as M (find, foldl', group, sort, sortBy,
union)
import Data.Maybe as M (catMaybes, fromMaybe, isJust,
isNothing, listToMaybe, mapMaybe,
maybeToList)
Expand Down
3 changes: 2 additions & 1 deletion server/src-lib/Hasura/RQL/DML/Select.hs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ convSelectQ fieldInfoMap selPermInfo selQ prepValBuilder = do
let tabFrom = TableFrom (spiTable selPermInfo) Nothing
tabPerm = TablePerm (spiFilter selPermInfo) mPermLimit
return $ AnnSelG annFlds tabFrom tabPerm $
TableArgs wClause annOrdByM mQueryLimit (S.intToSQLExp <$> mQueryOffset)
TableArgs wClause annOrdByM mQueryLimit
(S.intToSQLExp <$> mQueryOffset) Nothing

where
mQueryOffset = sqOffset selQ
Expand Down
79 changes: 53 additions & 26 deletions server/src-lib/Hasura/RQL/DML/Select/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

module Hasura.RQL.DML.Select.Internal where

import Control.Arrow ((&&&))
import Data.Aeson.Types
import Instances.TH.Lift ()
import Language.Haskell.TH.Syntax (Lift)
Expand Down Expand Up @@ -85,14 +86,15 @@ data AnnFld

data TableArgs
= TableArgs
{ _taWhere :: !(Maybe AnnBoolExpSQL)
, _taOrderBy :: !(Maybe (NE.NonEmpty AnnOrderByItem))
, _taLimit :: !(Maybe Int)
, _taOffset :: !(Maybe S.SQLExp)
{ _taWhere :: !(Maybe AnnBoolExpSQL)
, _taOrderBy :: !(Maybe (NE.NonEmpty AnnOrderByItem))
, _taLimit :: !(Maybe Int)
, _taOffset :: !(Maybe S.SQLExp)
, _taDistCols :: !(Maybe (NE.NonEmpty PGCol))
} deriving (Show, Eq)

noTableArgs :: TableArgs
noTableArgs = TableArgs Nothing Nothing Nothing Nothing
noTableArgs = TableArgs Nothing Nothing Nothing Nothing Nothing

data PGColFld
= PCFCol !PGCol
Expand Down Expand Up @@ -155,17 +157,18 @@ type AnnSel = AnnSelG [(FieldName, AnnFld)]

data BaseNode
= BaseNode
{ _bnPrefix :: !Iden
, _bnFrom :: !S.FromItem
, _bnWhere :: !S.BoolExp
, _bnOrderBy :: !(Maybe S.OrderByExp)
, _bnLimit :: !(Maybe Int)
, _bnOffset :: !(Maybe S.SQLExp)

, _bnExtrs :: !(HM.HashMap S.Alias S.SQLExp)
, _bnObjRels :: !(HM.HashMap RelName RelNode)
, _bnArrRels :: !(HM.HashMap S.Alias RelNode)
, _bnAggs :: !(HM.HashMap S.Alias AggNode)
{ _bnPrefix :: !Iden
, _bnDistinct :: !(Maybe S.DistinctExpr)
, _bnFrom :: !S.FromItem
, _bnWhere :: !S.BoolExp
, _bnOrderBy :: !(Maybe S.OrderByExp)
, _bnLimit :: !(Maybe Int)
, _bnOffset :: !(Maybe S.SQLExp)

, _bnExtrs :: !(HM.HashMap S.Alias S.SQLExp)
, _bnObjRels :: !(HM.HashMap RelName RelNode)
, _bnArrRels :: !(HM.HashMap S.Alias RelNode)
, _bnAggs :: !(HM.HashMap S.Alias AggNode)

} deriving (Show, Eq)

Expand Down Expand Up @@ -354,7 +357,7 @@ processAnnOrderByCol pfx = \case
((nesAls, nesCol), nesNodeM) = processAnnOrderByCol relPfx rest
qualCol = S.mkQIdenExp relPfx nesAls
relBaseNode =
BaseNode relPfx (S.FISimple relTab Nothing)
BaseNode relPfx Nothing (S.FISimple relTab Nothing)
(toSQLBoolExp (S.QualTable relTab) relFltr)
Nothing Nothing Nothing
(HM.singleton nesAls nesCol)
Expand All @@ -365,9 +368,25 @@ processAnnOrderByCol pfx = \case
, Just (rn, relNode)
)

processDistinctOnCol
:: Iden
-> NE.NonEmpty PGCol
-> ( S.DistinctExpr
-- additional column extractors
, [(S.Alias, S.SQLExp)]
)
processDistinctOnCol pfx neCols = (distOnExp, colExtrs)
where
cols = toList neCols
distOnExp = S.DistinctOn $ map (S.SEIden . toIden . mkQColAls) cols
mkQCol c = S.mkQIdenExp (mkBaseTableAls pfx) $ toIden c
mkQColAls = S.Alias . mkBaseTableColAls pfx
colExtrs = flip map cols $ mkQColAls &&& mkQCol


mkEmptyBaseNode :: Iden -> TableFrom -> BaseNode
mkEmptyBaseNode pfx tableFrom =
BaseNode pfx fromItem (S.BELit True) Nothing Nothing Nothing
BaseNode pfx Nothing fromItem (S.BELit True) Nothing Nothing Nothing
selOne HM.empty HM.empty HM.empty
where
selOne = HM.singleton (S.Alias $ pfx <> Iden "__one") (S.SEUnsafe "1")
Expand Down Expand Up @@ -420,11 +439,11 @@ mkBaseNode
:: Iden -> FieldName -> TableAggFld -> TableFrom
-> TablePerm -> TableArgs -> BaseNode
mkBaseNode pfx fldAls annSelFlds tableFrom tablePerm tableArgs =
BaseNode pfx fromItem finalWhere ordByExpM finalLimit offsetM
BaseNode pfx distExprM fromItem finalWhere ordByExpM finalLimit offsetM
allExtrs allObjsWithOb allArrs aggs
where
TablePerm fltr permLimitM = tablePerm
TableArgs whereM orderByM limitM offsetM = tableArgs
TableArgs whereM orderByM limitM offsetM distM = tableArgs
(allExtrs, allObjsWithOb, allArrs, aggs) = case annSelFlds of
TAFNodes flds ->
let selExtr = buildJsonObject pfx fldAls flds
Expand All @@ -437,14 +456,14 @@ mkBaseNode pfx fldAls annSelFlds tableFrom tablePerm tableArgs =
allObjs $ catMaybes $ maybe [] _3 procOrdByM
aggItems = HM.fromList $ map mkAggItem $
mapMaybe (\(als, f) -> (als,) <$> getAggFld f) flds
in ( HM.fromList $ selExtr:obExtrs
in ( HM.fromList $ selExtr:obExtrs <> distExtrs
, allObjRelsWithOb
, allArrRels
, aggItems
)
TAFAgg aggFlds ->
let extrs = concatMap (fetchExtrFromAggFld . snd) aggFlds
in ( HM.fromList $ extrs <> obExtrs
in ( HM.fromList $ extrs <> obExtrs <> distExtrs
, HM.empty
, HM.empty
, HM.empty
Expand Down Expand Up @@ -479,6 +498,10 @@ mkBaseNode pfx fldAls annSelFlds tableFrom tablePerm tableArgs =
_2 (_, b, _) = b
_3 (_, _, c) = c

distItemsM = processDistinctOnCol pfx <$> distM
distExprM = fst <$> distItemsM
distExtrs = fromMaybe [] (snd <$> distItemsM)

procOrdByM = unzip3 . map (processAnnOrderByItem pfx) . toList <$> orderByM
ordByExpM = S.OrderByExp . _2 <$> procOrdByM

Expand Down Expand Up @@ -525,14 +548,14 @@ annSelToBaseNode pfx fldAls annSel =

mergeBaseNodes :: BaseNode -> BaseNode -> BaseNode
mergeBaseNodes lNodeDet rNodeDet =
BaseNode pfx f whr ordBy limit offset
BaseNode pfx dExp f whr ordBy limit offset
(HM.union lExtrs rExtrs)
(HM.unionWith mergeRelNodes lObjs rObjs)
(HM.union lArrs rArrs)
(HM.union lAggs rAggs)
where
(BaseNode pfx f whr ordBy limit offset lExtrs lObjs lArrs lAggs) = lNodeDet
(BaseNode _ _ _ _ _ _ rExtrs rObjs rArrs rAggs) = rNodeDet
(BaseNode pfx dExp f whr ordBy limit offset lExtrs lObjs lArrs lAggs) = lNodeDet
(BaseNode _ _ _ _ _ _ _ rExtrs rObjs rArrs rAggs) = rNodeDet

-- should only be used to merge obj rel nodes
mergeRelNodes :: RelNode -> RelNode -> RelNode
Expand Down Expand Up @@ -574,15 +597,19 @@ mkJoinCond baseTableAls colMapn =
S.BECompare S.SEQ (S.mkQIdenExp baseTableAls lCol) (S.mkSIdenExp rCol)

baseNodeToSel :: S.BoolExp -> BaseNode -> S.Select
baseNodeToSel joinCond (BaseNode pfx fromItem whr ordByM limitM offsetM extrs objRels arrRels aggs) =
baseNodeToSel joinCond baseNode =
S.mkSelect
{ S.selExtr = [S.Extractor e $ Just a | (a, e) <- HM.toList extrs]
, S.selFrom = Just $ S.FromExp [joinedFrom]
, S.selOrderBy = ordByM
, S.selLimit = S.LimitExp . S.intToSQLExp <$> limitM
, S.selOffset = S.OffsetExp <$> offsetM
, S.selDistinct = dExp
}
where
BaseNode pfx dExp fromItem whr ordByM limitM
offsetM extrs objRels arrRels aggs
= baseNode
-- this is the table which is aliased as "pfx.base"
baseSel = S.mkSelect
{ S.selExtr = [S.Extractor S.SEStar Nothing]
Expand Down
Loading