14
loading...
This website collects cookies to deliver better user experience
interface MusicLibrary {
fun getAlbum(key: AlbumKey): Album
fun getTrack(key: TrackKey): Track
}
data class Album(
val album_title: String,
val album_artist: String,
val release_date: String,
val genre: String,
val tracks: List<Track>
)
data class Track(
val track_title: String,
val run_length: String
)
Primary Key | Attributes | ||||
partition_key | sort_key | ||||
ALBUM_1 | INFO | album_title | album_artiest | release_date | genre |
The Dark Side of the Moon | Pink Floyd | 1973-03-01 | Progressive rock | ||
ALBUM_1 | TRACK_1 | track_title | run_length | ||
Speak to Me | PT1M13S | ||||
ALBUM_1 | TRACK_2 | track_title | run_length | ||
Breathe | PT2M43S | ||||
ALBUM_1 | TRACK_3 | track_title | run_length | ||
On the Run | PT3M36S | ||||
... | |||||
ALBUM_2 | INFO | album_title | album_artiest | release_date | genre |
The Wall | Pink Floyd | 1979-11-30 | Progressive rock | ||
ALBUM_2 | TRACK_1 | track_title | run_length | ||
In the Flesh? | PT3M20S | ||||
... |
(parition_key, sort_key)
, to identify each item.("ALBUM_1", "INFO")
identifies ALBUM_1
's metadata.("ALBUM_1", "TRACK_1")
identifies ALBUM_1
's first track.aws dynamodb query \
--table-name music_library_items \
--key-conditions '{
"PK": {
"ComparisonOperator": "EQ",
"AttributeValueList": [ { "S": "ALBUM_1" } ]
}
}'
// NOTE: This is not Tempest! It is an example used for comparison.
@DynamoDBTable(tableName = "music_library_items")
class MusicLibraryItem {
// All Items.
@DynamoDBHashKey
var partition_key: String? = null
@DynamoDBRangeKey
var sort_key: String? = null
// AlbumInfo.
@DynamoDBAttribute
var album_title: String? = null
@DynamoDBAttribute
var album_artist: String? = null
@DynamoDBAttribute
var release_date: String? = null
@DynamoDBAttribute
var genre: String? = null
// AlbumTrack.
@DynamoDBAttribute
var track_title: String? = null
@DynamoDBAttribute
var run_length: String? = null
}
MusicLibraryItem
is a union type of all the entity types: AlbumInfo
and AlbumTrack
. Because all of its attributes are nullable and mutable, code that interacts with it is brittle and error prone.data class AlbumInfo(
@Attribute(name = "partition_key")
val album_token: String,
val album_title: String,
val album_artist: String,
val release_date: String,
val genre_name: String
) {
@Attribute(prefix = "INFO_")
val sort_key: String = ""
data class Key(
val album_token: String
) {
val sort_key: String = ""
}
}
data class AlbumTrack(
@Attribute(name = "partition_key")
val album_token: String,
@Attribute(name = "sort_key", prefix = "TRACK_")
val track_token: String,
val track_title: String,
val run_length: String
) {
data class Key(
val album_token: String,
val track_token: String
)
}
interface MusicLibraryTable : LogicalTable<MusicLibraryItem> {
val albumInfo: InlineView<AlbumInfo.Key, AlbumInfo>
val albumTracks: InlineView<AlbumTrack.Key, AlbumTrack>
}
private val musicLibrary: MusicLibraryTable
// Load.
fun getAlbumTitle(albumToken: String): String? {
val key = AlbumInfo.Key(albumToken)
val albumInfo = musicLibrary.albumInfo.load(key) ?: return null
return albumInfo.album_title
}
// Update.
fun addAlbumTrack(
albumToken: String,
track_token: String,
track_title: String,
run_length: String
) {
val newAlbumTrack = AlbumTrack(albumToken, track_token, track_title, run_length)
musicLibrary.albumTracks.save(newAlbumTrack)
}
// Query.
fun getAlbumTrackTitles(albumToken: String): List<String> {
val page = musicLibrary.albumTracks.query(
keyCondition = BeginsWith(AlbumTrack.Key(albumToken))
)
return page.contents.map { it.track_title }
}
implementation "app.cash.tempest:tempest:1.5.2"
implementation "app.cash.tempest:tempest2:1.5.2"
14