如何在不指定主键的情况下从DynamoDB表中获取所有项目?

79

我有一个名为products的表,它的主键是Id。我想选择表中的所有项目。这是我正在使用的代码:

$batch_get_response = $dynamodb->batch_get_item(array(
    'RequestItems' => array(

        'products' => array(
            'Keys' => array(
                array( // Key #1
                    'HashKeyElement'  => array( AmazonDynamoDB::TYPE_NUMBER => '1'),
                    'RangeKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => $current_time),
                ),
                array( // Key #2
                    'HashKeyElement'  => array( AmazonDynamoDB::TYPE_NUMBER => '2'),
                    'RangeKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => $current_time),
                ),
            )
        )
    )   
));

是否可以在不指定主键的情况下选择所有项目?我正在使用AWS SDK for PHP。

9个回答

79

Amazon DynamoDB 提供了 Scan 操作,用于此目的,它通过对表进行全面扫描返回一个或多个项目及其属性。请注意以下两个限制:

  • 根据您的表大小,您可能需要使用分页来检索整个结果集:

    注意
    如果扫描的项目总数超过1MB限制,则扫描将停止,并使用LastEvaluatedKey将结果返回给用户以在后续操作中继续扫描。结果还包括超出限制的项目数量。扫描可能导致未满足筛选条件的表数据。

    结果集最终一致。

  • 扫描操作在性能和消耗容量单位(即价格)方面潜在昂贵,请参见Amazon DynamoDB中的查询和扫描中的扫描和查询性能部分:

    [...] 此外,随着表的增长,扫描操作会变慢。扫描操作检查所请求值的每个项目,并且可能在单个操作中使用大型表的预配吞吐量。为了更快的响应时间,请设计可以使用Query、Get或BatchGetItem API的表。或者,设计您的应用程序以一种方式使用扫描操作,以使对表的请求速率的影响最小化。有关更多信息,请参见Amazon DynamoDB中的预配吞吐量指南[强调是我的]

您可以在使用AWS SDK for PHP低级API扫描Amazon DynamoDB表中找到有关此操作的更多详细信息和一些示例片段,其中最简单的示例说明操作如下:
$dynamodb = new AmazonDynamoDB();

$scan_response = $dynamodb->scan(array(
    'TableName' => 'ProductCatalog' 
));

foreach ($scan_response->body->Items as $item)
{
    echo "<p><strong>Item Number:</strong>"
         . (string) $item->Id->{AmazonDynamoDB::TYPE_NUMBER};
    echo "<br><strong>Item Name: </strong>"
         . (string) $item->Title->{AmazonDynamoDB::TYPE_STRING} ."</p>";
}

查询中是否可以添加条件? - Warrior
是的,检查_Request_部分以获取Scan,“ScanFilter:ComparisonOperator”提供了您可以执行的简要说明。根据您的情况,您可能还想了解Query,这通常是出于性能和成本原因而更可取的(但需要一个主键),如Amazon DynamoDB中的查询和扫描所述。 - Steffen Opel
你能看一下我的新问题吗?https://dev59.com/xmPVa4cB1Zd3GeqP6ole - Warrior
@THOmas:不,主键要么是哈希类型主键,要么是哈希和范围类型主键;后者允许项目的相对顺序(请参见查询RangeKeyCondition),但仅限于此。因此(与NoSQL解决方案通常一样),如果您真的需要这个功能,则需要自己进行建模(毕竟,您插入了该键)- 您似乎在这里仍然过多地考虑SQL术语 ;) - Steffen Opel
那么,当我插入新产品时,应该将什么分配给ID字段? - Warrior
显示剩余4条评论

24

嗨,你可以使用boto3在Python中进行下载。

import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Table')
response = table.scan()
items = response['Items']
while 'LastEvaluatedKey' in response:
    print(response['LastEvaluatedKey'])
    response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'])
    items.extend(response['Items'])


提供 lib/ 文档的链接会很好。 - m02ph3u5
扫描不是一项昂贵的操作吗?!@Steffen Opel解释得很好。 - Ronnie
2
@Ronnie 是的,没错。然而,OP想要从表中提取所有行。查询的成本也会同样昂贵。 - Derek Evermore

10

我猜你在使用PHP但没有提到(已编辑)。我通过互联网搜索找到了这个问题,因为我找到了一个可行的解决方案,对于那些使用nodejs的人,这里有一个使用scan的简单解决方案:

  var dynamoClient = new AWS.DynamoDB.DocumentClient();
  var params = {
    TableName: config.dynamoClient.tableName, // give it your table name 
    Select: "ALL_ATTRIBUTES"
  };

  dynamoClient.scan(params, function(err, data) {
    if (err) {
       console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
       console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
    }
  });

我认为相同的代码也可以用不同的AWS SDK转换成PHP。


2

我用以下查询语句从dynamodb中获取所有项目。它能够很好地工作。我在zend框架中将这些函数设置为通用功能,并在整个项目中使用这些函数。

        public function getQuerydata($tablename, $filterKey, $filterValue){
            return $this->getQuerydataWithOp($tablename, $filterKey, $filterValue, 'EQ');
        }

        public function getQuerydataWithOp($tablename, $filterKey, $filterValue, $compOperator){
        $result = $this->getClientdb()->query(array(
                'TableName'     => $tablename,
                'IndexName'     => $filterKey,
                'Select'        => 'ALL_ATTRIBUTES',
                'KeyConditions' => array(
                    $filterKey => array(
                        'AttributeValueList' => array(
                            array('S' => $filterValue)
                        ),
                'ComparisonOperator' => $compOperator
            )
            )
        ));
            return $result['Items'];
        }

       //Below i Access these functions and get data.
       $accountsimg = $this->getQuerydataWithPrimary('accounts', 'accountID',$msgdata[0]['accountID']['S']);

