使用angularfire将用户分组为n个大小组

时间:2015-08-14 01:22:47

标签: angularjs firebase grouping simulation angularfire

我正在编写一个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

3 个答案:

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