如何在 Sanity 中为每个文档递增 属性?

How to increment property per document in Sanity?

我们正在制作一个用户可以提交订单的系统。订单递增 属性 deliveryNumber;新订单的 deliveryNumber 应该比之前的订单高一个。

Sanity 对此有内置支持吗?
如果不是,我们如何才能做到这一点,而又不会因竞争条件而冒碰撞的风险?

据我所知,还没有对每个新文档递增属性的内置支持。但是,对于只有一个递增的 属性 的文档,您可以自己实现它(或者如果递增的属性都可以从一个递增的 属性 推导出)。

方法

为此,我们利用了以下事实:当您创建新文档时,您可以设置 _id 而不是让 Sanity 为您生成它。如果您尝试使用已用于另一个文档的 _id 创建文档,查询将失败。我们将其用作防止竞争条件的机制。

对于所讨论的用例,我们需要做三件事:

  1. 获取最后创建的订单的 deliveryNumber(参见
  2. 加一得到下一个deliveryNumber
  3. _iddeliveryNumber
  4. 创建一个具有相同值的新订单

如果同时下了两个订单,因此尝试创建两个具有相同 deliveryNumber 的订单,Sanity 处理的最后一个查询将失败,因为它具有相同的 _id作为第一个。只要不失败,每一个订单都应该有一个唯一的 deliveryNumber 比之前的更高。

失败时

如果订单是由于用户交互而创建的,我建议让用户知道它失败并让他们重试。这种情况不会经常发生,而且几乎肯定不会连续发生两次。

但是,如果您必须以编程方式确保您重试直到成功,我建议在重试之前先退出一段随机时间(每次失败时呈指数增长)。

生存能力

创建订单的频率越高,此解决方案的可行性就越低。对于大多数系统,我认为这根本不可能失败。

我刚刚重新发现了这个问题并认为我应该更新它,因为(现在已经有一段时间)有一种方法可以完全按照您的要求进行操作:

这是一个使用 Sanity Javascript client 的示例:

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)
  })

如果您不通过 Javascript 访问 Sanity,您可以使用 HTTP API.

来做同样的事情

现在,架构中可以有 initialValues 并且允许在架构中设置自动递增值。

该功能及其工作原理有 post

自动增加值

让我们用问题的例子,你想要一个递增的送货号。

因此计算每个 order 个文档并将其递增 1 以将其用作新文档的 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 就可以了,但我不确定这是否可行。

阅读以上所有答案后,我提出了自动提供文档编号的简单解决方案。

首先我像这样为文档编号创建一个字段

// ...
{
  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)