这是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
14 changes: 14 additions & 0 deletions docs/graphql/manual/api-reference/mutation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@ Input Object
{
field1: value,
field2: value,
<object-rel-name>: {
data: <Input-Object>!,
on_conflict: <Conflict-Clause>
},
<array-rel-name>: {
data: [<Input-Object>!]!,
on_conflict: <Conflict-Clause>
}
..
},
..
Expand All @@ -249,6 +257,12 @@ E.g.:
{
title: "Software is eating the world",
content: "This week, Hewlett-Packard...",
author: {
data: {
id: 1,
name: "Sydney"
}
}
}
]

Expand Down
56 changes: 55 additions & 1 deletion docs/graphql/manual/mutations/insert.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,60 @@ Insert multiple objects of the same type in the same mutation
}
}

Insert nested object and get nested object in response
-----------------------------------------------------
**Example:** Insert a new ``article`` object with its ``author`` and return the inserted article object with its author in the response


.. graphiql::
:view_only:
:query:
mutation insert_article {
insert_article(
objects: [
{
id: 21,
title: "Article 1",
content: "Sample article content",
author: {
data: {
id: 3,
name: "Sidney"
}
}
}
]
) {
returning {
id
title
author {
id
name
}
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 2,
"returning": [
{
"id": 21,
"title": "Article 1",
"author": {
"id": 3,
"name": "Sidney"
}
}
]
}
}
}


Insert object and get nested object in response
-----------------------------------------------
**Example:** Insert a new ``article`` object and return the inserted article object with its author in the response
Expand Down Expand Up @@ -297,4 +351,4 @@ OR
]
}
}
}
}
49 changes: 49 additions & 0 deletions docs/graphql/manual/mutations/upsert.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,52 @@ ignore the request:
}

In this case, the insert mutation is ignored because there is a conflict.

Upsert in nested mutations
--------------------------
You can specify ``on_conflict`` clause while inserting nested objects


.. graphiql::
:view_only:
:query:
mutation upsert_author_article {
insert_author(
objects: [
{ name: "John",
id: 10,
articles: {
data: [
{
id: 1,
title: "Article 1 title",
content: "Article 1 content"
}
],
on_conflict: {
constraint: article_pkey,
update_columns: [title, content]
}
}
}
]
) {
affected_rows
}
}
:response:
{
"data": {
"insert_author": {
"affected_rows": 2
}
}
}


.. warning::
Inserting nested objects fails when

1. Any of upsert in object relationships does not affect any rows (``update_columns: []`` or ``action: ignore``)

2. Array relationships are queued for insert and parent insert does not affect any rows (``update_columns: []`` or ``action: ignore``)
1 change: 1 addition & 0 deletions server/graphql-engine.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ library
, Hasura.GraphQL.Resolve.Context
, Hasura.GraphQL.Resolve.InputValue
, Hasura.GraphQL.Resolve.Introspect
, Hasura.GraphQL.Resolve.Insert
, Hasura.GraphQL.Resolve.Mutation
, Hasura.GraphQL.Resolve.Select

Expand Down
8 changes: 5 additions & 3 deletions server/src-lib/Hasura/GraphQL/Resolve.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ import Hasura.GraphQL.Validate.Field
import Hasura.RQL.Types
import Hasura.SQL.Types

import qualified Hasura.GraphQL.Resolve.Insert as RI
import qualified Hasura.GraphQL.Resolve.Mutation as RM
import qualified Hasura.GraphQL.Resolve.Select as RS

