我们正在建立一个用户可以提交订单的系统。订单具有递增属性deliveryNumber
;一个新订单应该得到一个比前一个订单高一个的deliveryNumber。
Sanity 中是否对此有任何内置支持?
如果不是,我们如何才能做到这一点,而不会因竞争条件而冒着碰撞的风险?
我们正在建立一个用户可以提交订单的系统。订单具有递增属性deliveryNumber
;一个新订单应该得到一个比前一个订单高一个的deliveryNumber。
Sanity 中是否对此有任何内置支持?
如果不是,我们如何才能做到这一点,而不会因竞争条件而冒着碰撞的风险?
我刚刚重新发现了这个问题,并认为我应该更新它,因为(一段时间以来)有一种方法可以完全按照您的要求进行操作:
这是一个使用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)
})
如果你不是通过 Javascript 访问 Sanity,你可以使用HTTP API做同样的事情。
据我所知,目前还没有为每个新文档增加属性的内置支持。但是,对于只有一个递增属性的文档(或者如果递增属性都可以从单个递增属性推导出来),您可以自己实现它。
为此,我们利用了这样一个事实,即当您创建新文档时,您可以设置_id
而不是让 Sanity 为您生成它。_id
如果您尝试使用已用于另一个文档的文档来创建文档,则查询将失败。我们将其用作防止竞争条件的机制。
对于有问题的用例,我们需要做三件事:
deliveryNumber
最后创建的订单(请参阅此答案)deliveryNumber
_id
为和创建一个具有相同值的新订单deliveryNumber
如果同时下两个订单,从而尝试创建两个相同的订单,那么deliveryNumber
Sanity 处理的最后一个查询将失败,因为它与_id
第一个相同。只要它没有失败,每个订单都应该deliveryNumber
比上一个订单高一个。
如果订单是作为用户交互的结果创建的,我建议让用户知道它失败并要求他们再试一次。这种情况不会经常发生,而且几乎可以肯定不会连续发生两次。
但是,如果您必须以编程方式确保重试直到成功,我建议您在重试之前退回一个随机的时间量(每次失败时都会以指数方式增加)。
创建订单的频率越高,此解决方案的可行性就越低。对于大多数系统,我认为这根本不可能失败。
现在,可以在模式中包含initialValue
s 并允许在模式中设置自动递增值。
该功能及其工作原理有一个帖子。
让我们使用问题的示例,您想要一个交付编号,它是一个递增的数字。
因此,计算每个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
但我不确定这是否可能。
在阅读了上述所有答案后,我提供了一个简单的解决方案来自动提供文档编号。
首先,我为这样的文档编号创建一个字段
// ...
{
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)