【问题标题】:Firebase: Checking for 'write or delete' for onWrite EventsFirebase:检查 onWrite 事件的“写入或删除”
【发布时间】:2017-11-19 23:47:25
【问题描述】:

我有以下函数来监听数据库触发器的 onWrite 事件,如下所示:

exports.sendNotifications = functions.database.ref('/events/{eventId}/registered')
        .onWrite(event => {

            ...
         });

无论删除还是添加节点,都会调用上述函数。如何检查 onWrite 事件是该特定节点的“删除”事件还是“添加”事件,以便仅在“添加”事件时调用此函数。

【问题讨论】:

    标签: javascript firebase google-cloud-functions


    【解决方案1】:

    如果您只想为添加事件触发此函数,onCreate() 触发器将是您的最佳选择。

    但是,您也可以使用Change 对象的beforeafter 属性检测它是否是onWrite() 中的添加事件:

    exports.sendNotifications = functions.database.ref('/events/{eventId}/registered')
      .onWrite((change, context) => {
        if (change.before.exists()) {
          //update event
          return null;
        } else if (!change.after.exists()) {
          //delete event
          return null;
        } else {
          //add event
          //do stuff
        }
    
      });
    

    在文档中查找更多详细信息:

    https://firebase.google.com/docs/functions/database-events#handle_event_data

    【讨论】:

    • 注意exists 不是函数。看我的回答:stackoverflow.com/a/67279160/336753
    • 这里的逻辑也是错误的。 change.before.exists 可能意味着:更新或删除。
    【解决方案2】:

    传递给函数的event 包含触发函数的位置的先前数据和新数据。有了这两条信息,就可以推断是新写入、更新还是删除。

    来自Firebase documentation for database functions

    exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite(event => {
      // Only edit data when it is first created.
      if (event.data.previous.exists()) {
        return;
      }
      // Exit when the data is deleted.
      if (!event.data.exists()) {
        return;
      }
    

    【讨论】:

    • 如果你不做任何工作,请确保你从 firebase 函数返回 null
    • @Frank van Puffelen 没有为我工作得到一个错误说cannot read property previous of undefined 如何摆脱它?。
    • @KPal 自 2017 年以来,API 似乎发生了很大变化,请参阅下面的答案:stackoverflow.com/a/67279160/336753
    【解决方案3】:

    onWrite 将在创建、更新或删除文档时触发。以下是使用 Firestore 云功能检查每个条件的方法。

    if(event.after.data == undefined){ 
      // Deleted: data before but no data after
      console.log('Deleted');
    }
    
    if(event.before.data && event.after.data){ 
      // Updated: data before and data after
      console.log('Update');
    }
    
    if(!event.before.data && event.after.data){ 
      // Created: no data before but data after
      console.log('Created');
    }
    

    【讨论】:

    • 现在不正确了。 data 是一个函数,因此第二个选项始终为真。应改为使用 exists getter。请参阅下面的答案:stackoverflow.com/a/67279160/336753
    【解决方案4】:

    自从这里的大多数答案以来,API 似乎已经发生了变化。发布当前(2021 年 5 月)适合我的版本。

    无论是Firestore 还是Realtime Database 在这两种情况下onWrite 钩子都接受change 对象,它只是一个包含两个文档snapshots 的对象:beforeafter

    可以使用exists(这是getter 属性,而不是函数)来检测创建或删除。

    此外,如果您只需要处理创建,最好使用onCreate 事件处理程序。它只获取已创建文档的快照,而不是 change 对象。

    // EITHER
    exports.sendNotifications = functions.database.ref('/events/{eventId}/registered')
      .onWrite((change, context) => {
        // Change contains before and after documents snapshots
        const { before, after } = change;
    
        // Each snapshot knows if it exists. 
        if (!before.exists) {
          const newEvent = after.data();
          console.log('Event was created', newEvent);
          return;
        }
    
        if (!after.exists) {
          const oldEvent = before.data();
          console.log('Event was deleted', oldEvent);
          return;
        }
    
        console.log('Event changed', { from: before.data(), to: after.data() });
    
      });
    
    // OR
    exports.sendNotifications = functions.database.ref('/events/{eventId}/registered')
      .onCreate((snapshot, context) => {
        const newEvent = snapshot.data();
        console.log('Event was created', newEvent); 
      });
    

    【讨论】:

    • 非常感谢您发布此更新信息。完美运行。
    • 个人英雄,感谢您提出这个问题。事情仍然存在,为什么没有人检查更改的类型?这种可能性不是更容易出错吗?
    • @DerStoffel 嗯,这是记录在案的 API - change 作为 onWrite 回调的第一个参数是一个 JSON 对象,其中 beforeafter 道具持有快照。我能想到的唯一容易出现的错误是 API 的更改(无论如何你都应该注意)或 Google 搞砸了 Firebase 平台,因为是他们将 change 参数传递给你的回调。我们都选择信任 Google。
    • 但它也表示上下文中有一个 eventType(更改类型)。 firebase.google.com/docs/reference/functions/…我想知道为什么整个网络似乎都在检查更改道具而不是那个?
    • @DerStoffel 啊,不清楚你的意思是context.eventType 属性。好吧,我想这种方式更简单,并提供相同的信息。如果不出意外,通过 rtdb 和 firestore 事件检查 exists 属性是通用的。使用eventType,您可以通过检查firebase 回调中的rtdb 事件来引入错误,反之亦然。还为事件类型添加文本常量感觉就像很多代码和更多错误空间。不过,我会使用context.eventType 进行日志记录。感谢您指出。
    【解决方案5】:

    如果您更改为 Cloud Firestore,那么它将是:

    // Only edit data when it is first created.
      if (event.data.previous.exists) {
        return;
      }
      // Exit when the data is deleted.
      if (!event.data.exists) {
        return;
      }
    

    【讨论】:

      【解决方案6】:

      看完下面的文章,在if-then语句中再添加一个对事件元素的检查有一些好处:https://firebase.googleblog.com/2017/07/cloud-functions-realtime-database.html

      exports.sendNotification = functions.database
              .ref("/messages/{messageId}").onWrite(event => {
           if (event.data.exists() && !event.data.previous.exists()) {
              // This is a new message, not a change or a delete.
              // Put code here
          }
      })
      

      如果从数据库读取失败(可能是网络错误???),那么event.data.previous.exists() 将为假。添加event.data.exists() 可确保事件元素确实具有您可以使用的数据。

      【讨论】:

        【解决方案7】:

        我在我的代码库中做了很多这样的事情,所以我写了一个你也许可以使用的助手:)

        import { Change, firestore } from 'firebase-functions';
        import { isNil } from 'lodash';
        
        export enum FirestoreWriteEventType {
          Delete,
          Create,
          Update,
          Unknown
        }
        
        /**
         * Returns what type of operation the onWrite was.
         * @param change 
         */
        const onWriteHelper = (change: Change<firestore.DocumentSnapshot>): FirestoreWriteEventType => {
          const oldData = change.before.data();
        
          const newData = change.after.data();
        
          if (isNil(newData)) {
            return FirestoreWriteEventType.Delete;
          }
        
          if (oldData && newData) {
            return FirestoreWriteEventType.Update;
          }
        
          if (!oldData && newData) {
            return FirestoreWriteEventType.Create;
          }
        
          return FirestoreWriteEventType.Unknown;
        };
        
        export default onWriteHelper;
        

        【讨论】:

          【解决方案8】:

          我会改用 .onCreate 方法,它的语法略有不同,但完成了你想要做的事情。 (请注意,在从 1.0.0 的跳转中,onWrite 和 onCreate 都已被修改)。

          试试这个:

          exports.sendNotifications = functions.database.ref('/events/{eventId}/registered')
                  .onCreate((snap, context) => {
                    const data = snap.val();
                    // ^ the value of the newly inserted data
                    // context.params contains all of the wildcard fields used in the document reference.
                   });

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-01-19
            • 1970-01-01
            • 2018-04-14
            • 1970-01-01
            • 2017-09-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多