Есть ли способ запросить несколько хеш-ключей в DynamoDB?

Есть ли способ запросить несколько хэш-ключей с помощью одного запроса в Amazon AWS SDK для Java?

Вот моя проблема; У меня есть таблица БД для статусов проектов. Хеш-ключ - это статус проекта (т. Е. Новый, назначен, обрабатывается или завершен). Ключ диапазона - это набор идентификаторов проектов. В настоящее время у меня есть настройка запроса, чтобы просто найти все проекты, перечисленные как статус (хэш) «назначено», и еще один запрос, настроенный для поиска статуса «обработка». Есть ли способ сделать это с помощью одного запроса вместо отправки нескольких запросов для каждого статуса, который мне нужно найти? Код ниже:

    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);

В общем, я хотел бы знать, можно ли исключить переменные queryAssigned и assigned и обрабатывать как assignedStatus, так и processStatus с помощью одного и того же запроса process, чтобы найти проекты, которые не являются новыми или завершенными.


person DGolberg    schedule 15.07.2013    source источник
comment
aws.typepad.com/aws/ 2013/04 /   -  person Guy    schedule 17.07.2013
comment
Извините, это даже не похоже на то, о чем я просил, и я уже знаю о вторичных индексах.   -  person DGolberg    schedule 17.07.2013
comment
Я думаю, ваша проблема предполагает, что ваша схема должна быть другой. Если вам постоянно нужно запрашивать 2 хэш-ключа, возможно, это должен быть специальный хеш-ключ сам по себе (дублирующий данные из обоих состояний).   -  person alexandroid    schedule 06.01.2017


Ответы (6)


Нет, на сегодняшний день нет возможности отправлять несколько запросов в одном запросе. Если вас беспокоит задержка, вы можете делать несколько запросов одновременно в разных потоках. Для этого потребуется такая же пропускная способность сети, как и для «двойного запроса», если бы Dynamo предлагал его (при условии, что вы делаете 2, а не сотни).

person Cory Kendall    schedule 25.07.2013
comment
Это была больше идея отправить строки один раз и позволить серверу сравнить текущий элемент, который он проверяет, с обоими, прежде чем перейти к следующему элементу для проверки. Было бы более эффективно запросить, если бы он мог это сделать, я представляю, но я думаю, мне придется подождать и посмотреть, решат ли они когда-нибудь это сделать. Надеюсь, но не ожидаю многого По крайней мере, спасибо за ответ ... Мне пока придется выполнить двойные / тройные запросы. - person DGolberg; 25.07.2013
comment
@DGolberg На самом деле, насколько мне известно, Dynamodb хранит индексы ключей диапазона для каждого ключа хеширования полностью отдельно друг от друга, возможно, даже на разных разделах / хостах. Таким образом, реализуя мультизапрос, динамо-машина должна была бы добавить накладные расходы, например, что происходит, когда 1 не удается, а 1 - нет? Что, если кто-то задушит? Что, если он будет быстрее? (какой хост ожидает и выполняет соединение данных)? и т. д. Я предполагаю, почему они этого не реализовали. Тем не менее, простота очень важна для клиентов, поэтому меня не удивит, если они когда-нибудь ее добавят. - person Cory Kendall; 25.07.2013
comment
Интересно. Я еще не очень хорошо знаком с внутренней работой DynamoDB, поэтому спасибо за информацию по этому поводу. Я обязательно обновлю это, если / когда они реализуют что-то подобное, поскольку это, безусловно, поможет ускорить работу, когда необходимы несколько запросов. - person DGolberg; 25.07.2013
comment
@CoryKendall А как насчет этого ответа? stackoverflow.com/questions/ 32100038 / При чем здесь UNION? - person TheTiger; 12.05.2016

Невозможно выполнить запрос по нескольким хеш-ключам, но с апреля 2014 года вы можете использовать QueryFilter, чтобы вы могли фильтровать по неключевым полям в дополнение к хэш-ключевым полям.

В сообщении в блоге от 24 апреля 2014 г. AWS объявила о выпуске опции «QueryFilter»:

