Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DynamoDB - How to query a nested attribute boto3

I am following the DynamoDB python tutorial. This step shows how to query the table based on a specific key: http://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/GettingStarted.Python.04.html.

Here is the code for this query:

from __future__ import print_function # Python 2/3 compatibility
import boto3
import json
import decimal
from boto3.dynamodb.conditions import Key, Attr

# Helper class to convert a DynamoDB item to JSON.
class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return str(o)
        return super(DecimalEncoder, self).default(o)

dynamodb = boto3.resource('dynamodb', region_name='us-west-2', endpoint_url="http://localhost:8000")

table = dynamodb.Table('Movies')

print("Movies from 1992 - titles A-L, with genres and lead actor")

response = table.query(
    ProjectionExpression="#yr, title, info.genres, info.actors[0]",
    ExpressionAttributeNames={ "#yr": "year" }, # Expression Attribute Names for Projection Expression only.
    KeyConditionExpression=Key('year').eq(1992) & Key('title').between('A', 'L')
)

for i in response[u'Items']:
    print(json.dumps(i, cls=DecimalEncoder))

An example response item is

{
    "title": "Juice",
    "year": "1992",
    "info": {
        "actors": [
            "Omar Epps"
        ],
        "genres": [
            "Crime",
            "Drama",
            "Thriller"
        ]
    }
}

The table has the two key attributes 'title' and 'year' as well as the nested attribute 'info'. What I am trying to do is query the database and filter the movies by genre, for example get all Drama movies. I am not sure how to do this since the genre key is nested inside info.

I tried to get all the Drama movies from 1992 like this but it came up blank.

response = table.query(
    KeyConditionExpression=Key('year').eq(1992),
    FilterExpression=Attr('info.genres').eq('Drama')
)

How do I properly filter this query with the nested info attribute?

like image 741
Dalton Sweeney Avatar asked Mar 23 '17 21:03

Dalton Sweeney


People also ask

What are nested attributes in DynamoDB?

Nested attributes. An attribute is said to be nested if it is embedded within another attribute. To access a nested attribute, you use dereference operators: [n] — for list elements.

Does DynamoDB support complex queries?

DynamoDB does support the complex condition on FilterExpression . Perfectly fine.

What is the difference between Query and Scan operations in DynamoDB?

DynamoDB supports two different types of read operations, which are query and scan. A query is a lookup based on either the primary key or an index key. A scan is, as the name indicates, a read call that scans the entire table in order to find a particular result.

Which is faster Scan or Query in DynamoDB?

The Query operation took less than 65 milliseconds at the 95 percentile, compared to 650 milliseconds for Scan. In comparison to a query that conducts a straight lookup based on the partition key, response times vary substantially depending on the item and how the scan process works.


2 Answers

You can use contains to filter the data from List data type.

genres -attribute stored as List inside info attribute which is a Map data type

FilterExpression=Attr('info.genres').contains('Drama')
like image 156
notionquest Avatar answered Oct 01 '22 02:10

notionquest


Unlike in the accepted answer, to be able to filter all the items with the attribute, you need to use scan() instead of query(). query() requires KeyCondition which is unnecessary in your case and forces you to create condition containing f.e. year.

Therefore

table.scan(FilterExpression=Attr('info.genres').contains('Drama'))

should do the job

like image 30
DavidS1992 Avatar answered Oct 01 '22 02:10

DavidS1992