需要Firebase数据结构建议

Bil*_*ill 1 firebase

我想跟踪和报告聊天室中的用户,我不确定如何最好地构建Firebase中的数据.

概况

  • 每个用户都有一个唯一的user_id
  • 聊天室总是打开和关闭
  • 每个聊天室都有自己独特的room_id
  • 用户经常进入和离开打开的聊天室
  • 如果聊天室关闭,则用户无法进入房间
  • 一个用户可能同时在多个聊天室中

获取数据

我们可以访问返回json的API,我计划每1分钟查看一次API查找所有聊天室(room_id),然后请求每个房间的所有用户(user_id).

设置数据

数据的设置完全在我们的控制之下

报告我希望能够得到

  • 我们从x到y的日期和时间看到了多少个唯一身份用户
  • 从x到y日期和时间在线用于1位用户的时间

问题

  • firebase会为我记录每条记录吗?或者我是否需要将时间写入每条记录?
  • 最好使用unix Epoch还是更容易理解的日期时间?
  • 我应该如何在firebase中构建这些数据?

Abe*_*ins 8

firebase会为我记录每条记录吗?或者我是否需要将时间写入每条记录?

不,但您可以使用文档中提到的Firebase.ServerValue.TIMESTAMP .Firebase仅存储您要求存储的内容.

最好使用unix Epoch还是更容易理解的日期时间?

对所有日期时间使用Firebase.ServerValue.TIMESTAMP(这是一个Unix Epoch)(如果可能).与使用new Date().getTime()或依赖于本地计算机时间的任何其他方法相比,这确保了一致性和正确性(这通常是错误的,因此您最终会得到混乱的数据).

Unix Epochs也是整数,它与Firebase的查询功能非常兼容,特别是我们可以使用.startAt()和.endAt()从特定日期范围获取内容(我们将在下面的答案中看到).

我应该如何在firebase中构建这些数据?

您需要问的第一个问题是"我将如何使用这些数据?" Firebase不是一个很大的SQL数据库,我们可以在这里使用我们的结构,然后依靠复杂的查询来弥补我们的错误.

在Firebase中构建结构时,请确保它允许您以特定方式加载数据 .这意味着如果您知道您将要从中room_id加载数据的列表,那么您的房间结构应该基于这些ID.

对于简单的聊天室,请考虑这样的结构(我们将使用$符号来表示通配符).

{
  "rooms": {
    $room_id: {
      "users": {
        $user_id: true
      },
      "_meta": {
        closed: Boolean
      },
      "messages": {
        $message_id: {
          "user_id": $user_id,
          "text": ""
        }
      }
    }
  },
  "users": {
    $user_id: {...}
  }
}
Run Code Online (Sandbox Code Playgroud)

当使用的ID的用户abe加入一个房间room_id的room_one,我们知道,他们需要自己标记为通过设置位置聊天室的积极成员/rooms/room_one/users/abe来true.

我们加入房间的功能看起来像这样.

function joinRoom(room_id) {
  // We assume `ref` is a Firebase reference to the root of our Firebase
  var roomRef = ref.child("rooms").child(room_id);
  roomRef.child("users").child(myUserId).set(true);
  return roomRef;
}
Run Code Online (Sandbox Code Playgroud)

这是具体的.我们获得了一些信息,因为我们的数据结构合乎逻辑,我们可以轻松地假设需要编写哪些数据,而无需从Firebase加载任何数据.

这对你的情况来说还不够好,因为你也需要报告.我们将根据您的需求逐步改进我们的结构

我们从x到y的日期和时间看到了多少个唯一身份用户

假设您正在以每个房间为基础进行交谈,这是一个很容易的改变.

{
  "rooms": {
    $room_id: {
      "users": {
        $user_id: true
      },
      "users_history": {
        $push_id: {
          user_id: ...,
          timestamp: ...
        } 
      },
      "messages": {
        $message_id: {...}
      }
    }
  },
  "users": {
    $user_id: {...}
  }
}
Run Code Online (Sandbox Code Playgroud)

