Skip to content
Open
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2f2849d
Updated example in java to use partitions
mahnushm Mar 24, 2026
717f609
Fix partition variable usage in query request
mahnushm Mar 25, 2026
3e766c9
Fix break statement in BasicSearchableEncryptionExample
mahnushm Mar 25, 2026
95948ae
Add partition condition to query request
mahnushm Mar 25, 2026
95a85a5
Update BasicSearchableEncryptionExample.java
mahnushm Mar 25, 2026
02cd1db
Update BasicSearchableEncryptionExample.java
mahnushm Mar 25, 2026
d9be133
Update BasicSearchableEncryptionExample.java
mahnushm Mar 26, 2026
396c1a3
Update BasicSearchableEncryptionExample.java
mahnushm Mar 26, 2026
94191f1
Update BasicSearchableEncryptionExample.java
mahnushm Mar 26, 2026
1bc11f6
Update BasicSearchableEncryptionExample.java
mahnushm Mar 27, 2026
6b073f0
Update Examples/runtimes/java/DynamoDbEncryption/src/main/java/softwa…
mahnushm Mar 27, 2026
26892a8
Update Examples/runtimes/java/DynamoDbEncryption/src/main/java/softwa…
mahnushm Mar 27, 2026
5452fbe
Update BasicSearchableEncryptionExample.java
mahnushm Mar 27, 2026
57bc056
Update BasicSearchableEncryptionExample.java
mahnushm Mar 30, 2026
76bdd6e
Refactor DynamoDB encryption configuration
mahnushm Mar 30, 2026
87c3096
Fix DynamoDbTablesEncryptionConfig initialization
mahnushm Mar 30, 2026
d4c9950
Change numQueries from 1 to 2
mahnushm Mar 31, 2026
1800890
Update BasicSearchableEncryptionExample.java
mahnushm Mar 31, 2026
90c966b
Modify partition settings in BasicSearchableEncryptionExample
mahnushm Mar 31, 2026
0e034b7
Refactor numQueries retrieval using queryInputTransform
mahnushm Mar 31, 2026
4426629
Modify beacon partitioning and query handling
mahnushm Mar 31, 2026
df2e135
Rename logicalTableName to tableName in transform input
mahnushm Mar 31, 2026
6940b55
Refactor query input transformation and number retrieval
mahnushm Mar 31, 2026
4e96f58
Update BasicSearchableEncryptionExample.java
mahnushm Mar 31, 2026
3ed5589
Remove unused imports in BasicSearchableEncryptionExample
mahnushm Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.cryptography.dbencryptionsdk.dynamodb.transforms.DynamoDbEncryptionTransforms;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
Expand All @@ -29,7 +30,8 @@
import software.amazon.cryptography.materialproviders.MaterialProviders;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsHierarchicalKeyringInput;
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;

