В протоколе HTTP есть методы POST
и PATCH
для частичного обновления. В методе PATCH
для JSON API есть RFC 5789, RFC 6902, RFC 7396.
Но у меня вопрос, специфичный для больших ресурсов, которые нужно частично обновлять с достаточно сложными условиями.
Допустим, у нас есть конечная точка API "/members"
, которая напоминает большую коллекцию с тысячами записей, где каждая запись имеет десятки полей. Таким образом, клиенты частично считывают эти значения с помощью метода "GET /members?fields=f1,f2,f3"
с разбиением на страницы, и каждый клиент читает только подмножество полей, необходимых для его текущей задачи.
Для упрощения вопроса предположим, что любая запись имеет одно обязательное поле "email"
для идентификации и несколько необязательных полей.
{
"email": "[email protected]",
"optional1": "x",
"optional2": "x",
"optional3": "x"
}
И у нас есть два клиента, которые работают параллельно, чтобы массово создавать и обновлять новые записи. Но каждый клиент хочет перезаписать только свое подмножество необязательных полей.
Таким образом, клиент А отправил бы запрос со значениями A
для массового исправления, которому было бы разрешено перезаписывать значение поля "optional1"
только потому, что это поле необходимо для его задачи. И этот клиент также укажет значение для поля "optional2"
для новых записей. А тот клиент вообще не указал поле "optional3"
.
{
"email": "[email protected]",
"optional1": "A",
"optional2": "A"
}
Пока эта запись новая, она полностью записывается в хранилище. Затем клиент Б отправит запрос со значениями B
для массового исправления, которому будет разрешено перезаписывать значение поля "optional2"
только потому, что это поле необходимо для его задачи.
{
"email": "[email protected]",
"optional1": "B",
"optional2": "B",
"optional3": "B"
}
Поскольку запись уже существует, поле "optional1"
этой записи должно остаться с тем же старым значением A
, поскольку это поле не является обязательным, значение поля "optional2"
должно быть перезаписано новым значением B
, поскольку это поле является обязательным, а значение поля "optional3"
должно получить значение B
, потому что это поле было просто пустым. Объединенный результат действий должен быть следующим.
{
"email": "[email protected]",
"optional1": "A",
"optional2": "B",
"optional3": "B"
}
Таким образом, поведение зависит от существования записи. Ребята, как бы вы реализовали этот многоразовый метод конечной точки JSON API для параллельных частичных обновлений таких коллекций со сложными условиями, которые позволили бы клиентам этого API перезаписывать только подмножество полей в записи, если эта запись уже существовала и содержала некоторые значения?
Решение RFC 6902 не подходит, поскольку обе операции add
и replace
фактически заменяют значение.
добавить — если в целевом местоположении указан член объекта, который существует, значение этого члена заменяется.
replace — операция замены заменяет значение в целевом местоположении новым значением. Объект операции ДОЛЖЕН содержать элемент значения, содержимое которого определяет замещающее значение.
Вы можете увидеть, как это соответствует Java Map.put().
Если карта ранее содержала сопоставление для ключа, старое значение заменяется указанным значением.
Но отсутствующая функция скорее соответствует Java Set.add().
Добавляет указанный элемент в этот набор, если он еще не присутствует (дополнительная операция). Более формально добавляет указанный элемент e к этому набору, если набор не содержит элемента e2 такого, что (e==null ? e2==null : e.equals(e2)). Если этот набор уже содержит элемент, вызов оставляет набор без изменений и возвращает false.
Как бы вы сделали JSON API, который позволял бы выполнять оба типа операций для массового исправления очень большой коллекции с элементами, содержащими десятки необязательных полей?
Обновлять
Спасибо @chad-jensen за идею правил приоритизации атрибутов. Упомянутый документ предлагает достаточно гибкий подход, поэтому я попытался адаптировать его под свои нужды. Я думаю, что у меня будет две операции: инициализация с более низким приоритетом и добавление или, возможно, замена с более высоким приоритетом.
Тогда в моем примере данные JSON, отправленные клиентом A и клиентом B, будут выглядеть следующим образом.
Данные, отправленные Клиентом A
, которые обновляют поле "optional2"
с более низким приоритетом и обновляют поле "optional1"
с наивысшим приоритетом.
{
"email": "[email protected]",
"initialize":
{
"optional2": "A"
},
"replace":
{
"optional1": "A"
}
}
Данные, отправленные Клиентом B
, которые обновляют поля "optional1"
и "optional3"
с более низким приоритетом и обновляют поле "optional2"
с наивысшим приоритетом.
{
"email": "[email protected]",
"initialize":
{
"optional1": "B",
"optional3": "B"
},
"replace":
{
"optional2": "B"
}
}