it-roy-ru.com

Почему сканирование DynamoDB с использованием Limit и FilterExpression не возвращает элементы, соответствующие требованиям фильтра?

Мне нужно сделать сканирование с лимитом и условием на DynamoDB. 

документы говорит:

В ответ DynamoDB возвращает все результаты сопоставления в пределах значения Limit. Например, если вы отправляете запрос или запрос сканирования со значением Limit 6 и без выражения фильтра, DynamoDB возвращает первые шесть элементов в таблице, которые соответствуют указанным ключевым условиям в запросе (или только первые шесть элементов в случай сканирования без фильтра). Если вы также предоставите значение FilterExpression, DynamoDB вернет элементы в первых шести, которые также соответствуют требованиям фильтра (количество возвращаемых результатов будет меньше или равно 6).


Код (NODEJS):

var params = {
    ExpressionAttributeNames: {"#user": "User"},
    ExpressionAttributeValues: {":user": parseInt(user.id)},
    FilterExpression: "#user = :user and attribute_not_exists(Removed)",
    Limit: 2,
    TableName: "XXXX"
};

DynamoDB.scan(params, function(err, data) {
    if (err) {
        dataToSend.message = "Unable to query. Error: " + err.message;
    } else if (data.Items.length == 0) {
        dataToSend.message = "No results were found.";
    } else {
        dataToSend.data = data.Items;
        console.log(dataToSend);
    }
});



Таблица XXXX определений:

  • Основной ключ раздела: пользователь (номер)
  • Основной ключ сортировки: идентификатор (строка)
  • ИНДЕКС:
    • Имя индекса: RemovedIndex
    • Тип: GSI
    • Ключ раздела: удален (номер)
    • Ключ сортировки: -
    • Атрибуты: ВСЕ


В приведенном выше коде, если я удалю параметр Limit, DynamoDB вернет элементы, соответствующие требованиям фильтра. Итак, условия в порядке. Но когда я сканирую с параметром Limit, результат будет пустым.

Таблица XXXX, имеет 5 предметов. Только 2 первых имеют атрибут Removed. Когда я сканирую без параметра Limit, DynamoDB возвращает 3 элемента без атрибута Removed.

Что я делаю не так?

8
Gabriel Cunha

Из документов, которые вы цитировали:

Если вы также предоставите значение FilterExpression, DynamoDB вернет элементы в первых шести которые также соответствуют требованиям фильтра

Комбинируя Limit и FilterExpression, вы приказали DynamoDB смотреть только первые два элемента в таблице и сравнивать FilterExpression с этими элементами. Ограничение в DynamoDB может сбивать с толку, потому что оно работает иначе, чем limit в выражении SQL в RDBMS.

15
Mark B

Также столкнулся с этой проблемой, я думаю, вам просто нужно будет отсканировать всю таблицу до 1 МБ

Сканирование Набор результатов сканирования ограничен 1 МБ на вызов. Вы можете использовать LastEvaluatedKey из ответа сканирования, чтобы получить больше результатов.

http://docs.aws.Amazon.com/amazondynamodb/latest/developerguide/Limits.html

1
Samuel Okpapi

Вы можете получить то, что вам нужно, используя вторичный индекс. Используя классический пример RDB, пример заказа клиента: у вас есть одна таблица для клиентов и одна для заказов. Таблица заказов имеет ключ, состоящий из клиента - HASH, заказ - RANGE. Так что если вы хотите получить последние 10 заказов, не было бы способа сделать это без сканирования

Но если вы создадите глобальный вторичный индекс по порядку «Некоторая константа» - HASH, Date RANGE и запросите его по этому индексу, они будут делать то, что вам нужно, и взимать плату только за RCU, связанные с возвращенными записями. Не требуется дорогостоящее сканирование. Обратите внимание, что запись будет стоить дороже, но в большинстве случаев чтения намного больше, чем записи. 

Теперь у вас есть исходная проблема, если вы хотите получить 10 самых больших заказов на день, превышающий 1000 долларов. Запрос вернет последние 10 заказов, а затем отфильтрует те, которые меньше 1000 долларов.

В этом случае вы можете создать вычисляемый ключ Date-OrderAmount, и запросы по этому индексу будут возвращать то, что вы хотите.

Это не так просто, как SQL, но вам нужно подумать и о шаблонах доступа в SQL. Если у вас много данных, вам нужно создать индексы в SQL, или БД с радостью выполнит сканирование таблиц от вашего имени, что снизит производительность и увеличит ваши затраты.

Обратите внимание, что все, что я предложил, нормализовано в том смысле, что существует только один источник правды. Вы не дублируете данные - вы просто переделываете их представления, чтобы получить то, что вам нужно от DynamoDB.

Имейте в виду, что CONSTANT, как HASH, ограничен пределом в 10 ГБ на раздел, так что вам придется создавать его, если у вас много активных данных. Например, в зависимости от вашего ожидаемого шаблона доступа, вы можете использовать Customer, а не константу в качестве HASH. Или используйте STreams для организации данных (или подмножеств) другими способами. 

1
Andy Brand

Небольшой взлом - итерируйте, пока не получите результаты 

lastEvaluatedKey = null;
do {

    if(lastEvaluatedKey != null) {
        // query or scan data with last evaluated key 
    } else {
        // query or scan data WITHOUT last evaluated key 
    }        

    lastEvaluatedKey == key of last item retrieved

} while(lastEvaluatedKey != null && retrievedResultSize == 0); // == 0 or < yourLimit

Если количество найденных элементов равно 0, а lastEvaluatedKey не равно нулю, это означает, что он отсканировал или запросил количество строк, соответствующих вашему пределу. (и размер результата равен нулю, потому что они не соответствуют выражению фильтра)

0
Naween Niroshan