<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Agost Biro</title>
    <description>The latest articles on Forem by Agost Biro (@agostbiro).</description>
    <link>https://forem.com/agostbiro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F296140%2F0936e5bb-2d24-4d36-9fe4-d8dea3747a26.jpg</url>
      <title>Forem: Agost Biro</title>
      <link>https://forem.com/agostbiro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/agostbiro"/>
    <language>en</language>
    <item>
      <title>Getting Started With Dokklib-DB for DynamoDB Single Table</title>
      <dc:creator>Agost Biro</dc:creator>
      <pubDate>Thu, 27 Feb 2020 20:55:41 +0000</pubDate>
      <link>https://forem.com/agostbiro/getting-started-with-dokklib-db-for-dynamodb-single-table-228a</link>
      <guid>https://forem.com/agostbiro/getting-started-with-dokklib-db-for-dynamodb-single-table-228a</guid>
      <description>&lt;h2&gt;
  
  
  DynamoDB Single Table
&lt;/h2&gt;

&lt;p&gt;Dokklib-DB is a &lt;a href="https://dokklib.com/libs/db/"&gt;Python library for the DynamoDB single table pattern.&lt;/a&gt; If you can anticipate your query patterns, DynamoDB is a particularly good fit for serverless applications, as it has minimal operational overhead and stateless connections. (Read more about &lt;a href="https://dokklib.com/guides/aws/dynamodb#dynamodb-vs-sql"&gt;DynamoDB vs SQL for serverless&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;With the single table pattern, you put all your data that you want to perform relational queries on into one DynamoDB table. The key to a successful single table design is being able to anticipate query patterns, as you have to structure the data accordingly. Let's see how this works in practice with Dokklib-DB!&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Dokklib-DB
&lt;/h2&gt;

&lt;p&gt;Install Dokklib-DB and Boto3 with Pip (you need Python 3.6 or later):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install "boto3&amp;gt;=1.10.34,&amp;lt;2" dokklib-db&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Setup
&lt;/h2&gt;

&lt;p&gt;We need a table with a generic composite primary key. We will call the partition key &lt;code&gt;PK&lt;/code&gt; and the sort key &lt;code&gt;SK&lt;/code&gt;. The generic key names allow us to store different entity types in the same table. We also a need a global secondary index that is the inverse of the primary index (ie. where the partition key is &lt;code&gt;SK&lt;/code&gt; and the sort key is &lt;code&gt;PK&lt;/code&gt;). We will use this inverse index for many-to-many relational queries. You can read more about the &lt;a href="https://dokklib.com/libs/db/table-setup/"&gt;single table setup in the Dokklib-DB docs&lt;/a&gt; or see a &lt;a href="https://github.com/dokklib/dokklib-db/blob/master/tests/integration/cloudformation.yml"&gt;complete CloudFormation template here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Define Entities
&lt;/h2&gt;

&lt;p&gt;Suppose that we want to be able to add our users to groups and make many-to-many relational queries to fetch a user's groups or the members of a group. First, we define the user and group entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dokklib_db&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EntityName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""User entity name.

    Key value: unique user name, eg. 'alice'.
    Example key: 'USER#alice'.

    """&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EntityName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""Group entity name.

    Key value: unique group name, eg. 'my-group'.
    Example key: 'GROUP#my-group'.

    """&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;These classes don't hold any data, they just provide type-safety and documentation for our entities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insert Data
&lt;/h2&gt;

&lt;p&gt;Let's see how we can add a user to a group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'SingleTable'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Construct entity keys
&lt;/span&gt;
&lt;span class="n"&gt;pk_alice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PartitionKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'alice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_alice&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# USER#alice
&lt;/span&gt;
&lt;span class="n"&gt;sk_group_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SortKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sk_group_1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# GROUP#1
&lt;/span&gt;
&lt;span class="c1"&gt;# Add Alice to group one
&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sk_group_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Suppose that we added another user and a second group and we have the following data in the table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PK&lt;/th&gt;
&lt;th&gt;SK&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;USER#alice&lt;/td&gt;
&lt;td&gt;GROUP#1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USER#alice&lt;/td&gt;
&lt;td&gt;GROUP#2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USER#bob&lt;/td&gt;
&lt;td&gt;GROUP#2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Many-to-Many Queries
&lt;/h1&gt;

&lt;p&gt;Now we can use the primary index to query all the groups of a user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Get all the groups that Alice belongs to
&lt;/span&gt;
&lt;span class="n"&gt;pk_alice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PartitionKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'alice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_alice&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# USER#alice
&lt;/span&gt;
&lt;span class="n"&gt;group_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrefixSortKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group_prefix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# GROUP#
&lt;/span&gt;
&lt;span class="n"&gt;alice_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group_prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# [{'SK': '1'}, {'SK': '2'}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the inverse index to query all the members of a group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Get all users in group two
&lt;/span&gt;
&lt;span class="n"&gt;pk_group_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PartitionKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'2'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_group_2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# GROUP#2
&lt;/span&gt;
&lt;span class="n"&gt;user_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrefixSortKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prefix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# USER#
&lt;/span&gt;
&lt;span class="n"&gt;inverse_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InversePrimaryIndex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk_group_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;global_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inverse_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# [{'PK': 'alice'}, {'PK': 'bob'}]
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This wraps up our short intro. I encourage you to continue exploring in the &lt;a href="https://dokklib.com/libs/db/"&gt;Dokklib-DB docs&lt;/a&gt; and to provide feedback/feature requests on &lt;a href="https://github.com/dokklib/dokklib-db"&gt;Github.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>serverless</category>
      <category>python</category>
    </item>
  </channel>
</rss>
