首页 > 解决方案 > 如何在 Firestore 事务中实现 getOrCreate?

问题描述

我正在尝试实施报价书签服务。

当用户尝试保存报价时,我想要一个交易来检查作者是否存在(通过名称查询。如果是,则返回 authorId。如果不,则创建作者)。源+类型也是如此。作者和来源都将返回各自的 ID。在保存报价时,报价对象将使用authorIdsourceId创建。

这种情况可能吗?我检查了 Firestore.firestore().transaction 中只有 getDocument 函数,我无法使用 whereField() 进行查询。

我在某处读过,我们可以通过规则强制执行创建,并在 try/catch 中抛出错误或某种错误,其中 catch 块将执行 getDocument?

如果我无法在事务中进行查询并且只能依赖 getDocument,这是否意味着作者/源集合的 ID 必须是一个复合键,例如“userId + hash(作者/源名称)” ?

执行此类操作有什么建议?还是 Firestore 无法处理这样的用例?

总之,我正在尝试这样做(在伪代码中)......

Firestore.transaction {

  // Get or create author
  let author = Author.getOrCreate("Yoda", userId)

  // Get or create source
  let source = Source.getOrCreate("Star Wars", "film", userId)

  // Save quote
  let quote = Quote.create({
     quote: "Do or do not. There is no try", 
     authorId: author.id, 
     sourceId: source.id, 
     userId: userId
  })

}

标签: firebasegoogle-cloud-firestore

解决方案


在事务中,所有读取都应该先进行。您可以使用transaction.getAll()获取作者和来源,如果它们不存在则创建它们:

const authorsRef = db.collection('authors')
const sourcesRef = db.collection('sources')
const quotesRef = db.collection('quotes')

db.runTransaction(transaction => transaction
  .getAll(
    authorsRef.where('name', '==', 'Yoda').get(),
    sourcesRef.where('name', '==', 'Star Wars').where('type', '==', 'film').get()
  )
  .then(([ authorDoc, sourceDoc ]) => {
    let author = authorDoc
    let source = sourceDoc
    

    if (!author.exists) {
      author = authorsRef.doc()
      transaction.set(author, { /* add author fields here */ })
    }

    if (!source.exists) {
      source = sourcesRef.doc()
      transaction.set(source, { /* add source fields here */ })
    }

    transaction.set(quotesRef.doc(), {
      // add other quote fields here
      authorId: author.id,
      sourceId: source.id
    })
  })
)

推荐阅读