用firestore生成自动递增序列

After more than a year of reading and playing around on the surface with various aspects of Firebase it was time take the plunge and dive into using Firebase with my next project.

经过一年多的阅读和对Firebase各个方面的了解,现在是时候投入我的下一个项目,并开始使用Firebase了。

Firebase simplifies many of the initial starting hurdles when commencing a new project such as providing Authentication out of the box, Firestore’s CRUD operations are straight forward with its NoSQL database as a service plus many more features I’m still learning.

在开始新项目时,Firebase简化了许多初始的启动障碍,例如开箱即用地提供身份验证,Firestore的CRUD操作直接使用NoSQL数据库即服务以及我仍在学习的更多功能。

How to generate your own sequence value?

如何生成自己的序列值

I know I’m possibly approaching this with traditional SQL database mindset, but who doesn’t want to be able to generate the next sequence value when creating an order? The random sets of characters of the document ID isn’t a user friendly order identifier when emailing a customer their latest purchase.

我知道我可能会以传统SQL数据库思维方式来实现这一目标,但是谁不想在创建订单时生成下一个序列值? 通过电子邮件向客户发送其最新购买信息时,文档ID的随机字符集不是用户友好的订单标识符。

Enter FieldValue.increment(), it allows for the incrementing or decrementing values atomically on an individual document with ease, according to the documentation and blog post here.

输入FieldValue.increment() ,根据此处的文档和博客文章,它允许轻松地在单个文档上原子地递增或递减值。

How to read the incremented value and set this on another document?

如何读取增量值并将其设置在另一个文档上?

I hit a road block with FieldValue.increment(), it simply didn’t solve my use case and reading through the various features of the Firestore database there was no easy way to implement an auto-incrementing sequence value.

我使用FieldValue.increment()遇到了FieldValue.increment() ,它根本无法解决我的用例,并且通读Firestore数据库的各种功能,没有简单的方法来实现自动递增序列值。

This is where Firebases’s Cloud Functions and transaction come to the rescue. I was able to generate an auto-incrementing sequence by storing a counter’s current value in a document and then splitting each counter into their own document, thus reducing locking contention.

这就是Firebases的云功能和事务得以解决的地方。 通过将计数器的当前值存储在文档中,然后将每个计数器拆分为自己的文档,从而能够减少锁定争用,我能够生成一个自动递增序列。

让我们开始吧 (Let’s get started)

First thing first, with Firebase Cloud Functions we need to initialise the instance of the Firebase Admin SDK globally. Here are some utility functions to ensure the Admin SDK is only initialised once:

首先,使用Firebase Cloud Functions,我们需要全局初始化Firebase Admin SDK的实例。 以下是一些实用程序功能,可确保仅将Admin SDK初始化一次:

import * as admin from 'firebase-admin';


let hasInit = false;


export function initialiseDatabase(): void {
  if (!hasInit) {
    admin.initializeApp();
    hasInit = true;
  }
}


let _db: admin.firestore.Firestore;


export function getDatabase(): admin.firestore.Firestore {
  initialiseDatabase();
  return _db ?? (_db = admin.firestore());
}

Next is implementing the Cloud Function to listen to the creation of the Order documents to trigger the generation of the next order number and then updating the order itself.

接下来是实现Cloud Function,以侦听Order文档的创建以触发下一个订单编号的生成,然后更新订单本身。

import * as functions from 'firebase-functions';
import { getDatabase } from './init';
import { getIncrement } from './counter';


export const createOrder = functions.firestore
  .document('Workspaces/{workspaceId}/Orders/{orderId}')
  .onCreate(async (snapshot, context) => {
    const counters = snapshot.ref.parent.parent?.collection('Counters');


    return getDatabase().runTransaction(async (transaction) => {


      // Read any other documents here before getting next increment value.


      const nextOrderCounter = await getIncrement({
        transaction,
        path: counters,
        counterName: 'order'
      });


      // Update orderNo with sequence value
      transaction.update(snapshot.ref, { orderNo: nextOrderCounter });


      // Insert or Update other documents here ....
      const activityRef = snapshot.ref.collection('Activities').doc();


      transaction.create(activityRef, {
        type: 'audit',
        description: 'Order Created'
      });
    });
  });

Notice the usage of database runTransaction function, this is to ensure that the call to getIncrement locks the counter’s document while the reading the current value and then updating with the incremented value.

请注意数据库runTransaction函数的用法,这是为了确保对getIncrement的调用在读取当前值然后用递增的值进行更新时锁定计数器的文档。

Note:- One nuance when working with transactions, Firebase requires all read operations need to happen before any write operations so order of execution is important.

注意:-与事务打交道时,Firebase要求所有读取操作必须在任何写入操作之前进行,因此执行顺序很重要。

import * as firebase from 'firebase-admin';
import { getDatabase } from './init';


const defaultPath = 'Counters';


export interface incrementParams {
  transaction: firebase.firestore.Transaction;
  counterName: string;
  path?: firebase.firestore.CollectionReference;
  startAt?: number;
  incrementValue?: number;
}


export async function getIncrement(args: incrementParams): Promise<number> {


  let result = args.startAt ?? 1;
  const counterRef = args.path
    ? args.path.doc(args.counterName)
    : getDatabase().doc(`${defaultPath}/${args.counterName}`);


  const counterDoc = await args.transaction.get<any>(counterRef);


  if (counterDoc.exists) {
    console.log(`Counter ${args.counterName} exists`);
    const { counterValue } = counterDoc.data();
    result = counterValue + (args.incrementValue ?? 1);
    args.transaction.update(counterRef, { counterValue: result });
  } else {
    const counterValue = result;
    console.log(`Counter ${args.counterName} created with next value ${counterValue}`);
    args.transaction.create(counterRef, { counterValue });
  }


  console.log(`Counter ${args.counterName} result ${result}`);
  return result;
}

Each named counter will have their own document in a collection reducing contention for an individual counter document, i.e. a counter document for orders and another for invoices.

每个命名柜台将在集合中拥有自己的文件,从而减少了对单个柜台文件的争用,即,针对订单的柜台文件和针对发票的柜台文件。

Image for post
Firestore Counters Collection
消防处柜台集合

The getIncrement() function could also be used via a Firebase HTTP Cloud function or a Firebase Callable function, if needing the sequence value prior to creating the document into the Firestore database.

如果在将文档创建到Firestore数据库之前需要序列值,则还可以通过Firebase HTTP Cloud函数Firebase Callable函数使用getIncrement()函数。

结论 (Conclusion)

Firebase is truly an amazing database as a service allowing the developer to concentrate on delivering value without the need to build out an API layer to get started.

Firebase确实是一个了不起的数据库即服务,它使开发人员可以专注于交付价值,而无需建立API层即可上手。

This solution introduces some performance impacts with sustained writes on the counter document itself limited to 1 per second and writes to the consuming collection are limited to 500 per second — see the docs.

该解决方案引入了一些性能影响,对计数器文档本身的持续写入限制为每秒1次,而对消耗量集合的写入限制为每秒500次- 请参阅docs

Hence, a feature request extension for Firestore would be for FieldValue.increment to take a parameter for a counter name and all this additional plumbing code could be handled by Firebase itself.

因此,Firestore的功能请求扩展将是让FieldValue.increment接受一个用于计数器名称的参数,并且所有这些其他管道代码都可以由Firebase本身处理。

翻译自: https://medium.com/firebase-developers/generating-auto-incrementing-sequences-with-firestore-b51ab713c571

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值