This example retrieves the Amazon DynamoDB items with a rating above a specified value in a specified year.

go run ScanItemsv2.go -t TABLE -r RATING -y YEAR

  • TABLE is the name of the table.
  • RATING is the rating of the item, from 0.0 to 10.0.
  • YEAR is the year of the item, which must be greater than 1900.

The unit test accepts similar values in config.json.

Source code

// Copyright, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (


// DynamoDBScanAPI defines the interface for the Scan function.
// We use this interface to test the function using a mocked service.
type DynamoDBScanAPI interface {
	Scan(ctx context.Context,
		params *dynamodb.ScanInput,
		optFns ...func(*dynamodb.Options)) (*dynamodb.ScanOutput, error)

// Item holds info about the items returned by Scan
type Item struct {
	Title string
	Info  struct {
		Rating float64

// GetItems retrieves the Amazon DynamoDB items above a minimum rating in a specified year.
// Note that this example only works if the table has a schema with:
//   year as a number (int)
//   info.rating as a number (float)
//   title as a string
// Inputs:
//     c is the context of the method call, which includes the AWS Region.
//     api is the interface that defines the method call.
//     input defines the input arguments to the service call.
// Output:
//     If successful, a ScanOutput object containing the result of the service call and nil.
//     Otherwise, nil and an error from the call to Scan.
func GetItems(c context.Context, api DynamoDBScanAPI, input *dynamodb.ScanInput) (*dynamodb.ScanOutput, error) {
	return api.Scan(c, input)

// Get the items above a minimum rating in a specific year.
func main() {
	table := flag.String("t", "", "The name of the table to scan.")
	rating := flag.Float64("r", -1.0, "The minimum rating for a movie to retrieve.")
	year := flag.Int("y", 1899, "The year when the movie was released.")
	verbose := flag.Bool("v", false, "Whether to show info about the movie.")


	if *table == "" || *rating < 0.0 || *year < 1900 {
		fmt.Println("You must supply the name of the table, a rating above zero, and a year after 1900:")
		fmt.Println("-t TABLE -r RATING -y YEAR")

	// Get items in that year.
	filt1 := expression.Name("year").Equal(expression.Value(*year))
	// Get items with a rating above the minimum.
	filt2 := expression.Name("info.rating").GreaterThan(expression.Value(*rating))

	// Get back the title and rating (we know the year).
	proj := expression.NamesList(expression.Name("title"), expression.Name("info.rating"))

	expr, err := expression.NewBuilder().WithFilter(filt1).WithFilter(filt2).WithProjection(proj).Build()
	if err != nil {
		fmt.Println("Got error building expression:")

	input := &dynamodb.ScanInput{
		ExpressionAttributeNames:  expr.Names(),
		ExpressionAttributeValues: expr.Values(),
		FilterExpression:          expr.Filter(),
		ProjectionExpression:      expr.Projection(),
		TableName:                 table,

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		panic("unable to load SDK config, " + err.Error())

	client := dynamodb.NewFromConfig(cfg)

	resp, err := GetItems(context.TODO(), client, input)
	if err != nil {
		fmt.Println("Got an error scanning the table:")

	items := []Item{}

	err = attributevalue.UnmarshalListOfMaps(resp.Items, &items)
	if err != nil {
		panic(fmt.Sprintf("failed to unmarshal Dynamodb Scan Items, %v", err))

	for _, item := range items {
		if *verbose {
			fmt.Println("Title: ", item.Title)
			fmt.Println("Rating:", item.Info.Rating)

	numItems := strconv.Itoa(len(items))

	fmt.Println("Found", numItems, "movie(s) with a rating above", *rating, "in", *year)

See the complete example in GitHub.