1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
package dynamodb
import (
"encoding/json"
"fmt"
"github.com/AdRoll/goamz/dynamodb/dynamizer"
)
const (
// MaxGetBatchSize limits the maximum number of items per batch get
// operation. Note also that the total size of all the items retrieved
// cannot exceed 16 MB, but that limit is not enforced client side.
// cf: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html
MaxGetBatchSize = 100
// MaxPutBatchSize limits the maximum number of items per batch put
// operation. Note also that the total size of all the items written
// cannot exceed 16 MB, but that limit is not enforced client side.
// cf: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html
MaxPutBatchSize = 25
)
type DynamoQuery struct {
TableName string `json:",omitempty"`
ConsistentRead bool `json:",omitempty"`
Item dynamizer.DynamoItem `json:",omitempty"`
Key dynamizer.DynamoItem `json:",omitempty"`
table *Table
}
type DynamoResponse struct {
Item dynamizer.DynamoItem `json:",omitempty"`
}
type batchGetPerTableQuery struct {
Keys []dynamizer.DynamoItem `json:",omitempty"`
ConsistentRead bool `json:",omitempty"`
}
type DynamoBatchGetQuery struct {
RequestItems map[string]*batchGetPerTableQuery `json:",omitempty"`
table *Table
}
type DynamoBatchGetResponse struct {
Responses map[string][]dynamizer.DynamoItem
UnprocessedKeys map[string]*batchGetPerTableQuery
}
type batchPutPerTableQuery struct {
PutRequest struct {
Item dynamizer.DynamoItem `json:",omitempty"`
} `json:",omitempty"`
}
type DynamoBatchPutQuery struct {
RequestItems map[string][]*batchPutPerTableQuery `json:",omitempty"`
table *Table
}
type DynamoBatchPutResponse struct {
UnprocessedItems map[string][]*batchPutPerTableQuery
}
func NewDynamoQuery(t *Table) *DynamoQuery {
q := &DynamoQuery{table: t}
q.TableName = t.Name
return q
}
func (q *DynamoQuery) AddKey(key *Key) error {
// Add in the hash/range keys.
keys, err := buildKeyMap(q.table, key)
if err != nil {
return err
}
q.Key = keys
return nil
}
func attributeFromDynamoAttribute(a *dynamizer.DynamoAttribute) (*Attribute, error) {
attr := &Attribute{}
if a.S != nil {
attr.Type = "S"
attr.Value = *a.S
return attr, nil
}
if a.N != "" {
attr.Type = "N"
attr.Value = a.N
return attr, nil
}
return nil, fmt.Errorf("Only string and numeric attributes are supported")
}
func dynamoAttributeFromAttribute(attr *Attribute, value string) (*dynamizer.DynamoAttribute, error) {
a := &dynamizer.DynamoAttribute{}
switch attr.Type {
case "S":
a.S = new(string)
*a.S = value
case "N":
a.N = value
default:
return nil, fmt.Errorf("Only string and numeric attributes are supported")
}
return a, nil
}
func buildKeyMap(table *Table, key *Key) (dynamizer.DynamoItem, error) {
if key.HashKey == "" {
return nil, fmt.Errorf("HashKey is always required")
}
k := table.Key
keyMap := make(dynamizer.DynamoItem)
hashKey, err := dynamoAttributeFromAttribute(k.KeyAttribute, key.HashKey)
if err != nil {
return nil, err
}
keyMap[k.KeyAttribute.Name] = hashKey
if k.HasRange() {
if key.RangeKey == "" {
return nil, fmt.Errorf("RangeKey is required by the table")
}
rangeKey, err := dynamoAttributeFromAttribute(k.RangeAttribute, key.RangeKey)
if err != nil {
return nil, err
}
keyMap[k.RangeAttribute.Name] = rangeKey
}
return keyMap, nil
}
func (q *DynamoQuery) AddItem(key *Key, item dynamizer.DynamoItem) error {
// Add in the hash/range keys.
keys, err := buildKeyMap(q.table, key)
if err != nil {
return err
}
for k, v := range keys {
item[k] = v
}
q.Item = item
return nil
}
func (q *DynamoQuery) SetConsistentRead(consistent bool) error {
q.ConsistentRead = consistent
return nil
}
func (q *DynamoQuery) Marshal() ([]byte, error) {
return json.Marshal(q)
}
func NewDynamoBatchGetQuery(t *Table) *DynamoBatchGetQuery {
q := &DynamoBatchGetQuery{table: t}
q.RequestItems = map[string]*batchGetPerTableQuery{
t.Name: &batchGetPerTableQuery{
Keys: make([]dynamizer.DynamoItem, 0, MaxGetBatchSize),
ConsistentRead: false,
},
}
return q
}
func (q *DynamoBatchGetQuery) AddKey(key *Key) error {
tq := q.RequestItems[q.table.Name]
if len(tq.Keys) >= MaxGetBatchSize {
return fmt.Errorf("Cannot add key, max batch size (%d) exceeded", MaxGetBatchSize)
}
keys, err := buildKeyMap(q.table, key)
if err != nil {
return err
}
tq.Keys = append(tq.Keys, keys)
return nil
}
func (q *DynamoBatchGetQuery) SetConsistentRead(consistent bool) error {
tq := q.RequestItems[q.table.Name]
tq.ConsistentRead = consistent
return nil
}
func (q *DynamoBatchGetQuery) Marshal() ([]byte, error) {
return json.Marshal(q)
}
func NewDynamoBatchPutQuery(t *Table) *DynamoBatchPutQuery {
q := &DynamoBatchPutQuery{table: t}
q.RequestItems = map[string][]*batchPutPerTableQuery{
t.Name: make([]*batchPutPerTableQuery, 0, MaxPutBatchSize),
}
return q
}
func (q *DynamoBatchPutQuery) AddItem(key *Key, item dynamizer.DynamoItem) error {
if len(q.RequestItems[q.table.Name]) >= MaxPutBatchSize {
return fmt.Errorf("Cannot add item, max batch size (%d) exceeded", MaxPutBatchSize)
}
// Add in the hash/range keys.
keys, err := buildKeyMap(q.table, key)
if err != nil {
return err
}
for k, v := range keys {
item[k] = v
}
tq := &batchPutPerTableQuery{}
tq.PutRequest.Item = item
q.RequestItems[q.table.Name] = append(q.RequestItems[q.table.Name], tq)
return nil
}
func (q *DynamoBatchPutQuery) Marshal() ([]byte, error) {
return json.Marshal(q)
}
|