有没有办法在DynamoDB中查询多个哈希键?

时间:2013-07-15 20:50:44

标签: java amazon-web-services amazon-dynamodb multivalue

有没有办法在Amazon的AWS SDK for Java中使用单个查询查询多个哈希键?

这是我的问题;我有一个项目状态的数据库表。哈希键是项目的状态(即:新建,分配,处理或完成)。范围键是一组项目ID。目前,我有一个查询设置,只是简单地找到列为“已分配”状态(哈希)的所有项目,另一个查询设置为查找“处理”状态。有没有办法使用单个查询执行此操作,而不是为我需要查找的每个状态发送多个查询?代码如下:

    DynamoDBMapper mapper = new DynamoDBMapper(new AmazonDynamoDBClient(credentials));
    PStatus assignedStatus = new PStatus();
    assignedStatus.setStatus("assigned");
    PStatus processStatus = new PStatus();
    processStatus.setStatus("processing");

    DynamoDBQueryExpression<PStatus> queryAssigned = new DynamoDBQueryExpression<PStatus>().withHashKeyValues(assignedStatus);
    DynamoDBQueryExpression<PStatus> queryProcessing = new DynamoDBQueryExpression<PStatus>().withHashKeyValues(processStatus);

    List<PStatus> assigned = mapper.query(PStatus.class, queryAssigned);
    List<PStatus> process = mapper.query(PStatus.class, queryProcessing);

所以基本上,我想知道是否有可能消除queryAssignedassigned变量并通过相同的查询同时处理assignedStatusprocessStatus { {1}},查找非新的或完整的项目。

6 个答案:

答案 0 :(得分:9)

不,截至今天,无法在同一请求中发送多个查询。如果您担心延迟,可以在不同的线程中同时发出多个请求。如果Dynamo提供它,则需要与“双重查询”相同的网络带宽量(假设你是2,而不是数百)。

答案 1 :(得分:5)

无法通过多个哈希键进行查询,但是,从2014年4月开始,您可以使用QueryFilter,以便除了哈希键字段之外还可以按非关键字段进行过滤。

在2014年4月24日的博客文章中,AWS宣布发布了&#34; QueryFilter&#34;选项:

  

随着今天的发布,我们正在扩展此模型,并支持非关键属性的查询过滤。您现在可以在查询功能的调用中包含 QueryFilter 。过滤器在基于密钥的检索之后和​​结果返回给您之前应用。以这种方式过滤可以减少返回到您的应用程序的数据量,同时还可以简化和简化代码

在那里检查一下 http://aws.amazon.com/blogs/aws/improved-queries-and-updates-for-dynamodb/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+AmazonWebServicesBlog+%28Amazon+Web+Services+Blog%29

答案 2 :(得分:0)

在C#中试试这个。我认为它与Java类似。 UserId 它是哈希键。

        var table = Table.LoadTable(DynamoClient, "YourTableName");
        var batchGet = table.CreateBatchGet();
        batchGet.AddKey(new Dictionary<string, DynamoDBEntry>() { { "UserId", 123 } });
        batchGet.AddKey(new Dictionary<string, DynamoDBEntry>() { { "UserId", 456 } });
        batchGet.Execute();
        var results = batchGet.Results;

答案 3 :(得分:0)

分享我的后代工作答案。截至2020年10月,有一种方法可以使用aws-java-sdk-dynamodb-1.11.813.jar通过单个查询来查询多个哈希键。在必须基于多个哈希键(分区键)选择项目的情况下,我有相同的要求,并且您可以将该要求与RDMS场景相关联,类似于查询select * from photo where id in ('id1','id2','id3'),此处id是主键。 photo表。

代码段

  • DynamoDB实体
package com.test.demo.dynamodb.entity;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@NoArgsConstructor
@AllArgsConstructor
@lombok.Data
@DynamoDBTable(tableName = "test_photos")
@Builder
public class Photo implements Serializable {
    @DynamoDBHashKey
    private String id;
    private String title;
    private String url;
    private String thumbnailUrl;
}

  • DynamoDB存储库类
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.KeyPair;
import com.test.demo.dynamodb.entity.Photo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Repository
public class PhotoRepository {

    @Autowired
    private DynamoDBMapper dynamoDBMapper = null;