我们添加/users/$room_id/users_history位置.这是每次用户进入此房间的列表.我们添加了一些复杂性,所以我们的连接功能看起来像这样.

function joinRoom(room_id) {
  var roomRef = ref.child("rooms").child(room_id);
  roomRef.child("users_history").push({
    user_id: myUserId,
    timestamp: Firebase.ServerValue.TIMESTAMP
  });
  roomRef.child("users").child(myUserId).set(true);
  return roomRef;
}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以使用Firebase查询轻松报告给定时间内有多少用户进入会议室.

function roomVisitors(room_id, start_datetime, end_datetime) {
  var roomRef = ref.child("rooms").child(room_id),
      queriedRoomRef = roomRef
        .orderByChild('timestamp')
        .startAt(start_datetime.getTime())
        .endAt(end_datetime.getTime());

  // Assuming we use some ES6 promise library
  return new Promise(function (resolve, reject) {
    queriedRoomRef.once("value", function (users) {
      /* Users will be a snapshot of all people who 
         came into the room for the given range of time. */
      resolve(users.val());
    }, function (err) {
      reject(err);;
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

我们将在稍后讨论这样做是否真的"具体",但这是一般性的想法.

从x到y日期和时间在线用于1位用户的时间

我们还没有充实我们的/users/$user_id结构,但我们必须在这里做到这一点.在这种情况下,我们必须查询用户在线时间的唯一信息是他们的user_id.所以我们必须存储这些信息,/user/$user_id因为如果我们存储它,/rooms/我们将不得不为所有房间加载数据并循环通过它来查找相关的用户信息,这不是非常具体.

{
  "rooms": {
    $room_id: {
      "users": {
        $user_id: true
      },
      "users_history": {
        $push_id: {
          user_id: ...,
          timestamp: ...
        } 
      },
      "messages": {
        $message_id: {...}
      }
    }
  },
  "users": {
    $user_id: {
      "online_history": {
        $push_id: {
          "action": "", // "online" or "offline" 
          "timestamp": ... 
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以建立一个ref.onAuth(func)跟踪我们在线时间的东西.

var userRef;
ref.onAuth(function (auth) {
  if (!auth && userRef) {
    // If we haven no auth, i.e. we log out, cancel any onDisconnect's
    userRef.onDisconnect().cancel();
    // and push a record saying the user went offline
    userRef.child("online_history").push({
      action: "offline",
      timestamp: Firebase.ServerValue.TIMESTAMP
    });
  } else if (auth) {
    userRef = ref.child('users').child(auth.uid);
    // add a record that we went offline
    userRef.child('online_history').push({
      action: "online",
      timestamp: Firebase.ServerValue.TIMESTAMP
    });
    // and if the user disconnects, add a record of going offline
    userRef.child('online_history').push().onDisconnect().set({
      action: "offline",
      timestamp: Firebase.ServerValue.TIMESTAMP
    });
  }
});
Run Code Online (Sandbox Code Playgroud)

使用这种方法,我们现在可以编写一个函数来遍历在线/离线日志,并使用上面使用的相同查询方法为给定范围添加时间,但我将把它留给读者练习.

关于特异性和性能的说明

两种报告功能都不具体.当我们获得在第一个查询中访问某个房间的用户列表时,我们抓住一个填充了用户名的大对象并将所有数据向下拉,然后在客户端解析,当我们真正想要的只是一个整数时唯一身份访问者数量的值.

在这种情况下,您确实希望使用服务器端SDK来使用NodeJS工作线程.这个工作人员可以坐下来观察您的数据结构的变化,并在数据发生变化时自动汇总数据,这样您的客户就可以查看类似的位置/rooms/$room_id/_meta/analytics/uniqueVisitorsThisWeek并简单地得到一个数字10.

关键是,存储很便宜,总结和缓存这样的数据很便宜,但只有在服务器端完成.如果您不是特定的并且加载太多并尝试执行总结客户端,则会浪费CPU周期和带宽.

如果您要从Firebase将数据加载到客户端而不显示该数据,那么您应该重新设计数据结构以使其更具体.