我正在编写一个Angular / Firebase应用程序,其中访问等候室页面的用户被分配了一个组,一旦该组有n
个用户,就会形成一个新组。使用transactions
似乎是写路径,但卡住了。
在下面的示例中,我有一个Config
服务,返回$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
答案 0 :(得分:2)
考虑到顺序的所有原因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()"
}
}
}
}
}
答案 1 :(得分:1)
Kato的回答是实施用例的好方法。我想了解你为什么要开始这个问题。
Firebase事务适用于混合的客户端和服务器模型。您为transaction()
编写的代码在客户端上运行。它将当前值作为输入并返回新值(如果不需要更改,则不返回任何值)。这整个&#34;当前值+新值&#34;然后将包发送到Firebase服务器。然后Firebase服务器执行比较和设置。如果存储的值与您启动事务的值相同,则将使用您的新值。如果当前值已更改,则会拒绝新值,并再次运行事务处理程序。
在您的交易处理程序中,您不必只更新当前值。你也有这个片段:
if(Object.keys(playerList).length % NUM_PLAYERS === NUM_PLAYERS) {
$scope.floodstudyConfig.groups++;
$scope.floodstudyConfig.$save();
}
由于这会修改当前/返回值之外的数据,因此它不是事务的一部分。因此,即使您从服务器上的事务返回的新值被拒绝,您也已经更新了该组。
答案 2 :(得分:0)
我相信我有一个解决方案。我不计算每组玩家的数量,而是计算当前组。我在{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
});