update.go 13 KB

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