import software.amazon.cryptography.dbencryptionsdk.dynamodb.transforms.model.GetNumberOfQueriesInput;
import software.amazon.cryptography.dbencryptionsdk.dynamodb.transforms.model.GetNumberOfQueriesOutput;
/*
This example demonstrates how to set up a beacon on an encrypted attribute,
put an item with the beacon, and query against that beacon.
Expand Down Expand Up @@ -83,10 +85,11 @@ public static void PutItemQueryItemWithBeacon(
// values are uniformly distributed across its range of possible values.
// In many use cases, the prefix of an identifier encodes some information
// about that identifier (e.g. zipcode and SSN prefixes encode geographic
// information), while the suffix does not and is more uniformly distributed.
// information), while the suffix does not encode such structure and tends
// to be closer to uniformly distributed, though not perfectly uniform.
// We will assume that the inspector ID field matches a similar use case.
// So for this example, we only store and use the last
// 4 digits of the inspector ID, which we assume is uniformly distributed.
// For this example, we use only the last four digits of the inspector ID and apply two partitions,
// assuming the suffix has sufficient uniformity for this purpose.
// Since the full ID's range is divisible by the range of the last 4 digits,
// then the last 4 digits of the inspector ID are uniformly distributed
// over the range from 0 to 9,999.
Expand Down Expand Up @@ -126,13 +129,14 @@ public static void PutItemQueryItemWithBeacon(
.builder()
.name("inspector_id_last4")
.length(10)
.numberOfPartitions(1)
.build();
standardBeaconList.add(last4Beacon);

// The configured DDB table has a GSI on the `aws_dbe_b_unit` AttributeName.
// This field holds a unit serial number.
// For this example, this is a 12-digit integer from 0 to 999,999,999,999 (10^12 possible values).
// We will assume values for this attribute are uniformly distributed across this range.
// We will assume values for this attribute are uniformly distributed across this range using 2 partitions.
// A single unit serial number may be assigned to multiple `work_id`s.
//
// This link provides guidance for choosing a beacon length:
Expand All @@ -159,6 +163,7 @@ public static void PutItemQueryItemWithBeacon(
.builder()
.name("unit")
.length(30)
.numberOfPartitions(1)
.build();
standardBeaconList.add(unitBeacon);

Expand Down Expand Up @@ -191,6 +196,8 @@ public static void PutItemQueryItemWithBeacon(
// 3. Create BeaconVersion.
// The BeaconVersion inside the list holds the list of beacons on the table.
// The BeaconVersion also stores information about the keystore.
// The BeaconVersion parameter specifies the maximumNumberOfPartitions associated with a given beacon configuration.
// "maximumNumberOfPartitions" must be greater than "numberOfPartitions"; we set it to 8 to allow room for future expansion.
// BeaconVersion must be provided:
// - keyStore: The keystore configured in step 2.
// - keySource: A configuration for the key source.
Expand All @@ -206,6 +213,8 @@ public static void PutItemQueryItemWithBeacon(
.builder()
.standardBeacons(standardBeaconList)
.version(1) // MUST be 1
.maximumNumberOfPartitions(4)
.defaultNumberOfPartitions(1) //For beacons that do not require partitioning, only a single partition is used. must be 0 < defaultNumberOfPartitions < maximumNumberOfPartitions.
.keyStore(keyStore)
.keySource(
BeaconKeySource
Expand Down Expand Up @@ -351,55 +360,111 @@ public static void PutItemQueryItemWithBeacon(
// This procedure is internal to the client and is abstracted away from the user;
// e.g. the user will only see "123456789012" and never
// "098765432109", though the actual query returned both.

List<Map<String, AttributeValue>> allResults = new ArrayList<>();


Map<String, String> expressionAttributesNames = new HashMap<>();
expressionAttributesNames.put("#last4", "inspector_id_last4");
expressionAttributesNames.put("#unit", "unit");

Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(
":last4",
AttributeValue.builder().s("4321").build()
);
expressionAttributeValues.put(
":unit",
AttributeValue.builder().s("123456789012").build()
);
":last4",
AttributeValue.builder().s("4321").build()
);
expressionAttributeValues.put(
":unit",
AttributeValue.builder().s("123456789012").build()
);

QueryRequest queryRequest = QueryRequest
.builder()
.tableName(ddbTableName)
.indexName(GSI_NAME)
.keyConditionExpression("#last4 = :last4 and #unit = :unit")
.expressionAttributeNames(expressionAttributesNames)
.expressionAttributeValues(expressionAttributeValues)
.build();
QueryRequest queryRequest = QueryRequest
.builder()
.tableName(ddbTableName)
.indexName(GSI_NAME)
.keyConditionExpression("#last4 = :last4 and #unit = :unit")
.expressionAttributeNames(expressionAttributesNames)
.expressionAttributeValues(expressionAttributeValues)
.build();

DynamoDbTablesEncryptionConfig tablesConfig =
DynamoDbTablesEncryptionConfig.builder()
.tableEncryptionConfigs(tableConfigs)
.build();

DynamoDbEncryptionTransforms transformClient =
DynamoDbEncryptionTransforms.builder()
.DynamoDbTablesEncryptionConfig(tablesConfig)
.build();


// The number can be obtained using transformClient.getNumberOfQueries(query)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to be specific on what number can be obtained. Number of queries?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will add transformClient.getNumberOfQueries(query) to show its use case too.

//int numQueries = transformClient.GetNumberOfQueries(queryRequest);
GetNumberOfQueriesInput numberOfQueriesInput = GetNumberOfQueriesInput
.builder()
.input(queryRequest)
.build();

GetNumberOfQueriesOutput numberOfQueriesOutput =
transformClient.GetNumberOfQueries(numberOfQueriesInput);

int numQueries = numberOfQueriesOutput.numberOfQueries();

//int numQueries = 1;

//We need to query for all possible partitions

for (int partition = 0; partition < numQueries; partition++) {

expressionAttributeValues.put(
":aws_dbe_partition",
AttributeValue.builder().n(Integer.toString(partition)).build()
);

QueryRequest updatedQueryRequest = QueryRequest
.builder()
.tableName(ddbTableName)
.indexName(GSI_NAME)
.keyConditionExpression("#last4 = :last4 and #unit = :unit")
.expressionAttributeNames(expressionAttributesNames)
.expressionAttributeValues(expressionAttributeValues)
.build();

// GSIs do not update instantly
// so if the results come back empty
// we retry after a short sleep
for (int i = 0; i < 10; ++i) {
final QueryResponse queryResponse = ddb.query(queryRequest);
List<Map<String, AttributeValue>> attributeValues = queryResponse.items();
// Validate query was returned successfully
assert 200 == queryResponse.sdkHttpResponse().statusCode();
for (int i = 0; i < 10; ++i) {
final QueryResponse queryResponse = ddb.query(updatedQueryRequest);
List<Map<String, AttributeValue>> attributeValues = queryResponse.items();
// Validate query was returned successfully
assert 200 == queryResponse.sdkHttpResponse().statusCode();

// if no results, sleep and try again
if (attributeValues.size() == 0) {
try {
Thread.sleep(20);
} catch (Exception e) {}
continue;
// if no results, sleep and try again
if (attributeValues.size() == 0) {
try {
Thread.sleep(20);
} catch (Exception e) {}
continue;
}
else {
// Adding the result for this partition
allResults.addAll(attributeValues);
break;
}
}
}

// Validate only 1 item was returned: the item we just put
assert attributeValues.size() == 1;
final Map<String, AttributeValue> returnedItem = attributeValues.get(0);
if (allResults.size() != 1) {
throw new RuntimeException("Expected exactly one result, got " + allResults.size());
}
final Map<String, AttributeValue> returnedItem = allResults.get(0);
// Validate the item has the expected attributes
assert returnedItem.get("inspector_id_last4").s().equals("4321");
assert returnedItem.get("unit").s().equals("123456789012");
break;
}

}


public static void main(final String[] args) {
if (args.length <= 1) {
Expand Down
Loading