question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Incrementing and Decrementing Numeric Attributes GraphQL

See original GitHub issue

Which Category is your question related to? GraphQL

Amplify CLI Version 4.13.1

What AWS Services are you utilizing? Cognito, AppSync, DynamoDB, API Gateway, Lambda

Is it possible to increment or decrement a number in a graph model like you can do with the dynamodb SET Update Expression (https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement)?

For example I have this model:

type ResetHistory @model @auth(
  rules: [ {allow: owner} ])
{
  id: ID!
  resetCount: Int!
  totalResetTime: Float!
}

I currently have a function that updates the dynamodb table directly using the following command:

...[snip]
const params = {
          TableName: `ResetHistory-${apiGraphQLAPIIdOutput}-${environment}`,
          Key: {
            id: unmarshalledNewRecord.id
          },
          UpdateExpression: 'SET resetCount = resetCount + :count, totalResetTime = totalResetTime + :resetTime',
          ExpressionAttributeValues: {
            ":count": 1,
            ":resetTime": parseFloat(resetTimeMinutes)
          },
        };
        try {
          //Write updates to agg table
          await documentClient.update(params).promise();
        ....[more code below]

This works but I need to create a subscription on this data for my app so I am looking to rewrite the function to execute an AppSync function (updateResetHistory) so it can trigger subscriptions. Is there a way to accomplish the same increment functionality within the amplify framework?

At the end of the day I really just need to set a field = field + [some number] but I am not seeing anything in the docs. I have been reading over posts about custom resolvers. Is that the only way to get this to work?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

8reactions
egreenmachinecommented, May 15, 2020

So I ended up creating a custom Mutation to do this. I made it pretty generic so it should help others. It’s not great - but it is a start and should be able to help people. I have left out the authentication and some other boilerplate included with the default update Mutation. You can copy it from what is generated in the amplify/backend/api/<ApiName>/build/resolvers/Mutation.<operation>.req.vtl. You should see where to start inserting this code by looking at the boilerplate. You will want to put your custom file at amplify/backend/api/<ApiName>/resolvers/Mutation.<operation>.req.vtl so it gets built every time you build the graphql schema.

The code looks at the values provided and instead of overwriting numbers will add them to the value that is already there. So if you pass 5 it will add 5 to whatever value is already in the field. It works with negative numbers as well. The caveat is that ALL NUMERIC FIELDS ARE ADDITIVE. There is no way to simply override the value with these operations.

#set( $expNames = {} )
#set( $expValues = {} )
#set( $expSet = {} )
#set( $expAdd = {} )
#set( $expRemove = [] )
#set( $expNumbers = [] )
#if( $modelObjectKey )
  #set( $keyFields = [] )
  #foreach( $entry in $modelObjectKey.entrySet() )
    $util.qr($keyFields.add("$entry.key"))
  #end
#else
  #set( $keyFields = ["id"] )
#end
#foreach( $entry in $util.map.copyAndRemoveAllKeys($context.args.input, $keyFields).entrySet() )
  #if( !$util.isNull($dynamodbNameOverrideMap) && $dynamodbNameOverrideMap.containsKey("$entry.key") )
    #set( $entryKeyAttributeName = $dynamodbNameOverrideMap.get("$entry.key") )
  #else
    #set( $entryKeyAttributeName = $entry.key )
  #end
  #if( $util.isNull($entry.value) )
    #set( $discard = $expRemove.add("#$entryKeyAttributeName") )
    $util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
  #else
    $util.qr($expSet.put("#$entryKeyAttributeName", ":$entryKeyAttributeName"))
    $util.qr($expNames.put("#$entryKeyAttributeName", "$entry.key"))
    #if ( $util.isNumber($entry.value) )
      $util.qr($expNumbers.add("#$entryKeyAttributeName"))
    #end
    $util.qr($expValues.put(":$entryKeyAttributeName", $util.dynamodb.toDynamoDB($entry.value)))
  #end
#end
#set( $expression = "" )
#if( !$expSet.isEmpty() )
  #set( $expression = "SET" )
  #foreach( $entry in $expSet.entrySet() )
    #if ( $expNumbers.contains($entry.key) )
      #set( $expression = "$expression $entry.key = $entry.key + $entry.value" )
    #else
      #set( $expression = "$expression $entry.key = $entry.value" )
    #end
    #if( $foreach.hasNext() )
      #set( $expression = "$expression," )
    #end
  #end
#end
#if( !$expAdd.isEmpty() )
  #set( $expression = "$expression ADD" )
  #foreach( $entry in $expAdd.entrySet() )
    #set( $expression = "$expression $entry.key $entry.value" )
    #if( $foreach.hasNext() )
      #set( $expression = "$expression," )
    #end
  #end
#end
#if( !$expRemove.isEmpty() )
  #set( $expression = "$expression REMOVE" )
  #foreach( $entry in $expRemove )
    #set( $expression = "$expression $entry" )
    #if( $foreach.hasNext() )
      #set( $expression = "$expression," )
    #end
  #end
#end
#set( $update = {} )
$util.qr($update.put("expression", "$expression"))
#if( !$expNames.isEmpty() )
  $util.qr($update.put("expressionNames", $expNames))
#end
#if( !$expValues.isEmpty() )
  $util.qr($update.put("expressionValues", $expValues))
#end
{
  "version": "2017-02-28",
  "operation": "UpdateItem",
  "key": #if( $modelObjectKey ) $util.toJson($modelObjectKey) #else {
  "id": {
      "S": $util.toJson($context.args.input.id)
  }
} #end,
  "update": $util.toJson($update),
  "condition": $util.toJson($condition)
}
1reaction
tfendtcommented, Mar 22, 2020

Is this not possible with Amplify? It is such a simple operation to not be able to do.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Incrementing/decrementing mutation using GraphQL and ...
I have two columns in the table for likes and dislikes (both integers denoting the number of likes or dislikes each comment has...
Read more >
Atomic set and increment operators for GraphQL mutations
Hasura supports an increment operator to atomically increment numerical values by a given amount to prevent common race conditions where ...
Read more >
Increment/Decrement across a set of nodes? - Discuss Dgraph
If I run your upsert and then the following query: upsert { query { counter(func: has(counter.val)) { old: A as counter.val new: B...
Read more >
Update expressions - Amazon DynamoDB
To update a DynamoDB item's attributes, use an action of an update expression in an API call. ... Incrementing and decrementing numeric attributes....
Read more >
React with Apollo and GraphQL Tutorial - Robin Wieruch
In this case, increment the number of stargazers. const updateAddStar = (. client ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found