Существуют методы POST
и PATCH
в HTTP-протоколе для частичных обновлений. В методе PATCH
для JSON API есть RFC 5789, RFC 6902, RFC 7396.
Но у меня есть вопрос, связанный с большими ресурсами, которые необходимо частично обновить с довольно сложными условиями.
Скажем, у нас есть API endpont "/members"
который похож на большую коллекцию с тысячами записей, где каждая запись содержит десятки полей. Поэтому клиенты частично читают эти значения с помощью метода "GET/members?fields=f1,f2,f3"
с разбиением на страницы, и каждый клиент читает только подмножество полей, необходимых для их текущей задачи.
Чтобы упростить вопрос, предположим, что у любой записи есть одно обязательное поле "email"
для идентификации и несколько необязательных полей.
{
"email": "[email protected]",
"optional1": "x",
"optional2": "x",
"optional3": "x"
}
И у нас есть два клиента, работающих параллельно, чтобы создавать и обновлять новые записи навалом. Но каждый клиент хочет переписать только их подмножество необязательных полей.
Таким образом, клиент A отправит запрос с значениями A
для массового патча, которому будет разрешено перезаписать значение поля "optional1"
только потому, что это поле имеет важное значение для их задачи. И этот клиент также укажет значение для поля "optional2"
для новых записей. И этот клиент вообще не указывал поле "optional3"
.
{
"email": "[email protected]",
"optional1": "A",
"optional2": "A"
}
Хотя эта запись новая, она полностью записывается в хранилище. Затем клиент B отправляет запрос с значениями 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
фактически заменяют значение.
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"
}
}
Вам потребуется какое-то сопоставление приоритетов, которое определяет, какие поля приоритеты для каждого клиента.
Кажется, что у вас уже есть это, но я собираюсь предположить, что он где-то хранится/сервер и клиент знают о том, в каком поле они имеют наивысший приоритет (приоритет). Вам также может потребоваться узнать, какой клиент обновил последнее поле.
С учетом сказанного вы должны иметь возможность сделать aa null/empty/check для каждого атрибута и только заменить значения, когда они являются значением по умолчанию (null/empty string). В сценарии, когда значение уже заполнено, вам нужно будет проверить, имеет ли входящий клиент более высокий/меньший приоритет, чем тот, кто последний раз обновлял значение.
Я нашел документ, который может прояснить, что я имею в виду (он немного разбирается в некоторых сторонних инструментах, но существует концепция): https://www.ibm.com/support/knowledgecenter/en/SSPLFC_7.2.0/UserGuide/c_cmdb_reconcilation_overview.html