diff --git a/server/src-lib/Hasura/GraphQL/Resolve/Insert.hs b/server/src-lib/Hasura/GraphQL/Resolve/Insert.hs index d967e51f..968b2bda 100644 --- a/server/src-lib/Hasura/GraphQL/Resolve/Insert.hs +++ b/server/src-lib/Hasura/GraphQL/Resolve/Insert.hs @@ -154,7 +154,7 @@ parseOnConflict inpCols val = withPathK "on_conflict" $ flip withObject val $ \_ obj -> do actionM <- forM (OMap.lookup "action" obj) parseAction constraint <- parseConstraint obj - updColsM <- forM (OMap.lookup "update_columns" obj) parseUpdCols + updColsM <- forM (OMap.lookup "update_columns" obj) parseColumns -- consider "action" if "update_columns" is not mentioned return $ mkConflictClause $ case (updColsM, actionM) of (Just [], _) -> RI.CCDoNothing $ Just constraint @@ -177,11 +177,6 @@ parseOnConflict inpCols val = withPathK "on_conflict" $ (_, enumVal) <- asEnumVal v return $ ConstraintName $ G.unName $ G.unEnumValue enumVal - parseUpdCols v = flip withArray v $ \_ enumVals -> - forM enumVals $ \eVal -> do - (_, ev) <- asEnumVal eVal - return $ PGCol $ G.unName $ G.unEnumValue ev - mkConflictClause (RI.CCDoNothing constrM) = RI.CP1DoNothing $ fmap RI.Constraint constrM mkConflictClause (RI.CCUpdate constr updCols) = diff --git a/server/src-lib/Hasura/GraphQL/Resolve/Select.hs b/server/src-lib/Hasura/GraphQL/Resolve/Select.hs index 78cd3072..38949d1c 100644 --- a/server/src-lib/Hasura/GraphQL/Resolve/Select.hs +++ b/server/src-lib/Hasura/GraphQL/Resolve/Select.hs @@ -10,6 +10,7 @@ module Hasura.GraphQL.Resolve.Select ( convertSelect , convertSelectByPKey , convertAggSelect + , parseColumns , withSelSet , fromSelSet , fieldAsPath @@ -32,6 +33,7 @@ import qualified Hasura.SQL.DML as S import Hasura.GraphQL.Resolve.BoolExp import Hasura.GraphQL.Resolve.Context import Hasura.GraphQL.Resolve.InputValue +import Hasura.GraphQL.Schema (isAggFld) import Hasura.GraphQL.Validate.Field import Hasura.GraphQL.Validate.Types import Hasura.RQL.DML.Internal (onlyPositiveInt) @@ -199,6 +201,29 @@ convertSelectByPKey qt permFilter fld = do return $ RS.selectP2 True (selData, prepArgs) -- agg select related +parseColumns :: MonadError QErr m => AnnGValue -> m [PGCol] +parseColumns val = + flip withArray val $ \_ vals -> + forM vals $ \v -> do + (_, enumVal) <- asEnumVal v + return $ PGCol $ G.unName $ G.unEnumValue enumVal + +convertCount :: MonadError QErr m => ArgsMap -> m S.CountType +convertCount args = do + columnsM <- withArgM args "columns" parseColumns + isDistinct <- or <$> withArgM args "distinct" parseDistinct + maybe (return S.CTStar) (mkCType isDistinct) columnsM + where + parseDistinct v = do + (_, val) <- asPGColVal v + case val of + PGValBoolean b -> return b + _ -> + throw500 "expecting Boolean for \"distinct\"" + + mkCType isDistinct cols = return $ + bool (S.CTSimple cols) (S.CTDistinct cols) isDistinct + convertColFlds :: Monad m => G.NamedType -> SelSet -> m RS.ColFlds convertColFlds ty selSet = @@ -216,12 +241,14 @@ convertAggFld ty selSet = fSelSet = _fSelSet fld case _fName fld of "__typename" -> return $ RS.AFExp $ G.unName $ G.unNamedType ty - "count" -> return RS.AFCount - "sum" -> RS.AFSum <$> convertColFlds fType fSelSet - "avg" -> RS.AFAvg <$> convertColFlds fType fSelSet - "max" -> RS.AFMax <$> convertColFlds fType fSelSet - "min" -> RS.AFMin <$> convertColFlds fType fSelSet - G.Name t -> throw500 $ "unexpected field in _agg node: " <> t + "count" -> RS.AFCount <$> convertCount (_fArguments fld) + n -> do + colFlds <- convertColFlds fType fSelSet + unless (isAggFld n) $ throwInvalidFld n + return $ RS.AFOp $ RS.AggOp (G.unName n) colFlds + where + throwInvalidFld (G.Name t) = + throw500 $ "unexpected field in _aggregate node: " <> t fromAggField :: (MonadError QErr m, MonadReader r m, Has FieldMap r, Has OrdByCtx r) diff --git a/server/src-lib/Hasura/GraphQL/Schema.hs b/server/src-lib/Hasura/GraphQL/Schema.hs index fa7cf7d9..6aea90db 100644 --- a/server/src-lib/Hasura/GraphQL/Schema.hs +++ b/server/src-lib/Hasura/GraphQL/Schema.hs @@ -17,6 +17,7 @@ module Hasura.GraphQL.Schema , InsCtx(..) , InsCtxMap , RelationInfoMap + , isAggFld ) where import Data.Has @@ -153,6 +154,17 @@ isRelNullable fim ri = isNullable lColInfos = getColInfos lCols allCols isNullable = any pgiIsNullable lColInfos +numAggOps :: [G.Name] +numAggOps = [ "sum", "avg", "stddev", "stddev_samp", "stddev_pop" + , "variance", "var_samp", "var_pop" + ] + +compAggOps :: [G.Name] +compAggOps = ["max", "min"] + +isAggFld :: G.Name -> Bool +isAggFld = flip elem (numAggOps <> compAggOps) + mkColName :: PGCol -> G.Name mkColName (PGCol n) = G.Name n @@ -378,7 +390,14 @@ mkTableAggObj tn = {- type table_aggregate_fields{ count: Int - sum: table_num_fields + sum: table_sum_fields + avg: table_avg_fields + stddev: table_stddev_fields + stddev_pop: table_stddev_pop_fields + variance: table_variance_fields + var_pop: table_var_pop_fields + max: table_max_fields + min: table_min_fields } -} mkTableAggFldsObj @@ -390,22 +409,24 @@ mkTableAggFldsObj tn numCols compCols = desc = G.Description $ "aggregate fields of " <>> tn - countFld = ObjFldInfo Nothing "count" Map.empty $ G.toGT $ + countFld = ObjFldInfo Nothing "count" countParams $ G.toGT $ mkScalarTy PGInteger - numFlds = bool [sumFld, avgFld] [] $ null numCols - compFlds = bool [maxFld, minFld] [] $ null compCols + countParams = fromInpValL [countColInpVal, distinctInpVal] - sumFld = mkColOpFld "sum" - avgFld = mkColOpFld "avg" - maxFld = mkColOpFld "max" - minFld = mkColOpFld "min" + countColInpVal = InpValInfo Nothing "columns" $ G.toGT $ + G.toLT $ G.toNT $ mkSelColumnInpTy tn + distinctInpVal = InpValInfo Nothing "distinct" $ G.toGT $ + mkScalarTy PGBoolean + + numFlds = bool (map mkColOpFld numAggOps) [] $ null numCols + compFlds = bool (map mkColOpFld compAggOps) [] $ null compCols mkColOpFld op = ObjFldInfo Nothing op Map.empty $ G.toGT $ mkTableColAggFldsTy op tn {- -type table_sum_fields{ +type table__fields{ num_col: Int . . . . @@ -840,10 +861,15 @@ mkConstraintInpTy :: QualifiedTable -> G.NamedType mkConstraintInpTy tn = G.NamedType $ qualTableToName tn <> "_constraint" --- table_column -mkColumnInpTy :: QualifiedTable -> G.NamedType -mkColumnInpTy tn = - G.NamedType $ qualTableToName tn <> "_column" +-- table_update_column +mkUpdColumnInpTy :: QualifiedTable -> G.NamedType +mkUpdColumnInpTy tn = + G.NamedType $ qualTableToName tn <> "_update_column" + +--table_select_column +mkSelColumnInpTy :: QualifiedTable -> G.NamedType +mkSelColumnInpTy tn = + G.NamedType $ qualTableToName tn <> "_select_column" {- input table_obj_rel_insert_input { data: table_insert_input! @@ -946,7 +972,7 @@ mkOnConflictInp tn = G.toGT $ G.toNT $ mkConstraintInpTy tn updateColumnsInpVal = InpValInfo Nothing (G.Name "update_columns") $ - G.toGT $ G.toLT $ G.toNT $ mkColumnInpTy tn + G.toGT $ G.toLT $ G.toNT $ mkUpdColumnInpTy tn {- insert_table( @@ -991,17 +1017,27 @@ mkConstriantTy tn cons = enumTyInfo EnumValInfo (Just "unique or primary key constraint") (G.EnumValue $ G.Name n) False -mkColumnTy :: QualifiedTable -> [PGCol] -> EnumTyInfo -mkColumnTy tn cols = enumTyInfo +mkColumnEnumVal :: PGCol -> EnumValInfo +mkColumnEnumVal (PGCol col) = + EnumValInfo (Just "column name") (G.EnumValue $ G.Name col) False + +mkUpdColumnTy :: QualifiedTable -> [PGCol] -> EnumTyInfo +mkUpdColumnTy tn cols = enumTyInfo where - enumTyInfo = EnumTyInfo (Just desc) (mkColumnInpTy tn) $ + enumTyInfo = EnumTyInfo (Just desc) (mkUpdColumnInpTy tn) $ mapFromL _eviVal $ map mkColumnEnumVal cols desc = G.Description $ - "columns of table " <>> tn + "update columns of table " <>> tn - mkColumnEnumVal (PGCol col) = - EnumValInfo (Just "column name") (G.EnumValue $ G.Name col) False +mkSelColumnTy :: QualifiedTable -> [PGCol] -> EnumTyInfo +mkSelColumnTy tn cols = enumTyInfo + where + enumTyInfo = EnumTyInfo (Just desc) (mkSelColumnInpTy tn) $ + mapFromL _eviVal $ map mkColumnEnumVal cols + + desc = G.Description $ + "select columns of table " <>> tn mkConflictActionTy :: EnumTyInfo mkConflictActionTy = EnumTyInfo (Just desc) ty $ mapFromL _eviVal @@ -1108,7 +1144,7 @@ mkOnConflictTypes tn c cols = where tyInfos = [ TIEnum mkConflictActionTy , TIEnum $ mkConstriantTy tn constraints - , TIEnum $ mkColumnTy tn cols + , TIEnum $ mkUpdColumnTy tn cols , TIInpObj $ mkOnConflictInp tn ] constraints = filter isUniqueOrPrimary c @@ -1159,6 +1195,7 @@ mkGCtxRole' tn insPermM selPermM updColsM delPermM pkeyCols constraints viM allC , TIInpObj <$> mutHelper viIsUpdatable updSetInpObjM , TIInpObj <$> mutHelper viIsUpdatable updIncInpObjM , TIObj <$> mutRespObjM + , TIEnum <$> selColInpTyM ] mutHelper f objM = bool Nothing objM $ isMutable f viM @@ -1190,6 +1227,8 @@ mkGCtxRole' tn insPermM selPermM updColsM delPermM pkeyCols constraints viM allC updSetInpObjFldsM = mkColFldMap (mkUpdSetTy tn) <$> updColsM selFldsM = snd <$> selPermM + selColsM = (map pgiName . lefts) <$> selFldsM + selColInpTyM = mkSelColumnTy tn <$> selColsM -- boolexp input type boolExpInpObjM = case selFldsM of Just selFlds -> Just $ mkBoolExpInp tn selFlds @@ -1240,15 +1279,18 @@ mkGCtxRole' tn insPermM selPermM updColsM delPermM pkeyCols constraints viM allC _ -> [] getNumCols = onlyNumCols . lefts getCompCols = onlyComparableCols . lefts + onlyFloat = const $ mkScalarTy PGFloat + + mkTypeMaker "sum" = mkScalarTy + mkTypeMaker _ = onlyFloat + mkColAggFldsObjs flds = let numCols = getNumCols flds compCols = getCompCols flds - sumFldsObj = mkTableColAggFldsObj tn "sum" mkScalarTy numCols - avgFldsObj = mkTableColAggFldsObj tn "avg" (const $ mkScalarTy PGFloat) numCols - maxFldsObj = mkTableColAggFldsObj tn "max" mkScalarTy compCols - minFldsObj = mkTableColAggFldsObj tn "min" mkScalarTy compCols - numFldsObjs = bool [sumFldsObj, avgFldsObj] [] $ null numCols - compFldsObjs = bool [maxFldsObj, minFldsObj] [] $ null compCols + mkNumObjFld n = mkTableColAggFldsObj tn n (mkTypeMaker n) numCols + mkCompObjFld n = mkTableColAggFldsObj tn n mkScalarTy compCols + numFldsObjs = bool (map mkNumObjFld numAggOps) [] $ null numCols + compFldsObjs = bool (map mkCompObjFld compAggOps) [] $ null compCols in numFldsObjs <> compFldsObjs -- the fields used in table object selObjFldsM = mkFldMap (mkTableTy tn) <$> selFldsM diff --git a/server/src-lib/Hasura/RQL/DML/Count.hs b/server/src-lib/Hasura/RQL/DML/Count.hs index a95a543e..5fcc703c 100644 --- a/server/src-lib/Hasura/RQL/DML/Count.hs +++ b/server/src-lib/Hasura/RQL/DML/Count.hs @@ -41,7 +41,7 @@ mkSQLCount :: CountQueryP1 -> S.Select mkSQLCount (CountQueryP1 tn (permFltr, mWc) mDistCols) = S.mkSelect - { S.selExtr = [S.Extractor (S.SEFnApp "count" [S.SEStar] Nothing) Nothing] + { S.selExtr = [S.Extractor S.countStar Nothing] , S.selFrom = Just $ S.FromExp [S.mkSelFromExp False innerSel $ TableName "r"] } diff --git a/server/src-lib/Hasura/RQL/DML/Returning.hs b/server/src-lib/Hasura/RQL/DML/Returning.hs index b98df324..ecb3733b 100644 --- a/server/src-lib/Hasura/RQL/DML/Returning.hs +++ b/server/src-lib/Hasura/RQL/DML/Returning.hs @@ -54,7 +54,7 @@ mkMutFldExp :: QualifiedTable -> Bool -> MutFld -> S.SQLExp mkMutFldExp qt singleObj = \case MCount -> S.SESelect $ S.mkSelect - { S.selExtr = [S.Extractor (S.SEUnsafe "count(*)") Nothing] + { S.selExtr = [S.Extractor S.countStar Nothing] , S.selFrom = Just $ S.FromExp $ pure frmItem } MExp t -> S.SELit t diff --git a/server/src-lib/Hasura/RQL/DML/Select/Internal.hs b/server/src-lib/Hasura/RQL/DML/Select/Internal.hs index 8ac8da95..954ee0cd 100644 --- a/server/src-lib/Hasura/RQL/DML/Select/Internal.hs +++ b/server/src-lib/Hasura/RQL/DML/Select/Internal.hs @@ -101,12 +101,15 @@ data PGColFld type ColFlds = [(T.Text, PGColFld)] +data AggOp + = AggOp + { _aoOp :: !T.Text + , _aoFlds :: !ColFlds + } deriving (Show, Eq) + data AggFld - = AFCount - | AFSum !ColFlds - | AFAvg !ColFlds - | AFMax !ColFlds - | AFMin !ColFlds + = AFCount !S.CountType + | AFOp !AggOp | AFExp !T.Text deriving (Show, Eq) @@ -165,14 +168,11 @@ aggFldToExp aggFlds = jsonRow jsonRow = S.applyJsonBuildObj (concatMap aggToFlds aggFlds) withAls fldName sqlExp = [S.SELit fldName, sqlExp] aggToFlds (t, fld) = withAls t $ case fld of - AFCount -> S.SEUnsafe "count(*)" - AFSum sumFlds -> colFldsToObj "sum" sumFlds - AFAvg avgFlds -> colFldsToObj "avg" avgFlds - AFMax maxFlds -> colFldsToObj "max" maxFlds - AFMin minFlds -> colFldsToObj "min" minFlds - AFExp e -> S.SELit e + AFCount cty -> S.SECount cty + AFOp aggOp -> aggOpToObj aggOp + AFExp e -> S.SELit e - colFldsToObj op flds = + aggOpToObj (AggOp op flds) = S.applyJsonBuildObj $ concatMap (colFldsToExtr op) flds colFldsToExtr op (t, PCFCol col) = @@ -442,14 +442,17 @@ mkBaseNode pfx fldAls annSelFlds tableFrom tablePerm tableArgs = ) TAFExp _ -> (HM.fromList obExtrs, HM.empty, HM.empty, HM.empty) - fetchExtrFromAggFld AFCount = [] - fetchExtrFromAggFld (AFSum sumFlds) = colFldsToExps sumFlds - fetchExtrFromAggFld (AFAvg avgFlds) = colFldsToExps avgFlds - fetchExtrFromAggFld (AFMax maxFlds) = colFldsToExps maxFlds - fetchExtrFromAggFld (AFMin minFlds) = colFldsToExps minFlds - fetchExtrFromAggFld (AFExp _) = [] + fetchExtrFromAggFld (AFCount cty) = countTyToExps cty + fetchExtrFromAggFld (AFOp aggOp) = aggOpToExps aggOp + fetchExtrFromAggFld (AFExp _) = [] - colFldsToExps = mapMaybe (mkColExp . snd) + countTyToExps S.CTStar = [] + countTyToExps (S.CTSimple cols) = colsToExps cols + countTyToExps (S.CTDistinct cols) = colsToExps cols + + colsToExps = mapMaybe (mkColExp . PCFCol) + + aggOpToExps = mapMaybe (mkColExp . snd) . _aoFlds mkColExp (PCFCol c) = let qualCol = S.mkQIdenExp (mkBaseTableAls pfx) (toIden c) diff --git a/server/src-lib/Hasura/SQL/DML.hs b/server/src-lib/Hasura/SQL/DML.hs index 35818a54..3a6cda4f 100644 --- a/server/src-lib/Hasura/SQL/DML.hs +++ b/server/src-lib/Hasura/SQL/DML.hs @@ -238,6 +238,19 @@ jsonType = AnnType "json" jsonbType :: AnnType jsonbType = AnnType "jsonb" +data CountType + = CTStar + | CTSimple ![PGCol] + | CTDistinct ![PGCol] + deriving(Show, Eq) + +instance ToSQL CountType where + toSQL CTStar = "*" + toSQL (CTSimple cols) = + paren $ ", " <+> cols + toSQL (CTDistinct cols) = + "DISTINCT" <-> paren (", " <+> cols) + data SQLExp = SEPrep !Int | SELit !T.Text @@ -255,6 +268,7 @@ data SQLExp | SEBool !BoolExp | SEExcluded !T.Text | SEArray ![SQLExp] + | SECount !CountType deriving (Show, Eq) newtype Alias @@ -270,6 +284,9 @@ instance ToSQL Alias where toAlias :: (IsIden a) => a -> Alias toAlias = Alias . toIden +countStar :: SQLExp +countStar = SECount CTStar + instance ToSQL SQLExp where toSQL (SEPrep argNumber) = TB.char '$' <> fromString (show argNumber) @@ -304,6 +321,7 @@ instance ToSQL SQLExp where <> toSQL (PGCol t) toSQL (SEArray exps) = "ARRAY" <> TB.char '[' <> (", " <+> exps) <> TB.char ']' + toSQL (SECount ty) = "COUNT" <> paren (toSQL ty) intToSQLExp :: Int -> SQLExp intToSQLExp = diff --git a/server/src-lib/Hasura/SQL/Rewrite.hs b/server/src-lib/Hasura/SQL/Rewrite.hs index 6b6fc10e..c251c6c7 100644 --- a/server/src-lib/Hasura/SQL/Rewrite.hs +++ b/server/src-lib/Hasura/SQL/Rewrite.hs @@ -173,6 +173,7 @@ uSqlExp = restoringIdens . \case S.SEExcluded <$> return t S.SEArray l -> S.SEArray <$> mapM uSqlExp l + S.SECount cty -> return $ S.SECount cty where uQual = \case S.QualIden iden -> S.QualIden <$> getIden iden diff --git a/server/tests-py/queries/graphql_query/aggregations/article_agg_where.yaml b/server/tests-py/queries/graphql_query/aggregations/article_agg_where.yaml index de06ad1f..5c44ca29 100644 --- a/server/tests-py/queries/graphql_query/aggregations/article_agg_where.yaml +++ b/server/tests-py/queries/graphql_query/aggregations/article_agg_where.yaml @@ -6,6 +6,8 @@ response: article_aggregate: aggregate: count: 2 + count_columns_id: 2 + count_columns_id_distinct: 2 sum: id: 5 id_sum: 5 @@ -26,6 +28,66 @@ response: id_avg: 2.5 author_id: 1.5 author_id_avg: 1.5 + stddev: + id: 0.7071067811865476 + id_stddev: 0.7071067811865476 + author_id: 0.7071067811865476 + author_id_stddev: 0.7071067811865476 + stddev_fields: + id: 0.7071067811865476 + id_stddev: 0.7071067811865476 + author_id: 0.7071067811865476 + author_id_stddev: 0.7071067811865476 + stddev_samp: + id: 0.7071067811865476 + id_stddev_samp: 0.7071067811865476 + author_id: 0.7071067811865476 + author_id_stddev_samp: 0.7071067811865476 + stddev_samp_fields: + id: 0.7071067811865476 + id_stddev_samp: 0.7071067811865476 + author_id: 0.7071067811865476 + author_id_stddev_samp: 0.7071067811865476 + stddev_pop: + id: 0.5 + id_stddev_pop: 0.5 + author_id: 0.5 + author_id_stddev_pop: 0.5 + stddev_pop_fields: + id: 0.5 + id_stddev_pop: 0.5 + author_id: 0.5 + author_id_stddev_pop: 0.5 + variance: + id: 0.5 + id_variance: 0.5 + author_id: 0.5 + author_id_variance: 0.5 + variance_fields: + id: 0.5 + id_variance: 0.5 + author_id: 0.5 + author_id_variance: 0.5 + var_samp: + id: 0.5 + id_var_samp: 0.5 + author_id: 0.5 + author_id_var_samp: 0.5 + var_samp_fields: + id: 0.25 + id_var_pop: 0.25 + author_id: 0.25 + author_id_var_pop: 0.25 + var_pop: + id: 0.25 + id_var_pop: 0.25 + author_id: 0.25 + author_id_var_pop: 0.25 + var_pop_fields: + id: 0.25 + id_var_pop: 0.25 + author_id: 0.25 + author_id_var_pop: 0.25 max: id: 3 id_max: 3 @@ -95,94 +157,168 @@ response: query: query: | - query{ - article_aggregate(where: {id: {_gt: 1}}){ - aggregate{ - count - sum{ - id - id_sum: id - author_id - author_id_sum: author_id - } - sum_fields: sum{ - id - id_sum: id - author_id - author_id_sum: author_id - } - avg{ - id - id_avg: id - author_id - author_id_avg: author_id - } - avg_fields: avg{ - id - id_avg: id - author_id - author_id_avg: author_id - } - max{ - id - id_max: id - title - title_max: title - content - content_max: content - author_id - author_id_max: author_id - } - max_fields: max{ - id - id_max: id - title - title_max: title - content - content_max: content - author_id - author_id_max: author_id - } - min{ - id - id_min: id - title - title_min: title - content - content_min: content - author_id - author_id_min: author_id - } - min_fields: min{ - id - id_min: id - title - title_min: title - content - content_min: content - author_id - author_id_min: author_id - } - } - nodes{ - id - title - content - is_published - author{ - id - name - } - } - articles: nodes{ - id - title - content - is_published - author{ - id - name - } - } - } - } + query { + article_aggregate(where: {id: {_gt: 1}}) { + aggregate { + count + count_columns_id: count(columns: [id]) + count_columns_id_distinct: count(columns: [id], distinct: true) + sum { + id + id_sum: id + author_id + author_id_sum: author_id + } + sum_fields: sum { + id + id_sum: id + author_id + author_id_sum: author_id + } + avg { + id + id_avg: id + author_id + author_id_avg: author_id + } + avg_fields: avg { + id + id_avg: id + author_id + author_id_avg: author_id + } + stddev { + id + id_stddev: id + author_id + author_id_stddev: author_id + } + stddev_fields: stddev { + id + id_stddev: id + author_id + author_id_stddev: author_id + } + stddev_samp { + id + id_stddev_samp: id + author_id + author_id_stddev_samp: author_id + } + stddev_samp_fields: stddev_samp { + id + id_stddev_samp: id + author_id + author_id_stddev_samp: author_id + } + stddev_pop { + id + id_stddev_pop: id + author_id + author_id_stddev_pop: author_id + } + stddev_pop_fields: stddev_pop { + id + id_stddev_pop: id + author_id + author_id_stddev_pop: author_id + } + variance { + id + id_variance: id + author_id + author_id_variance: author_id + } + variance_fields: variance { + id + id_variance: id + author_id + author_id_variance: author_id + } + var_samp { + id + id_var_samp: id + author_id + author_id_var_samp: author_id + } + var_samp_fields: var_pop { + id + id_var_pop: id + author_id + author_id_var_pop: author_id + } + var_pop { + id + id_var_pop: id + author_id + author_id_var_pop: author_id + } + var_pop_fields: var_pop { + id + id_var_pop: id + author_id + author_id_var_pop: author_id + } + max { + id + id_max: id + title + title_max: title + content + content_max: content + author_id + author_id_max: author_id + } + max_fields: max { + id + id_max: id + title + title_max: title + content + content_max: content + author_id + author_id_max: author_id + } + min { + id + id_min: id + title + title_min: title + content + content_min: content + author_id + author_id_min: author_id + } + min_fields: min { + id + id_min: id + title + title_min: title + content + content_min: content + author_id + author_id_min: author_id + } + } + nodes { + id + title + content + is_published + author { + id + name + } + } + articles: nodes { + id + title + content + is_published + author { + id + name + } + } + } + }