On this page:
5.1 Making requests to SDB
sdb-endpoint
sdb-region
create-domain
delete-domain
list-domains
domain-metadata
always-replace?
put-attributes
get-attributes
delete-attributes
delete-item
select
5.2 Batch
batch-put-attributes
batch-delete-attributes
5.3 Hash/  Set style
put-attributes-hash
get-attributes-hash
select-hash
item
5.4 Values as strings
int<->str
str->int/  u8
int->str/  u8
str->int/  s8
int->str/  s8
str->int/  u16
int->str/  u16
str->int/  s16
int->str/  s16
str->int/  u32
int->str/  u32
str->int/  s32
int->str/  s32
5.5 SDB Examples

5 SimpleDB (Database)

 (require aws/sdb) package: aws

SimpleDB is Amazon’s older “NoSQL” service — for new applications, consider instead using DynamoDB.

You should review the SDB docs to understand the basic concepts and names. For example an SDB domain is like a SQL table, an SDB item is like a SQL row, and an SDB item name is like a SQL primary key value to unqiuely identify a row.

Instead of columns, each item has attributes. Each attribute is a key/value hash. In other words, unlike a SQL column which has one value, each SDB attribute may have multiple values. For example an attribute with the key "Sizes" might have the values "Small", "Medium", and "Large". The values should be considered a strict set (just one occurrence of each unique value).

5.1 Making requests to SDB

parameter

(sdb-endpoint)  endpoint?

