使用angularfire将用户分为n个大小组
Grouping users into n size groups using angularfire
我正在编写一个 Angular/Firebase 应用程序,其中为访问等候室页面的用户分配了一个组,一旦该组有 n
个用户,就会形成一个新组。使用 transactions
似乎是写入路径,但卡住了。
在下面的示例中,我有一个 Config
服务 returns 一个 $firebaseObject
此对象包含组大小或 playerLimit
.
angular.module('floodStudyApp')
.controller('WaitingroomCtrl', function ( $scope, $routeParams, Ref, $location, Config) {
Config.getConfig($routeParams.floodstudy).$loaded(function (config) {
$scope.floodstudyConfig = config;
var NUM_PLAYERS = config.playerLimit;
Ref.child('floodStudy/'+ config.name+ '/group' + $scope.floodstudyConfig.groups + '/players').transaction(function(playerList) {
if (playerList === null) {
playerList = {};
}
if(playerList.hasOwnProperty($routeParams.player)){
console.log("you already are here dude!");
return;
}
if(Object.keys(playerList).length % NUM_PLAYERS === 0) {
$scope.floodstudyConfig.groups++;
$scope.floodstudyConfig.$save();
}
playerList[$routeParams.player] = {
name: $routeParams.player,
startTime: Firebase.ServerValue.TIMESTAMP,
playerIndex: Object.keys(playerList).length+1
};
return playerList;
//}
}, function(error, committed, snapshot){
if(!error, committed){
$scope.$apply(function () {
$location.path('floodstudy/' + $routeParams.floodstudy+ '/group' + $scope.floodstudyConfig.groups + '/' + $routeParams.player);
});
}
});//end transaction
});// end get config
});
假设用户激增,我需要每个组恰好有 n
个用户。上面的代码处理了少量用户,而不是激增的用户。当被敲定时,每个组包含 2-6 个用户。将不胜感激关于如何实现这一点的任何建议。
这是浪涌后的示例输出:https://gist.github.com/shawnzam/041f4e26bc98a3f89a7b
考虑到顺序的所有原因,numeric ids fall over in distributed data 与其尝试对数组执行此操作,不如建议您使用计数器,简化并从每个 Zig 中获得很好的公正性。
建议的数据结构:
/rooms/$roomid/counter
/members/$counter_value/$memberid
更新计数器的函数:
angular.factory('updateRoomCounter', function($q, Ref) {
return function(roomId) {
return $q(function(resolve, reject) {
Ref.child('rooms/'+roomId+'/counter').transaction(function(currentValue) {
if( currentValue >= <NUM_PLAYERS> ) { return; }
return (currentValue||0)+1;
}, function(err, committed, snap) {
if( err || !committed ) {
reject(err || 'Too Many Players');
}
else {
resolve(snap.val());
}
});
});
}
});
使用计数器更新:
angular.controller(..., function(updateRoomCounter, Ref) {
function addAPlayer(roomId, userId) {
updateRoomCounter(roomId).then(function(myIndex) {
Ref.child('members/'+roomId+'/'+myIndex).set(userId, <doneFunction>, <errorFunction>);
}, function() {
// failed: try the next room?
})
}
});
强制执行结构的安全规则:
{
"rules": {
"rooms": {
"$room_id": {
"counter": {
".write": "newData.exists()",
".validate": "newData.isNumber() && (newData.val() == data.val() + 1 || !data.exists() && newData.val() == 1)"
}
}
},
"members": {
"$room_id": {
"$counter_value": {
".write": "newData.val() === auth.uid && !data.exists() && newData.exists() && $counter_value <= root.child('rooms/'+$room_id+'/counter').val()"
}
}
}
}
}
Kato 的回答是实现您的用例的好方法。我想谈谈您为什么会遇到这个问题。
Firebase 事务在混合的客户端和服务器模型上工作。您为客户端上的 transaction()
运行 编写的代码。它获取当前值作为输入和 returns 新值(如果不需要更改,则不获取任何内容)。然后将整个 "current value + new value" 包发送到 Firebase 服务器。然后 Firebase 服务器进行比较和设置。如果存储的值与您开始交易时使用的值相同,则将使用您的新值。如果当前值在此期间发生变化,您的新值将被拒绝,您的交易处理程序将再次 运行。
在您的事务处理程序中,您不只是更新当前值。你还有这个片段:
if(Object.keys(playerList).length % NUM_PLAYERS === NUM_PLAYERS) {
$scope.floodstudyConfig.groups++;
$scope.floodstudyConfig.$save();
}
由于这修改了 current/return 值之外的数据,因此它不是事务的一部分。因此,即使您 return 来自服务器交易的新值被拒绝,您也已经更新了组。
我相信我有解决办法。我不只计算每个组的玩家,还计算当前组。我在 {currentPlayers: 0, groups: 0}
.
形式的单个对象中执行此操作
更新计数器对象的函数:
angular.module('floodStudyApp')
.factory('updateGroupCounter', function($q, Ref) {
return function(floodstudy, NUM_PLAYERS) {
return $q(function(resolve, reject) {
Ref.child('floodStudyConfig/'+floodstudy+'/currentPlayers').transaction(function(currentValue) {
if(currentValue.currentPlayers >= NUM_PLAYERS ) { return {currentPlayers: 1, groups: currentValue.groups +1}; }
return ({currentPlayers: currentValue.currentPlayers+1, groups: currentValue.groups});
}, function(err, committed, snap) {
if( err || !committed ) {
reject(err || 'Too Many Players');
}
else {
resolve(snap.val());
}
});
});
}
});
更新计数器对象的函数:
angular.module('floodStudyApp')
.controller('WaitingroomCtrl', function ( $scope, $routeParams, Ref, $location, Config, updateGroupCounter) {
function addAPlayer(roomId, userId, NUM_Player) {
updateGroupCounter(roomId, NUM_Player).then(function(currentConfig) {
Ref.child('floodstudy/' + roomId + '/group' + currentConfig.groups+ '/players/' + currentConfig.currentPlayers).set({
user: userId,
playerIndex: currentConfig.currentPlayers,
})
}, function() {
console.log("full");
})
}
Config.getConfig($routeParams.floodstudy).$loaded(function (config) {
$scope.floodstudyConfig = config;
var NUM_PLAYERS = config.playerLimit;
addAPlayer($routeParams.floodstudy, $routeParams.player, NUM_PLAYERS);
});// end get config
});
我正在编写一个 Angular/Firebase 应用程序,其中为访问等候室页面的用户分配了一个组,一旦该组有 n
个用户,就会形成一个新组。使用 transactions
似乎是写入路径,但卡住了。
在下面的示例中,我有一个 Config
服务 returns 一个 $firebaseObject
此对象包含组大小或 playerLimit
.
angular.module('floodStudyApp')
.controller('WaitingroomCtrl', function ( $scope, $routeParams, Ref, $location, Config) {
Config.getConfig($routeParams.floodstudy).$loaded(function (config) {
$scope.floodstudyConfig = config;
var NUM_PLAYERS = config.playerLimit;
Ref.child('floodStudy/'+ config.name+ '/group' + $scope.floodstudyConfig.groups + '/players').transaction(function(playerList) {
if (playerList === null) {
playerList = {};
}
if(playerList.hasOwnProperty($routeParams.player)){
console.log("you already are here dude!");
return;
}
if(Object.keys(playerList).length % NUM_PLAYERS === 0) {
$scope.floodstudyConfig.groups++;
$scope.floodstudyConfig.$save();
}
playerList[$routeParams.player] = {
name: $routeParams.player,
startTime: Firebase.ServerValue.TIMESTAMP,
playerIndex: Object.keys(playerList).length+1
};
return playerList;
//}
}, function(error, committed, snapshot){
if(!error, committed){
$scope.$apply(function () {
$location.path('floodstudy/' + $routeParams.floodstudy+ '/group' + $scope.floodstudyConfig.groups + '/' + $routeParams.player);
});
}
});//end transaction
});// end get config
});
假设用户激增,我需要每个组恰好有 n
个用户。上面的代码处理了少量用户,而不是激增的用户。当被敲定时,每个组包含 2-6 个用户。将不胜感激关于如何实现这一点的任何建议。
这是浪涌后的示例输出:https://gist.github.com/shawnzam/041f4e26bc98a3f89a7b
考虑到顺序的所有原因,numeric ids fall over in distributed data 与其尝试对数组执行此操作,不如建议您使用计数器,简化并从每个 Zig 中获得很好的公正性。
建议的数据结构:
/rooms/$roomid/counter
/members/$counter_value/$memberid
更新计数器的函数:
angular.factory('updateRoomCounter', function($q, Ref) {
return function(roomId) {
return $q(function(resolve, reject) {
Ref.child('rooms/'+roomId+'/counter').transaction(function(currentValue) {
if( currentValue >= <NUM_PLAYERS> ) { return; }
return (currentValue||0)+1;
}, function(err, committed, snap) {
if( err || !committed ) {
reject(err || 'Too Many Players');
}
else {
resolve(snap.val());
}
});
});
}
});
使用计数器更新:
angular.controller(..., function(updateRoomCounter, Ref) {
function addAPlayer(roomId, userId) {
updateRoomCounter(roomId).then(function(myIndex) {
Ref.child('members/'+roomId+'/'+myIndex).set(userId, <doneFunction>, <errorFunction>);
}, function() {
// failed: try the next room?
})
}
});
强制执行结构的安全规则:
{
"rules": {
"rooms": {
"$room_id": {
"counter": {
".write": "newData.exists()",
".validate": "newData.isNumber() && (newData.val() == data.val() + 1 || !data.exists() && newData.val() == 1)"
}
}
},
"members": {
"$room_id": {
"$counter_value": {
".write": "newData.val() === auth.uid && !data.exists() && newData.exists() && $counter_value <= root.child('rooms/'+$room_id+'/counter').val()"
}
}
}
}
}
Kato 的回答是实现您的用例的好方法。我想谈谈您为什么会遇到这个问题。
Firebase 事务在混合的客户端和服务器模型上工作。您为客户端上的 transaction()
运行 编写的代码。它获取当前值作为输入和 returns 新值(如果不需要更改,则不获取任何内容)。然后将整个 "current value + new value" 包发送到 Firebase 服务器。然后 Firebase 服务器进行比较和设置。如果存储的值与您开始交易时使用的值相同,则将使用您的新值。如果当前值在此期间发生变化,您的新值将被拒绝,您的交易处理程序将再次 运行。
在您的事务处理程序中,您不只是更新当前值。你还有这个片段:
if(Object.keys(playerList).length % NUM_PLAYERS === NUM_PLAYERS) {
$scope.floodstudyConfig.groups++;
$scope.floodstudyConfig.$save();
}
由于这修改了 current/return 值之外的数据,因此它不是事务的一部分。因此,即使您 return 来自服务器交易的新值被拒绝,您也已经更新了组。
我相信我有解决办法。我不只计算每个组的玩家,还计算当前组。我在 {currentPlayers: 0, groups: 0}
.
更新计数器对象的函数:
angular.module('floodStudyApp')
.factory('updateGroupCounter', function($q, Ref) {
return function(floodstudy, NUM_PLAYERS) {
return $q(function(resolve, reject) {
Ref.child('floodStudyConfig/'+floodstudy+'/currentPlayers').transaction(function(currentValue) {
if(currentValue.currentPlayers >= NUM_PLAYERS ) { return {currentPlayers: 1, groups: currentValue.groups +1}; }
return ({currentPlayers: currentValue.currentPlayers+1, groups: currentValue.groups});
}, function(err, committed, snap) {
if( err || !committed ) {
reject(err || 'Too Many Players');
}
else {
resolve(snap.val());
}
});
});
}
});
更新计数器对象的函数:
angular.module('floodStudyApp')
.controller('WaitingroomCtrl', function ( $scope, $routeParams, Ref, $location, Config, updateGroupCounter) {
function addAPlayer(roomId, userId, NUM_Player) {
updateGroupCounter(roomId, NUM_Player).then(function(currentConfig) {
Ref.child('floodstudy/' + roomId + '/group' + currentConfig.groups+ '/players/' + currentConfig.currentPlayers).set({
user: userId,
playerIndex: currentConfig.currentPlayers,
})
}, function() {
console.log("full");
})
}
Config.getConfig($routeParams.floodstudy).$loaded(function (config) {
$scope.floodstudyConfig = config;
var NUM_PLAYERS = config.playerLimit;
addAPlayer($routeParams.floodstudy, $routeParams.player, NUM_PLAYERS);
});// end get config
});