Как увеличить свойство для каждого документа в Sanity?

Мы делаем систему, в которой пользователи могут делать заказы. Заказы имеют свойство увеличения deliveryNumber; новый заказ должен получить номер доставки на единицу больше, чем у предыдущего заказа.

Есть ли встроенная поддержка этого в Sanity?
Если нет, как мы можем этого добиться, не рискуя столкнуться с конфликтами из-за состояния гонки?


person ArneHugo    schedule 03.05.2018    source источник


Ответы (4)


Я только что заново открыл для себя этот вопрос и подумал, что мне следует обновить его, поскольку (в течение некоторого времени) был способ сделать именно то, что вы просите:

Вот пример использования клиента Sanity Javascript:

const sanityClient = require('@sanity/client')
const client = sanityClient({
  projectId: 'my-project-id',
  dataset: 'my-dataset',
  token: 'sanity-auth-token',
  useCdn: false
})

client
  .patch('order-123') // Document ID to patch
  .inc({deliveryNumber: 1}) // Increment field by 1
  .commit() // Perform patch and return a promise
  .then(updatedOrder => {
    console.log('Order delivery number', updatedOrder.deliveryNumber)
  })
  .catch(err => {
    console.error('Ouch. Update failed: ', err.message)
  })

Если вы не получаете доступ к Sanity через Javascript, вы можете сделать то же самое, используя HTTP API.

person thomax    schedule 24.04.2019

Насколько мне известно, еще нет встроенной поддержки увеличения свойств для каждого нового документа. Однако вы можете добиться этого самостоятельно для документов, имеющих только одно свойство приращения (или если все свойства приращения могут быть выведены из одного свойства приращения).

Метод

Для этого мы используем тот факт, что при создании нового документа вы можете установить _id вместо того, чтобы позволить Sanity сгенерировать его для вас. Если вы попытаетесь создать документ с _id, который уже используется для другого документа, запрос завершится ошибкой. Мы используем это как механизм защиты от условий гонки.

Для рассматриваемого варианта использования нам нужно сделать три вещи:

  1. Получите deliveryNumber последнего созданного заказа (см. этот ответ)
  2. Увеличивайте на единицу, чтобы получить следующий deliveryNumber
  3. Создайте новый заказ с тем же значением для _id и deliveryNumber

Если два заказа размещаются одновременно, так что есть попытка создать два заказа с одним и тем же deliveryNumber, последний запрос, обработанный Sanity, завершится ошибкой, потому что он имеет тот же _id, что и первый. Пока это не сработает, каждый заказ должен иметь уникальный deliveryNumber на единицу выше предыдущего.

Когда это не удается

Если заказы создаются в результате взаимодействия с пользователем, я предлагаю сообщить пользователю, что это не удалось, и попросить его повторить попытку. Это будет происходить не часто, и почти наверняка не дважды подряд.

Если, однако, вы должны программно убедиться, что вы попробуете еще раз, пока не добьетесь успеха, я предлагаю отложить случайное количество времени (которое увеличивается экспоненциально каждый раз при неудаче), прежде чем пытаться снова.

Жизнеспособность

Чем чаще создаются заказы, тем менее жизнеспособно это решение. Я полагаю, что для большинства систем это вообще маловероятно.

person ArneHugo    schedule 03.05.2018

Теперь в схеме можно иметь initialValues, что позволяет устанавливать в схеме автоматическое увеличивающееся значение.

Есть сообщение об этой функции и о том, как она работает.

Автоинкремент значения

Давайте возьмем пример вопроса, вы хотите иметь номер доставки, который является возрастающим числом.

Поэтому подсчитайте каждый order документ и увеличьте его на единицу, чтобы использовать его как initialValue для нового документа.

Схема order.js

import client from 'part:@sanity/base/client';

export default {
  title: "Order",
  name: "order",
  type: "document",
  initialValue: async () => ({
    deliveryNumber: (await client.fetch(`//groq
       count(*[_type == "order"])
    }) + 1
  }),
  fields: [
    {
      title: "Delivery number",
      name: "deliveryNumber",
      type: "number"
    },
    // other fields
  ]
}

Не уверен, есть ли более простой способ, но он работает. Было бы здорово, если бы у нас был помощник - что-то вроде autoIncrement()

Возможная реализация:

const autoIncrement = async type = (await client.fetch(`//groq
  count(*[_type == "${type}"])
}) + 1

// usage:
{
  // ...,
  initialValue: async () => ({
        deliveryNumber: await autoIncrement("order")
  }),
  // ....
}

Примечание. Было бы неплохо, если бы это работало без async / await и без передачи типа в autoIncrement, но я не уверен, возможно ли это.

person AWolf    schedule 23.11.2019
comment
Следует отметить, что initialValue работает в Studio только тогда, когда пользователь создает новый документ с помощью пользовательского интерфейса. Так что, если кто-то планирует сделать это через API, то этот ответ будет правильным решением. - person knut; 23.11.2019

Прочитав все ответы выше, я пришел к своему простому решению для автоматизации присвоения номеров документов.

Сначала я создаю такое поле для номера документа

// ...
{
  name: 'docnum',
  title: 'Document Number',
  type: 'number',
},
// ...

затем я создаю помощник initialValue внизу

import sanityClient from 'part:@sanity/base/client';

// ...

initialValue: async () => {
  const query = '*[_type == "documentName"] | order(_createdAt desc){docnum}';
  const result = await sanityClient.fetch(query);

  if (result.length > 0) {
    return { docnum: hasil[0].docnum + 1 };
  } else {
    return { docnum: 1 };
  }
},
// ...

при таком подходе мой первый документ будет иметь начальное значение docnum 1. А более поздний документ будет иметь начальное значение last + 1. Я использую новейшую версию btw (1.150.2)

person muhsalaa    schedule 22.08.2020