DynamoDB - ดัชนีทุติยภูมิทั่วโลก

แอปพลิเคชันที่ต้องการแบบสอบถามประเภทต่างๆที่มีแอตทริบิวต์ต่างกันสามารถใช้ดัชนีรองส่วนกลางเดียวหรือหลายดัชนีในการดำเนินการสืบค้นโดยละเอียดเหล่านี้

For example - ระบบติดตามผู้ใช้สถานะการเข้าสู่ระบบและเวลาในการเข้าสู่ระบบการเติบโตของตัวอย่างก่อนหน้านี้ทำให้การสืบค้นข้อมูลช้าลง

ดัชนีทุติยภูมิสากลเร่งการสืบค้นโดยจัดการการเลือกแอตทริบิวต์จากตาราง พวกเขาใช้คีย์หลักในการจัดเรียงข้อมูลและไม่จำเป็นต้องมีแอตทริบิวต์ตารางคีย์หรือสคีมาคีย์ที่เหมือนกับตาราง

ดัชนีรองส่วนกลางทั้งหมดต้องมีคีย์พาร์ติชันพร้อมตัวเลือกของคีย์การเรียงลำดับ สคีมาคีย์ดัชนีอาจแตกต่างจากตารางและแอตทริบิวต์คีย์ดัชนีสามารถใช้แอตทริบิวต์สตริงระดับบนสุดตัวเลขหรือตารางไบนารี

ในการฉายภาพคุณสามารถใช้แอตทริบิวต์ตารางอื่น ๆ ได้อย่างไรก็ตามคิวรีจะไม่ดึงข้อมูลจากตารางหลัก

การคาดการณ์คุณสมบัติ

การคาดการณ์ประกอบด้วยชุดแอตทริบิวต์ที่คัดลอกจากตารางไปยังดัชนีรอง การฉายภาพจะเกิดขึ้นกับคีย์พาร์ติชันตารางและคีย์การเรียงลำดับเสมอ ในแบบสอบถามการคาดการณ์อนุญาตให้ DynamoDB เข้าถึงแอตทริบิวต์ใด ๆ ของการฉายภาพ โดยพื้นฐานแล้วมีอยู่เป็นโต๊ะของตัวเอง

ในการสร้างดัชนีรองคุณต้องระบุแอตทริบิวต์สำหรับการฉายภาพ DynamoDB เสนอสามวิธีในการทำงานนี้ -

  • KEYS_ONLY- รายการดัชนีทั้งหมดประกอบด้วยพาร์ติชันตารางและจัดเรียงค่าคีย์และค่าคีย์ดัชนี สิ่งนี้สร้างดัชนีที่เล็กที่สุด

  • INCLUDE - ประกอบด้วยแอตทริบิวต์ KEYS_ONLY และแอตทริบิวต์ที่ไม่ใช่คีย์ที่ระบุ

  • ALL - ประกอบด้วยแอตทริบิวต์ตารางแหล่งที่มาทั้งหมดสร้างดัชนีที่ใหญ่ที่สุดเท่าที่จะเป็นไปได้

สังเกตการแลกเปลี่ยนในการคาดการณ์แอตทริบิวต์ในดัชนีทุติยภูมิทั่วโลกซึ่งเกี่ยวข้องกับปริมาณงานและต้นทุนการจัดเก็บ

พิจารณาประเด็นต่อไปนี้ -

  • หากคุณต้องการเข้าถึงแอตทริบิวต์เพียงไม่กี่รายการโดยมีเวลาแฝงต่ำให้ฉายเฉพาะที่คุณต้องการ ซึ่งจะช่วยลดต้นทุนการจัดเก็บและการเขียน

  • หากแอปพลิเคชันเข้าถึงแอตทริบิวต์ที่ไม่ใช่คีย์บางรายการบ่อยครั้งให้คาดการณ์เนื่องจากค่าใช้จ่ายในการจัดเก็บข้อมูลซีดเมื่อเทียบกับปริมาณการสแกน

  • คุณสามารถฉายชุดแอตทริบิวต์จำนวนมากที่เข้าถึงบ่อยได้อย่างไรก็ตามมีค่าใช้จ่ายในการจัดเก็บข้อมูลสูง

  • ใช้ KEYS_ONLY สำหรับการสืบค้นตารางที่ไม่บ่อยนักและการเขียน / อัปเดตบ่อยๆ สิ่งนี้ควบคุมขนาด แต่ยังคงให้ประสิทธิภาพที่ดีในการสืบค้น

แบบสอบถามและการสแกนดัชนีทุติยภูมิสากล

คุณสามารถใช้แบบสอบถามเพื่อเข้าถึงรายการเดียวหรือหลายรายการในดัชนี คุณต้องระบุดัชนีและชื่อตารางคุณลักษณะที่ต้องการและเงื่อนไข พร้อมตัวเลือกในการส่งคืนผลลัพธ์ในลำดับจากน้อยไปมากหรือมากไปหาน้อย

คุณยังสามารถใช้การสแกนเพื่อรับข้อมูลดัชนีทั้งหมด ต้องใช้ชื่อตารางและดัชนี คุณใช้นิพจน์ตัวกรองเพื่อดึงข้อมูลเฉพาะ

การซิงโครไนซ์ข้อมูลตารางและดัชนี

DynamoDB ทำการซิงโครไนซ์ดัชนีกับตารางพาเรนต์โดยอัตโนมัติ การดำเนินการแก้ไขแต่ละรายการทำให้เกิดการอัปเดตแบบอะซิงโครนัสอย่างไรก็ตามแอปพลิเคชันไม่ได้เขียนลงในดัชนีโดยตรง

คุณต้องเข้าใจผลกระทบของการบำรุงรักษา DynamoDB ต่อดัชนี ในการสร้างดัชนีคุณต้องระบุแอตทริบิวต์หลักและประเภทข้อมูลซึ่งหมายความว่าในการเขียนประเภทข้อมูลเหล่านั้นต้องตรงกับประเภทข้อมูลสคีมาหลัก

ในการสร้างหรือลบไอเท็มดัชนีจะอัพเดตในลักษณะที่สอดคล้องกันในที่สุดอย่างไรก็ตามการอัพเดตข้อมูลจะแพร่กระจายในเสี้ยววินาที (เว้นแต่ว่าระบบบางประเภทจะล้มเหลว) คุณต้องคำนึงถึงความล่าช้าในการใช้งานนี้

Throughput Considerations in Global Secondary Indexes- ดัชนีทุติยภูมิทั่วโลกหลายรายการส่งผลต่อปริมาณงาน การสร้างดัชนีต้องใช้ข้อกำหนดหน่วยความจุซึ่งมีอยู่แยกต่างหากจากตารางส่งผลให้การดำเนินงานใช้หน่วยความจุดัชนีมากกว่าหน่วยตาราง

ซึ่งอาจส่งผลให้เกิดการควบคุมปริมาณหากข้อความค้นหาหรือการเขียนเกินปริมาณงานที่จัดเตรียมไว้ ดูการตั้งค่าปริมาณงานโดยใช้DescribeTable.

Read Capacity- ดัชนีทุติยภูมิทั่วโลกให้ความสอดคล้องในที่สุด ในแบบสอบถาม DynamoDB จะทำการคำนวณการจัดเตรียมเหมือนกับที่ใช้สำหรับตารางโดยมีความแตกต่างเพียงอย่างเดียวในการใช้ขนาดรายการดัชนีแทนที่จะเป็นขนาดรายการ ขีด จำกัด ของการส่งคืนแบบสอบถามยังคงอยู่ที่ 1MB ซึ่งรวมถึงขนาดชื่อแอตทริบิวต์และค่าในทุกรายการที่ส่งคืน

เขียนความจุ

เมื่อการดำเนินการเขียนเกิดขึ้นดัชนีที่ได้รับผลกระทบจะใช้หน่วยการเขียน ต้นทุนการเขียนข้อมูลคือผลรวมของหน่วยความสามารถในการเขียนที่ใช้ในการเขียนตารางและหน่วยที่ใช้ในการปรับปรุงดัชนี การดำเนินการเขียนที่ประสบความสำเร็จต้องใช้ความสามารถเพียงพอหรือส่งผลให้เกิดการควบคุมปริมาณ