-- {-# SCC buildTx #-}
buildTx :: UserInfo -> GCtx -> Field -> Q.TxE QErr BL.ByteString
buildTx userInfo gCtx fld = do
opCxt <- getOpCtx $ _fName fld
join $ fmap fst $ runConvert (fldMap, orderByCtx) $ case opCxt of
join $ fmap fst $ runConvert (fldMap, orderByCtx, insCtxMap) $ case opCxt of

OCSelect tn permFilter permLimit hdrs ->
validateHdrs hdrs >> RS.convertSelect tn permFilter permLimit fld

OCSelectPkey tn permFilter hdrs ->
validateHdrs hdrs >> RS.convertSelectByPKey tn permFilter fld
-- RS.convertSelect tn permFilter fld
OCInsert tn vn cols hdrs ->
validateHdrs hdrs >> RM.convertInsert roleName (tn, vn) cols fld
OCInsert tn hdrs ->
validateHdrs hdrs >> RI.convertInsert roleName tn fld
-- RM.convertInsert (tn, vn) cols fld
OCUpdate tn permFilter hdrs ->
validateHdrs hdrs >> RM.convertUpdate tn permFilter fld
Expand All @@ -52,6 +53,7 @@ buildTx userInfo gCtx fld = do
opCtxMap = _gOpCtxMap gCtx
fldMap = _gFields gCtx
orderByCtx = _gOrdByEnums gCtx
insCtxMap = _gInsCtxMap gCtx

getOpCtx f =
onNothing (Map.lookup f opCtxMap) $ throw500 $
Expand Down
33 changes: 30 additions & 3 deletions server/src-lib/Hasura/GraphQL/Resolve/Context.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module Hasura.GraphQL.Resolve.Context
( FieldMap
( InsResp(..)
, FieldMap
, OrdByResolveCtx
, OrdByResolveCtxElem
, NullsOrder(..)
, OrdTy(..)
, RelationInfoMap
, InsCtx(..)
, InsCtxMap
, RespTx
, InsertTxConflictCtx(..)
, getFldInfo
Expand All @@ -28,6 +33,9 @@ module Hasura.GraphQL.Resolve.Context
import Data.Has
import Hasura.Prelude

import qualified Data.Aeson as J
import qualified Data.Aeson.Casing as J
import qualified Data.Aeson.TH as J
import qualified Data.ByteString.Lazy as BL
import qualified Data.HashMap.Strict as Map
import qualified Data.Sequence as Seq
Expand All @@ -44,6 +52,13 @@ import Hasura.SQL.Value
import qualified Hasura.RQL.DML.Select as RS
import qualified Hasura.SQL.DML as S

data InsResp
= InsResp
{ _irAffectedRows :: !Int
, _irResponse :: !(Maybe J.Object)
} deriving (Show, Eq)
$(J.deriveJSON (J.aesonDrop 3 J.snakeCase) ''InsResp)

type FieldMap
= Map.HashMap (G.NamedType, G.Name)
(Either PGColInfo (RelInfo, S.BoolExp, Maybe Int, Bool))
Expand All @@ -66,6 +81,18 @@ type OrdByResolveCtxElem = RS.AnnOrderByItem
type OrdByResolveCtx
= Map.HashMap (G.NamedType, G.EnumValue) OrdByResolveCtxElem

-- insert context
type RelationInfoMap = Map.HashMap RelName RelInfo

data InsCtx
= InsCtx
{ icView :: !QualifiedTable
, icColumns :: ![PGColInfo]
, icRelations :: !RelationInfoMap
} deriving (Show, Eq)

type InsCtxMap = Map.HashMap QualifiedTable InsCtx

getFldInfo
:: (MonadError QErr m, MonadReader r m, Has FieldMap r)
=> G.NamedType -> G.Name -> m (Either PGColInfo (RelInfo, S.BoolExp, Maybe Int, Bool))
Expand Down Expand Up @@ -126,7 +153,7 @@ withArgM args arg f = prependArgsInPath $ nameAsPath arg $
type PrepArgs = Seq.Seq Q.PrepArg

type Convert =
StateT PrepArgs (ReaderT (FieldMap, OrdByResolveCtx) (Except QErr))
StateT PrepArgs (ReaderT (FieldMap, OrdByResolveCtx, InsCtxMap) (Except QErr))

prepare
:: (MonadState PrepArgs m)
Expand All @@ -138,7 +165,7 @@ prepare (colTy, colVal) = do

runConvert
:: (MonadError QErr m)
=> (FieldMap, OrdByResolveCtx) -> Convert a -> m (a, PrepArgs)
=> (FieldMap, OrdByResolveCtx, InsCtxMap) -> Convert a -> m (a, PrepArgs)
runConvert ctx m =
either throwError return $
runExcept $ runReaderT (runStateT m Seq.empty) ctx
12 changes: 12 additions & 0 deletions server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ module Hasura.GraphQL.Resolve.InputValue
, asPGColVal
, asEnumVal
, withObject
, asObject
, withObjectM
, withArray
, asArray
, withArrayM
, parseMany
, asPGColText
Expand Down Expand Up @@ -80,6 +82,11 @@ withObject fn v = case v of
<> G.showGT (G.TypeNamed nt)
_ -> tyMismatch "object" v

asObject
:: (MonadError QErr m)
=> AnnGValue -> m AnnGObject
asObject = withObject (\_ o -> return o)

withObjectM
:: (MonadError QErr m)
=> (G.NamedType -> Maybe AnnGObject -> m a) -> AnnGValue -> m a
Expand All @@ -103,6 +110,11 @@ withArray fn v = case v of
<> G.showGT (G.TypeList lt)
_ -> tyMismatch "array" v

asArray
:: (MonadError QErr m)
=> AnnGValue -> m [AnnGValue]
asArray = withArray (\_ vals -> return vals)

parseMany
:: (MonadError QErr m)
=> (AnnGValue -> m a) -> AnnGValue -> m (Maybe [a])
Expand Down
Loading