Skip to content

Adding support for Select in ScanEnhancedRequest #5519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,6 @@
{
"category": "DynamoDB Enhanced Client",
"contributor": "shetsa-amzn",
"type": "feature",
"description": "Adding support for Select in ScanEnhancedRequest"
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public ScanRequest generateRequest(TableSchema<T> tableSchema,
.exclusiveStartKey(this.request.exclusiveStartKey())
.consistentRead(this.request.consistentRead())
.returnConsumedCapacity(this.request.returnConsumedCapacity())
.select(this.request.select())
Copy link
Contributor

@millems millems Aug 26, 2024

Choose a reason for hiding this comment

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

In case the "select" enum value isn't known to the SDK version, it would be nice to use 'String' internally, like the code-generated models do. That way, customers that can't upgrade their SDK version can still specify new "select" options we might add in the future.

I see we're not doing that for other enums, and I don't think "select" changes much, so "in a separate PR" is totally fine. Just LMK what you think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should do as part of separate PR for all the Operations where select is used. AFAIK its for Query and Scan.

.expressionAttributeValues(expressionValues)
.expressionAttributeNames(expressionNames)
.projectionExpression(projectionExpressionAsString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
import software.amazon.awssdk.services.dynamodb.model.Select;
import software.amazon.awssdk.utils.Validate;

/**
Expand All @@ -48,6 +49,7 @@ public final class ScanEnhancedRequest {
private final Integer limit;
private final Boolean consistentRead;
private final Expression filterExpression;
private final Select select;
private final List<NestedAttributeName> attributesToProject;
private final Integer segment;
private final Integer totalSegments;
Expand All @@ -60,6 +62,7 @@ private ScanEnhancedRequest(Builder builder) {
this.totalSegments = builder.totalSegments;
this.consistentRead = builder.consistentRead;
this.filterExpression = builder.filterExpression;
this.select = builder.select;
this.returnConsumedCapacity = builder.returnConsumedCapacity;
this.attributesToProject = builder.attributesToProject != null
? Collections.unmodifiableList(builder.attributesToProject)
Expand All @@ -83,6 +86,7 @@ public Builder toBuilder() {
.totalSegments(totalSegments)
.consistentRead(consistentRead)
.filterExpression(filterExpression)
.select(select)
.returnConsumedCapacity(returnConsumedCapacity)
.addNestedAttributesToProject(attributesToProject);
}
Expand Down Expand Up @@ -129,6 +133,24 @@ public Expression filterExpression() {
return filterExpression;
}

/**
* Returns the value of select, or null if it doesn't exist.
* @return
*/
public Select select() {
return select;
}

/**
* Returns the value of select as a string, or null if it doesn't exist.
* @return
*/
public String selectAsString() {
return String.valueOf(select);
}

/**

/**
* Returns the list of projected attributes on this request object, or an null if no projection is specified.
* Nested attributes are represented using the '.' separator. Example : foo.bar is represented as "foo.bar" which is
Expand Down Expand Up @@ -204,6 +226,11 @@ public boolean equals(Object o) {
? !returnConsumedCapacity.equals(scan.returnConsumedCapacity) : scan.returnConsumedCapacity != null) {
return false;
}

if (select != null ? ! select.equals(scan.select) : scan.select != null) {
return false;
}

return filterExpression != null ? filterExpression.equals(scan.filterExpression) : scan.filterExpression == null;
}

Expand All @@ -215,6 +242,7 @@ public int hashCode() {
result = 31 * result + (totalSegments != null ? totalSegments.hashCode() : 0);
result = 31 * result + (consistentRead != null ? consistentRead.hashCode() : 0);
result = 31 * result + (filterExpression != null ? filterExpression.hashCode() : 0);
result = 31 * result + (select != null ? select.hashCode() : 0);
result = 31 * result + (attributesToProject != null ? attributesToProject.hashCode() : 0);
result = 31 * result + (returnConsumedCapacity != null ? returnConsumedCapacity.hashCode() : 0);
return result;
Expand All @@ -229,6 +257,7 @@ public static final class Builder {
private Integer limit;
private Boolean consistentRead;
private Expression filterExpression;
private Select select;
private List<NestedAttributeName> attributesToProject;
private Integer segment;
private Integer totalSegments;
Expand Down Expand Up @@ -335,6 +364,18 @@ public Builder filterExpression(Expression filterExpression) {
return this;
}

/**
* Determines the attributes to be returned in the result. See {@link Select} for examples and constraints.
* By default, all attributes are returned.
* @param select
* @return a builder of this type
*/
public Builder select(Select select) {
this.select = select;
return this;
}


/**
* <p>
* Sets a collection of the attribute names to be retrieved from the database. These attributes can include
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@
import software.amazon.awssdk.enhanced.dynamodb.Expression;
import software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.InnerAttributeRecord;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.NestedTestRecord;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.Page;
import software.amazon.awssdk.enhanced.dynamodb.model.ScanEnhancedRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.Select;

public class BasicScanTest extends LocalDynamoDbSyncTestBase {
private static class Record {
Expand Down Expand Up @@ -645,4 +647,98 @@ public void scanAllRecordsWithNestedProjectionNameEmptyNameMap() {
Page<NestedTestRecord> next = resultsAttributeToProject.next();
});
}

@Test
public void scanAllRecordsWithSelect_specific_attr() {
insertRecords();
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":min_value", numberValue(3));
expressionValues.put(":max_value", numberValue(5));
Expression expression = Expression.builder()
.expression("#sort >= :min_value AND #sort <= :max_value")
.expressionValues(expressionValues)
.putExpressionName("#sort", "sort")
.build();

Iterator<Page<Record>> results =
mappedTable.scan(
ScanEnhancedRequest.builder()
.attributesToProject("sort")
.select(Select.SPECIFIC_ATTRIBUTES)
.filterExpression(expression)
.build()
).iterator();

assertThat(results.hasNext(), is(true));
Page<Record> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.items(), hasSize(3));

Record record = page.items().get(0);

assertThat(record.id, is(nullValue()));
assertThat(record.sort, is(3));
}

@Test
public void scanAllRecordsWithSelect_All_Attr() {
insertRecords();
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":min_value", numberValue(3));
expressionValues.put(":max_value", numberValue(5));
Expression expression = Expression.builder()
.expression("#sort >= :min_value AND #sort <= :max_value")
.expressionValues(expressionValues)
.putExpressionName("#sort", "sort")
.build();

Iterator<Page<Record>> results =
mappedTable.scan(
ScanEnhancedRequest.builder()
.select(Select.ALL_ATTRIBUTES)
.filterExpression(expression)
.build()
).iterator();

assertThat(results.hasNext(), is(true));
Page<Record> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.items(), hasSize(3));

Record record = page.items().get(0);

assertThat(record.id, is("id-value"));
assertThat(record.sort, is(3));
}

@Test
public void scanAllRecordsWithSelect_Count() {
insertRecords();
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":min_value", numberValue(3));
expressionValues.put(":max_value", numberValue(5));
Expression expression = Expression.builder()
.expression("#sort >= :min_value AND #sort <= :max_value")
.expressionValues(expressionValues)
.putExpressionName("#sort", "sort")
.build();

Iterator<Page<Record>> results =
mappedTable.scan(
ScanEnhancedRequest.builder()
.select(Select.COUNT)
.filterExpression(expression)
.build()
).iterator();

assertThat(results.hasNext(), is(true));
Page<Record> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.count(), is(3));

assertThat(page.items().size(), is(0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isEmptyString;
import static org.hamcrest.Matchers.nullValue;
import static software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider.defaultProvider;
import static software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues.numberValue;
Expand Down Expand Up @@ -58,6 +59,7 @@
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.Select;

public class BasicScanTest extends LocalDynamoDbSyncTestBase {
private DynamoDbClient lowLevelClient;
Expand Down Expand Up @@ -668,4 +670,57 @@ public void scanAllRecordsWithNestedProjectionNameEmptyNameMap() {
Page<EnhancedDocument> next = resultsAttributeToProject.next();
});
}

@Test
public void scanAllRecordsDefaultSettings_select() {
insertDocuments();

Iterator<Page<EnhancedDocument>> results =
docMappedtable.scan(b -> b.select(Select.ALL_ATTRIBUTES)).iterator();

assertThat(results.hasNext(), is(true));
Page<EnhancedDocument> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.items().size(), is(DOCUMENTS.size()));

EnhancedDocument firstRecord = page.items().get(0);
assertThat(firstRecord.getString("id"), is("id-value"));
assertThat(firstRecord.getNumber("sort").intValue(), is(0));
assertThat(firstRecord.getNumber("value").intValue(), is(0));
}

@Test
public void scanAllRecordsDefaultSettings_select_specific_attr() {
insertDocuments();

Iterator<Page<EnhancedDocument>> results =
docMappedtable.scan(b -> b.attributesToProject("sort").select(Select.SPECIFIC_ATTRIBUTES)).iterator();

assertThat(results.hasNext(), is(true));
Page<EnhancedDocument> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.items().size(), is(DOCUMENTS.size()));

EnhancedDocument firstRecord = page.items().get(0);
assertThat(firstRecord.getString("id"), is(nullValue()));
assertThat(firstRecord.getNumber("sort").intValue(), is(0));
}


@Test
public void scanAllRecordsDefaultSettings_select_count() {
insertDocuments();

Iterator<Page<EnhancedDocument>> results =
docMappedtable.scan(b -> b.select(Select.COUNT)).iterator();

assertThat(results.hasNext(), is(true));
Page<EnhancedDocument> page = results.next();
assertThat(results.hasNext(), is(false));

assertThat(page.count(), is(DOCUMENTS.size()));
assertThat(page.items().size(), is(0));
}
}
Loading