У меня есть простой API для записи / запроса тренировок и связанных с ними упражнений, созданный с помощью AWS Lambda / API Gateway / Dynamodb. Я ожидаю, что мой вопрос в равной степени применим и к другим базам данных NoSQL, например, MongoDB.
Основная структура проста: между тренировками и упражнениями существует отношение один ко многим, поэтому с точки зрения API /workouts/{id}
возвращает тренировку в виде объекта JSON, а /workouts/{id}/exercises
возвращает принадлежащие упражнения. на тренировку. Я также хотел бы, чтобы /exercises?exerciseTypeID={exerciseTypeID}
вернул все упражнения определенного типа, например, /exercises?exerciseTypeID=4xBT
должен вернуть все беговые упражнения.
При структурировании этих данных в DynamodB кажется, что у меня есть два варианта: денормализовать, вложив упражнения в объект тренировки, как показано ниже:
Тренировка (денормализованная):
'id': '8977-9823-QbUU',
'name': 'Marathon training',
'date': '22-08-2015',
'exercises': {
'1112-0120-XaBt': {
'name': 'Warmup',
'exerciseTypeID': '4xBT', # Running
'distance': '2500', # meters
'speed': '9', # km/h
'duration': 1000 # seconds
},
'2253-4288-TKhg': {
'name': '10K practice',
'exerciseTypeID': '4xBT', # Running
'distance': '10000',
'speed': '11',
'duration': 3272 # seconds
}
}
}
Или, в качестве альтернативы, нормализуйте схему и сохраните упражнения в отдельной таблице, связанной с тренировкой с помощью workoutID
, как показано ниже:
Тренировки (нормализованные):
{
'id': '8977-9823-QbUU',
'name': 'Marathon training',
'date': '22-08-2015'
}
Упражнения (нормализованные):
{
'id': '1112-0120-XaBt',
'workoutID': '8977-9823-QbUU',
'name': 'Warmup',
'exerciseTypeID': '4xBT', # Running
'distance': 2500, # meters
'speed': 9, # km/h
'duration': 1000 # seconds
}
{
'id': '2253-4288-TKhg',
'workoutID': '8977-9823-QbUU',
'name': '10K run',
'exerciseTypeID': '4xBT', # Running
'distance': 10000, # meters
'speed': 11, # km/h
'duration': 3272 # seconds
}
Первичным запросом к упражнениям было бы получить все упражнения, связанные с конкретной тренировкой. Исходя из этого, я должен денормализовать, сохранив данные упражнения в объекте тренировки, в соответствии с первым примером, так что мне потребуется выполнить только один запрос, чтобы получить тренировку и все связанные упражнения. В качестве альтернативы, если я нормализую, тогда мне нужно будет выполнить 2 запроса в большинстве случаев, когда я получаю тренировку, поскольку необходимость видеть упражнения в тренировке будет интуитивно очень распространенным запросом, то есть нормализованный менее эффективен для этого варианта использования.
Осложнение связано с необходимостью доступа к упражнениям напрямую, независимо от тренировки (возможно, 10% частоты просмотра тренировки и ее упражнений). Похоже, что это будет медленно / дорого в денормализованной схеме. Например, если я хочу понять, как улучшилось мое время выполнения за последние 10 запусков (что я мог бы сделать с помощью вызова API /exercises?exerciseTypeID={exerciseTypeID}
), в денормализованном примере мне потребуется выполнить дорогой запрос на все тренировки, чтобы найти Exercises
с exerciseTypeID == '4xBT'
, поскольку я не могу спроецировать свой вложенный workoutTypeID
в глобальный вторичный индекс, поскольку он не является атрибутом верхнего уровня.
Я полагаю, что это не может быть особенно экстремальным или необычным сценарием - я упускаю очевидный / простой способ запроса вложенных объектов, который позволил бы мне придерживаться денормализованной схемы? Если нет, то как мне найти компромисс между очень частыми, немного более дорогими запросами к базе данных (2 запроса против 1 запроса) и очень частыми очень эффективными запросами в большинстве случаев и очень медленными в 10% случаев?