-
-
Save justcallmelarry/0aaaf5eab0f0d529fd3c6c5e79c5bd22 to your computer and use it in GitHub Desktop.
Build ExpressionAttributeValues and UpdateExpression from map[string]interface{} - AWS Go SDK, DynamoDB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strings" | |
"github.com/aws/aws-sdk-go/aws" | |
"github.com/aws/aws-sdk-go/service/dynamodb" | |
"github.com/fatih/structs" | |
) | |
type TestStruct struct { | |
Name string | |
NestedStuff NestedStuff | |
SuperNestedStuff SuperNestedStuff | |
} | |
type NestedStuff struct { | |
Something string | |
} | |
type SuperNestedStuff struct { | |
SomethingNested NestedStuff | |
} | |
// Useful for https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/dynamo-example-update-table-item.html | |
// | |
// input := &dynamodb.UpdateItemInput{ | |
// ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ | |
// ":r": { | |
// N: aws.String("0.5"), | |
// }, | |
// }, | |
// TableName: aws.String("Movies"), | |
// Key: map[string]*dynamodb.AttributeValue{ | |
// "year": { | |
// N: aws.String("2015"), | |
// }, | |
// "title": { | |
// S: aws.String("The Big New Movie"), | |
// }, | |
// }, | |
// ReturnValues: aws.String("UPDATED_NEW"), | |
// UpdateExpression: aws.String("set info.rating = :r"), | |
// } | |
func main() { | |
// Just a test nested Struct | |
test := TestStruct{ | |
Name: "hello", | |
NestedStuff: NestedStuff{ | |
Something: "something1", | |
}, | |
SuperNestedStuff: SuperNestedStuff{ | |
SomethingNested: NestedStuff{ | |
Something: "something2", | |
}, | |
}, | |
} | |
// Get map[string]interface{} from struct | |
m := structs.Map(test) | |
// Get ExpressionAttributeValues from a map[string]interface{}. | |
// Second parameter is parent's name that is used recursively. The first time it's an empty string. | |
exprAttrValues := BuildExpressionAttributeValues(m, "") | |
fmt.Println(fmt.Sprintf("ExpressionAttributeValues %v", exprAttrValues)) | |
// ExpressionAttributeValues map[:name:{ | |
// S: "hello" | |
// } :nestedstuffsomething:{ | |
// S: "something1" | |
// } :supernestedstuffsomethingnestedsomething:{ | |
// S: "something2" | |
// }] | |
// Get ExpressionAttributeNames from a map[string]interface{}. | |
exprAttrNames := BuildExpressionAttributeNames(m) | |
fmt.Println(fmt.Sprintf("ExpressionAttributeNames %v", exprAttrNames)) | |
// Get UpdateExpression from a map[string]interface{}. | |
// Second and Third parameters are parent's name that are used recursively. The first time they are both empty strings. | |
updateExpr := BuildUpdateExpression(m, "", "") | |
// ExpressionAttributeNames map[#name:0x14000013c80 #nestedstuff:0x14000013c90 #something:0x14000013cd0 #somethingnested:0x14000013cc0 #supernestedstuff:0x14000013cb0] | |
fmt.Println(fmt.Sprintf("UpdateExpression %v", updateExpr)) | |
// UpdateExpression #name = :name, #nestedstuff.#something = :nestedstuffsomething, #supernestedstuff.#somethingnested.#something = :supernestedstuffsomethingnestedsomething | |
} | |
func BuildExpressionAttributeValues(input map[string]interface{}, parentName string) map[string]*dynamodb.AttributeValue { | |
// Map to be returned | |
m := make(map[string]*dynamodb.AttributeValue) | |
for k, v := range input { | |
keyName := strings.Join([]string{":", parentName, strings.ToLower(k)}, "") | |
// add more type cases if more are added to the struct | |
switch t := v.(type) { | |
case map[string]interface{}: | |
nextName := strings.TrimPrefix(keyName, ":") | |
// create a similar returned map from the child node and add it to the current return map | |
child := BuildExpressionAttributeValues(t, nextName) | |
for k, v := range child { | |
m[k] = v | |
} | |
case int: | |
m[keyName] = &dynamodb.AttributeValue{ | |
N: aws.String(fmt.Sprint(t)), | |
} | |
case string: | |
m[keyName] = &dynamodb.AttributeValue{ | |
S: aws.String(t), | |
} | |
case bool: | |
m[keyName] = &dynamodb.AttributeValue{ | |
BOOL: aws.Bool(t), | |
} | |
} | |
} | |
return m | |
} | |
func BuildExpressionAttributeNames(input map[string]interface{}) map[string]*string { | |
// Map to be returned | |
m := make(map[string]*string) | |
for k, v := range input { | |
keyName := strings.Join([]string{"#", strings.ToLower(k)}, "") | |
keyValue := string(k) | |
// add more type cases if more are added to the struct | |
switch t := v.(type) { | |
case string, bool, int: | |
m[keyName] = &keyValue | |
case map[string]interface{}: | |
m[keyName] = &keyValue | |
// create a similar returned map from the child node and add it to the current return map | |
child := BuildExpressionAttributeNames(t) | |
for kk, vv := range child { | |
m[kk] = vv | |
} | |
} | |
} | |
return m | |
} | |
func BuildUpdateExpression(input map[string]interface{}, parentName, exprParentName string) string { | |
var s []string | |
for k, v := range input { | |
keyName := strings.Join([]string{":", exprParentName, strings.ToLower(k)}, "") | |
k = fmt.Sprintf("#%v", strings.ToLower(k)) | |
// add more type cases if more are added to the struct | |
switch t := v.(type) { | |
case string, bool, int: | |
currAttrName := k | |
if len(parentName) > 0 { | |
currAttrName = strings.Join([]string{parentName, currAttrName}, ".") | |
} | |
s = append(s, strings.Join([]string{currAttrName, "=", keyName}, " ")) | |
case map[string]interface{}: | |
nextExprParentName := strings.TrimPrefix(keyName, ":") | |
nextParentName := k | |
if len(parentName) > 0 { | |
nextParentName = strings.Join([]string{parentName, k}, ".") | |
} | |
s = append(s, BuildUpdateExpression(t, nextParentName, nextExprParentName)) | |
} | |
} | |
return strings.Join(s, ", ") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment