首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >触发了防火墙云功能onUpdate,但没有按需要执行

触发了防火墙云功能onUpdate,但没有按需要执行
EN

Stack Overflow用户
提问于 2021-05-28 10:42:15
回答 2查看 189关注 0票数 0

我目前正在制作一个有反应前端和防火墙后端的web应用程序。这是一个当地健身房的申请,由两部分组成:

  • 当地健身房培训人员的客户应用程序
  • 当地健身房教练的申请

当地的健身房为公司提供节目。这样公司就可以订阅,公司的员工可以在当地的健身房接受培训,并使用客户端应用程序。重要的是要跟踪公司员工的个人进度以及整个进度(x公司所有员工总共损失的公斤数)。

在Firestore集合“user”中,每个用户文档都具有字段的权重。每当培训师在对特定客户进行物理评估后填写进度表时,客户的用户文档中的体重字段将被更新为新的体重。

在Firestore中,还有另一个集合“公司”,每个公司都有一个文档。我的目标是把公司员工损失的总重量放在具体的文件中。因此,每次培训师更新员工的体重时,公司文档都需要更新。我制作了一个云功能,可以监听用户文档的更新。该职能列于下:

代码语言:javascript
复制
exports.updateCompanyProgress = functions.firestore
  .document("users/{userID}")
  .onUpdate((change, context) => {

  const previousData = change.before.data();
  const data = change.after.data();

  if (previousData === data) {
    return null;
  }

  const companyRef = admin.firestore.doc(`/companies/${data.company}`);
  const newWeight = data.bodyweight;
  const oldWeight = previousData.bodyweight;
  const lostWeight = oldWeight > newWeight;
  const difference = diff(newWeight, oldWeight);
  const currentWeightLost = companyRef.data().weightLostByAllEmployees;

  if (!newWeight || difference === 0 || !oldWeight) {
    return null;
  } else {
    const newCompanyWeightLoss = calcNewCWL(
      currentWeightLost,
      difference,
      lostWeight
    );
    companyRef.update({ weightLostByAllEmployees: newCompanyWeightLoss });
  }
});

在上面的云函数中有两个简单的函数:

代码语言:javascript
复制
const diff = (a, b) => (a > b ? a - b : b - a);

const calcNewCWL = (currentWeightLost, difference, lostWeight) => {
  if (!lostWeight) {
    return currentWeightLost - difference;
  }
  return currentWeightLost + difference;
};

我已经将云功能部署到Firebase来测试它,但是我无法让它工作。只要用户文档被更新,该函数就会触发,但它不会用新的weightLostByAllEmployees值更新公司文档。这是我第一次使用Firebase云功能,所以大的改变是某种新手的错误。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-28 14:23:52

您目前的解决方案中有一些缺陷,我们可以消除。

始终检查false是否相等

您可以使用以下等式检查来确定数据是否没有更改:

代码语言:javascript
复制
if (previousData === data) {
  return null;
}

这将始终是false,因为change.before.data()change.after.data()返回的对象总是不同的实例,即使它们包含相同的数据。

公司变更从不处理。

虽然这可能是一个罕见的,也许不可能的事件,如果一个用户的公司被改变,你应该删除他们的重量从原来的公司总数,并添加到新的公司。

同样,当员工离开公司或删除他们的帐户时,您应该在onDelete处理程序中从总数中删除他们的权重。

处理浮点和

如果你不知道,浮点算术有一些小的怪癖。举个例子,0.1 + 0.2,对人类来说,答案是0.3,但是对于JavaScript和许多语言来说,答案是0.30000000000000004。有关详细信息,请参阅这个问题和线索

与其将权重作为浮点数存储在数据库中,不如考虑将其存储为整数。由于权重通常不是一个整数(例如9.81kg),所以您应该将此值乘以100 (对于2个重要数字),然后将其舍入最接近的整数。然后,当您显示它时,要么将它除以100,要么在适当的十进制符号中拼接。

代码语言:javascript
复制
const v = 1201;
console.log(v/100); // -> 12.01

const vString = String(v);
console.log(vString.slice(0,-2) + "." + vString.slice(-2) + "kg"); // -> "12.01kg"

所以对于求和,0.1 + 0.2,你会把它放大到10 + 20,结果是30

代码语言:javascript
复制
console.log(0.1 + 0.2); // -> 0.30000000000000004
console.log((0.1*100 + 0.2*100)/100); // -> 0.3

但是,这种策略本身并不是防弹的,因为一些乘法仍然会导致这些错误,比如0.14*100 = 14.0000000000000020.29*100 = 28.999999999999996。为了剔除这些值,我们将乘积的值除以。

代码语言:javascript
复制
console.log(0.01 + 0.14); // -> 0.15000000000000002
console.log((0.01*100 + 0.14*100)/100); // -> 0.15000000000000002
console.log((Math.round(0.01*100) + Math.round(0.14*100))/100) // -> 0.15

您可以使用以下方法进行比较:

代码语言:javascript
复制
const arr = Array.from({length: 100}).map((_,i)=>i/100);

console.table(arr.map((a) => arr.map((b) => a + b)));
console.table(arr.map((a) => arr.map((b) => (a*100 + b*100)/100)));
console.table(arr.map((a) => arr.map((b) => (Math.round(a*100) + Math.round(b*100))/100)));

因此,我们可以得到以下帮助函数:

代码语言:javascript
复制
function sumFloats(a,b) {
  return (Math.round(a * 100) + Math.round(b * 100)) / 100;
}

function sumFloatsForStorage(a,b) {
  return (Math.round(a * 100) + Math.round(b * 100));
}

以这种方式处理权重的主要好处是,您现在可以使用FieldValue#increment()而不是完全成熟的事务来快捷地更新值。在同一公司的两个用户发生更新冲突的罕见情况下,您可以重试增量,也可以返回完整事务。

低效数据解析

在当前代码中,您可以使用.data()在前后状态中获取函数所需的数据。但是,因为要提取用户的整个文档,所以最终解析文档中的所有字段,而不是只解析所需的-- bodyweightcompany字段。您可以使用DocumentSnapshot#get(fieldName)来完成这个任务。

代码语言:javascript
复制
const afterData = change.after.data(); // parses everything - username, email, etc.
const { bodyweight, company } = afterData;

与以下方面相比:

代码语言:javascript
复制
const bodyweight = change.after.get("bodyweight"); // parses only "bodyweight"
const company = change.after.get("company"); // parses only "company"

冗余数学

由于某些原因,您正在计算权重之间差值的绝对值,将差异符号存储为布尔值,然后将它们一起用于将更改应用到损失的总重量上。

以下几行:

代码语言:javascript
复制
const previousData = change.before.data();
const data = change.after.data();

const newWeight = data.bodyweight;
const oldWeight = previousData.bodyweight;
const lostWeight = oldWeight > newWeight;
const difference = diff(newWeight, oldWeight);
const currentWeightLost = companyRef.data().weightLostByAllEmployees;

const calcNewCWL = (currentWeightLost, difference, lostWeight) => {
  if (!lostWeight) {
    return currentWeightLost - difference;
  }
  return currentWeightLost + difference;
};

const newWeightLost = calcNewCWL(currentWeightLost, difference, lostWeight);

可代之以:

代码语言:javascript
复制
const newWeight = change.after.get("bodyweight");
const oldWeight = change.before.get("bodyweight");
const deltaWeight = newWeight - oldWeight;
const currentWeightLost = companyRef.get("weightLostByAllEmployees") || 0;

const newWeightLost = currentWeightLost + deltaWeight;

把一切都卷在一起

代码语言:javascript
复制
exports.updateCompanyProgress = functions.firestore
  .document("users/{userID}")
  .onUpdate(async (change, context) => {

  // "bodyweight" is the weight scaled up by 100
  // i.e. "9.81kg" is stored as 981
  const oldHundWeight = change.before.get("bodyweight") || 0;
  const newHundWeight = change.after.get("bodyweight") || 0;
  
  const oldCompany = change.before.get("company");
  const newCompany = change.after.get("company");
  
  const db = admin.firestore();
  
  if (oldCompany === newCompany) {
    // company unchanged
    const deltaHundWeight = newHundWeight - oldHundWeight;
    
    if (deltaHundWeight === 0) {
      return null; // no action needed
    }
    
    const companyRef = db.doc(`/companies/${newCompany}`);
    
    await companyRef.update({
      weightLostByAllEmployees: admin.firestore.FieldValue.increment(deltaHundWeight)
    });
  } else {
    // company was changed
    
    const batch = db.batch();
    
    const oldCompanyRef = db.doc(`/companies/${oldCompany}`);
    const newCompanyRef = db.doc(`/companies/${newCompany}`);
    
    // remove weight from old company
    batch.update(oldCompanyRef, {
      weightLostByAllEmployees: admin.firestore.FieldValue.increment(-oldHundWeight)
    });
    
    // add weight to new company
    batch.update(newCompanyRef, {
      weightLostByAllEmployees: admin.firestore.FieldValue.increment(newHundWeight)
    });
    
    // apply changes
    await db.batch();
  }
});

事务回退

在很少遇到写冲突的情况下,此变体返回到传统事务以重新尝试更改。

代码语言:javascript
复制
/**
 * Increments weightLostByAllEmployees in all documents atomically
 * using a transaction.
 *
 * `arrayOfCompanyRefToDeltaWeightPairs` is an array of company-increment pairs.
 */
function transactionIncrementWeightLostByAllEmployees(db, arrayOfCompanyRefToDeltaWeightPairs) {
  return db.runTransaction((transaction) => {
    // get all needed documents, then add the update for each to the transaction
    return Promise
      .all( 
        arrayOfCompanyRefToDeltaWeightPairs
          .map(([companyRef, deltaWeight]) => {
            return transaction.get(companyRef)
              .then((companyDocSnapshot) => [companyRef, deltaWeight, companyDocSnapshot])
          })
      )
      .then((arrayOfRefWeightSnapshotGroups) => {
        arrayOfRefWeightSnapshotGroups.forEach(([companyRef, deltaWeight, companyDocSnapshot]) => {
          const currentValue = companyDocSnapshot.get("weightLostByAllEmployees") || 0;
          transaction.update(companyRef, {
            weightLostByAllEmployees: currentValue + deltaWeight
          })
        });
      });
  });
}

exports.updateCompanyProgress = functions.firestore
  .document("users/{userID}")
  .onUpdate(async (change, context) => {

  // "bodyweight" is the weight scaled up by 100
  // i.e. "9.81kg" is stored as 981
  const oldHundWeight = change.before.get("bodyweight") || 0;
  const newHundWeight = change.after.get("bodyweight") || 0;
  
  const oldCompany = change.before.get("company");
  const newCompany = change.after.get("company");
  
  const db = admin.firestore();
  
  if (oldCompany === newCompany) {
    // company unchanged
    const deltaHundWeight = newHundWeight - oldHundWeight;
    
    if (deltaHundWeight === 0) {
      return null; // no action needed
    }
    
    const companyRef = db.doc(`/companies/${newCompany}`);
    
    await companyRef
      .update({
        weightLostByAllEmployees: admin.firestore.FieldValue.increment(deltaHundWeight)
      })
      .catch((error) => {
        // if an unexpected error, just rethrow it
        if (error.code !== "resource-exhausted")
          throw error;
      
        // encountered write conflict, fall back to transaction
        return transactionIncrementWeightLostByAllEmployees(db, [
          [companyRef, deltaHundWeight]
        ]);
      });
  } else {
    // company was changed
    
    const batch = db.batch();
    
    const oldCompanyRef = db.doc(`/companies/${oldCompany}`);
    const newCompanyRef = db.doc(`/companies/${newCompany}`);
    
    // remove weight from old company
    batch.update(oldCompanyRef, {
      weightLostByAllEmployees: admin.firestore.FieldValue.increment(-oldHundWeight)
    });
    
    // add weight to new company
    batch.update(newCompanyRef, {
      weightLostByAllEmployees: admin.firestore.FieldValue.increment(newHundWeight)
    });
    
    // apply changes
    await db.batch()
      .catch((error) => {
        // if an unexpected error, just rethrow it
        if (error.code !== "resource-exhausted")
          throw error;
      
        // encountered write conflict, fall back to transaction
        return transactionIncrementWeightLostByAllEmployees(db, [
          [oldCompanyRef, -oldHundWeight],
          [newCompanyRef, newHundWeight]
        ]);
      });
  }
});
票数 1
EN

Stack Overflow用户

发布于 2021-05-28 10:52:25

在云功能中有几点需要调整:

  • admin.firestore()而不是admin.firestore
  • 您不能通过执行companyRef.data()获取公司文档的数据。必须调用异步get()方法。
  • 在更新公司文档时使用事务返回此事务返回的承诺(有关此关键方面的详细信息,请参阅这里 )。

因此,下面的代码应该可以做到这一点。

注意,由于我们使用的是事务,所以实际上没有实现上面第二个要点的建议。我们使用transaction.get(companyRef)代替。

代码语言:javascript
复制
exports.updateCompanyProgress = functions.firestore
    .document("users/{userID}")
    .onUpdate((change, context) => {

        const previousData = change.before.data();
        const data = change.after.data();

        if (previousData === data) {
            return null;
        }

        // You should do admin.firestore() instead of admin.firestore
        const companyRef = admin.firestore().doc(`/companies/${data.company}`);


        const newWeight = data.bodyweight;
        const oldWeight = previousData.bodyweight;
        const lostWeight = oldWeight > newWeight;
        const difference = diff(newWeight, oldWeight);

        if (!newWeight || difference === 0 || !oldWeight) {
            return null;
        } else {
            return admin.firestore().runTransaction((transaction) => {

                return transaction.get(companyRef).then((compDoc) => {
                    if (!compDoc.exists) {
                        throw "Document does not exist!";
                    }
                    const currentWeightLost = compDoc.data().weightLostByAllEmployees;
                    const newCompanyWeightLoss = calcNewCWL(
                        currentWeightLost,
                        difference,
                        lostWeight
                    );

                    transaction.update(companyRef, { weightLostByAllEmployees: newCompanyWeightLoss });
                });
            })
        }
    });
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67737410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档