(sdb-endpoint v)  void?
  v : endpoint?
 = (endpoint "sdb.amazonaws.com" #t)
The endpoint for the service.

parameter

(sdb-region)  string?

(sdb-region v)  void?
  v : string?
 = "us-east-1"
The region for the service.

procedure

(create-domain name)  void?

  name : string?
Create an SDB domain, which is like a “table” in SQL. Remember that SDB operations are idempotent; doing this more than once is equivalent to doing it once.

Tip: Although you may put non alpha-numeric characters in a name, and most of the API will work, the select procedures won’t, unless you bracket the domain name to make it legal SQL.

procedure

(delete-domain name)  void?

  name : string?
Delete an SDB domain. Remember that SDB operations are idempotent; doing this more than once is equivalent to doing it once.

procedure

(list-domains)  (listof (list/c 'DomainName string?))

List all of the SDB domains associated with the AWS SDB account.

procedure

(domain-metadata name)  (listof (list/c symbol? string?))

  name : string?
Show metadata for a specific SDB domain.

parameter

(always-replace?)  boolean?

(always-replace? always?)  void?
  always? : boolean?
Set this parameter to #t to make the Item.Replace true for all calls to put-attributes. Else if at default #f value, Item.Replace will be specified only if you do it for each attribute, using (key val 'replace) instead of (key val).

procedure

(put-attributes domain-name    
  item-name    
  attributes)  any
  domain-name : string?
  item-name : string?
  attributes : (listof (list/c symbol? string?))
Put the attributes to item-name in the domain-name. Remember that SDB operations are idempotent; doing this more than once is equivalent to doing it once.

procedure

(get-attributes domain-name item-name)

  (listof (list/c symbol? string?))
  domain-name : string?
  item-name : string?
Get the attributes for item-name in domain-name. Keep in mind that SDB has “eventual consistency”; it may take awhile for the result of put-attributes to be reflected in get-attributes.

procedure

(delete-attributes domain-name    
  item-name    
  attributes)  void?
  domain-name : string?
  item-name : string?
  attributes : (listof (list/c symbol? string?))
Delete the attributes for item-name in domain-name. Remember that SDB operations are idempotent; doing this more than once is equivalent to doing it once and is not an error.

procedure

(delete-item domain-name item-name)  void?

  domain-name : string?
  item-name : string?
Delete item-name from domain-name. Remember that SDB operations are idempotent; doing this more than once is equivalent to doing it once and is not an error.

procedure

(select expr)  (listof (list/c symbol? string?))

  expr : string?
Execute the SQL-ish expr. See the SDB docs for the subset of SQL that is supported.

5.2 Batch

procedure

(batch-put-attributes domain-name xs)  any

  domain-name : string?
  xs : (listof (cons/c string? (listof (list/c symbol? string?))))
For efficiency, SDB provides this to put multiple attributes to multiple items in one request.

procedure

(batch-delete-attributes domain-name xs)  void?

  domain-name : string?
  xs : (listof (cons/c string? (listof (list/c symbol? string?))))
For efficiency, SDB provides this to delete put multiple attributes from multiple items in one request.

5.3 Hash/Set style

procedure

(put-attributes-hash domain item attribs)  void?

  domain : string?
  item : string?
  attribs : (hash/c symbol? (set/c string?))

procedure

(get-attributes-hash domain item)

  (hash/c symbol? (set/c string?))
  domain : string?
  item : string?

procedure

(select-hash expr)  (listof item?)

  expr : string?

struct

(struct item (name attribs)
    #:extra-constructor-name make-item)
  name : string?
  attribs : (hash/c symbol? (set/c string?))
put-attributes and get-attributes are a low-level interface that wraps SDB fairly thinly. When you want exact control, use them.

These procedures provide a set-oriented interface. For a multi-valued attribute, you get and set all its values together as one set. The attribute’s values are represented as (set/c string?). A collection of attributes is (hash/c symbol? (set/c string?)). When you get a multi-valued attribute, all of its values are grouped and presented as a (set/c string?). When you put the attribute set, all of its existing values in SDB are replaced by the new set of values you supply. (At a lower level, this means the first attribute is put to SDB using parameter Replace=trueto clear any/all existing values. The other values for the attribute are put with Replace=falseto preserve all of the multiple new values we are setting.)

5.4 Values as strings

SDB stores all values as strings. You choose how a number is represented as a string. Your choice matters for sorts and compares. The SDB docs recommend:

Analgesic below.

procedure

(int<->str [width offset pad-char])

  
(number? -> string?) (string? -> number)
  width : exact-positive-integer? = 5
  offset : exact-nonnegative-integer? = 0
  pad-char : character? = #\0
This procedure creates a pair of procedures, to convert in each direction between a number and its padded/offset string representation.

procedure

(str->int/u8 s)  number?

  s : string?

procedure

(int->str/u8 n)  string?

  n : number?

procedure

(str->int/s8 s)  number?

  s : string?

procedure

(int->str/s8 n)  string?

  n : number?

procedure

(str->int/u16 s)  number?

  s : string?

procedure

(int->str/u16 n)  string?

  n : number?

procedure

(str->int/s16 s)  number?

  s : string?

procedure

(int->str/s16 n)  string?

  n : number?

procedure

(str->int/u32 s)  number?

  s : string?

procedure

(int->str/u32 n)  string?

  n : number?

procedure

(str->int/s32 s)  number?

  s : string?

procedure

(int->str/s32 n)  string?

  n : number?
Converters created using int<->str for signed and unsigned integers of 8, 16, and 32 bytes.

Examples:
> (int->str/u8 0)
"000"
> (int->str/u8 255)
"255"
> (int->str/s8 -128)
"000"
> (int->str/s8 0)
"128"
> (int->str/s8 127)
"255"
> (int->str/u32 0)
"0000000000"
> (int->str/u32 (expt 2 32))
"4294967296"
> (int->str/s32 (- (expt 2 31)))
"0000000000"
> (int->str/s32 0)
"2147483648"
> (int->str/s32 (+ (expt 2 31)))
"4294967296"

5.5 SDB Examples

In the examples below, the reason for using sleep is that SDB has an “eventual consistency” model. As a result, there may be a short delay before the values we set are available to get.

(require aws/keys
         aws/sdb)
 
(credentials-from-file!)
 
(define test-domain "TestDomain")
 
(delete-domain test-domain)
(create-domain test-domain)
(sleep 1)
(member? `(DomainName ,test-domain) (list-domains))
(domain-metadata test-domain)
 
(define attribs '((BPM "130")
                  (Genre "Disco")))
(put-attributes test-domain "item" attribs)
(sleep 1)
(get-attributes test-domain "item")
(select (string-append "select Genre from " test-domain))
(delete-attributes test-domain "item" attribs)
(sleep 1)
(get-attributes test-domain "item")
 
(define cnt 5)
(for ([n (in-range cnt)])
    (put-attributes test-domain
                    (format "Item~a" n)
                    `((n ,(format "~a" n)))))
(for ([n (in-range cnt)])
    (displayln (get-attributes test-domain (format "Item~a" n))))
 
(select (string-append "SELECT Count(*) FROM " test-domain))
(select (string-append "SELECT * FROM " test-domain))
 
(for ([n (in-range cnt)])
    (delete-attributes test-domain
                       (format "Item~a" n)
                       `((n ,(format "~a" n)))))
(for ([n (in-range cnt)])
    (displayln (get-attributes test-domain (format "Item~a" n))))
 
;; BatchXxxAttributes
(define (batch-attribs n)
  (for/list ([i (in-range 6)])
      (list (string->symbol (format "key/~a/~a" n i))
            (format "val/~a/~a" n i))))
(define batch-item-count 5)
(define (batch-items)
  (for/list ([n (in-range batch-item-count)])
      (cons (format "item~a" n)
            (batch-attribs n))))
(batch-put-attributes test-domain (batch-items))
(sleep 3)
(for ([n (in-range batch-item-count)])
    (printf "item~a:\n" n)
    (displayln (get-attributes test-domain (format "item~a" n))))
(batch-delete-attributes test-domain (batch-items))
(sleep 3)
(for ([n (in-range batch-item-count)])
    (displayln (get-attributes test-domain (format "item~a" n))))
 
;; hash style
(define attribs/hash (hash 'bpm   (set "100")
                           'genre (set "Rock" "Metal")))
(put-attributes-hash test-domain "itemHash" attribs/hash)
(sleep 1)
(get-attributes-hash test-domain "itemHash")
(select-hash (format "SELECT * FROM ~a WHERE ItemName() = 'itemHash'"
                     test-domain))
 
(delete-domain test-domain)