    public List<Photo> findByIds(Collection<String> photoIds) {
        //Constructing `KeyPair` instance and setting the HashKey,
        // in this example I have only hash key,
        // if you have RangeKey(Sort) you can set that also here using KeyPair#withRangeKey

        List<KeyPair> keyPairs = photoIds.stream()
                                         .map(id -> new KeyPair().withHashKey(id))
                                         .collect(Collectors.toList());

        //Creating Map where Key as Class<?> and value as a list of created keyPairs 
        //you can also directly use batchLoad(List<Photo> itemsToGet), the only constraint 
        //is if you didn't specify the Type as key and simply using the 
        //DynamoDBMapper#batchLoad(Iterable<? extends Object> itemsToGet)
        //then the Type of Iterable should have annotated with @DynamoDBTable


        Map<Class<?>, List<KeyPair>> keyPairForTable = new HashMap<>();
        keyPairForTable.put(Photo.class, keyPairs);
        Map<String, List<Object>> listMap = dynamoDBMapper.batchLoad(keyPairForTable);

        //result map contains key as dynamoDBtable name of Photo.class
        //entity(test_photo) and values as matching results of given ids

        String tableName = dynamoDBMapper.generateCreateTableRequest(Photo.class)
                                         .getTableName();
        return listMap.get(tableName).stream()
                                     .map(e -> (Photo) e)
                                     .collect(Collectors.toList());
    }
}

  • 测试课程

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.TableCollection;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.ListTablesRequest;
import com.amazonaws.services.dynamodbv2.model.ListTablesResult;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.test.demo.dynamodb.Application;
import com.test.demo.dynamodb.entity.Photo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@ActiveProfiles("test")
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class,
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DynamoDBFindByIdsITest {

    @Autowired
    private DynamoDBMapper dynamoDBMapper = null;

    @Autowired
    private DynamoDB dynamoDB = null;

    @Autowired
    private PhotoRepository photoRepository = null;


    @Test
    void findByIdsTest() throws InterruptedException {
        //Creating dynamodb table if not already exists
        createDataTableIfNotExists("test", Photo.class);
        int size = 5;
        //creating dummy entries for test and persisting and collecting it to
        //validate with results
        List<Photo> photos = IntStream.range(0, size)
                .mapToObj(e -> UUID.randomUUID().toString())
                .map(id ->
                        Photo.builder()
                                .id(id)
                                .title("Dummy title")
                                .url("http://photos.info/" + id)
                                .thumbnailUrl("http://photos.info/thumbnails/" + id)
                                .build()
                ).peek(dynamoDBMapper::save)
                .collect(Collectors.toList());

        //calling findByIds with the Collection of HashKey ids (Partition Key Ids)
        Set<String> photoIds = photos.stream()
                .map(Photo::getId)
                .collect(Collectors.toSet());
        List<Photo> photosResultSet = photoRepository.findByIds(photoIds);

        Assertions.assertEquals(size, photosResultSet.size());

        //validating returned photoIds with the created Ids
        Set<String> resultedPhotoIds = photosResultSet.stream()
                .map(Photo::getId)
                .collect(Collectors.toSet());
        Assertions.assertTrue(photoIds.containsAll(resultedPhotoIds));
    }

    public <T> void createDataTableIfNotExists(String tablePrefix, Class<T> clazz)
            throws InterruptedException {
        ListTablesRequest listTablesRequest = new ListTablesRequest();
        listTablesRequest.setExclusiveStartTableName(tablePrefix);
        TableCollection<ListTablesResult> tables = dynamoDB.listTables();
        List<String> tablesList = new ArrayList<>();
        tables.forEach((tableResult) -> {
            tablesList.add(tableResult.getTableName());
        });
        String tableName = dynamoDBMapper.generateCreateTableRequest(clazz).getTableName();
        if (!tablesList.contains(tableName)) {
            CreateTableRequest tableRequest = dynamoDBMapper.generateCreateTableRequest(clazz);
            tableRequest.withProvisionedThroughput(new ProvisionedThroughput(5L, 5L));
            Table table = dynamoDB.createTable(tableRequest);
            table.waitForActive();
        }
    }
}

答案 4 :(得分:-1)

您可以查看BatchGetItem操作或batchLoad()的{​​{1}}方法。虽然与查询略有不同,因为它不是在散列键上具有DynamoDBMapper条件的查询,它将允许您(通常)完成相同的操作。这是language agnostic documentation,这里是Javadoc

答案 5 :(得分:-2)

Amazon API不支持多个hashkey过滤器,但您可以使用HASH KEY + RANGE KEY过滤器来使用batchGetItem方法获取结果。

http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/batch-operation-lowlevel-java.html#LowLevelJavaBatchGet