#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. Это итеративный процесс, который действительно заставляет вас мыслить нестандартно. Продолжай идти!