ต้นทุนการเขียนยังคงขึ้นอยู่กับปัจจัยบางประการซึ่งมีดังนี้ -

  • รายการใหม่ที่กำหนดแอตทริบิวต์ที่จัดทำดัชนีหรือการอัปเดตรายการที่กำหนดแอตทริบิวต์ที่จัดทำดัชนีที่ไม่ได้กำหนดใช้การดำเนินการเขียนเดียวเพื่อเพิ่มรายการลงในดัชนี

  • การอัปเดตการเปลี่ยนค่าแอตทริบิวต์คีย์ที่จัดทำดัชนีใช้การเขียนสองรายการเพื่อลบรายการและเขียนรายการใหม่

  • การเขียนตารางที่ทริกเกอร์การลบแอตทริบิวต์ที่จัดทำดัชนีจะใช้การเขียนครั้งเดียวเพื่อลบการคาดการณ์รายการเก่าในดัชนี

  • รายการที่ไม่มีอยู่ในดัชนีก่อนและหลังการดำเนินการอัพเดตไม่ใช้การเขียน

  • การอัปเดตที่เปลี่ยนเฉพาะค่าแอ็ตทริบิวต์ที่คาดการณ์ไว้ในสคีมาคีย์ดัชนีและไม่ได้จัดทำดัชนีค่าแอตทริบิวต์ของคีย์ให้ใช้การเขียนเพื่ออัปเดตค่าของแอตทริบิวต์ที่คาดการณ์ไว้ในดัชนี

ปัจจัยทั้งหมดนี้ถือว่าขนาดรายการน้อยกว่าหรือเท่ากับ 1KB

การจัดเก็บดัชนีทุติยภูมิทั่วโลก

ในการเขียนรายการ DynamoDB จะคัดลอกชุดแอตทริบิวต์ที่ถูกต้องโดยอัตโนมัติไปยังดัชนีใด ๆ ที่ต้องมีแอตทริบิวต์ สิ่งนี้ส่งผลกระทบต่อบัญชีของคุณโดยการเรียกเก็บเงินจากการจัดเก็บรายการในตารางและการจัดเก็บแอตทริบิวต์ ช่องว่างที่ใช้เป็นผลมาจากผลรวมของปริมาณเหล่านี้ -

  • ขนาดไบต์ของคีย์หลักของตาราง
  • ขนาดไบต์ของแอตทริบิวต์คีย์ดัชนี
  • ขนาดไบต์ของแอตทริบิวต์ที่คาดการณ์ไว้
  • ค่าโสหุ้ย 100 ไบต์ต่อรายการดัชนี

คุณสามารถประมาณความต้องการพื้นที่จัดเก็บได้โดยการประมาณขนาดรายการโดยเฉลี่ยและคูณด้วยปริมาณของรายการในตารางที่มีแอตทริบิวต์คีย์ดัชนีรองส่วนกลาง

DynamoDB ไม่เขียนข้อมูลรายการสำหรับรายการตารางด้วยแอตทริบิวต์ที่ไม่ได้กำหนดซึ่งกำหนดเป็นพาร์ติชันดัชนีหรือคีย์การเรียงลำดับ

Crud ดัชนีทุติยภูมิทั่วโลก

สร้างตารางที่มีดัชนีรองส่วนกลางโดยใช้ CreateTable การทำงานที่จับคู่กับ GlobalSecondaryIndexesพารามิเตอร์. คุณต้องระบุแอ็ตทริบิวต์เพื่อใช้เป็นคีย์พาร์ติชันดัชนีหรือใช้อื่นสำหรับคีย์การเรียงลำดับดัชนี แอตทริบิวต์คีย์ดัชนีทั้งหมดต้องเป็นสตริงตัวเลขหรือสเกลาร์ไบนารี คุณต้องระบุการตั้งค่าปริมาณงานซึ่งประกอบด้วยReadCapacityUnits และ WriteCapacityUnits.

ใช้ UpdateTable เพื่อเพิ่มดัชนีรองส่วนกลางลงในตารางที่มีอยู่โดยใช้พารามิเตอร์ GlobalSecondaryIndexes อีกครั้ง

ในการดำเนินการนี้คุณต้องระบุอินพุตต่อไปนี้ -

  • ชื่อดัชนี
  • คีย์สคีมา
  • แอตทริบิวต์ที่คาดการณ์ไว้
  • การตั้งค่าปริมาณงาน

