需要Firebase数据结构建议 [英] Firebase Data Structure Advice Required

查看:142
本文介绍了需要Firebase数据结构建议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想跟踪和报告聊天室中的用户,但我不确定如何最好地在Firebase中构建数据。 总体情况


  • 每个用户都有一个唯一的user_id

  • 聊天室始终是
    开始和结束

  • 每个聊天室都有其独特的room_id

  • 用户经常进入和离开聊天室

  • 如果聊天室关闭,用户无法进入房间

  • 一位用户可能同时在多个聊天室里



获取数据



我们可以访问返回json的API,我打算每隔1min轮询一次API,聊天室(room_id)然后请求每个房间的所有用户(user_id)。

设置数据



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

报告我希望能够得到




  • 多少我们从x到y日期&时间

  • 从x到y的一个用户在线所用的时间date&时间



问题




  • 为我记录每个记录?或者我需要写入每个记录的时间?

  • 最好使用unix Epoch或更易于理解的日期时间吗?我在firebase中构造这些数据?


解决方案

或者是否需要将时间写入每条记录?



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

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



对所有日期时间使用Firebase.ServerValue.TIMESTAMP(这是一个Unix Epoch)(如果可能的话)。与使用新的Date().getTime()或任何其他依赖于本地机器时间的方法相比,这确保了一致性和正确性最后会有混乱的数据)。

Unix Epochs也是整数,与Firebase的查询能力非常一致,特别是我们可以使用。 startAt() .endAt()来获取特定日期范围内的内容(如下面的答案中所示)。 p>

我应该如何在firebase中构造这个数据?



你需要问的第一个问题是我将如何消耗这些数据? Firebase不是一个大型的SQL数据库,我们可以从中获得我们的结构,然后依靠复杂的查询来弥补我们的错误。

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



考虑一个像这样的简单聊天室的结构(我们将使用 $ 表示法来表示通配符)。
$ b

{
房间:{
$ room_id: {
users:{
$ user_id:true
},
_meta:{
closed:Boolean
},
messages:{
$ message_id:{
user_id:$ user_id,
text:
}
}
}
},
users:{
$ user_id:{...}
}
}
abe 标识的用户加入一个房间并且 room_id 时, code> room_one ,我们知道他们需要通过设置将自己标记为聊天室的活动成员将位置 / rooms / room_one / users / abe 改为 true



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

 函数joinRoom(room_id){
//我们假设`ref`是对Firebase
var roomRef = ref.child(rooms)根目录的Firebase引用。孩子(ROOM_ID);
roomRef.child(users)。child(myUserId).set(true);
返回roomRef;
}

这是特定的。我们给了一些信息,因为我们的数据结构是合乎逻辑的,所以我们可以很容易地假设哪些数据需要写入,而不需要从Firebase加载任何数据。



虽然你的情况已经足够好了,因为你也想要报告。我们将根据您的需求逐步改进我们的结构。我们从x到y有多少个独特的用户?date&时间

假设您正在按房间进行交谈,这是一个简单的变化。

  {
rooms:{
$ room_id:{
users:{
$ user_id:true
},
users_history:{
$ push_id:{
user_id:...,
timestamp:...
}

messages:{
$ message_id:{...}
}
}
},
用户:{
$ user_id:{...}
}
}

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


$ b

函数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);
返回roomRef;





$ b现在我们可以很容易地报告一个房间里有多少用户可以使用 Firebase查询

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

//假设我们使用了一些ES6承诺库
返回新的Promise(function(resolve,reject){
queriedRoomRef.once(value,function(users) b $ b / *用户将是在给定时间范围内
进入房间的所有人的快照。* /
resolve(users.val());
},函数(err){
reject(err);;
});
});

$ / code>

我们将讨论这样做是否真的特定一时,但这是一般的想法。

