Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2015-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License").
   * You may not use this file except in compliance with the License.
   * A copy of the License is located at
   *
   *  http://aws.amazon.com/apache2.0
   *
  * or in the "license" file accompanying this file. This file is distributed
  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  * express or implied. See the License for the specific language governing
  * permissions and limitations under the License.
  */

A request-centric Expression Specification Builder package that can be used to construct valid expressions, and the respective name maps and value maps, for various DynamoDB requests in a typeful manner.

ExpressionSpecBuilder is the API entry point to this library.

Sample Usage 1: Conditional Updates with Expressions

 import static com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder.*;
 ...
 Table table = dynamo.getTable(TABLE_NAME);
 
 UpdateItemExpressionSpec xspec = new ExpressionSpecBuilder()
      // SET num1 = num1 + 20
      .addUpdate(
          N("num1").set(N("num1").plus(20)))
      // SET string-attr = "string-value"
      .addUpdate(
          S("string-attr").set("string-value")
      )
      // num2 BETWEEN 0 AND 100
      .withCondition(
          N("num2").between(0, 100)
      ).buildForUpdate();
 
 table.updateItem(HASH_KEY_NAME, "hashKeyValue", RANGE_KEY_NAME, 0, xspec);
 

Sample Usage 2: Conditional Updates with complex Condition Expression

Let's say you want to include a complex condition expression such as:

   (attribute_not_exists(item_version) AND attribute_not_exists(config_id) AND attribute_not_exists(config_version)) OR
   (item_version < 123) OR
   (item_version = 123 AND config_id < 456) OR
   (item_version = 123 AND config_id = 456 AND config_version < 999)
 
Here is how:

 import static com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder.*;
 ...
 Table table = dynamo.getTable(TABLE_NAME);
 
 UpdateItemExpressionSpec xspec = new ExpressionSpecBuilder()
      // SET num1 = num1 + 20
      .addUpdate(
          N("num1").set(N("num1").plus(20)))
      // SET string-attr = "string-value"
      .addUpdate(
          S("string-attr").set("string-value")
      )
      // a complex condition expression (as shown above)
      .withCondition(
          // add explicit parenthesis
          parenthesize( attribute_not_exists("item_version")
              .and( attribute_not_exists("config_id") )
              .and( attribute_not_exists("config_version") )
          ).or( N("item_version").lt(123) )
           .or( N("item_version").eq(123)
              .and( N("config_id").lt(456) ) )
           .or( N("item_version").eq(123)
              .and( N("config_id").eq(456) )
              .and( N("config_version").lt(999) ))
      ).buildForUpdate();
 
 table.updateItem(HASH_KEY_NAME, "hashKeyValue", RANGE_KEY_NAME, 0, xspec);
 

Sample Usage 3: Scan with Filter Expression

Without ExpressionSpecBuilder, the code (using the DynamoDB Document API) could be something like:

 ItemCollection<?> col = table.scan(
         "(#hk = :hashkeyAttrValue) AND (#rk BETWEEN :lo AND :hi)",
         new NameMap().with("#hk", HASH_KEY_NAME).with("#rk", RANGE_KEY_NAME),
         new ValueMap().withString(":hashkeyAttrValue", "allDataTypes")
                 .withInt(":lo", 1).withInt(":hi", 10));
 
In contrast, using ExpressionSpecBuilder:

 import static com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder.*;
 ...
 ScanExpressionSpec xspec = new ExpressionSpecBuilder()
     .withCondition(
         S(HASH_KEY_NAME).eq("allDataTypes")
             .and(N(RANGE_KEY_NAME).between(1, 10))
 ).buildForScan();
 
 ItemCollection<?> col = table.scan(xspec);
 

Sample Usage 4: Updates with SET, ADD, DELETE and REMOVE

 import static com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder.*;
 ...
 Table table = dynamo.getTable(TABLE_NAME);
 
 UpdateItemExpressionSpec xspec = new ExpressionSpecBuilder()
     .addUpdate(S("mapAttr.colors[0]").set("red"))
     .addUpdate(S("mapAttr.colors[1]").set("blue"))
     .addUpdate(L("mapAttr.members").set(
         L("mapAttr.members").listAppend("marry", "liza")))
     .addUpdate(SS("mapAttr.countries").append("cn", "uk"))
     .addUpdate(SS("mapAttr.brands").delete("Facebook", "LinkedIn"))
     .addUpdate(attribute("mapAttr.foo").remove())
     .buildForUpdate();

 assertEquals("SET #0.#1[0] = :0, #0.#1[1] = :1, #0.#2 = list_append(#0.#2, :2) ADD #0.#3 :3 DELETE #0.#4 :4 REMOVE #0.#5",
     xspec.getUpdateExpression());
     
 final String hashkey = "addRemoveDeleteColors";
 table.updateItem(HASH_KEY_NAME, hashkey, RANGE_KEY_NAME, 0, xspec);
 

Notes on Design, Scope and Purposes

  1. The purpose of this library is to provide an easy-to-use Expression Builder API that can be used to construct valid expressions (for use in a DynamoDB request) in a typeful manner. The intent is to leverage on the type system/compiler to ensure that all the expressions produced by the builder are well-formed and syntactically valid. (In contrast, directly specifying raw strings for the expressions can provide no such guarantee.)
  2. This builder library can be used independently to construct valid expressions (as strings) and the associated name-maps and value-maps. These expressions and maps can then be used to make requests to DynamoDB via the DynamoDB Document API. In other words, the expression builder library should be as independent as possible (ie with as little or no dependency on other libraries as possible.)
  3. A dot (".") character in a user specified document path, such as "Product.Reviews", is always assumed by the builder in this proposed library to mean the dereference of a DynamoDB Map element. See more info at Reading and Writing Items Using Expressions. The idea is to handle the majority of cases when the dot (".") character, and sequence (of regex pattern) "\[0-9]+\]" are not literally part of an attribute name.
  4. An integer enclosed in square bracket in a user specified document path, such as "[2]", is always assumed by the builder in this proposed library to mean the dereference of a DynamoDB List element. The idea is to handle the majority of cases when character sequence such as "[0]" are not literally part of an attribute name.
  5. To avoid attribute names that may conflict with the DynamoDB reserved words, this library will automatically transform every component of a document path into the use of an "expression attribute name" (that begins with "#") as a placeholder. The actual mapping from the "expression attribute name" to the actual attribute name is automatically taken care of by the builder in a "name map". Similarly, the actual mapping from the "expression attribute value" (that begins with ":") to the actual attribute value is automatically taken care of by the builder in a "value map". See more information at Using Placeholders for Attribute Names and Values.
package com.amazonaws.services.dynamodbv2.xspec;
    
New to GrepCode? Check out our FAQ X