你的数据库中有多少条记录?似乎限制为1MB,这对于较小的数据库非常适用,但如果其中包含大量数据,则无法完全获取。 - dev_row
如果您获取的记录大于1 MB,可以使用亚马逊数据库提供的限制参数。 - Hassan Raza
一个 Query 或 Scan 操作最多可以检索 1 MB 的数据。该限制适用于应用任何筛选表达式之前的结果。 - dev_row
不符合 OP 要求,无法获取所有项目。 - Kashyap

2

通过指定AWS服务的区域,列出DynamoDB表中所有项目的简单代码。

import boto3

dynamodb = boto3.resource('dynamodb', region_name='ap-south-1')
table = dynamodb.Table('puppy_store')
response = table.scan()
items = response['Items']

# Prints All the Items at once
print(items)

# Prints Items line by line
for i, j in enumerate(items):
    print(f"Num: {i} --> {j}")

我漏掉了item中的额外的's'。 - vijayraj34

1

这里有一个Java的例子。在withAttributesToGet中,您可以指定要读取的内容。在运行之前,您需要将凭据文件放置到您的.aws文件夹中。

 public static final String TABLE_NAME = "table_name";

    public static final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
            .withRegion(Regions.CA_CENTRAL_1)
            .build();

    public static void main(String[] args) throws IOException, InterruptedException {
        downloadAllRecords();
    }

    public static void downloadAllRecords() throws InterruptedException, IOException {
        final Object[] FILE_HEADER = {"key", "some_param"};
        CSVFormat csvFormat = CSVFormat.DEFAULT.withRecordSeparator("\n");
        CSVPrinter csvPrinter = new CSVPrinter(new FileWriter(TABLE_NAME + ".csv"), csvFormat);
        csvPrinter.printRecord(FILE_HEADER);

        ScanRequest scanRequest = new ScanRequest()
                .withTableName(TABLE_NAME)
                .withConsistentRead(false)
                .withLimit(100)
                .withAttributesToGet("key", "some_param");
        int counter = 0;
        do {
            ScanResult result = client.scan(scanRequest);
            Map<String, AttributeValue> lastEvaluatedKey = result.getLastEvaluatedKey();
            for (Map<String, AttributeValue> item : result.getItems()) {
                AttributeValue keyIdAttribute = item.getOrDefault("key", new AttributeValue());
                AttributeValue createdDateAttribute = item.getOrDefault("some_param", new AttributeValue());

                    counter++;
                    List record = new ArrayList();
                    record.add(keyIdAttribute.getS());
                    record.add(createdDateAttribute.getS());
                    csvPrinter.printRecord(record);
                    TimeUnit.MILLISECONDS.sleep(50);

            }
            scanRequest.setExclusiveStartKey(lastEvaluatedKey);
        } while (scanRequest.getExclusiveStartKey() != null);
        csvPrinter.flush();
        csvPrinter.close();
        System.out.println("CSV file generated successfully.");
    }

同时指定必要的依赖项。

<dependencies>
   <dependency>
       <groupId>com.sparkjava</groupId>
       <artifactId>spark-core</artifactId>
       <version>2.5.4</version>
   </dependency>
   <!-- https://mvnrepository.com/artifact/com.sparkjava/spark-template-velocity -->
   <dependency>
       <groupId>com.sparkjava</groupId>
       <artifactId>spark-template-velocity</artifactId>
       <version>2.7.1</version>
   </dependency>
   <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-logs -->
   <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-java-sdk-logs</artifactId>
       <version>1.12.132</version>
   </dependency>
   <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
       <version>2.8.9</version>
   </dependency>
   <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <version>31.0.1-jre</version>
   </dependency>
   <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-collections4</artifactId>
       <version>4.4</version>
   </dependency>
   <dependency>
       <groupId>com.opencsv</groupId>
       <artifactId>opencsv</artifactId>
       <version>5.3</version>
   </dependency>
   <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-dynamodb -->
   <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-java-sdk-dynamodb</artifactId>
       <version>1.12.161</version>
   </dependency>
   <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-csv</artifactId>
       <version>1.1</version>
   </dependency>

</dependencies>

凭据文件示例

[default]
aws_access_key_id = AAAAAAA
aws_secret_access_key = AAAAAAAA
aws_session_token = AAAAAAA

0

一种在不使用主键和扫描的情况下获取所有行的方法是在表上定义/添加全局二级索引(GSI),并指定一个额外的属性作为GSI键。将此值设置为每行中的相同值(除非您想将其用于多个目的)。

然后,在GSI上执行查询,指定添加属性的值。

您可能需要对返回的值进行分页处理。

使用GSI将增加表的大小,并可能增加成本。


0

以下代码中我未指定 pk:

client = boto3.client('dynamodb')
table = 'table_name'
response = client.scan(
    TableName=table,
    AttributesToGet=['second_field_in_order', 'first_field_in_order']
)

-3

这段C#代码是使用BatchGet或CreateBatchGet从dynamodb表中获取所有项的。

        string tablename = "AnyTableName"; //table whose data you want to fetch

        var BatchRead = ABCContext.Context.CreateBatchGet<ABCTable>(  

            new DynamoDBOperationConfig
            {
                OverrideTableName = tablename; 
            });

        foreach(string Id in IdList) // in case you are taking string from input
        {
            Guid objGuid = Guid.Parse(Id); //parsing string to guid
            BatchRead.AddKey(objGuid);
        }

        await BatchRead.ExecuteAsync();
        var result = BatchRead.Results;

// ABCTable是用于在dynamodb中创建的表模型,您想要获取的数据


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接