การเพิ่มดัชนีทุติยภูมิสากลอาจใช้เวลานานพอสมควรกับตารางขนาดใหญ่เนื่องจากปริมาณสินค้าปริมาณแอตทริบิวต์ที่คาดการณ์ความจุในการเขียนและกิจกรรมการเขียน ใช้CloudWatch เมตริกเพื่อตรวจสอบกระบวนการ

ใช้ DescribeTableเพื่อดึงข้อมูลสถานะสำหรับดัชนีรองส่วนกลาง จะส่งกลับหนึ่งในสี่IndexStatus สำหรับ GlobalSecondaryIndexes -

  • CREATING - ระบุขั้นตอนการสร้างดัชนีและความไม่พร้อมใช้งาน

  • ACTIVE - แสดงถึงความพร้อมของดัชนีในการใช้งาน

  • UPDATING - แสดงสถานะการอัปเดตของการตั้งค่าปริมาณงาน

  • DELETING - ระบุสถานะการลบของดัชนีและความไม่พร้อมใช้งานถาวรสำหรับการใช้งาน

อัปเดตการตั้งค่าทรูพุตที่จัดเตรียมดัชนีรองส่วนกลางระหว่างขั้นตอนการโหลด / การเติมย้อนกลับ (แอตทริบิวต์การเขียน DynamoDB ไปยังดัชนีและการติดตามรายการที่เพิ่ม / ลบ / อัปเดต) ใช้UpdateTable เพื่อดำเนินการนี้

คุณควรจำไว้ว่าคุณไม่สามารถเพิ่ม / ลบดัชนีอื่น ๆ ในระหว่างขั้นตอนการเติม

ใช้ UpdateTable เพื่อลบดัชนีรองส่วนกลาง อนุญาตให้ลบเพียงดัชนีเดียวต่อการดำเนินการอย่างไรก็ตามคุณสามารถเรียกใช้การดำเนินการหลายรายการพร้อมกันได้สูงสุดห้ารายการ กระบวนการลบไม่มีผลต่อกิจกรรมการอ่าน / เขียนของตารางพาเรนต์ แต่คุณไม่สามารถเพิ่ม / ลบดัชนีอื่น ๆ ได้จนกว่าการดำเนินการจะเสร็จสิ้น

การใช้ Java เพื่อทำงานกับ Global Secondary Indexes

สร้างตารางด้วยดัชนีผ่าน CreateTable เพียงสร้างอินสแตนซ์คลาส DynamoDB aCreateTableRequest อินสแตนซ์คลาสสำหรับการร้องขอข้อมูลและส่งอ็อบเจ็กต์คำร้องขอไปยังเมธอด CreateTable

โปรแกรมต่อไปนี้เป็นตัวอย่างสั้น ๆ -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
// Attributes 
ArrayList<AttributeDefinition> attributeDefinitions = new 
   ArrayList<AttributeDefinition>();  
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("City") 
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("Date") 
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("Wind") 
   .withAttributeType("N"));
   
// Key schema of the table 
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>(); 
tableKeySchema.add(new KeySchemaElement()
   .withAttributeName("City") 
   .withKeyType(KeyType.HASH));              //Partition key
   
tableKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Date") 
   .withKeyType(KeyType.RANGE));             //Sort key
   
// Wind index 
GlobalSecondaryIndex windIndex = new GlobalSecondaryIndex() 
   .withIndexName("WindIndex") 
   .withProvisionedThroughput(new ProvisionedThroughput() 
   .withReadCapacityUnits((long) 10) 
   .withWriteCapacityUnits((long) 1)) 
   .withProjection(new Projection().withProjectionType(ProjectionType.ALL));
   
ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>(); 
indexKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Date") 
   .withKeyType(KeyType.HASH));              //Partition key
   
indexKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Wind") 
   .withKeyType(KeyType.RANGE));             //Sort key
   
windIndex.setKeySchema(indexKeySchema);  
CreateTableRequest createTableRequest = new CreateTableRequest() 
   .withTableName("ClimateInfo") 
   .withProvisionedThroughput(new ProvisionedThroughput() 
   .withReadCapacityUnits((long) 5) 
   .withWriteCapacityUnits((long) 1))
   .withAttributeDefinitions(attributeDefinitions) 
   .withKeySchema(tableKeySchema) 
   .withGlobalSecondaryIndexes(windIndex); 
Table table = dynamoDB.createTable(createTableRequest); 
System.out.println(table.getDescription());