В сегодняшнем выпуске мы расширяем эту модель за счет поддержки фильтрации запросов по неключевым атрибутам. Теперь вы можете включить QueryFilter как часть вызова функции Query. Фильтр применяется после получения на основе ключа и до того, как вам будут возвращены результаты. Подобная фильтрация может уменьшить объем данных, возвращаемых вашему приложению, а также упростить и оптимизировать ваш код.

Проверьте это там http://aws.amazon.com/blogs/aws/improved-queries-and-updates-for-dynamodb/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+AmazonWebServicesBlog+%28Amazon+Услуги+Блог%29

person Aybat    schedule 30.05.2014
comment
@AybatDuyshokov Производительность QueryFilter фильтрующей части такая же, как и при сканировании? - person Tuukka Mustonen; 08.08.2014
comment
Туукка Мустонен 1. выбрать с помощью хэш-ключей 2. отфильтровать набор результатов с помощью фильтра запроса (да, производительность этого шага такая же, как и при сканировании) 3. вернуть окончательный набор результатов. - person Aybat; 11.08.2014

Попробуйте это на C #. Думаю, в Java аналогично. UserId - это ключ hask.

        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;
person deverton    schedule 26.05.2018

Делюсь своим рабочим ответом для потомков. По состоянию на октябрь 2020 года есть способ запрашивать несколько хеш-ключей с помощью одного запроса с использованием 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();
        }
    }
}

person Prasanth Rajendran    schedule 12.10.2020
comment
Это не отвечает на вопрос OP. Если в таблице нет ключа сортировки, легко запросить несколько идентификаторов. Вопрос в том, как это сделать, если в таблице есть ключ сортировки без указания ключа сортировки в запросе. - person Ahmad Abdelghany; 25.06.2021
comment
@AhmadAbdelghany, На самом деле, если вы попробуете мой ответ, вы сможете его узнать. Не имеет отношения к таблице, имеющей ключ сортировки (ключ диапазона) или нет, вы можете получить несколько идентификаторов, используя приведенный выше ответ, и я много раз реализовал его несколько вариантов использования - person Prasanth Rajendran; 26.06.2021
comment
ну, я попробовал, и это не сработало. Это дает сообщение об ошибке, говорящее об отсутствии ключевого значения RANGE. - person Ahmad Abdelghany; 28.06.2021

Вы можете взглянуть на BatchGetItem операцию или batchLoad() метод DynamoDBMapper. Хотя он немного отличается от запроса в том смысле, что это не запрос с OR условием для хэш-ключа, он позволит вам выполнить (как правило) то же самое. Вот языковая независимая документация и Javadoc.

person rpmartz    schedule 17.02.2014
comment
Не работает. Вы ДОЛЖНЫ предоставить ключ диапазона для операции batchLoad(), если таблица содержит его или вы получаете DynamoDBMappingException. Это не дает вам просто захватить все элементы, хеш-ключи которых совпадают. Например; скажем, у вас есть список серверов. В этом списке указано, какие серверы находятся в сети / в автономном режиме / приостановлены / и т. Д. через хэш-ключ, а имя сервера - через ключ диапазона. ЕДИНСТВЕННЫЙ способ получить все серверы, перечисленные как подключенные к сети, а также перечисленные как автономные (не зная их индивидуальных имен), - это выполнить сканирование или 2 запроса. - person DGolberg; 18.02.2014

Amazon API не поддерживает фильтр с несколькими хэш-ключами, но вы можете использовать фильтр HASH KEY + RANGE KEY для получения результатов с помощью метода batchGetItem ..

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

person Vasanth Umapathy    schedule 13.03.2014
comment
Это то, что предлагалось на последнем плакате, и это не работает в данной ситуации. Вся цель запроса - найти идентификаторы, соответствующие определенному статусу (которые, кстати, также являются ключом диапазона). Запрос batchGetItem требует, чтобы вы знали как хеш-код, так и ключ диапазона. Единственная причина, по которой у меня даже есть ключ диапазона, заключается в том, что у меня может быть несколько хеш-ключей с одним и тем же значением, которые можно запросить. - person DGolberg; 17.03.2014