我如何限制可以对Cloud Firestore中的某个集合执行的写入数量?

问题描述 投票:0回答:1

我正在寻找一种方法,以防止在给定期间内向(子)集合写入超出给定限制的文档。

例如:信使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添加新消息。但是,两个请求可以读取,从而写入相同的索引吗?还是可以通过交易来处理?

或者有完全不同的方式来解决我的问题吗?

firebase google-cloud-firestore limit
1个回答
0
投票

我强烈反对在服务器端代码中使用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
}

剩下要做的步骤:

  • 您将希望确保对计数器字段的写访问权限,因为Messenger不能自行修改它们。
  • 如果您担心Messenger的文档太大,可能会定期清除较旧的邮件计数。我更喜欢保留这些类型的计数器,因为如果需要,它们可以提供便宜地提供一些统计信息的机会。
© www.soinside.com 2019 - 2024. All rights reserved.