ดึงข้อมูลดัชนีด้วย DescribeTable. ขั้นแรกสร้างอินสแตนซ์คลาส DynamoDB จากนั้นสร้างอินสแตนซ์คลาส Table เพื่อกำหนดเป้าหมายดัชนี สุดท้ายส่งตารางไปยังวิธีการอธิบาย

นี่คือตัวอย่างสั้น ๆ -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
Table table = dynamoDB.getTable("ClimateInfo"); 
TableDescription tableDesc = table.describe();  
Iterator<GlobalSecondaryIndexDescription> gsiIter = 
   tableDesc.getGlobalSecondaryIndexes().iterator(); 

while (gsiIter.hasNext()) { 
   GlobalSecondaryIndexDescription gsiDesc = gsiIter.next(); 
   System.out.println("Index data " + gsiDesc.getIndexName() + ":");  
   Iterator<KeySchemaElement> kse7Iter = gsiDesc.getKeySchema().iterator(); 
   
   while (kseIter.hasNext()) { 
      KeySchemaElement kse = kseIter.next(); 
      System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType()); 
   }
   Projection projection = gsiDesc.getProjection(); 
   System.out.println("\tProjection type: " + projection.getProjectionType()); 
   
   if (projection.getProjectionType().toString().equals("INCLUDE")) { 
      System.out.println("\t\tNon-key projected attributes: " 
         + projection.getNonKeyAttributes()); 
   } 
}

ใช้แบบสอบถามเพื่อดำเนินการแบบสอบถามดัชนีเช่นเดียวกับแบบสอบถามตาราง เพียงสร้างอินสแตนซ์คลาส DynamoDB อินสแตนซ์คลาสตารางสำหรับดัชนีเป้าหมายอินสแตนซ์คลาสดัชนีสำหรับดัชนีเฉพาะและส่งดัชนีและวัตถุเคียวรีไปยังเมธอดคิวรี

ดูรหัสต่อไปนี้เพื่อทำความเข้าใจให้ดีขึ้น -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
Table table = dynamoDB.getTable("ClimateInfo"); 
Index index = table.getIndex("WindIndex");  
QuerySpec spec = new QuerySpec() 
   .withKeyConditionExpression("#d = :v_date and Wind = :v_wind") 
   .withNameMap(new NameMap() 
   .with("#d", "Date"))
   .withValueMap(new ValueMap() 
   .withString(":v_date","2016-05-15") 
   .withNumber(":v_wind",0));
   
ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator();

while (iter.hasNext()) {
   System.out.println(iter.next().toJSONPretty()); 
}

โปรแกรมต่อไปนี้เป็นตัวอย่างที่ใหญ่กว่าเพื่อความเข้าใจที่ดีขึ้น -

Note- โปรแกรมต่อไปนี้อาจถือว่าเป็นแหล่งข้อมูลที่สร้างขึ้นก่อนหน้านี้ ก่อนที่จะพยายามดำเนินการให้จัดหาไลบรารีที่สนับสนุนและสร้างแหล่งข้อมูลที่จำเป็น (ตารางที่มีคุณสมบัติที่ต้องการหรือแหล่งอ้างอิงอื่น ๆ )

ตัวอย่างนี้ยังใช้ Eclipse IDE, ไฟล์ข้อมูลรับรอง AWS และ AWS Toolkit ภายใน Eclipse AWS Java Project

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;

import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class GlobalSecondaryIndexSample {  
   static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
      new ProfileCredentialsProvider()));  
   public static String tableName = "Bugs";   
   public static void main(String[] args) throws Exception {  
      createTable(); 
      queryIndex("CreationDateIndex"); 
      queryIndex("NameIndex"); 
      queryIndex("DueDateIndex"); 
   }
   public static void createTable() {  
      // Attributes 
      ArrayList<AttributeDefinition> attributeDefinitions = new 
         ArrayList<AttributeDefinition>();  
      attributeDefinitions.add(new AttributeDefinition()
         .withAttributeName("BugID") 
         .withAttributeType("S")); 
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("Name")
         .withAttributeType("S"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("CreationDate")
         .withAttributeType("S"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("DueDate") 
         .withAttributeType("S"));
         
      // Table Key schema
      ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>(); 
      tableKeySchema.add (new KeySchemaElement() 
         .withAttributeName("BugID") 
         .withKeyType(KeyType.HASH));              //Partition key 
      
      tableKeySchema.add (new KeySchemaElement() 
         .withAttributeName("Name") 
         .withKeyType(KeyType.RANGE));             //Sort key
         
      // Indexes' initial provisioned throughput
      ProvisionedThroughput ptIndex = new ProvisionedThroughput()
         .withReadCapacityUnits(1L)
         .withWriteCapacityUnits(1L);
         
      // CreationDateIndex 
      GlobalSecondaryIndex creationDateIndex = new GlobalSecondaryIndex() 
         .withIndexName("CreationDateIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement() 
         .withAttributeName("CreationDate") 
         .withKeyType(KeyType.HASH),               //Partition key 
         new KeySchemaElement()
         .withAttributeName("BugID") 
         .withKeyType(KeyType.RANGE))              //Sort key 
         .withProjection(new Projection() 
         .withProjectionType("INCLUDE") 
         .withNonKeyAttributes("Description", "Status"));
         
      // NameIndex 
      GlobalSecondaryIndex nameIndex = new GlobalSecondaryIndex() 
         .withIndexName("NameIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement()  
         .withAttributeName("Name")  
         .withKeyType(KeyType.HASH),                  //Partition key 
         new KeySchemaElement()  
         .withAttributeName("BugID")  
         .withKeyType(KeyType.RANGE))                 //Sort key 
         .withProjection(new Projection() 
         .withProjectionType("KEYS_ONLY"));
         
      // DueDateIndex 
      GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex() 
         .withIndexName("DueDateIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement() 
         .withAttributeName("DueDate") 
         .withKeyType(KeyType.HASH))               //Partition key 
         .withProjection(new Projection() 
         .withProjectionType("ALL"));
         
      CreateTableRequest createTableRequest = new CreateTableRequest() 
         .withTableName(tableName) 
         .withProvisionedThroughput( new ProvisionedThroughput() 
         .withReadCapacityUnits( (long) 1) 
         .withWriteCapacityUnits( (long) 1)) 
         .withAttributeDefinitions(attributeDefinitions)
         .withKeySchema(tableKeySchema)
         .withGlobalSecondaryIndexes(creationDateIndex, nameIndex, dueDateIndex);  
         System.out.println("Creating " + tableName + "..."); 
         dynamoDB.createTable(createTableRequest);  
      
      // Pause for active table state 
      System.out.println("Waiting for ACTIVE state of " + tableName); 
      try { 
         Table table = dynamoDB.getTable(tableName); 
         table.waitForActive(); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 
   }
   public static void queryIndex(String indexName) { 
      Table table = dynamoDB.getTable(tableName);  
      System.out.println 
      ("\n*****************************************************\n"); 
      System.out.print("Querying index " + indexName + "...");  
      Index index = table.getIndex(indexName);  
      ItemCollection<QueryOutcome> items = null; 
      QuerySpec querySpec = new QuerySpec();  
      
      if (indexName == "CreationDateIndex") { 
         System.out.println("Issues filed on 2016-05-22"); 
         querySpec.withKeyConditionExpression("CreationDate = :v_date and begins_with
            (BugID, :v_bug)") 
            .withValueMap(new ValueMap() 
            .withString(":v_date","2016-05-22")
            .withString(":v_bug","A-")); 
         items = index.query(querySpec); 
      } else if (indexName == "NameIndex") { 
         System.out.println("Compile error"); 
         querySpec.withKeyConditionExpression("Name = :v_name and begins_with
            (BugID, :v_bug)") 
            .withValueMap(new ValueMap() 
            .withString(":v_name","Compile error") 
            .withString(":v_bug","A-")); 
         items = index.query(querySpec); 
      } else if (indexName == "DueDateIndex") { 
         System.out.println("Items due on 2016-10-15"); 
         querySpec.withKeyConditionExpression("DueDate = :v_date") 
         .withValueMap(new ValueMap() 
         .withString(":v_date","2016-10-15")); 
         items = index.query(querySpec); 
      } else { 
         System.out.println("\nInvalid index name"); 
         return; 
      }  
      Iterator<Item> iterator = items.iterator(); 
      System.out.println("Query: getting result..."); 
      
      while (iterator.hasNext()) { 
         System.out.println(iterator.next().toJSONPretty()); 
      } 
   } 
}