Сводка
При запросе ключа двоичного диапазона с использованием begins_with
некоторые результаты не возвращаются, даже если они начинаются с запрашиваемого значения. Похоже, что это происходит только с определенными значениями, и только в локальном DynamoDB, а не в AWS размещенной версии DynamoDB.
Подробности
У меня есть таблица DynamoDB со следующей схемой:
user_id - Primary Key - binary - Contains 16 byte UUID
project_id_item_id - Sort Key - binary - 32 bytes - two UUIDs concatinated
Во время выполнения моих модульных тестов локально с использованием dynamodb-local docker image Я наблюдал странное поведение
Я вставил 20 элементов в свою таблицу например:
table.put_item(
Item={
'user_id': user_id.bytes,
'project_id_item_id': project_id.bytes + item_id.bytes
}
)
Каждый элемент имеет одинаковые user_id
и одинаковые project_id
с разными item_id
.
Когда я пытаюсь запросить те же данные обратно, иногда (возможно, 1 из 5 раз, когда я запускаю тест), я получаю только некоторые элементы обратно:
table.query(
KeyConditionExpression=
Key('user_id').eq(user_id.bytes) &
Key('project_id_item_id').begins_with(project_id.bytes))
)
# Only returns 14 items
Если я отбрасываю 2-е условие из выражения KeyConditionExpression, я получаю все 20 элементов.
Если я запускаю сканирование вместо запроса и использую одно и то же выражение условия, я получаю все 20 элементов
table.scan(
FilterExpression=
Key('user_id').eq(user_id.bytes) &
Key('project_id_item_id').begins_with(project_id.bytes))
)
# 20 items are returned
Если я печатаю project_id_item_id для каждого элемента в таблице, я вижу, что все они начинаются с одного и того же project_id:
[i['project_id_item_id'].value.hex() for i in table.scan()['Items']]
# Result:
|---------Project Id-----------|
['76761923aeba4edf9fccb9eeb5f80cc40604481b26c84c73b63308dd588a4df1',
'76761923aeba4edf9fccb9eeb5f80cc40ec926452c294c909befa772b86e2175',
'76761923aeba4edf9fccb9eeb5f80cc460ff943b36ec44518175525d6eb30480',
'76761923aeba4edf9fccb9eeb5f80cc464e427afe84d49a5b3f890f9d25ee73b',
'76761923aeba4edf9fccb9eeb5f80cc466f3bfd77b14479a8977d91af1a5fa01',
'76761923aeba4edf9fccb9eeb5f80cc46cd5b7dec9514714918449f8b49cbe4e',
'76761923aeba4edf9fccb9eeb5f80cc47d89f44aae584c1c9da475392cb0a085',
'76761923aeba4edf9fccb9eeb5f80cc495f85af4d1f142608fae72e23f54cbfb',
'76761923aeba4edf9fccb9eeb5f80cc496374432375a498b937dec3177d95c1a',
'76761923aeba4edf9fccb9eeb5f80cc49eba93584f964d13b09fdd7866a5e382',
'76761923aeba4edf9fccb9eeb5f80cc4a6086f1362224115b7376bc5a5ce66b8',
'76761923aeba4edf9fccb9eeb5f80cc4b5c6872aa1a84994b6f694666288b446',
'76761923aeba4edf9fccb9eeb5f80cc4be07cd547d804be4973041cfd1529734',
'76761923aeba4edf9fccb9eeb5f80cc4c48daab011c449f993f061da3746a660',
'76761923aeba4edf9fccb9eeb5f80cc4d09bc44973654f39b95a91eb3e291c68',
'76761923aeba4edf9fccb9eeb5f80cc4d0edda3d8c6643ad8e93afe2f1b518d4',
'76761923aeba4edf9fccb9eeb5f80cc4d8d1f6f4a85e47d78e2d06ec1938ee2a',
'76761923aeba4edf9fccb9eeb5f80cc4dc7323adfa35423fba15f77facb9a41b',
'76761923aeba4edf9fccb9eeb5f80cc4f948fb40873b425aa644f220cdcb5d4b',
'76761923aeba4edf9fccb9eeb5f80cc4fc7f0583f593454d92a8a266a93c6fcd']
В качестве проверки работоспособности, вот Project_id, который я использую в своем запросе:
print(project_id)
76761923-aeba-4edf-9fcc-b9eeb5f80cc4 # Matches what's returned by scan above
Наконец, самая странная часть - я могу попытаться сопоставить меньшее количество байтов идентификатора проекта и Я начинаю видеть все 20 предметов, затем ноль предметов, затем снова все 20 предметов:
hash_key = Key('hash_key').eq(hash_key)
for n in range(1,17):
short_key = project_id.bytes[:n]
range_key = Key('project_id_item_id').begins_with(short_key)
count = table.query(KeyConditionExpression=hash_key & range_key)['Count']
print("If I only query for 0x{:32} I find {} items".format(short_key.hex(), count))
Получаю меня:
If I only query for 0x76 I find 20 items
If I only query for 0x7676 I find 20 items
If I only query for 0x767619 I find 20 items
If I only query for 0x76761923 I find 20 items
If I only query for 0x76761923ae I find 20 items
If I only query for 0x76761923aeba I find 20 items
If I only query for 0x76761923aeba4e I find 20 items
If I only query for 0x76761923aeba4edf I find 0 items
If I only query for 0x76761923aeba4edf9f I find 20 items
If I only query for 0x76761923aeba4edf9fcc I find 0 items
If I only query for 0x76761923aeba4edf9fccb9 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9ee I find 0 items
If I only query for 0x76761923aeba4edf9fccb9eeb5 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f8 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f80c I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f80cc4 I find 15 items
Я совершенно ошеломлен этим паттерном. Если ключ диапазона, который я ищу, имеет длину 8, 10 или 12 байт, я не получаю совпадений. Если его длина составляет 16 байт, я получаю меньше 20, но больше 0 совпадений.
Кто-нибудь знает, что здесь происходит? В документации указано, что выражение begin_with работает с двоичными данными. Я в полном недоумении относительно того, что может пойти не так. Интересно, что делает DynamoDB-local что-то вроде внутреннего преобразования двоичных данных в строки для сравнения, и некоторые из этих двоичных шаблонов не конвертируются правильно.
Похоже, это может быть связано с UUID project_id , Если в тесте я жестко закодирую его на 76761923-aeba-4edf-9fcc-b9eeb5f80cc4
, я могу заставить его пропускать каждый раз.