如何在一个 'onUpdate' Cloud Function 中加载 2 个不同的 Firestore 文档?

How to load 2 different Firestore docs in one 'onUpdate' Cloud Function?

我正在尝试创建一个 "onUpdate" 函数来加载已更新的文档。然后我想使用通配符接收的数据加载另一个文档。所以总而言之,我想访问已更新的文档和同一集合中的另一个文档。

我想要:/userProfiles/{doc1}/employees/{doc2}/userProfiles/{doc1}

我可以得到它们两个,但是当我尝试使用其中一个的数据时,它不会读取以前的数据并给我一个 ReferenceError

最终目标是使用这两个文档通过 nodemailer 发送电子邮件。感谢您的帮助。


const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require('nodemailer');
admin.initializeApp();

exports.testLog = functions.firestore
  .document('/userProfiles/{doc1}/employees/{doc2}')
  .onUpdate((change, context) => {
    var info = [];
    const doc1 = context.params.doc1;
    const doc2 = context.params.doc2;

    const db = admin.firestore();

    return (
      db
        .collection("userProfiles")
        .doc(`${doc1}`)
        .get()
        .then(doc => {
          var email = doc.data().email;
          var phone = doc.data().phone;

          info.push(doc.data());

          console.log(email, phone); // sees and gets info

          return email, phone;
        }),
      db
        .collection("userProfiles")
        .doc(`${doc1}`)
        .collection(`employees`)
        .doc(`${doc2}`)
        .get()
        .then(doc => {
          info.push(doc.data());

          var Status = doc.data().Status;

          console.log(phone, `${Status}`); //phone is undefined

          if (`${Status}` === "Alarm") {
            // replace with variables from the users settings page

            console.log(`${info.phone}`); // phone is undefined

            let transporter = nodemailer.createTransport({
              host: "smtp.gmail.com",
              port: 587,
              secure: false, 
              auth: {
                user: "xxxxxx@gmail.com",
                pass: "xxxxxxxxxx"
              }
            });

            // send mail with defined transport object
            let mailOptions = {
              from: '"Fred Foo " <foo@example.com>', 
              to: `${info.phone}`,  // tried phone as well
              subject: "Hello ✔", 
              text: "216+?", 

            };

            transporter.sendMail(mailOptions, error => {
              if (error) {
                return console.log(error);
              } else {
                return console.log("message sent");
              }
            });
          }

          console.log(Status);
          // return
          return console.log("im after the if statement. No alarm triggered");
        })

        .then(message => console.log(message.sid, "success"))
        .catch(err => console.log(err))
    );
  });

所以我想在这两张图片中获取 phone 号码和状态 返回的错误:

ReferenceError: phone is not defined

有两件事并没有按照您预期的方式工作,从而导致了您的问题:

  • promise 的处理并没有真正按照您期望的方式传递数据——特别是,变量 phone 和 email 只存在于一个 promise 处理程序中,它们不是全局的在范围内,因此 phoneemail 不会沿着承诺链向下传递。

  • 您实际上不需要阅读第二个文档,因为内容是在函数本身中传递给您的。这实际上大大简化了您正在做的整体事情,并使处理第一点变得几乎微不足道,因为您可以跳过第二个数据库调用。

看看这段代码,为了清楚起见,我省略了消息传递代码,只保留了大部分日志消息:

exports.firestoreOnUpdateTest = functions.firestore
    .document('/userProfiles/{doc1}/employees/{doc2}')
    .onUpdate((change, context) => {
  // var info = []; I have removed this list, it is not necessary
  const doc1 = context.params.doc1;
  // no need to get the doc2 parameter, as we are handed the doc itself by the function call.

  const doc2content = change.after.data();

  const db = admin.firestore();

  return (
    db
      .collection("userProfiles")
      .doc(`${doc1}`)
      .get()
      .then(doc => {
        const doc1content = doc.data();
        const email = doc1content.email;
        const phone = doc1content.phone;

        console.log(email, phone); // sees and gets info

        console.log(`No need to fetch doc2, as I already have it: ${JSON.stringify(doc2content)}`);
        const Status = doc2content.Status;

        console.log(`email for user is still: ${email}`); // email is now defined
        console.log(phone, `${Status}`); // phone is now defined

        if (`${Status}` === "Alarm") {
          console.log(`${phone}`); // phone is now defined

          return console.log('message would be sent here - code omitted')
        }

        console.log(Status);

        return console.log("im after the if statement. No alarm triggered");
      })
      .catch(err => console.error(err))
  );
});

在新版本中,我们只存储触发我们的文档中的内容,包括 Status 参数。然后我们获取包含我们需要的内容的文档——在树的更高级别。一旦该文档被 returned,我们只需对其进行处理并与来自 d​​oc2 的数据相结合。现在所有字段都已定义(当然,假设数据库对象格式正确)。

如果明显的日志消息是,您的消息代码将被重新插入。

最后,info 列表我认为现在没有必要,所以我将其删除。相反,我建议您在根据现有数据构建消息本身时构建所需的内容。也就是说,您的原始代码无论如何都没有正确访问它(即,作为列表),并且可能让您更加困惑。

最后,我没有解决 Nodemailer 模块的使用问题,因为问题主要集中在未定义的字段上,但我怀疑您的原始代码可能也不完全正确——因为它也不完全正确 return 来自 sendMail() 的承诺或对该调用执行 await(并使整个函数 async),因此您需要更仔细地查看它。