On Part 1 we went over the basic operations - creating a table, checking its status, getting data in and performing a simple retrieval.
We’ll now look into various ways of retrieving items, including querying, scanning, and using projections to get only a few properties.
This assumes you’ve already completed part 1, since we’ll be using the data we added. As a reminder, I’m following the Javascript examples on basic DynamoDB operations, since they are the ones where the data set-up will more closely match Clojure. You may want to follow along for extra explanations and for comparison purposes.
The next step on DynamoDB’s Getting Started is to retrieve items by their key.
We already skipped ahead a bit on the last part and saw how to use get-item
. To refresh our memories, get-item
receives a table and a map of the key to retrieve (using attribute and value).
1 | (far/get-item client-opts :music {:artist "No One You Know" |
Notice we need to use both the values for the key, and if your key is a string, it’ll be case-sensitive.
It returns the item as a map, including all attributes.
1 | {:artist "No One You Know", |
DynamoDB also allows us to retrieve only a few attributes. To retrieve only the album title and year:
1 | (far/get-item client-opts :music {:artist "No One You Know" |
Which gets us a smaller map.
1 | {:year 2015N, :album-title "Somewhat Famous"} |
We used a legacy parameter up there instead of an expression, though. Expressions are a lot more flexible than just sending an attribute list, but have some restrictions.
Mainly, when using expressions:
We are hitting both cases on the previous call. album-title
has a dash on the name, and year
is a reserved word.
The way around that is to add a placeholder token on the expression that stands in for the attribute, and then adding the attribute name on the expression attribute values.
The equivalent call to the previous example, using a projection expression, would be:
1 | (far/get-item client-opts :music |
That would seem rather cumbersome if we can simply request the attributes, but the next example will give you a better idea of what you can do with expressions.
You’ll recall that when we created the item, we added a complex attribute :tags
which was itself a map.
1 | {:artist "No One You Know" |
On a projection expression, we can use path notation to retrieve only some attributes inside :tags
, even down to requesting a specific item inside the :composers
vector.
Like before, we’ll need to use expression attributes for those elements that have a dash in the name.
1 | (far/get-item client-opts :music |
This returns only those attributes we requested, retaining their original nesting.
1 | {:year 2015N, |
We can also retrieve multiple items in a single call by their primary key, using batch-get-item
. Like we saw on the call to batch-write-item
(part 1), we can also requests items from multiple tables by adding a request for each one to the map.
1 | (far/batch-get-item client-opts |
This results on us just getting the relevant attributes for those elements, organized by table. If a requested attribute isn’t preset on the item, there will not be a corresponding key on the return map.
1 | {:music [{:price 0.99M} |
You can’t assume that the items will be returned on the same order as the request, so unlike in the example above you’ll want to include the key as well (I left it out to directly reproduce Amazon’s example).
We can also do queries and scans on tables. The syntax for these will be very similar to the one we have been using up until now.
We can query on a table using its partition key.
1 | (far/query client-opts :music {:artist [:eq "No One You Know"]}) |
This will return all items, sorting by the range key (the song’s title).
We can also filter using key attributes, and ask DynamoDB to return only a few of them. To get only songs for “The Acme Band” where the title starts with a character:
1 | (far/query client-opts :music {:artist [:eq "The Acme Band"] |
Querying looks for items based on its primary key (or a fraction of it). We can also use filter expressions to filter out some items from those that match, using even complex expressions and nested values.
On the following example, we’ll need to use both expression attribute values and expression attribute names, since they have the same limitations as the expressions we used for projections, and we can’t reference on them attributes with dashes on the name.
1 | (far/query client-opts :music {:artist [:eq "The Acme Band"] |
Instead of adding the filtering values directly on the filter expression, we used an expression attribute values map - passed via :expr-attr-vals
- to provide them.
This query results on a vector with the requested attributes for the single matching item.
1 | [{:promotion-info {:rotation "heavy"}, :song-title "Still In Love"}] |
Finally, we can do always do a full table scan (which will likely get expensive!).
1 | (far/scan client-opts :music) |
scan
supports filter and projection expressions, just like query
, but you don’t need to know the primary key in order to do it.
That’s it for retrieving, querying and scanning. On part 3 we’ll look at maintaining and working with secondary indexes. Until then!
Published: 2016-01-07