在线花费1个用户的时间从x到y日期&我们还没有完善我们的 / users / $ user_id 结构,但是我们必须在这里做。在这种情况下,我们必须查询用户在线时间的唯一信息是他们的 user_id 。所以我们必须在 / user / $ user_id 下存储这些信息,因为如果我们把它存储在 / rooms / 我们将不得不加载所有房间的数据,并循环查找相关的用户信息,这并不是非常具体。

  {
rooms:{
$ room_id:{
users:{
$ user_id:true
$ b $ users_history $ {
$ push_id:{
user_id:...,
timestamp:...
}
} ,
messages:{
$ message_id:{...}
}
}
},
users:{
$ user_id:{
online_history:{
$ push_id:{
action:,//onlineoroffline
timestamp ...
}
}
}
}
}

现在我们可以建立一个 ref.onAuth(func) trac ks我们在线时间。
$ b

  var userRef; 
ref.onAuth(function(auth){
if(!auth&& userRef){
//如果我们没有auth,即我们注销,取消任何onDisconnect的
userRef.onDisconnect()。cancel();
//推送一条记录说用户已经离线
userRef.child(online_history)。push({
action:离线,
时间戳:Firebase.ServerValue.TIMESTAMP
});
} else if(auth){
userRef = ref.child('users')。child(auth .uid);
//添加一条离线记录
userRef.child('online_history')。push({
action:online,
timestamp:Firebase .ServerValue.TIMESTAMP
});
//如果用户断开连接,则添加脱机记录
userRef.child('online_history')。push()。onDisconnect()。 set({
action:offline,
timestamp:Firebase.ServerValue.TIMESTAMP
});
}
});

使用此方法,我们现在可以编写一个函数来循环在线/离线日志并累计时间对于给定的范围,使用上面使用的同样的查询方法,但是我将把它作为读者的练习。



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

这种情况下,您真的想要使用服务器端SDK使用NodeJS工作。此工作人员可以坐下来观察对数据结构的更改,并在数据更改时自动汇总数据,以便客户端可以查看诸如 / rooms / $ room_id / _meta / analytics / uniqueVisitorsThisWeek ,并简单地得到一个数字,如 10

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

如果您曾经从Firebase将数据加载到客户端,而不显示该数据,则应该将您的数据结构改为更加具体。 b $ b

I am wanting to track and report users in chat rooms, and I'm not sure how best to structure the data in Firebase.

General Situation

  • Users each have a unique user_id
  • The chat rooms are are always opening and closing
  • Each chat room has its own unique room_id
  • The users often enter and leave chat rooms that are open
  • If the chat room is closed users can not enter the room
  • One user might be in more than one chat room at any one time

Getting the data

We have access to the API that returns json, I plan to poll the API every 1min find all the chat rooms (room_id) then request all the users (user_id) for each room.

Setting the data

The setting of the data is totally under our control

Reporting I want to be able to get

  • How many unique users have we seen from x to y date & time
  • Time spent online for 1 user from x to y date & time

Questions

  • Will firebase time stamp each record for me? or do I need to write the time into each record?
  • Is it best using the unix Epoch or a more understandable date time?
  • How should I structure this data in firebase?

解决方案

Will firebase time stamp each record for me? or do I need to write the time into each record?

Nope, but you can use Firebase.ServerValue.TIMESTAMP as mentioned in the docs. Firebase stores only what you ask it to store.

Is it best using the unix Epoch or a more understandable date time?

Use Firebase.ServerValue.TIMESTAMP (which is a Unix Epoch) for all datetimes (if possible). This ensures consistency and correctness when compared with using new Date().getTime() or any other method which is dependent on the local machine's time (which is often wrong, so you'll end up with messed up data).

Unix Epochs are also integers which work very well with Firebase's querying abilities, specifically we can use .startAt() and .endAt() to fetch things from a specific date range (as we'll see below in the answer).

How should I structure this data in firebase?

The first question you need to ask is "how will I be consuming this data?" Firebase isn't a big SQL database where we can get our structure kind of right then lean on complex querying to make up for our mistakes.

When you build a structure in Firebase, ensure that it allows you to load your data in specifc way. This means that if you know you're going to have a list of room_ids that you'll want to load data from, then your room structure should be based around those IDs.

Consider a structure like this for a simple chat room (we'll use $ notation to indicate wild cards).

{
  "rooms": {
    $room_id: {
      "users": {
        $user_id: true
      },
      "_meta": {
        closed: Boolean
      },
      "messages": {
        $message_id: {
          "user_id": $user_id,
          "text": ""
        }
      }
    }
  },
  "users": {
    $user_id: {...}
  }
}

When a user with an id of abe joins a room with a room_id of room_one, we know that they need to mark themselves as an active member of the chat room by setting the location /rooms/room_one/users/abe to true.

Our function to join a room would look like this.

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;
}

This is being specific. We're given some information and because our data structure is logical we can easily make assumptions about what data needs to be written without loading any data from Firebase.

This isn't good enough for your situation though, since you also want reporting. We'll incrementally improve our structure based on your needs

How many unique users have we seen from x to y date & time

Assuming you're talking on a per-room basis, this is an easy change.

{
  "rooms": {
    $room_id: {
      "users": {
        $user_id: true
      },
      "users_history": {
        $push_id: {
          user_id: ...,
          timestamp: ...
        } 
      },
      "messages": {
        $message_id: {...}
      }
    }
  },
  "users": {
    $user_id: {...}
  }
}

We add the /users/$room_id/users_history location. This is a list of every time a user enters this room. We've added a bit of complexity, so our join room function would look like this.

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;
}

Now we can easily report how many users have been in a room in a given time using a Firebase Query.

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);;
    });
  });
}

We'll talk about whether or not doing this is truly "specific" in a moment, but this is the general idea.

Time spent online for 1 user from x to y date & time

We haven't fleshed out our /users/$user_id structure yet, but we'll have to do that here. In this situation the only information we'll have to look up a user's time spent online will be their user_id. So we'll have to store this information under /user/$user_id because if we stored it under /rooms/ we would have to load data for all the rooms and loop through it to find relevant user information and that's not very specific.

{
  "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": ... 
        }
      }
    }
  }
}

Now we can build a ref.onAuth(func) that tracks our time online.

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
    });
  }
});

Using this method we can now write a function to loop through the online/offline log and add up time for a given range using the same method of querying used above, but I'll leave this as an exercise for the reader.

Notes about specificity and performance

Neither of the reporting functions are specific. When we're getting a list of users who visited a room in the first query, we're grabbing a big object filled with usernames and pulling all that data down then parsing it client-side, when what we really want is just an integer value of the number of unique visitors.

This is a situation where you really want to employ a NodeJS worker using the server-side SDK. This worker can sit and watch changes to your data structure and automatically summarize data as it changes so your client can then look at a location like /rooms/$room_id/_meta/analytics/uniqueVisitorsThisWeek and simply get a number like 10.

The point is, storage is cheap, summarizing and caching data like this is cheap, but only if it's done server-side. If you're not specific and you load too much and attempt to perform summarizing client side, you'll waste CPU cycles and bandwidth.

If you're ever loading data onto a client from Firebase and not displaying that data, you should be reworking your data structure to be more specific.

这篇关于需要Firebase数据结构建议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