#amazon-dynamodb
#amazon-dynamodb
Вопрос:
Я все еще пытаюсь разобраться с выбором первичного ключа в DynamoDB. Моя текущая структура следующая, где userId HASH
и sort RANGE
.
Идентификатор пользователя | сортировка | событие |
---|---|---|
1 | 2021-01-18#u2d3-f3d5-s22d-3f52 | … |
1 | 2021-01-08#f1d3-s30x-s22d-w2d3 | … |
2 | 2021-02-21#s2d2-u2d3-230s-3f52 | … |
2 | 2021-02-13#w2d3-e5d5-w2d3-3f52 | … |
1 | 2021-01-19#f2d4-f3d5-s22d-3f52 | … |
1 | 2020-12-13#f3d5-e5d5-s22d-w2d3 | … |
2 | 2020-11-11#e5d5-u2d3-s22d-0j32 | … |
Чего я хочу добиться, так это запросить все события для конкретного пользователя между датой A и датой B. Я протестировал несколько решений, которые все работают, например
- Определите ближайший общий
begins_with
для нужного мне диапазона. Если дата A равна 2019-02-01, а дата B равна 2021-01-03, то это будетuserId = 1 and begins_with (sort, 20)
, что вернет все из двадцать первого века. - Перебирайте все месяцы между датой A и датой B и выполняйте кучу небольших запросов, таких как
userId = 1 and begins_with (sort, 2021-01)
, а затем объединяйте результаты впоследствии.
Все они работают, но имеют свои недостатки. Я также немного не уверен, когда я просто усложняю ситуацию до такой степени, что сканирование может действительно стоить того. Возможность использования between
, конечно, была бы лучшим вариантом, но мне нужно поместить unique #guid
в конец ключа диапазона, чтобы сделать каждый первичный ключ уникальным.
Я неправильно подхожу к этому?
Комментарии:
1. Не работает ли между датой A и датой B ( 1 день)? Он должен выбирать те, которые основаны на порядке байтов, поэтому я бы попробовал это в первую очередь.
2. Я не могу поверить, что это было так просто и так очевидно, когда вы это слышите. По какой-то причине я предположил, что весь ключ должен быть действительной датой. Спасибо @Maurice!
3. 🙂 — Я нахожусь в процессе создания демо, я добавлю ответ через пару минут
Ответ №1:
Я создал небольшое демонстрационное приложение, чтобы показать, как это работает.
Вы можете просто использовать условие between, потому что оно использует порядок байтов для реализации условия between. Идея заключается в том, что вы используете обычную начальную дату A и преобразуете ее в строку в качестве начала диапазона. Затем вы добавляете день к своему концу, преобразуете его в строку и используете это как конец.
Скрипт создает эту таблицу (при запуске она будет выглядеть по-другому):
PK | SK
------------------------------------------------------
demo | 2021-02-26#a4d0f5f3-588a-49d9-8eaa-a3e2f9436ade
demo | 2021-02-27#92b9a41b-9fa5-4ee7-8663-7b801192d8dd
demo | 2021-02-28#e5d162ac-3bbf-417a-9ec7-4024410e1b01
demo | 2021-03-01#7752629e-dc8f-47e0-8cb6-5ed219c434b5
demo | 2021-03-02#dd89ca33-965c-4fe1-8bcc-3d5eee5d6874
demo | 2021-03-03#b696a7fc-ba17-47d5-9d19-454c19e9bccc
demo | 2021-03-04#ee30b1ce-3910-4a59-9e62-09f051b0dc72
demo | 2021-03-05#f0e2405f-6ce9-4fcb-a798-394f7a2f9490
demo | 2021-03-06#bcf76e07-7582-4fe3-8ffd-14f450e60120
demo | 2021-03-07#58d01231-a58d-4c23-b1ed-e525ba102b80
И когда я запускаю эту функцию для выбора элементов между двумя заданными датами, она возвращает результат ниже:
def select_in_date_range(pk: str, start: datetime, end: datetime):
table = boto3.resource("dynamodb").Table(TABLE_NAME)
start = start.isoformat()[:10]
end = (end timedelta(days=1)).isoformat()[:10]
print(f"Requesting all items starting at {start} and ending before {end}")
result = table.query(
KeyConditionExpression=
conditions.Key("PK").eq(pk) amp; conditions.Key("SK").between(start, end)
)
print("Got these items")
for item in result["Items"]:
print(f"PK={item['PK']}, SK={item['SK']}")
Requesting all items starting at 2021-02-27 and ending before 2021-03-04
Got these items
PK=demo, SK=2021-02-27#92b9a41b-9fa5-4ee7-8663-7b801192d8dd
PK=demo, SK=2021-02-28#e5d162ac-3bbf-417a-9ec7-4024410e1b01
PK=demo, SK=2021-03-01#7752629e-dc8f-47e0-8cb6-5ed219c434b5
PK=demo, SK=2021-03-02#dd89ca33-965c-4fe1-8bcc-3d5eee5d6874
PK=demo, SK=2021-03-03#b696a7fc-ba17-47d5-9d19-454c19e9bccc
Полный сценарий, чтобы попробовать его самостоятельно.
import uuid
from datetime import datetime, timedelta
import boto3
import boto3.dynamodb.conditions as conditions
TABLE_NAME = "sorting-test"
def create_table():
ddb = boto3.client("dynamodb")
ddb.create_table(
AttributeDefinitions=[{"AttributeName": "PK", "AttributeType": "S"}, {"AttributeName": "SK", "AttributeType": "S"}],
TableName=TABLE_NAME,
KeySchema=[{"AttributeName": "PK", "KeyType": "HASH"}, {"AttributeName": "SK", "KeyType": "RANGE"}],
BillingMode="PAY_PER_REQUEST"
)
def create_sample_data():
pk = "demo"
amount_of_events = 10
table = boto3.resource("dynamodb").Table(TABLE_NAME)
start_date = datetime.now()
increment = timedelta(days=1)
print("PK | SK")
print("------------------------------------------------------")
for i in range(amount_of_events):
date = start_date.isoformat()[:10]
unique_id = str(uuid.uuid4())
sk = f"{date}#{unique_id}"
print(f"{pk} | {sk}")
start_date = increment
table.put_item(Item={"PK": pk, "SK": sk})
def select_in_date_range(pk: str, start: datetime, end: datetime):
table = boto3.resource("dynamodb").Table(TABLE_NAME)
start = start.isoformat()[:10]
end = (end timedelta(days=1)).isoformat()[:10]
print(f"Requesting all items starting at {start} and ending before {end}")
result = table.query(
KeyConditionExpression=
conditions.Key("PK").eq(pk) amp; conditions.Key("SK").between(start, end)
)
print("Got these items")
for item in result["Items"]:
print(f"PK={item['PK']}, SK={item['SK']}")
def main():
pass
# create_table()
# create_sample_data()
start = datetime.now() timedelta(days=1)
end = datetime.now() timedelta(days=5)
select_in_date_range("demo",start, end)
if __name__ == "__main__":
main()