github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/expression/update.go (about)

     1  package expression
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  )
     8  
     9  // operationMode specifies the types of update operations that the
    10  // updateBuilder is going to represent. The const is in a string to use the
    11  // const value as a map key and as a string when creating the formatted
    12  // expression for the exprNodes.
    13  type operationMode string
    14  
    15  const (
    16  	setOperation    operationMode = "SET"
    17  	removeOperation               = "REMOVE"
    18  	addOperation                  = "ADD"
    19  	deleteOperation               = "DELETE"
    20  )
    21  
    22  // Implementing the Sort interface
    23  type modeList []operationMode
    24  
    25  func (ml modeList) Len() int {
    26  	return len(ml)
    27  }
    28  
    29  func (ml modeList) Less(i, j int) bool {
    30  	return string(ml[i]) < string(ml[j])
    31  }
    32  
    33  func (ml modeList) Swap(i, j int) {
    34  	ml[i], ml[j] = ml[j], ml[i]
    35  }
    36  
    37  // UpdateBuilder represents Update Expressions in DynamoDB. UpdateBuilders
    38  // are the building blocks of the Builder struct. Note that there are different
    39  // update operations in DynamoDB and an UpdateBuilder can represent multiple
    40  // update operations.
    41  // More Information at: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html
    42  type UpdateBuilder struct {
    43  	operationList map[operationMode][]operationBuilder
    44  }
    45  
    46  // operationBuilder represents specific update actions (SET, REMOVE, ADD,
    47  // DELETE). The mode specifies what type of update action the
    48  // operationBuilder represents.
    49  type operationBuilder struct {
    50  	name  NameBuilder
    51  	value OperandBuilder
    52  	mode  operationMode
    53  }
    54  
    55  // buildOperation builds an exprNode from an operationBuilder. buildOperation
    56  // is called recursively by buildTree in order to create a tree structure
    57  // of exprNodes representing the parent/child relationships between
    58  // UpdateBuilders and operationBuilders.
    59  func (ob operationBuilder) buildOperation() (exprNode, error) {
    60  	pathChild, err := ob.name.BuildOperand()
    61  	if err != nil {
    62  		return exprNode{}, err
    63  	}
    64  
    65  	node := exprNode{
    66  		children: []exprNode{pathChild.exprNode},
    67  		fmtExpr:  "$c",
    68  	}
    69  
    70  	if ob.mode == removeOperation {
    71  		return node, nil
    72  	}
    73  
    74  	valueChild, err := ob.value.BuildOperand()
    75  	if err != nil {
    76  		return exprNode{}, err
    77  	}
    78  	node.children = append(node.children, valueChild.exprNode)
    79  
    80  	switch ob.mode {
    81  	case setOperation:
    82  		node.fmtExpr += " = $c"
    83  	case addOperation, deleteOperation:
    84  		node.fmtExpr += " $c"
    85  	default:
    86  		return exprNode{}, fmt.Errorf("build update error: build operation error: unsupported mode: %v", ob.mode)
    87  	}
    88  
    89  	return node, nil
    90  }
    91  
    92  // Delete returns an UpdateBuilder representing one Delete operation for
    93  // DynamoDB Update Expressions. The argument name should specify the item
    94  // attribute and the argument value should specify the value to be deleted. The
    95  // resulting UpdateBuilder can be used as an argument to the WithUpdate() method
    96  // for the Builder struct.
    97  //
    98  // Example:
    99  //
   100  //     // update represents the delete operation to delete the string value
   101  //     // "subsetToDelete" from the item attribute "pathToList"
   102  //     update := expression.Delete(expression.Name("pathToList"), expression.Value("subsetToDelete"))
   103  //
   104  //     // Adding more update methods
   105  //     anotherUpdate := update.Remove(expression.Name("someName"))
   106  //     // Creating a Builder
   107  //     builder := Update(update)
   108  //
   109  // Expression Equivalent:
   110  //
   111  //     expression.Delete(expression.Name("pathToList"), expression.Value("subsetToDelete"))
   112  //     // let :del be an ExpressionAttributeValue representing the value
   113  //     // "subsetToDelete"
   114  //     "DELETE pathToList :del"
   115  func Delete(name NameBuilder, value ValueBuilder) UpdateBuilder {
   116  	emptyUpdateBuilder := UpdateBuilder{}
   117  	return emptyUpdateBuilder.Delete(name, value)
   118  }
   119  
   120  // Delete adds a Delete operation to the argument UpdateBuilder. The
   121  // argument name should specify the item attribute and the argument value should
   122  // specify the value to be deleted. The resulting UpdateBuilder can be used as
   123  // an argument to the WithUpdate() method for the Builder struct.
   124  //
   125  // Example:
   126  //
   127  //     // Let update represent an already existing update expression. Delete()
   128  //     // adds the operation to delete the value "subsetToDelete" from the item
   129  //     // attribute "pathToList"
   130  //     update := update.Delete(expression.Name("pathToList"), expression.Value("subsetToDelete"))
   131  //
   132  //     // Adding more update methods
   133  //     anotherUpdate := update.Remove(expression.Name("someName"))
   134  //     // Creating a Builder
   135  //     builder := Update(update)
   136  //
   137  // Expression Equivalent:
   138  //
   139  //     Delete(expression.Name("pathToList"), expression.Value("subsetToDelete"))
   140  //     // let :del be an ExpressionAttributeValue representing the value
   141  //     // "subsetToDelete"
   142  //     "DELETE pathToList :del"
   143  func (ub UpdateBuilder) Delete(name NameBuilder, value ValueBuilder) UpdateBuilder {
   144  	if ub.operationList == nil {
   145  		ub.operationList = map[operationMode][]operationBuilder{}
   146  	}
   147  	ub.operationList[deleteOperation] = append(ub.operationList[deleteOperation], operationBuilder{
   148  		name:  name,
   149  		value: value,
   150  		mode:  deleteOperation,
   151  	})
   152  	return ub
   153  }
   154  
   155  // Add returns an UpdateBuilder representing the Add operation for DynamoDB
   156  // Update Expressions. The argument name should specify the item attribute and
   157  // the argument value should specify the value to be added. The resulting
   158  // UpdateBuilder can be used as an argument to the WithUpdate() method for the
   159  // Builder struct.
   160  //
   161  // Example:
   162  //
   163  //     // update represents the add operation to add the value 5 to the item
   164  //     // attribute "aPath"
   165  //     update := expression.Add(expression.Name("aPath"), expression.Value(5))
   166  //
   167  //     // Adding more update methods
   168  //     anotherUpdate := update.Remove(expression.Name("someName"))
   169  //     // Creating a Builder
   170  //     builder := Update(update)
   171  //
   172  // Expression Equivalent:
   173  //
   174  //     expression.Add(expression.Name("aPath"), expression.Value(5))
   175  //     // Let :five be an ExpressionAttributeValue representing the value 5
   176  //     "ADD aPath :5"
   177  func Add(name NameBuilder, value ValueBuilder) UpdateBuilder {
   178  	emptyUpdateBuilder := UpdateBuilder{}
   179  	return emptyUpdateBuilder.Add(name, value)
   180  }
   181  
   182  // Add adds an Add operation to the argument UpdateBuilder. The argument
   183  // name should specify the item attribute and the argument value should specify
   184  // the value to be added. The resulting UpdateBuilder can be used as an argument
   185  // to the WithUpdate() method for the Builder struct.
   186  //
   187  // Example:
   188  //
   189  //     // Let update represent an already existing update expression. Add() adds
   190  //     // the operation to add the value 5 to the item attribute "aPath"
   191  //     update := update.Add(expression.Name("aPath"), expression.Value(5))
   192  //
   193  //     // Adding more update methods
   194  //     anotherUpdate := update.Remove(expression.Name("someName"))
   195  //     // Creating a Builder
   196  //     builder := Update(update)
   197  //
   198  // Expression Equivalent:
   199  //
   200  //     Add(expression.Name("aPath"), expression.Value(5))
   201  //     // Let :five be an ExpressionAttributeValue representing the value 5
   202  //     "ADD aPath :5"
   203  func (ub UpdateBuilder) Add(name NameBuilder, value ValueBuilder) UpdateBuilder {
   204  	if ub.operationList == nil {
   205  		ub.operationList = map[operationMode][]operationBuilder{}
   206  	}
   207  	ub.operationList[addOperation] = append(ub.operationList[addOperation], operationBuilder{
   208  		name:  name,
   209  		value: value,
   210  		mode:  addOperation,
   211  	})
   212  	return ub
   213  }
   214  
   215  // Remove returns an UpdateBuilder representing the Remove operation for
   216  // DynamoDB Update Expressions. The argument name should specify the item
   217  // attribute to delete. The resulting UpdateBuilder can be used as an argument
   218  // to the WithUpdate() method for the Builder struct.
   219  //
   220  // Example:
   221  //
   222  //     // update represents the remove operation to remove the item attribute
   223  //     // "itemToRemove"
   224  //     update := expression.Remove(expression.Name("itemToRemove"))
   225  //
   226  //     // Adding more update methods
   227  //     anotherUpdate := update.Remove(expression.Name("someName"))
   228  //     // Creating a Builder
   229  //     builder := Update(update)
   230  //
   231  // Expression Equivalent:
   232  //
   233  //     expression.Remove(expression.Name("itemToRemove"))
   234  //     "REMOVE itemToRemove"
   235  func Remove(name NameBuilder) UpdateBuilder {
   236  	emptyUpdateBuilder := UpdateBuilder{}
   237  	return emptyUpdateBuilder.Remove(name)
   238  }
   239  
   240  // Remove adds a Remove operation to the argument UpdateBuilder. The
   241  // argument name should specify the item attribute to delete. The resulting
   242  // UpdateBuilder can be used as an argument to the WithUpdate() method for the
   243  // Builder struct.
   244  //
   245  // Example:
   246  //
   247  //     // Let update represent an already existing update expression. Remove()
   248  //     // adds the operation to remove the item attribute "itemToRemove"
   249  //     update := update.Remove(expression.Name("itemToRemove"))
   250  //
   251  //     // Adding more update methods
   252  //     anotherUpdate := update.Remove(expression.Name("someName"))
   253  //     // Creating a Builder
   254  //     builder := Update(update)
   255  //
   256  // Expression Equivalent:
   257  //
   258  //     Remove(expression.Name("itemToRemove"))
   259  //     "REMOVE itemToRemove"
   260  func (ub UpdateBuilder) Remove(name NameBuilder) UpdateBuilder {
   261  	if ub.operationList == nil {
   262  		ub.operationList = map[operationMode][]operationBuilder{}
   263  	}
   264  	ub.operationList[removeOperation] = append(ub.operationList[removeOperation], operationBuilder{
   265  		name: name,
   266  		mode: removeOperation,
   267  	})
   268  	return ub
   269  }
   270  
   271  // Set returns an UpdateBuilder representing the Set operation for DynamoDB
   272  // Update Expressions. The argument name should specify the item attribute to
   273  // modify. The argument OperandBuilder should specify the value to modify the
   274  // the item attribute to. The resulting UpdateBuilder can be used as an argument
   275  // to the WithUpdate() method for the Builder struct.
   276  //
   277  // Example:
   278  //
   279  //     // update represents the set operation to set the item attribute
   280  //     // "itemToSet" to the value "setValue" if the item attribute does not
   281  //     // exist yet. (conditional write)
   282  //     update := expression.Set(expression.Name("itemToSet"), expression.IfNotExists(expression.Name("itemToSet"), expression.Value("setValue")))
   283  //
   284  //     // Adding more update methods
   285  //     anotherUpdate := update.Remove(expression.Name("someName"))
   286  //     // Creating a Builder
   287  //     builder := Update(update)
   288  //
   289  // Expression Equivalent:
   290  //
   291  //     expression.Set(expression.Name("itemToSet"), expression.IfNotExists(expression.Name("itemToSet"), expression.Value("setValue")))
   292  //     // Let :val be an ExpressionAttributeValue representing the value
   293  //     // "setValue"
   294  //     "SET itemToSet = :val"
   295  func Set(name NameBuilder, operandBuilder OperandBuilder) UpdateBuilder {
   296  	emptyUpdateBuilder := UpdateBuilder{}
   297  	return emptyUpdateBuilder.Set(name, operandBuilder)
   298  }
   299  
   300  // Set adds a Set operation to the argument UpdateBuilder. The argument name
   301  // should specify the item attribute to modify. The argument OperandBuilder
   302  // should specify the value to modify the the item attribute to. The resulting
   303  // UpdateBuilder can be used as an argument to the WithUpdate() method for the
   304  // Builder struct.
   305  //
   306  // Example:
   307  //
   308  //     // Let update represent an already existing update expression. Set() adds
   309  //     // the operation to to set the item attribute "itemToSet" to the value
   310  //     // "setValue" if the item attribute does not exist yet. (conditional
   311  //     // write)
   312  //     update := update.Set(expression.Name("itemToSet"), expression.IfNotExists(expression.Name("itemToSet"), expression.Value("setValue")))
   313  //
   314  //     // Adding more update methods
   315  //     anotherUpdate := update.Remove(expression.Name("someName"))
   316  //     // Creating a Builder
   317  //     builder := Update(update)
   318  //
   319  // Expression Equivalent:
   320  //
   321  //     Set(expression.Name("itemToSet"), expression.IfNotExists(expression.Name("itemToSet"), expression.Value("setValue")))
   322  //     // Let :val be an ExpressionAttributeValue representing the value
   323  //     // "setValue"
   324  //     "SET itemToSet = :val"
   325  func (ub UpdateBuilder) Set(name NameBuilder, operandBuilder OperandBuilder) UpdateBuilder {
   326  	if ub.operationList == nil {
   327  		ub.operationList = map[operationMode][]operationBuilder{}
   328  	}
   329  	ub.operationList[setOperation] = append(ub.operationList[setOperation], operationBuilder{
   330  		name:  name,
   331  		value: operandBuilder,
   332  		mode:  setOperation,
   333  	})
   334  	return ub
   335  }
   336  
   337  // buildTree builds a tree structure of exprNodes based on the tree
   338  // structure of the input UpdateBuilder's child UpdateBuilders/Operands.
   339  // buildTree() satisfies the TreeBuilder interface so ProjectionBuilder can be a
   340  // part of Expression struct.
   341  func (ub UpdateBuilder) buildTree() (exprNode, error) {
   342  	if ub.operationList == nil {
   343  		return exprNode{}, newUnsetParameterError("buildTree", "UpdateBuilder")
   344  	}
   345  	ret := exprNode{
   346  		children: []exprNode{},
   347  	}
   348  
   349  	modes := modeList{}
   350  
   351  	for mode := range ub.operationList {
   352  		modes = append(modes, mode)
   353  	}
   354  
   355  	sort.Sort(modes)
   356  
   357  	for _, key := range modes {
   358  		ret.fmtExpr += string(key) + " $c\n"
   359  
   360  		childNode, err := buildChildNodes(ub.operationList[key])
   361  		if err != nil {
   362  			return exprNode{}, err
   363  		}
   364  
   365  		ret.children = append(ret.children, childNode)
   366  	}
   367  
   368  	return ret, nil
   369  }
   370  
   371  // buildChildNodes creates the list of the child exprNodes.
   372  func buildChildNodes(operationBuilderList []operationBuilder) (exprNode, error) {
   373  	if len(operationBuilderList) == 0 {
   374  		return exprNode{}, fmt.Errorf("buildChildNodes error: operationBuilder list is empty")
   375  	}
   376  
   377  	node := exprNode{
   378  		children: make([]exprNode, 0, len(operationBuilderList)),
   379  		fmtExpr:  "$c" + strings.Repeat(", $c", len(operationBuilderList)-1),
   380  	}
   381  
   382  	for _, val := range operationBuilderList {
   383  		valNode, err := val.buildOperation()
   384  		if err != nil {
   385  			return exprNode{}, err
   386  		}
   387  		node.children = append(node.children, valNode)
   388  	}
   389  
   390  	return node, nil
   391  }