避免客户端工作的 Firebase Cloud 功能总是值得的吗?
Firebase Cloud functions to avoid client side work is always worth?
我最近在使用 Firebase 云功能将大量工作从我的客户端委托给服务器,从而降低用户的数据成本。但最近我想知道是否值得,或者更好的数据库结构可以解决它。
我有一个社交应用程序,用户可以在其中锻炼并 post 他们的结果,您可以关注用户和各种 "typical" 社交媒体内容。好吧,当我想实现分页检索我应该在每个用户的提要上显示给他们的最后 X 次锻炼时,我的问题就出现了。
我的问题是:在 Firebase Cloud 函数上的一个常见事件触发器上,从数据库上的 1-1000(最坏情况)字段更新可能有多昂贵。它在客户端已经足够昂贵以寻求避免它并寻找更好的方式来谈论性能,即使它在客户端更昂贵?
我会看我的例子来解释它:
数据库结构
"privateUserData" : {
"user1" : {
"messagingTokens": {
"someToken": true,
"someToken2": true,
},
"accountCreationDate" : 1495819217216,
"email" : "abcd@gmail.com",
"followedBy" : {
"user2": true,
"user3": true,
},
"following" : {
"user2": true,
"user3": true,
},
"lastLogin" : 1498654134543,
"photoUrl" : "photo.png",
"username" : "Francisco Durdin Garcia"
},
},
"publicUserData": {
"user1": {
"username": "someUserName",
"followersCount": 5,
"followingCount": 1,
"photoUrl" : "someUrl"
}
...
},
"workouts" : {
"workout1" : {
"likes": {
"user1": true,
"user2": true,
...
},
"followers": {
"user1": true,
"user2": true,
...
},
"comments": {
"comment1": {
"owner": "user1",
"content": "somecomment",
"time": 1493153530311,
"replies": {
"reply1": {
"owner": "user1",
"content": "somecomment",
"time": 1493153530311,
}
}
}
}
"authorUid" : "user1",
"description" : "desc",
"points" : 63,
"time" : "00:03",
"createdAt" : 1493153530311,
"title" : "someTitle",
"workoutJson" : "workoutJsonDataHere"
}
}
为了能够执行该查询,我应该为我关注的每个用户执行单独的查询:
问题是我可以执行 "global" 查询并将其限制为仅 X 个数据快照。我可以为每个单独的查询过滤一些锻炼:
mDatabase.child("workouts").orderByChild("authorId).equalTo("userIFollow").limitToLast(10)
此查询将 return 我只应用一个过滤器 userIFollow
不可能对所有过滤器都应用,所以我有三个选择:
1.创建一个 table 来存储 usersId
和 workoutsId
之间的关系,它们可以看到 timeStamp
值。但我应该
通过 Firebase 云函数跟踪这个值,并且
显然,也许我跟随一个用户进行了数千次锻炼
云功能需要将它们全部复制到右边
参考。
这是我想走的路,但我不知道是不是
谈论客户端成本的正确方式。
2。我可以在 publicUserData
上添加一个 lastActivityTimeStamp
并通过过滤检索最后一个使用 activity 的用户的一些锻炼,也可以通过分页来增加这个查询。
3。最后,我总能从该用户那里检索所有锻炼并在客户端进行过滤,这将很昂贵,因为稍后缓存会更容易地完成所有事情。
这是我找到的解决问题的方法,我的问题仍然是 Firebase Cloud 函数使用常见触发器复制大量数据的成本和用处有多大。
从您对问题的措辞来看,您似乎熟悉 Firebase 的数据库云功能,而且 'workouts' 似乎也是您的有效负载(您不想下载的最大数据块)反复)。
我会根据 GitHub's API 的工作原理大致推荐以下方法。
先决条件
在您的 /privateUserData/{user}
数据中,您似乎有已关注用户 ID 的列表(位于 /privateUserData/{user}/following
)。为了使您的查询更简单,我建议实施由该用户编写的锻炼 ID 列表(在 /publicUserData/{user}/authorOf
之类的内容下)。
实施
我建议构建一个 HTTP Cloud Function,比如 https://FUNCTION_URL/followedWorkouts
。调用时,您将通过检查给定用户关注的人然后获取每个关注用户编写的锻炼列表并将 return 它们作为一个数组来为给定用户生成锻炼 ID 列表。要识别用户,您可以使用 GET 参数(例如 ?user=<someUserId>
或通过某种形式的身份验证)传入他们的 ID。你如何去做取决于你。
该函数应return以下(或类似)格式的数据(在本例中我使用JSON):
[{"id": "workoutId1", "lastMod": "1493153530311"}, {"id": "workoutId2", "lastMod": "1493153530521"}, ...]
id
是锻炼 ID。
lastMod
(上次修改的缩写形式)是锻炼数据的最后一次更新时间(来自 {workoutId}/lastModificationDate
)。请参阅下面的 'caching' 部分。
过滤
我还会在 Cloud Function 上实现以下 'filters':
- Since (
?since=<someTimeStamp>
): 将 return 自该时间戳以来已修改的锻炼 ID。 (假设您在某个时间 T
下载了一些信息,然后您可以将 since=T
设置为仅接收在那之后更改的锻炼。
- Max (
?max=X
): 将 return X 个最近的条目。
- Start At (
?startAt=X
): 将 return 从索引 X 开始的最新条目(我将其设为基于 1指数)。
因此,如果您想获取 10 个最近的条目,您可以调用 https://FUNCTION_URL/followedWorkouts?max=10
,它会为您提供第 1-10 个最近更新的锻炼的 ID。对于下一个 'page' 条目,您将调用 https://FUNCTION_URL/followedWorkouts?startAt=10&max=10
,这将为您提供第 11-20 个最近更新的锻炼 ID。
缓存
由于每次锻炼都是有效负载,因此下载多次没有意义。我建议缓存这些数据以防止这种情况发生。在我上面建议的响应中,字段 lastMod
(最后修改)允许您检查本地缓存的版本是否需要更新。你如何处理这件事,又取决于你。
正在扩展
如果您需要更多这样的分页提要,您可以更笼统地命名该函数,例如 https://FUNCTION_URL/feeds
并将提要类型作为参数传入 https://FUNCTION_URL/feeds?type=workouts
。您可以将其用于 followers
、following
、comments
等
如果您需要更多信息,请随时与我们联系。
我最近在使用 Firebase 云功能将大量工作从我的客户端委托给服务器,从而降低用户的数据成本。但最近我想知道是否值得,或者更好的数据库结构可以解决它。
我有一个社交应用程序,用户可以在其中锻炼并 post 他们的结果,您可以关注用户和各种 "typical" 社交媒体内容。好吧,当我想实现分页检索我应该在每个用户的提要上显示给他们的最后 X 次锻炼时,我的问题就出现了。
我的问题是:在 Firebase Cloud 函数上的一个常见事件触发器上,从数据库上的 1-1000(最坏情况)字段更新可能有多昂贵。它在客户端已经足够昂贵以寻求避免它并寻找更好的方式来谈论性能,即使它在客户端更昂贵?
我会看我的例子来解释它:
数据库结构
"privateUserData" : {
"user1" : {
"messagingTokens": {
"someToken": true,
"someToken2": true,
},
"accountCreationDate" : 1495819217216,
"email" : "abcd@gmail.com",
"followedBy" : {
"user2": true,
"user3": true,
},
"following" : {
"user2": true,
"user3": true,
},
"lastLogin" : 1498654134543,
"photoUrl" : "photo.png",
"username" : "Francisco Durdin Garcia"
},
},
"publicUserData": {
"user1": {
"username": "someUserName",
"followersCount": 5,
"followingCount": 1,
"photoUrl" : "someUrl"
}
...
},
"workouts" : {
"workout1" : {
"likes": {
"user1": true,
"user2": true,
...
},
"followers": {
"user1": true,
"user2": true,
...
},
"comments": {
"comment1": {
"owner": "user1",
"content": "somecomment",
"time": 1493153530311,
"replies": {
"reply1": {
"owner": "user1",
"content": "somecomment",
"time": 1493153530311,
}
}
}
}
"authorUid" : "user1",
"description" : "desc",
"points" : 63,
"time" : "00:03",
"createdAt" : 1493153530311,
"title" : "someTitle",
"workoutJson" : "workoutJsonDataHere"
}
}
为了能够执行该查询,我应该为我关注的每个用户执行单独的查询:
问题是我可以执行 "global" 查询并将其限制为仅 X 个数据快照。我可以为每个单独的查询过滤一些锻炼:
mDatabase.child("workouts").orderByChild("authorId).equalTo("userIFollow").limitToLast(10)
此查询将 return 我只应用一个过滤器 userIFollow
不可能对所有过滤器都应用,所以我有三个选择:
1.创建一个 table 来存储 usersId
和 workoutsId
之间的关系,它们可以看到 timeStamp
值。但我应该
通过 Firebase 云函数跟踪这个值,并且
显然,也许我跟随一个用户进行了数千次锻炼
云功能需要将它们全部复制到右边
参考。
这是我想走的路,但我不知道是不是 谈论客户端成本的正确方式。
2。我可以在 publicUserData
上添加一个 lastActivityTimeStamp
并通过过滤检索最后一个使用 activity 的用户的一些锻炼,也可以通过分页来增加这个查询。
3。最后,我总能从该用户那里检索所有锻炼并在客户端进行过滤,这将很昂贵,因为稍后缓存会更容易地完成所有事情。
这是我找到的解决问题的方法,我的问题仍然是 Firebase Cloud 函数使用常见触发器复制大量数据的成本和用处有多大。
从您对问题的措辞来看,您似乎熟悉 Firebase 的数据库云功能,而且 'workouts' 似乎也是您的有效负载(您不想下载的最大数据块)反复)。
我会根据 GitHub's API 的工作原理大致推荐以下方法。
先决条件
在您的 /privateUserData/{user}
数据中,您似乎有已关注用户 ID 的列表(位于 /privateUserData/{user}/following
)。为了使您的查询更简单,我建议实施由该用户编写的锻炼 ID 列表(在 /publicUserData/{user}/authorOf
之类的内容下)。
实施
我建议构建一个 HTTP Cloud Function,比如 https://FUNCTION_URL/followedWorkouts
。调用时,您将通过检查给定用户关注的人然后获取每个关注用户编写的锻炼列表并将 return 它们作为一个数组来为给定用户生成锻炼 ID 列表。要识别用户,您可以使用 GET 参数(例如 ?user=<someUserId>
或通过某种形式的身份验证)传入他们的 ID。你如何去做取决于你。
该函数应return以下(或类似)格式的数据(在本例中我使用JSON):
[{"id": "workoutId1", "lastMod": "1493153530311"}, {"id": "workoutId2", "lastMod": "1493153530521"}, ...]
id
是锻炼 ID。lastMod
(上次修改的缩写形式)是锻炼数据的最后一次更新时间(来自{workoutId}/lastModificationDate
)。请参阅下面的 'caching' 部分。
过滤
我还会在 Cloud Function 上实现以下 'filters':
- Since (
?since=<someTimeStamp>
): 将 return 自该时间戳以来已修改的锻炼 ID。 (假设您在某个时间T
下载了一些信息,然后您可以将since=T
设置为仅接收在那之后更改的锻炼。 - Max (
?max=X
): 将 return X 个最近的条目。 - Start At (
?startAt=X
): 将 return 从索引 X 开始的最新条目(我将其设为基于 1指数)。
因此,如果您想获取 10 个最近的条目,您可以调用 https://FUNCTION_URL/followedWorkouts?max=10
,它会为您提供第 1-10 个最近更新的锻炼的 ID。对于下一个 'page' 条目,您将调用 https://FUNCTION_URL/followedWorkouts?startAt=10&max=10
,这将为您提供第 11-20 个最近更新的锻炼 ID。
缓存
由于每次锻炼都是有效负载,因此下载多次没有意义。我建议缓存这些数据以防止这种情况发生。在我上面建议的响应中,字段 lastMod
(最后修改)允许您检查本地缓存的版本是否需要更新。你如何处理这件事,又取决于你。
正在扩展
如果您需要更多这样的分页提要,您可以更笼统地命名该函数,例如 https://FUNCTION_URL/feeds
并将提要类型作为参数传入 https://FUNCTION_URL/feeds?type=workouts
。您可以将其用于 followers
、following
、comments
等
如果您需要更多信息,请随时与我们联系。