1package expression
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
8	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
9)
10
11// ValueBuilder represents an item attribute value operand and implements the
12// OperandBuilder interface. Methods and functions in the package take
13// ValueBuilder as an argument and establishes relationships between operands.
14// ValueBuilder should only be initialized using the function Value().
15//
16// Example:
17//
18//     // Create a ValueBuilder representing the string "aValue"
19//     valueBuilder := expression.Value("aValue")
20type ValueBuilder struct {
21	value interface{}
22}
23
24// NameBuilder represents a name of a top level item attribute or a nested
25// attribute. Since NameBuilder represents a DynamoDB Operand, it implements the
26// OperandBuilder interface. Methods and functions in the package take
27// NameBuilder as an argument and establishes relationships between operands.
28// NameBuilder should only be initialized using the function Name().
29//
30// Example:
31//
32//     // Create a NameBuilder representing the item attribute "aName"
33//     nameBuilder := expression.Name("aName")
34type NameBuilder struct {
35	name string
36}
37
38// SizeBuilder represents the output of the function size ("someName"), which
39// evaluates to the size of the item attribute defined by "someName". Since
40// SizeBuilder represents an operand, SizeBuilder implements the OperandBuilder
41// interface. Methods and functions in the package take SizeBuilder as an
42// argument and establishes relationships between operands. SizeBuilder should
43// only be initialized using the function Size().
44//
45// Example:
46//
47//     // Create a SizeBuilder representing the size of the item attribute
48//     // "aName"
49//     sizeBuilder := expression.Name("aName").Size()
50type SizeBuilder struct {
51	nameBuilder NameBuilder
52}
53
54// KeyBuilder represents either the partition key or the sort key, both of which
55// are top level attributes to some item in DynamoDB. Since KeyBuilder
56// represents an operand, KeyBuilder implements the OperandBuilder interface.
57// Methods and functions in the package take KeyBuilder as an argument and
58// establishes relationships between operands. However, KeyBuilder should only
59// be used to describe Key Condition Expressions. KeyBuilder should only be
60// initialized using the function Key().
61//
62// Example:
63//
64//     // Create a KeyBuilder representing the item key "aKey"
65//     keyBuilder := expression.Key("aKey")
66type KeyBuilder struct {
67	key string
68}
69
70// setValueMode specifies the type of SetValueBuilder. The default value is
71// unsetValue so that an UnsetParameterError when BuildOperand() is called on an
72// empty SetValueBuilder.
73type setValueMode int
74
75const (
76	unsetValue setValueMode = iota
77	plusValueMode
78	minusValueMode
79	listAppendValueMode
80	ifNotExistsValueMode
81)
82
83// SetValueBuilder represents the outcome of operator functions supported by the
84// DynamoDB Set operation. The operator functions are the following:
85//     Plus()  // Represents the "+" operator
86//     Minus() // Represents the "-" operator
87//     ListAppend()
88//     IfNotExists()
89// For documentation on the above functions,
90// see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET
91// Since SetValueBuilder represents an operand, it implements the OperandBuilder
92// interface. SetValueBuilder structs are used as arguments to the Set()
93// function. SetValueBuilders should only initialize a SetValueBuilder using the
94// functions listed above.
95type SetValueBuilder struct {
96	leftOperand  OperandBuilder
97	rightOperand OperandBuilder
98	mode         setValueMode
99}
100
101// Operand represents an item attribute name or value in DynamoDB. The
102// relationship between Operands specified by various builders such as
103// ConditionBuilders and UpdateBuilders for example is processed internally to
104// write Condition Expressions and Update Expressions respectively.
105type Operand struct {
106	exprNode exprNode
107}
108
109// OperandBuilder represents the idea of Operand which are building blocks to
110// DynamoDB Expressions. Package methods and functions can establish
111// relationships between operands, representing DynamoDB Expressions. The method
112// BuildOperand() is called recursively when the Build() method on the type
113// Builder is called. BuildOperand() should never be called externally.
114// OperandBuilder and BuildOperand() are exported to allow package functions to
115// take an interface as an argument.
116type OperandBuilder interface {
117	BuildOperand() (Operand, error)
118}
119
120// Name creates a NameBuilder. The argument should represent the desired item
121// attribute. It is possible to reference nested item attributes by using
122// square brackets for lists and dots for maps. For documentation on specifying
123// item attributes,
124// see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Attributes.html
125//
126// Example:
127//
128//     // Specify a top-level attribute
129//     name := expression.Name("TopLevel")
130//     // Specify a nested attribute
131//     nested := expression.Name("Record[6].SongList")
132//     // Use Name() to create a condition expression
133//     condition := expression.Name("foo").Equal(expression.Name("bar"))
134func Name(name string) NameBuilder {
135	return NameBuilder{
136		name: name,
137	}
138}
139
140// Value creates a ValueBuilder and sets its value to the argument. The value
141// will be marshalled using the attributevalue package, unless it is of
142// type types.AttributeValue, where it will be used directly.
143//
144// Empty slices and maps will be encoded as their respective empty types.AttributeValue
145// types. If a NULL value is required, pass a dynamodb.AttributeValue, e.g.:
146// emptyList := &types.AttributeValueMemberNULL{Value: true}
147//
148// Example:
149//
150//     // Use Value() to create a condition expression
151//     condition := expression.Name("foo").Equal(expression.Value(10))
152//     // Use Value() to set the value of a set expression.
153//     update := Set(expression.Name("greets"), expression.Value(&types.AttributeValueMemberS{Value: "hello"}))
154func Value(value interface{}) ValueBuilder {
155	return ValueBuilder{
156		value: value,
157	}
158}
159
160// Size creates a SizeBuilder representing the size of the item attribute
161// specified by the argument NameBuilder. Size() is only valid for certain types
162// of item attributes. For documentation,
163// see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html
164// SizeBuilder is only a valid operand in Condition Expressions and Filter
165// Expressions.
166//
167// Example:
168//
169//     // Use Size() to create a condition expression
170//     condition := expression.Name("foo").Size().Equal(expression.Value(10))
171//
172// Expression Equivalent:
173//
174//     expression.Name("aName").Size()
175//     "size (aName)"
176func (nb NameBuilder) Size() SizeBuilder {
177	return SizeBuilder{
178		nameBuilder: nb,
179	}
180}
181
182// Size creates a SizeBuilder representing the size of the item attribute
183// specified by the argument NameBuilder. Size() is only valid for certain types
184// of item attributes. For documentation,
185// see: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html
186// SizeBuilder is only a valid operand in Condition Expressions and Filter
187// Expressions.
188//
189// Example:
190//
191//     // Use Size() to create a condition expression
192//     condition := expression.Size(expression.Name("foo")).Equal(expression.Value(10))
193//
194// Expression Equivalent:
195//
196//     expression.Size(expression.Name("aName"))
197//     "size (aName)"
198func Size(nameBuilder NameBuilder) SizeBuilder {
199	return nameBuilder.Size()
200}
201
202// Key creates a KeyBuilder. The argument should represent the desired partition
203// key or sort key value. KeyBuilders should only be used to specify
204// relationships for Key Condition Expressions. When referring to the partition
205// key or sort key in any other Expression, use Name().
206//
207// Example:
208//
209//     // Use Key() to create a key condition expression
210//     keyCondition := expression.Key("foo").Equal(expression.Value("bar"))
211func Key(key string) KeyBuilder {
212	return KeyBuilder{
213		key: key,
214	}
215}
216
217// Plus creates a SetValueBuilder to be used in as an argument to Set(). The
218// arguments can either be NameBuilders or ValueBuilders. Plus() only supports
219// DynamoDB Number types, so the ValueBuilder must be a Number and the
220// NameBuilder must specify an item attribute of type Number.
221// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
222//
223// Example:
224//
225//     // Use Plus() to set the value of the item attribute "someName" to 5 + 10
226//     update, err := expression.Set(expression.Name("someName"), expression.Plus(expression.Value(5), expression.Value(10)))
227//
228// Expression Equivalent:
229//
230//     expression.Plus(expression.Value(5), expression.Value(10))
231//     // let :five and :ten be ExpressionAttributeValues for the values 5 and
232//     // 10 respectively.
233//     ":five + :ten"
234func Plus(leftOperand, rightOperand OperandBuilder) SetValueBuilder {
235	return SetValueBuilder{
236		leftOperand:  leftOperand,
237		rightOperand: rightOperand,
238		mode:         plusValueMode,
239	}
240}
241
242// Plus creates a SetValueBuilder to be used in as an argument to Set(). The
243// arguments can either be NameBuilders or ValueBuilders. Plus() only supports
244// DynamoDB Number types, so the ValueBuilder must be a Number and the
245// NameBuilder must specify an item attribute of type Number.
246// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
247//
248// Example:
249//
250//     // Use Plus() to set the value of the item attribute "someName" to the
251//     // numeric value of item attribute "aName" incremented by 10
252//     update, err := expression.Set(expression.Name("someName"), expression.Name("aName").Plus(expression.Value(10)))
253//
254// Expression Equivalent:
255//
256//     expression.Name("aName").Plus(expression.Value(10))
257//     // let :ten be ExpressionAttributeValues representing the value 10
258//     "aName + :ten"
259func (nb NameBuilder) Plus(rightOperand OperandBuilder) SetValueBuilder {
260	return Plus(nb, rightOperand)
261}
262
263// Plus creates a SetValueBuilder to be used in as an argument to Set(). The
264// arguments can either be NameBuilders or ValueBuilders. Plus() only supports
265// DynamoDB Number types, so the ValueBuilder must be a Number and the
266// NameBuilder must specify an item attribute of type Number.
267// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
268//
269// Example:
270//
271//     // Use Plus() to set the value of the item attribute "someName" to 5 + 10
272//     update, err := expression.Set(expression.Name("someName"), expression.Value(5).Plus(expression.Value(10)))
273//
274// Expression Equivalent:
275//
276//     expression.Value(5).Plus(expression.Value(10))
277//     // let :five and :ten be ExpressionAttributeValues representing the value
278//     // 5 and 10 respectively
279//     ":five + :ten"
280func (vb ValueBuilder) Plus(rightOperand OperandBuilder) SetValueBuilder {
281	return Plus(vb, rightOperand)
282}
283
284// Minus creates a SetValueBuilder to be used in as an argument to Set(). The
285// arguments can either be NameBuilders or ValueBuilders. Minus() only supports
286// DynamoDB Number types, so the ValueBuilder must be a Number and the
287// NameBuilder must specify an item attribute of type Number.
288// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
289//
290// Example:
291//
292//     // Use Minus() to set the value of item attribute "someName" to 5 - 10
293//     update, err := expression.Set(expression.Name("someName"), expression.Minus(expression.Value(5), expression.Value(10)))
294//
295// Expression Equivalent:
296//
297//     expression.Minus(expression.Value(5), expression.Value(10))
298//     // let :five and :ten be ExpressionAttributeValues for the values 5 and
299//     // 10 respectively.
300//     ":five - :ten"
301func Minus(leftOperand, rightOperand OperandBuilder) SetValueBuilder {
302	return SetValueBuilder{
303		leftOperand:  leftOperand,
304		rightOperand: rightOperand,
305		mode:         minusValueMode,
306	}
307}
308
309// Minus creates a SetValueBuilder to be used in as an argument to Set(). The
310// arguments can either be NameBuilders or ValueBuilders. Minus() only supports
311// DynamoDB Number types, so the ValueBuilder must be a Number and the
312// NameBuilder must specify an item attribute of type Number.
313// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
314//
315// Example:
316//
317//     // Use Minus() to set the value of item attribute "someName" to the
318//     // numeric value of "aName" decremented by 10
319//     update, err := expression.Set(expression.Name("someName"), expression.Name("aName").Minus(expression.Value(10)))
320//
321// Expression Equivalent:
322//
323//     expression.Name("aName").Minus(expression.Value(10)))
324//     // let :ten be ExpressionAttributeValues represent the value 10
325//     "aName - :ten"
326func (nb NameBuilder) Minus(rightOperand OperandBuilder) SetValueBuilder {
327	return Minus(nb, rightOperand)
328}
329
330// Minus creates a SetValueBuilder to be used in as an argument to Set(). The
331// arguments can either be NameBuilders or ValueBuilders. Minus() only supports
332// DynamoDB Number types, so the ValueBuilder must be a Number and the
333// NameBuilder must specify an item attribute of type Number.
334// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.IncrementAndDecrement
335//
336// Example:
337//
338//     // Use Minus() to set the value of item attribute "someName" to 5 - 10
339//     update, err := expression.Set(expression.Name("someName"), expression.Value(5).Minus(expression.Value(10)))
340//
341// Expression Equivalent:
342//
343//     expression.Value(5).Minus(expression.Value(10))
344//     // let :five and :ten be ExpressionAttributeValues for the values 5 and
345//     // 10 respectively.
346//     ":five - :ten"
347func (vb ValueBuilder) Minus(rightOperand OperandBuilder) SetValueBuilder {
348	return Minus(vb, rightOperand)
349}
350
351// ListAppend creates a SetValueBuilder to be used in as an argument to Set().
352// The arguments can either be NameBuilders or ValueBuilders. ListAppend() only
353// supports DynamoDB List types, so the ValueBuilder must be a List and the
354// NameBuilder must specify an item attribute of type List.
355// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.UpdatingListElements
356//
357// Example:
358//
359//     // Use ListAppend() to set item attribute "someName" to the item
360//     // attribute "nameOfList" with "some" and "list" appended to it
361//     update, err := expression.Set(expression.Name("someName"), expression.ListAppend(expression.Name("nameOfList"), expression.Value([]string{"some", "list"})))
362//
363// Expression Equivalent:
364//
365//     expression.ListAppend(expression.Name("nameOfList"), expression.Value([]string{"some", "list"})
366//     // let :list be a ExpressionAttributeValue representing the list
367//     // containing "some" and "list".
368//     "list_append (nameOfList, :list)"
369func ListAppend(leftOperand, rightOperand OperandBuilder) SetValueBuilder {
370	return SetValueBuilder{
371		leftOperand:  leftOperand,
372		rightOperand: rightOperand,
373		mode:         listAppendValueMode,
374	}
375}
376
377// ListAppend creates a SetValueBuilder to be used in as an argument to Set().
378// The arguments can either be NameBuilders or ValueBuilders. ListAppend() only
379// supports DynamoDB List types, so the ValueBuilder must be a List and the
380// NameBuilder must specify an item attribute of type List.
381// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.UpdatingListElements
382//
383// Example:
384//
385//     // Use ListAppend() to set item attribute "someName" to the item
386//     // attribute "nameOfList" with "some" and "list" appended to it
387//     update, err := expression.Set(expression.Name("someName"), expression.Name("nameOfList").ListAppend(expression.Value([]string{"some", "list"})))
388//
389// Expression Equivalent:
390//
391//     expression.Name("nameOfList").ListAppend(expression.Value([]string{"some", "list"})
392//     // let :list be a ExpressionAttributeValue representing the list
393//     // containing "some" and "list".
394//     "list_append (nameOfList, :list)"
395func (nb NameBuilder) ListAppend(rightOperand OperandBuilder) SetValueBuilder {
396	return ListAppend(nb, rightOperand)
397}
398
399// ListAppend creates a SetValueBuilder to be used in as an argument to Set().
400// The arguments can either be NameBuilders or ValueBuilders. ListAppend() only
401// supports DynamoDB List types, so the ValueBuilder must be a List and the
402// NameBuilder must specify an item attribute of type List.
403// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.UpdatingListElements
404//
405// Example:
406//
407//     // Use ListAppend() to set item attribute "someName" to a string list
408//     // equal to {"a", "list", "some", "list"}
409//     update, err := expression.Set(expression.Name("someName"), expression.Value([]string{"a", "list"}).ListAppend(expression.Value([]string{"some", "list"})))
410//
411// Expression Equivalent:
412//
413//     expression.Name([]string{"a", "list"}).ListAppend(expression.Value([]string{"some", "list"})
414//     // let :list1 and :list2 be a ExpressionAttributeValue representing the
415//     // list {"a", "list"} and {"some", "list"} respectively
416//     "list_append (:list1, :list2)"
417func (vb ValueBuilder) ListAppend(rightOperand OperandBuilder) SetValueBuilder {
418	return ListAppend(vb, rightOperand)
419}
420
421// IfNotExists creates a SetValueBuilder to be used in as an argument to Set().
422// The first argument must be a NameBuilder representing the name where the new
423// item attribute is created. The second argument can either be a NameBuilder or
424// a ValueBuilder. In the case that it is a NameBuilder, the value of the item
425// attribute at the name specified becomes the value of the new item attribute.
426// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites
427//
428// Example:
429//
430//     // Use IfNotExists() to set item attribute "someName" to value 5 if
431//     // "someName" does not exist yet. (Prevents overwrite)
432//     update, err := expression.Set(expression.Name("someName"), expression.IfNotExists(expression.Name("someName"), expression.Value(5)))
433//
434// Expression Equivalent:
435//
436//     expression.IfNotExists(expression.Name("someName"), expression.Value(5))
437//     // let :five be a ExpressionAttributeValue representing the value 5
438//     "if_not_exists (someName, :five)"
439func IfNotExists(name NameBuilder, setValue OperandBuilder) SetValueBuilder {
440	return SetValueBuilder{
441		leftOperand:  name,
442		rightOperand: setValue,
443		mode:         ifNotExistsValueMode,
444	}
445}
446
447// IfNotExists creates a SetValueBuilder to be used in as an argument to Set().
448// The first argument must be a NameBuilder representing the name where the new
449// item attribute is created. The second argument can either be a NameBuilder or
450// a ValueBuilder. In the case that it is a NameBuilder, the value of the item
451// attribute at the name specified becomes the value of the new item attribute.
452// More information: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites
453//
454// Example:
455//
456//     // Use IfNotExists() to set item attribute "someName" to value 5 if
457//     // "someName" does not exist yet. (Prevents overwrite)
458//     update, err := expression.Set(expression.Name("someName"), expression.Name("someName").IfNotExists(expression.Value(5)))
459//
460// Expression Equivalent:
461//
462//     expression.Name("someName").IfNotExists(expression.Value(5))
463//     // let :five be a ExpressionAttributeValue representing the value 5
464//     "if_not_exists (someName, :five)"
465func (nb NameBuilder) IfNotExists(rightOperand OperandBuilder) SetValueBuilder {
466	return IfNotExists(nb, rightOperand)
467}
468
469// BuildOperand creates an Operand struct which are building blocks to DynamoDB
470// Expressions. Package methods and functions can establish relationships
471// between operands, representing DynamoDB Expressions. The method
472// BuildOperand() is called recursively when the Build() method on the type
473// Builder is called. BuildOperand() should never be called externally.
474// BuildOperand() aliases all strings to avoid stepping over DynamoDB's reserved
475// words.
476// More information on reserved words at http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
477func (nb NameBuilder) BuildOperand() (Operand, error) {
478	if nb.name == "" {
479		return Operand{}, newUnsetParameterError("BuildOperand", "NameBuilder")
480	}
481
482	node := exprNode{
483		names: []string{},
484	}
485
486	nameSplit := strings.Split(nb.name, ".")
487	fmtNames := make([]string, 0, len(nameSplit))
488
489	for _, word := range nameSplit {
490		var substr string
491		if word == "" {
492			return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
493		}
494
495		if word[len(word)-1] == ']' {
496			for j, char := range word {
497				if char == '[' {
498					substr = word[j:]
499					word = word[:j]
500					break
501				}
502			}
503		}
504
505		if word == "" {
506			return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
507		}
508
509		// Create a string with special characters that can be substituted later: $p
510		node.names = append(node.names, word)
511		fmtNames = append(fmtNames, "$n"+substr)
512	}
513	node.fmtExpr = strings.Join(fmtNames, ".")
514	return Operand{
515		exprNode: node,
516	}, nil
517}
518
519// BuildOperand creates an Operand struct which are building blocks to DynamoDB
520// Expressions. Package methods and functions can establish relationships
521// between operands, representing DynamoDB Expressions. The method
522// BuildOperand() is called recursively when the Build() method on the type
523// Builder is called. BuildOperand() should never be called externally.
524// BuildOperand() aliases all strings to avoid stepping over DynamoDB's reserved
525// words.
526// More information on reserved words at http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
527func (vb ValueBuilder) BuildOperand() (Operand, error) {
528	var (
529		expr types.AttributeValue
530		err  error
531	)
532
533	switch v := vb.value.(type) {
534	case types.AttributeValueMemberS:
535		expr = &v
536	case types.AttributeValueMemberN:
537		expr = &v
538	case types.AttributeValueMemberB:
539		expr = &v
540	case types.AttributeValueMemberSS:
541		expr = &v
542	case types.AttributeValueMemberNS:
543		expr = &v
544	case types.AttributeValueMemberBS:
545		expr = &v
546	case types.AttributeValueMemberM:
547		expr = &v
548	case types.AttributeValueMemberL:
549		expr = &v
550	case types.AttributeValueMemberNULL:
551		expr = &v
552	case types.AttributeValueMemberBOOL:
553		expr = &v
554	case types.AttributeValue:
555		expr = v
556	default:
557		expr, err = attributevalue.Marshal(vb.value)
558		if err != nil {
559			return Operand{}, newInvalidParameterError("BuildOperand", "ValueBuilder")
560		}
561	}
562
563	// Create a string with special characters that can be substituted later: $v
564	operand := Operand{
565		exprNode: exprNode{
566			values:  []types.AttributeValue{expr},
567			fmtExpr: "$v",
568		},
569	}
570	return operand, nil
571}
572
573// BuildOperand creates an Operand struct which are building blocks to DynamoDB
574// Expressions. Package methods and functions can establish relationships
575// between operands, representing DynamoDB Expressions. The method
576// BuildOperand() is called recursively when the Build() method on the type
577// Builder is called. BuildOperand() should never be called externally.
578// BuildOperand() aliases all strings to avoid stepping over DynamoDB's reserved
579// words.
580// More information on reserved words at http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
581func (sb SizeBuilder) BuildOperand() (Operand, error) {
582	operand, err := sb.nameBuilder.BuildOperand()
583	operand.exprNode.fmtExpr = "size (" + operand.exprNode.fmtExpr + ")"
584
585	return operand, err
586}
587
588// BuildOperand creates an Operand struct which are building blocks to DynamoDB
589// Expressions. Package methods and functions can establish relationships
590// between operands, representing DynamoDB Expressions. The method
591// BuildOperand() is called recursively when the Build() method on the type
592// Builder is called. BuildOperand() should never be called externally.
593// BuildOperand() aliases all strings to avoid stepping over DynamoDB's reserved
594// words.
595// More information on reserved words at http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
596func (kb KeyBuilder) BuildOperand() (Operand, error) {
597	if kb.key == "" {
598		return Operand{}, newUnsetParameterError("BuildOperand", "KeyBuilder")
599	}
600
601	ret := Operand{
602		exprNode: exprNode{
603			names:   []string{kb.key},
604			fmtExpr: "$n",
605		},
606	}
607
608	return ret, nil
609}
610
611// BuildOperand creates an Operand struct which are building blocks to DynamoDB
612// Expressions. Package methods and functions can establish relationships
613// between operands, representing DynamoDB Expressions. The method
614// BuildOperand() is called recursively when the Build() method on the type
615// Builder is called. BuildOperand() should never be called externally.
616// BuildOperand() aliases all strings to avoid stepping over DynamoDB's reserved
617// words.
618// More information on reserved words at http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
619func (svb SetValueBuilder) BuildOperand() (Operand, error) {
620	if svb.mode == unsetValue {
621		return Operand{}, newUnsetParameterError("BuildOperand", "SetValueBuilder")
622	}
623
624	left, err := svb.leftOperand.BuildOperand()
625	if err != nil {
626		return Operand{}, err
627	}
628	leftNode := left.exprNode
629
630	right, err := svb.rightOperand.BuildOperand()
631	if err != nil {
632		return Operand{}, err
633	}
634	rightNode := right.exprNode
635
636	node := exprNode{
637		children: []exprNode{leftNode, rightNode},
638	}
639
640	switch svb.mode {
641	case plusValueMode:
642		node.fmtExpr = "$c + $c"
643	case minusValueMode:
644		node.fmtExpr = "$c - $c"
645	case listAppendValueMode:
646		node.fmtExpr = "list_append($c, $c)"
647	case ifNotExistsValueMode:
648		node.fmtExpr = "if_not_exists($c, $c)"
649	default:
650		return Operand{}, fmt.Errorf("build operand error: unsupported mode: %v", svb.mode)
651	}
652
653	return Operand{
654		exprNode: node,
655	}, nil
656}
657