Как запросить GSI DynamoDB со сложными условиями

#amazon-web-services #aws-lambda #amazon-dynamodb

Вопрос:

У меня есть таблица DynamoDB под названием «фрэнк» с одним GSI. Ключ раздела называется PK, ключ сортировки называется SK, ключ раздела GSI называется GSI1_PK, а ключ сортировки GSI называется GSI1_SK. У меня есть одна карта «данных», на которой хранятся фактические данные.

Заполненный некоторыми тестовыми данными, он выглядит так: введите описание изображения здесь

Ключ раздела GSI и ключ сортировки напрямую сопоставляются с атрибутами с одинаковыми именами в таблице.

Я могу запустить запрос partiql, чтобы получить результаты, показанные на изображении. Вот код partiql:

 select PK, SK, GSI1_PK, GSI1_SK, data from "frank"."GSI1"
where 
("GSI1_PK"='tesla')
and 
(
 (  "GSI1_SK" >= 'A_VISITOR#2021-06-01-00-00-00-000' and  "GSI1_SK" <= 'A_VISITOR#2021-06-20-23-59-59-999' )
 or
 (  "GSI1_SK" >= 'B_INTERACTION#2021-06-01-00-00-00-000'  and   "GSI1_SK" <= 'B_INTERACTION#2021-06-20-23-59-59-999' )
)
 

Обратите внимание, как код partiql ссылается на «GSI1_SK» несколько раз. Запрос partiql работает и возвращает данные, показанные на изображении. Пока все отлично.

Однако теперь я хочу перенести это в лямбда-функцию. Как мне структурировать AWS.Запрос DynamoDB.DocumentClient для выполнения именно того, что делает этот запрос partiql?

Я могу заставить это работать в моей лямбда-функции:

 const visitorStart="A_VISITOR#2021-06-01-00-00-00-000";
        const visitorEnd="A_VISITOR#2021-06-20-23-59-59-999";
        
        var params = {
          TableName: "frank",
          IndexName: "GSI1",
          KeyConditionExpression: "#GSI1_PK=:tmn AND #GSI1_SK BETWEEN :visitorStart AND :visitorEnd",
          ExpressionAttributeNames :{  "#GSI1_PK":"GSI1_PK", "#GSI1_SK":"GSI1_SK" },
          ExpressionAttributeValues: {
            ":tmn": lowerCaseTeamName,
            ":visitorStart": visitorStart,
            ":visitorEnd": visitorEnd
          }
        };
        
        const data = await documentClient.query(params).promise();
        console.log(data); 
 

Но как только я пробую более сложное составное условие, я получаю эту ошибку:

 ValidationException: Invalid operator used in KeyConditionExpression: OR
 

Вот более сложная попытка:

 const visitorStart="A_VISITOR#2021-06-01-00-00-00-000";
        const visitorEnd="A_VISITOR#2021-06-20-23-59-59-999";
        const interactionStart="B_INTERACTION#2021-06-01-00-00-00-000";
        const interactionEnd="B_INTERACTION#2021-06-20-23-59-59-999";
        
        var params = {
          TableName: "frank",
          IndexName: "GSI1",
          KeyConditionExpression: "#GSI1_PK=:tmn AND (#GSI1_SK BETWEEN :visitorStart AND :visitorEnd OR #GSI1_SK BETWEEN :interactionStart AND :interactionEnd) ",
          ExpressionAttributeNames :{  "#GSI1_PK":"GSI1_PK", "#GSI1_SK":"GSI1_SK" },
          ExpressionAttributeValues: {
            ":tmn": lowerCaseTeamName,
            ":visitorStart": visitorStart,
            ":visitorEnd": visitorEnd,
            ":interactionStart": interactionStart,
            ":interactionEnd": interactionEnd
          }
        };
        
        const data = await documentClient.query(params).promise();
        console.log(data);  
 

В документах говорится, что выражения KeyConditionExpressions не поддерживают «ИЛИ». Итак, как мне воспроизвести мой более сложный запрос partiql в Lambda с помощью AWS.DynamoDB.DocumentClient?

Ответ №1:

Если вы посмотрите документацию PartiQL для DynamoDB, они предупредят вас, что PartiQL без колебаний использует полное сканирование таблиц для получения ваших данных: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html#ql-reference.select.syntax

Чтобы гарантировать, что инструкция SELECT не приведет к сканированию всей таблицы, условие предложения WHERE должно указывать ключ раздела. Используйте оператор равенства или В.

В этих случаях PartiQL выполнит сканирование и использует a FilterExpression для фильтрации данных.

Конечно, в вашем примере вы указали ключ раздела, поэтому я бы предположил, что PartiQL выполнит запрос с ключом раздела и a FilterExpression , чтобы применить остальные условия.

Вы можете скопировать его таким образом, и в зависимости от размера ваших разделов это может работать просто отлично. Однако, если размер раздела превысит 1 МБ и большая часть данных будет отфильтрована, вам придется иметь дело с разбиением на страницы, даже если вы не получите никаких данных.

Из-за этого я бы посоветовал вам просто разделить его и выполнить каждое условие или как отдельный запрос, а также объединить данные на клиенте.

Комментарии:

1. Спасибо @peter — можно ли заставить DynamoDB точно сообщить вам, как он собирается выполнить запрос (или как он выполнил запрос). например, SQL Server может предоставить вам профиль выполнения, в котором точно указано, что он будет делать при выполнении запроса?

2. Да, я тоже думал об этом, но я ничего не смог найти по этому поводу, поэтому это всего лишь предположения, основанные на том, что написано в качестве предупреждения в документации.

Ответ №2:

К сожалению, DynamoDB не поддерживает несколько логических операций в KeyConditionExpression . Выполняемый вами запрос partiql, вероятно, выполняет полное сканирование таблицы, чтобы вернуть результаты.

Если вы хотите реплицировать запрос partiql с помощью DocumentClient, вы можете использовать эту scan операцию. Если вы хотите избежать использования scan , вы можете выполнить две отдельные query операции и объединить результаты в коде приложения.

Комментарии:

1. Спасибо, Сет — я изо всех сил стараюсь следовать модели с одной таблицей, предложенной Риком Хоулиханом, с несколькими сущностями в одной таблице и одним запросом, который затем сможет предоставить то, что требуется. Выполнение двух запросов делает все это избыточным — с таким же успехом я могу просто использовать две таблицы и избежать всех сложностей с составным ключом. Так раздражает, что у DynamoDB есть эти маленькие слабости, которые делают все, что они обещают, таким неуловимым.

2. Моделирование данных NoSQL, безусловно, может быть сложным. Я слышал, как Рик сказал что-то вроде «NoSQL-это непросто, это эффективно». Похоже, вы хорошо справляетесь со своим моделированием данных. У вас уже есть нужные данные в том же разделе. Теперь вам нужно разработать ключ сортировки, чтобы вы могли получить то, что вам нужно. Я предполагаю, что A_VISITOR и B_INTERACTION поддерживают другой шаблон доступа, возможно, вы можете перенести его в другой GSI. Это итеративный процесс, который действительно заставляет вас мыслить нестандартно. Продолжай идти!