{"id":18219,"date":"2017-08-11T12:15:18","date_gmt":"2017-08-11T09:15:18","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=18219"},"modified":"2017-08-11T10:09:57","modified_gmt":"2017-08-11T07:09:57","slug":"working-with-dynamodb","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/","title":{"rendered":"Working with DynamoDB"},"content":{"rendered":"<p>Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser. Further, we needed to generate reports and analyze gathered information.<\/p>\n<p>To deal with the continuous data flowing from the sensors, we chose DynamoDB for storage. DynamoDB promises to handle large data with single-digit millisecond latency, at any scale. Since it\u2019s a fully managed database service, we never had to worry about scaling, architecture, and hardware provisioning. Plus, we were using AWS IoT for sensors, so choosing a NoSQL database from AWS services like DynamoDB was the right decision.<\/p>\n<p>Here is what Amazon says about DynamoDB:<\/p>\n<blockquote><p>Fully managed NoSQL database service that provides fast and predictable performance with seamless scalability.<\/p><\/blockquote>\n<h2>The Basics<\/h2>\n<p>DynamoDB tables are made of <em>items<\/em>, which are similar to <em>rows<\/em> in relational databases, and each <em>item<\/em> can have more than one attribute. These attributes can be either scalar types or nested.<\/p>\n<h3>Everything starts with the Primary Key Index<\/h3>\n<p>Each item has one attribute as the Primary Key Index that uniquely identifies the item. The Primary Key is made of two parts: the Partition Key (Hash Key) and the Sort Key (Range Key); where the Range Key is optional. DynamoDB doesn\u2019t just magically spread the data into multiple different servers to boost performance, it relies on partitioning to achieve that.<\/p>\n<p>Partitioning is similar to the concept of sharding seen in MongoDB and other distributed databases where data is spread across different database servers to distribute load across multiple servers to give consistent high performance. Now think of partitions as similar to shards and the <em>Hash Key<\/em> specified in the Primary Key determines in which shard the item will be stored.<\/p>\n<p>In order to determine in which partition the item will be stored, the Hash Key is passed to a special hash function which ensures that all items are evenly spread across all partitions. This also explains why it is called a Partition Key or Hash Key. The Sort Key on the other hand, determines the order of items being stored and allows DynamoDB to have more than one item with the same Hash Key.<\/p>\n<p>The Sort Key, when present and combined with the Partition Key (Hash Key) forms the Primary Key Index, which is used to uniquely identify a particular item.<\/p>\n<p>This is very useful for time series data such as the price of stocks, where the price of one stock item changes over time and you need to track the price in comparison to the stock. In such cases, the stock name can be the Partition Key and the date can be used as a Range Key to sort data according to time.<\/p>\n<h3>Secondary indexes<\/h3>\n<p>Primary indexes are useful to identify items and allow us to store infinitely large amounts of data without having to worry about performance or scaling, but soon you will realize that querying data becomes extremely difficult and inefficient.<\/p>\n<p>Having worked with relational databases mostly, querying with DynamoDB was the most confusing aspect. You don\u2019t have joins or views as in relational databases; denormalization helps, but not much.<\/p>\n<p>Secondary indexes in DynamoDB follow the same structure as the Primary Key Index, where one part is the Partition Key and the second part is the Sort Key, which is optional. Two types of secondary indexes are supported by DynamoDB: the Local Secondary Index and the Global Secondary Index.<\/p>\n<h3>Local Secondary Index (LSI):<\/h3>\n<p>The Local Secondary Index is a data structure that shares the Partition Key defined in the Primary Index, and allows you to define the Sort Key with an attribute other than the one defined in the Primary Index. The Sort Key attribute must be of scalar type.<\/p>\n<p>While creating the LSI, you define attributes to be projected other than the Partition Key and the Sort Key, and the LSI maintains projected attributes along with the Partition Key and Sort Key. The LSI data and the table data for each item is stored inside the same partition.<\/p>\n<h3>Global Secondary Index (GSI):<\/h3>\n<p>You will need to query data from a different attribute than the Partition Key. You can achieve this by creating a Global Secondary Index for that attribute. GSI follows the same structure as the Primary Key, though it has a different Partition Key than the Primary Index and can optionally have one Sort Key.<\/p>\n<p>Similar to the LSI, attributes to be projected need to be specified while creating the GSI. Both the Partition Key attribute and Sort Key attribute need to be scalar.<\/p>\n<p>You definitely should look up the official documentation for <a href=\"http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/GSI.html\">GSI<\/a> and <a href=\"http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/LSI.html\">LSI<\/a> to understand how indexes work.<\/p>\n<p>!Sign up for a free Codeship Account<\/p>\n<h2>Setup for DynamoDB<\/h2>\n<p>DynamoDB doesn\u2019t require special setup, as it is a web service fully managed by AWS. You just need API credentials to start working with DynamoDB. There are two primary ways you can interact with DynamoDB, using <a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/Aws\/DynamoDB\/Client.html\">AWS SDK for Ruby<\/a> or <a href=\"https:\/\/github.com\/Dynamoid\/Dynamoid\">Dynamoid<\/a>.<\/p>\n<p>Both libraries are quite good, and Dynamoid offers an <em>Active Record<\/em> kind of interface. But to get an overview of how DynamoDB works, it\u2019s better to start with <a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/Aws\/DynamoDB\/Client.html\">AWS SDK for Ruby<\/a>.<\/p>\n<p>In Gemfile,<\/p>\n<pre class=\"brush:php\">gem 'aws-sdk', '~&gt; 2'<\/pre>\n<p>First of all, you need to initialize a DynamoDB client, preferably via an initializer so as to avoid instantiating a new client for every request you make to DynamoDB.<\/p>\n<pre class=\"brush:php\"># dynamodb_client.rb\r\n$ddb = Aws::DynamoDB::Client.new({\r\n    access_key_id: ENV['AWS_ACCESS_KEY_ID'],\r\n    secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],\r\n    region: ENV['AWS_REGION']\r\n})<\/pre>\n<p>AWS provides a downloadable version of DynamoDB, \u2018DynamoDB local\u2019, which can be used for development and testing. First, <a href=\"http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/DynamoDBLocal.html#DynamoDBLocal.DownloadingAndRunning\">download<\/a> the local version and follow the steps specified in the documentation to set up and run it on a local machine.<\/p>\n<p>To use it, just specify an endpoint in the DynamoDB client initialization hash as shown below:<\/p>\n<pre class=\"brush:php\"># dynamodb_client.rb\r\n$ddb = Aws::DynamoDB::Client.new({\r\n    access_key_id: ENV['AWS_ACCESS_KEY_ID'],\r\n    secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],\r\n    region: ENV['AWS_REGION'],\r\n    endpoint: 'http:\/\/localhost:8000'\r\n})<\/pre>\n<p>The DynamoDB local server runs on port 8000 by default.<\/p>\n<h2>Put it all together with this simple example<\/h2>\n<p>Suppose you need to store the information of users along with shipping addresses for an ecommerce website. The users table will hold information such as <code>first_name<\/code>, <code>last_name<\/code>, <code>email<\/code>, <code>profile_pictures<\/code>, <code>authentication_tokens<\/code>, <code>addresses<\/code>, and much more.<\/p>\n<p>In relational databases, a users table might look like this:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.24-PM.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-18221\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.24-PM.png\" alt=\"\" width=\"757\" height=\"117\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.24-PM.png 757w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.24-PM-300x46.png 300w\" sizes=\"(max-width: 757px) 100vw, 757px\" \/><\/a><\/p>\n<p>And addresses and authentication tokens will need to be placed separately in other tables with the id of a user as Foreign Key:<\/p>\n<p>Addresses: <a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.35-PM.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-18222\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.35-PM.png\" alt=\"\" width=\"735\" height=\"109\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.35-PM.png 735w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.35-PM-300x44.png 300w\" sizes=\"(max-width: 735px) 100vw, 735px\" \/><\/a><\/p>\n<p>Authentication Tokens: <a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.46-PM.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-18223\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.46-PM.png\" alt=\"\" width=\"724\" height=\"99\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.46-PM.png 724w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-09-at-2.05.46-PM-300x41.png 300w\" sizes=\"(max-width: 724px) 100vw, 724px\" \/><\/a><\/p>\n<p>In DynamoDB, there is no concept of Foreign Keys and no joins. There are ways to reference data related to an item from another table as we do in relational databases, but it\u2019s not efficient. A better way would be to denormalize data into a single users table. As DynamoDB is a key value store, each item in a <code>users<\/code> table would look as shown below:<\/p>\n<pre class=\"brush:php\">{\r\n  \"first_name\": \"string\",\r\n  \"last_name\": \"string\",\r\n  \"email\": \"string\",\r\n  \"created_at\": \"Date\",\r\n  \"updated_at\": \"Date\",\r\n  \"authentication_tokens\": [\r\n    {\r\n      \"token\": \"string\",\r\n      \"last_used_at\": \"Date\"\r\n    }\r\n  ],\r\n  \"addresses\": [\r\n    {\r\n      \"city\": \"string\",\r\n      \"state\": \"string\",\r\n      \"country\": \"string\" \r\n    }\r\n  ]\r\n}<\/pre>\n<p>Make <code>email<\/code> as the Partition Key of the Primary Key and leave the Range Key optional, as each user will have a unique email id, and we definitely need to look up a user having a particular email id.<\/p>\n<p>In the future, you might need to search users with <code>first_name<\/code> or <code>last_name<\/code>. This requirement makes <code>first_name<\/code> and <code>last_name<\/code> ideal candidates for the Range Key. Additionally, you may want to get users registered on a particular date or updated on a particular date, which can be found with <code>created_at<\/code> and <code>updated_at fields<\/code>, making them ideal for the Partition Key in the Global Secondary Index.<\/p>\n<p>For now, we will make one Global Secondary Index (GSI), where <code>created_at<\/code> will be the Partition Key and <code>first_name<\/code> will be Range Key, allowing you to run queries like:<\/p>\n<pre class=\"brush:php\">select users.* where users.created_at = ' xxx ' and users.first_name starts_with(' xxx ')<\/pre>\n<h2>Basic CRUD Operations<\/h2>\n<p>All logic related to persistence and querying stays in the model, so following that convention, first create the class <code>User<\/code> and include <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveModel\/Model.html\">ActiveModel::Model<\/a> and <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveModel\/Serialization.html\">ActiveModel::Serialization<\/a> modules inside the class.<\/p>\n<p><code>ActiveModel::Model<\/code> adds callbacks, validations, and an initializer. The main purpose for adding it is to initialize the <code>User<\/code> model with parameters hash like Active Record does.<\/p>\n<p><code>ActiveModel::Serialization<\/code> provides serialization helper methods such as <code>to_json<\/code>, <code>as_json<\/code>, and <code>serializable_hash<\/code> for objects of the <code>User<\/code> class. After adding these two modules, you can specify the attributes related to the <code>User<\/code> model with <code>attr_accessor<\/code> method. At this point, the <code>User<\/code> model looks like this:<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\nclass User \r\n    include include ActiveModel::Model, ActiveModel::Serialization\r\n    \r\n    attr_accessor :first_name, :last_name, :email, :addresses, :authentication_tokens, :created_at, :updated_at\r\nend<\/pre>\n<p>You can create <code>User<\/code> objects with <code>User.new<\/code>, pass parameters hash, serialize, and deserialize it, but you cannot persist them in DynamoDB. To be able to persist the data, you will need to create a table in DynamoDB and allow the model to know about and access that table.<\/p>\n<p>I prefer to create a <code>migrate_table!<\/code> class method where I put the logic required for table creation. If the table already exists, it will be recreated, and the application should wait until the table is created as table creation on DynamoDB takes around a few minutes.<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\n\r\n...\r\ndef self.migrate_table\r\n      $ddb.delete_table(table_name: table_name) if $ddb.list_tables.table_names.include?(table_name)\r\n      create_table_params = {\r\n        table_name: table_name,\r\n        # array of attributes name and their type that describe schema for the Table and Indexes\r\n        attribute_definitions: [\r\n          {\r\n            attribute_name: \"first_name\",\r\n            attribute_type: \"S\"\r\n          },\r\n          {\r\n            attribute_name: \"created_at\",\r\n            attribute_type: \"S\"\r\n          },\r\n          {\r\n            attribute_name: \"email\",\r\n            attribute_type: \"S\"\r\n          },\r\n        ],\r\n\r\n        # key_schema specifies the attributes that make up the primary key for a table\r\n        # HASH - specifies Partition Key\r\n        # RANGE - specifies Range Key\r\n        # key_type can be either HASH or RANGE\r\n        key_schema: [\r\n          {\r\n            attribute_name: \"email\",\r\n            key_type: \"HASH\",\r\n          }\r\n        ],\r\n\r\n        # global_secondary_indexes array specifies one or more keys that makes up index,\r\n        # with name of index and provisioned throughput for global secondary indexes\r\n        global_secondary_indexes: [\r\n\r\n          index_name: \"created_at_first_name_index\",\r\n          key_schema: [\r\n              {\r\n                  attribute_name: \"created_at\",\r\n                  key_type: \"HASH\"\r\n              },\r\n              {\r\n                  attribute_name: \"first_name\",\r\n                  key_type: \"RANGE\"\r\n              }\r\n          ],\r\n\r\n          # Projection - Specifies attributes that are copied (projected) from the table into the index.\r\n          # Allowed values are - ALL, INCLUDE, KEYS_ONLY\r\n          # KEYS_ONLY - only the index and primary keys are projected into the index.\r\n          # ALL - All of the table attributes are projected into the index.\r\n          # INCLUDE - Only the specified table attributes are projected into the index. The list of projected attributes are then needs to be specified in non_key_attributes array\r\n          projection: {\r\n              projection_type: \"ALL\"\r\n          },\r\n\r\n          # Represents the provisioned throughput settings for specified index.\r\n          provisioned_throughput: {\r\n              read_capacity_units: 1,\r\n              write_capacity_units: 1\r\n          }\r\n        ],\r\n\r\n        # Represents the provisioned throughput settings for specified table.\r\n        provisioned_throughput: {\r\n          read_capacity_units: 1,\r\n          write_capacity_units: 1,\r\n        }\r\n      }\r\n      $ddb.create_table(create_table_params)\r\n\r\n      # wait till table is created\r\n      $ddb.wait_until(:table_exists, {table_name: \"movies\"})\r\n    end\r\n    \r\n...<\/pre>\n<h2>Creating an Item<\/h2>\n<p>DynamoDB provides the <code>#put_item<\/code> method, which creates a new item with passed attributes and the Primary Key. If an item with the same Primary Key exists, it is replaced with new item attributes.<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\nclass User\r\n    ...\r\n    def save\r\n        item_hash = instance_values\r\n        begin\r\n          resp = $ddb.put_item({\r\n            table_name: self.table_name,\r\n            item: item_hash,\r\n            return_values: 'NONE'\r\n          })\r\n          resp.successful?\r\n        rescue Aws::DynamoDB::Errors::ServiceError =&gt; e\r\n          false\r\n        end\r\n    end\r\n    ...\r\nend<\/pre>\n<p>The instance method <code>save<\/code> simply saves an item and returns either true or false depending upon the response. The <code>instance_values<\/code> method returns hash of all the <code>attr_accessor<\/code> fields, which is passed to the <code>item<\/code> key as <code>item_hash<\/code>.<\/p>\n<p>The <code>return_values<\/code> option inside the <code>put_item<\/code> request determines whether you want to receive a saved item or not. We are just interested in knowing whether an item is saved successfully or not, hence \u2018NONE\u2019 was passed.<\/p>\n<h3>Reading an item<\/h3>\n<p>Getting an item from DynamoDB with the Primary Key is similar to the way records are found by ids by Active Record in relational databases.<\/p>\n<p>The <code>#get_item<\/code> method is used to fetch a single item for a given Primary Key. If no item is found, then the method returns nil in the item\u2019s element of response.<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\n...\r\ndef self.find(email)\r\n    if email.present?\r\n      begin\r\n        resp = $ddb.get_item({\r\n          table_name: self.table_name,\r\n          key: {\r\n            email: email\r\n          }\r\n        })\r\n      resp.item\r\n      rescue Aws::DynamoDB::Errors::ServiceError =&gt; e\r\n        nil\r\n      end\r\n    else\r\n      nil\r\n    end\r\n  end\r\n ...<\/pre>\n<h3>Updating an item<\/h3>\n<p>An item is updated with the <code>#update_item<\/code> method, which behaves more like the upsert (update or insert) of PostgreSQL. In other words, it updates an item with given attributes. But in case no item is found with those attributes, a new item is created. This might sound similar to how <code>#put_item<\/code> works, but the difference is that <code>#put_item<\/code> replaces an existing item, whereas <code>#update_item<\/code> updates an existing item.<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\n  def update(attrs)\r\n    item_hash = attrs\r\n    item_hash['updated_at'] = DateTime.current.to_s\r\n    item_hash.keys.each do |key|\r\n      item_hash[key] = {\r\n        'value' =&gt; item_hash[key],\r\n        'action' =&gt; 'PUT'\r\n      }\r\n    end\r\n    begin\r\n      resp = $ddb.update_item({\r\n        table_name: self.class.table_name,\r\n        key: {\r\n          email: email\r\n        },\r\n        attribute_updates: item_hash\r\n      })\r\n      resp.successful?\r\n    rescue Aws::DynamoDB::Errors::ServiceError =&gt; e\r\n      false\r\n    end\r\n  end<\/pre>\n<p>While updating an item, you need to specify the Primary Key of that item and whether you want to replace or add new values to an existing attribute.<\/p>\n<p>Look closely at how <code>item_hash<\/code> is formed. The attribute hash is passed normally to update the method, which is processed further to add two fields \u2014 <code>value<\/code> and <code>action<\/code> \u2014 in place of simple <code>key =&gt; value<\/code> pairs. This modifies a given simple hash as shown below to <code>attribute_updates<\/code> key compatible values.<\/p>\n<h3>Deleting an item<\/h3>\n<p>The <code>#delete_item<\/code> method deletes items with a specified Primary Key. If the item is not present, it doesn\u2019t return an error.<\/p>\n<pre class=\"brush:php\"># models\/user.rb\r\n  def delete\r\n    if email.present?\r\n      begin\r\n        resp = $ddb.delete_item({\r\n          table_name: self.class.table_name,\r\n          key: {\r\n            email: email\r\n          }\r\n        })\r\n        resp.successful?\r\n      rescue Aws::DynamoDB::Errors::ServiceError =&gt; e\r\n        false\r\n      end\r\n    else\r\n      false\r\n    end\r\n  end<\/pre>\n<h2>Conditional Writes<\/h2>\n<p>All DynamoDB operations can be categorized into two types: <code>read<\/code> operations, such as <code>get_item<\/code>, and write operations, such as <code>put_item<\/code>, <code>update_item<\/code>, and <code>delete_item<\/code>.<\/p>\n<p>These write operations can be constrained with specified conditions, such as <code>put_item<\/code>, and should be performed only if a certain item with the same Primary Key does not exist. All write operations support these kinds of conditional writes.<\/p>\n<p>For example, if you want to create an item only if it isn\u2019t present, you can add <code>attribute_not_exists(attribute_name)<\/code> as a value of the <code>condition_expression<\/code> key in the <code>#put_item<\/code> method params.<\/p>\n<pre class=\"brush:php\">def save!\r\n    item_hash = instance_values\r\n    item_hash['updated_at'] = DateTime.current.to_s\r\n    item_hash['created_at'] = DateTime.current.to_s\r\n    begin\r\n      resp = $ddb.put_item({\r\n        table_name: self.class.table_name,\r\n        item: item_hash,\r\n        return_values: 'NONE',\r\n        condition_expression: 'attribute_not_exists(email)'\r\n      })\r\n      resp.successful?\r\n    rescue Aws::DynamoDB::Errors::ServiceError =&gt; e\r\n      false\r\n    end\r\n  end<\/pre>\n<p><a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/\">Ruby SDK v2<\/a> provides an interface to interact with DynamoDB, and you can read more about the methods provided in the given <a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/Aws\/DynamoDB.html\">SDK documentation<\/a>.<\/p>\n<h2>Batch Operations<\/h2>\n<p>Apart from four basic CRUD operations, DynamoDB provides two types of batch operations:<\/p>\n<ul>\n<li><code>#batch_get_item<\/code> \u2013 This can be used to read a maximum of 100 items from one or more tables.\n<ul>\n<li><em>ie<\/em>, you can batch up to 100 <code>#get_item<\/code> calls with a single <code>#batch_get_item<\/code>.<\/li>\n<\/ul>\n<\/li>\n<li><code>#batch_write_item<\/code> \u2013 This can be used to perform write operations up to a maximum of 25 items in a single <code>#batch_write_item<\/code>.\n<ul>\n<li>Batch Write Item can perform any write operation such as <code>#put_item<\/code>, <code>#update_item<\/code>, or <code>#delete_item<\/code> for one or more tables.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>You can read more about batch operations in the <a href=\"http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/WorkingWithItems.html#WorkingWithItems.BatchOperations\">AWS developer guide<\/a>.<\/p>\n<h2>Query and Scan Operations<\/h2>\n<p>Batch operations are not very useful for querying data, so DynamoDB provides <em>Query<\/em> and <em>Scan<\/em> for fetching records.<\/p>\n<ul>\n<li><strong>Query<\/strong>: Lets you fetch records based on the Partition Key and the Hash Key of the Primary or Secondary Indexes.\n<ul>\n<li>You need to pass the Partition Key, a single value for that Partition Key, and optionally the Range Key with normal comparison operators (such as <code>=<\/code>, <code>&gt;<\/code>, and <code>&lt;<\/code>) if you want to further narrow down the results.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Scan<\/strong>: A Scan operation reads every item in a table or a secondary index, either in ascending or descending order.<\/li>\n<\/ul>\n<p>Both the Query and Scan operations allow filters to filter the result returned from the operation.<\/p>\n<h2>Conclusion<\/h2>\n<p>The fully managed NoSQL database service DynamoDB is good if you don\u2019t need to worry about scaling and handling large amounts of data. Even though it promises the high performance of single-digit milliseconds and is infinitely scalable, you need to be careful when designing table architecture and choosing Primary Key and Secondary Indexes, otherwise, you can lose these benefits and costs may go high.<\/p>\n<p><a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/\">Ruby SDK v2<\/a> provides an interface to interact with DynamoDB, and you can read more about methods provided in the given <a href=\"http:\/\/docs.aws.amazon.com\/sdkforruby\/api\/Aws\/DynamoDB.html\">SDK Documentation<\/a>. Also, the <a href=\"http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/Introduction.html\">Developer Guide<\/a> is a perfect place to understand DynamoDB in depth.<\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"https:\/\/blog.codeship.com\/working-with-dynamodb\/\">Working with DynamoDB<\/a> from our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\">WCG partner<\/a> Parth Modi at the <a href=\"http:\/\/blog.codeship.com\/\">Codeship Blog<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser. Further, we needed to generate reports and analyze gathered information. To deal with the continuous data flowing from the sensors, we chose DynamoDB for storage. DynamoDB promises to handle large &hellip;<\/p>\n","protected":false},"author":393,"featured_media":927,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[377],"class_list":["post-18219","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development","tag-dynamodb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Working with DynamoDB - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Working with DynamoDB - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-11T09:15:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Parth Modi\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Parth Modi\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\"},\"author\":{\"name\":\"Parth Modi\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/3a83197943a3d804e6e56f7466eecde4\"},\"headline\":\"Working with DynamoDB\",\"datePublished\":\"2017-08-11T09:15:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\"},\"wordCount\":2150,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"keywords\":[\"DynamoDB\"],\"articleSection\":[\"Web Dev\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\",\"name\":\"Working with DynamoDB - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"datePublished\":\"2017-08-11T09:15:18+00:00\",\"description\":\"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Web Dev\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/web-development\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Working with DynamoDB\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/3a83197943a3d804e6e56f7466eecde4\",\"name\":\"Parth Modi\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7ac2855e5b525623c04cf99f0a5af51d75cdec92038d8b801fdebb0b9f3c8ed5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/7ac2855e5b525623c04cf99f0a5af51d75cdec92038d8b801fdebb0b9f3c8ed5?s=96&d=mm&r=g\",\"caption\":\"Parth Modi\"},\"description\":\"Parth Modi is a Ruby on Rails developer, with an interest in web applications, exploring IoT, cloud computing, and building efficient, scalable, and robust APIs.\",\"url\":\"https:\/\/www.webcodegeeks.com\/author\/parth-modi\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Working with DynamoDB - Web Code Geeks - 2026","description":"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/","og_locale":"en_US","og_type":"article","og_title":"Working with DynamoDB - Web Code Geeks - 2026","og_description":"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.","og_url":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2017-08-11T09:15:18+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","type":"image\/jpeg"}],"author":"Parth Modi","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Parth Modi","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/"},"author":{"name":"Parth Modi","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/3a83197943a3d804e6e56f7466eecde4"},"headline":"Working with DynamoDB","datePublished":"2017-08-11T09:15:18+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/"},"wordCount":2150,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","keywords":["DynamoDB"],"articleSection":["Web Dev"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/","url":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/","name":"Working with DynamoDB - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","datePublished":"2017-08-11T09:15:18+00:00","description":"Recently, I worked on an IoT-based project where we had to store time-series data coming from multiple sensors and show real-time data to an enduser.","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/web-development\/working-with-dynamodb\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Web Dev","item":"https:\/\/www.webcodegeeks.com\/category\/web-development\/"},{"@type":"ListItem","position":3,"name":"Working with DynamoDB"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/3a83197943a3d804e6e56f7466eecde4","name":"Parth Modi","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/7ac2855e5b525623c04cf99f0a5af51d75cdec92038d8b801fdebb0b9f3c8ed5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/7ac2855e5b525623c04cf99f0a5af51d75cdec92038d8b801fdebb0b9f3c8ed5?s=96&d=mm&r=g","caption":"Parth Modi"},"description":"Parth Modi is a Ruby on Rails developer, with an interest in web applications, exploring IoT, cloud computing, and building efficient, scalable, and robust APIs.","url":"https:\/\/www.webcodegeeks.com\/author\/parth-modi\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/18219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/393"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=18219"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/18219\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/927"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=18219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=18219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=18219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}