我正在寻找一种方法,以防止在给定期间内向(子)集合写入超出给定限制的文档。
例如:信使A不允许每24小时写超过1000条消息。
这应该在Firebase Function API端点的上下文中完成,因为它是由第三方调用的。
端点
app.post('/message', async function(req:any, res:any) {
// get the messenger's API key
const key = req.query.key
// if no key was provided, return error
if(!key) {
res.status(400).send('Please provide a valid key')
return
}
// get the messenger by the provided key
const getMessengerResult = await admin.firestore().collection('messengers')
.where('key', '==', key).limit(1).get()
// if there is no result the messenger is not valid, return error
if (getMessengerResult.empty){
res.status(400).send('Please provide a valid key')
return
}
// get the messenger from the result
const messenger = getMessengerResult.docs[0]
// TODO: check if messenger hasn't reached limit of 1000 messages per 24 hours
// get message info from the body
const title:String = req.body.title
const body: String = req.body.body
// add message
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
// send response
res.status(201).send('The notification has been created');
})
我尝试过的一件事是用下面的代码代替TODO:
:
// get the limit message and validate its timestamp
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
if(!limitMessageResult.empty) {
const limitMessage = limitMessageResult.docs[0]
const timestamp: Timestamp = limitMessage.data()['timestamp']
// create a date object for 24 hours ago
const 24HoursAgo = new Date()
24HoursAgo.setDate(24HoursAgo.getDate()-1)
if(24HoursAgo < timestamp.toDate()) {
res.status(405).send('You\'ve exceeded your messages limit, please try again later!')
return
}
}
此代码有效,但是有很大的缺点。抵销额确实跳过了1000个结果,但Firebase仍会向您收取费用!因此,每次Messenger尝试添加1条消息时,都会读取1000条以上……这非常昂贵。
因此,我需要一种更好的(更便宜的)方式来做到这一点。
我想出但尚未尝试的一件事是在邮件中添加索引/计数器字段,每增加一条邮件就增加1。然后,不要这样做:
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
我可以做类似的事情:
const limitMessageResult = await messenger.ref.collection('messages')
.where('index','==', currentIndex-1000).limit(1).get()
但是我想知道这是否是一种保存方法。例如,如果同时有多个请求,将会发生什么。我首先需要从最后一条消息中获取当前索引,并使用index + 1添加新消息。但是,两个请求可以读取,从而写入相同的索引吗?还是可以通过交易来处理?
或者有完全不同的方式来解决我的问题吗?
我强烈反对在服务器端代码中使用offset()
,这恰恰是因为它看起来像是在跳过文档,实际上是在读取和丢弃它们。
我想到的实现每日最大写入次数的最简单方法是为每个Messenger保留每日写入次数计数器,然后在他们每次写消息时进行更新。
例如,每当编写一条消息时,您都可以执行以下操作:
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
const today = new Date().toISOString().substring(0, 10); // e.g. "2020-04-11"
await messenger.ref.set({
[today]: admin.firestore.FieldValue.increment(1)
}, { merge: true })
因此,这会每天在Messenger文档中添加一个附加字段,然后它会记录Messenger在当天写的消息数。
然后您将使用此计数而不是当前的limitMessageResult
。
const messageCount = (await messenger.get()).data()[today] || 0;
if (messageCount < 1000) {
... post the message and increase the counter
}
else {
... reject the message, and return a message
}
剩下要做的步骤: