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 }