监控Firebase中的子节点值并分配数字等级

时间:2018-04-18 20:27:53

标签: javascript reactjs firebase firebase-realtime-database

我有upvote和downvote函数正在执行交易并正确操作我的体育运动员名称数据库中的投票计数。

投票为1&1和#1。然后进行数学运算以计算玩家的投票数,并将其作为votes

放入数据库

db

每次投票时,我都想要一个函数或一段代码来查看players中的所有名称,并为每个名称分配一个数字,描述他们在数据库中的所有人中的排名(基于他们的投票从大多数到最少)(即詹姆斯有10个赞成票和0个投票票(votes = 10),他的等级1.约翰有10个赞成票和1个下注票(votes = 9)并且排名第2如果我upvote John,我应该刷新页面并看到它们被绑在1.这在一定程度上与我当前的代码有关,但是一旦我开始通过输入添加更多名称并做一些upvoting,downvoting和收回我的投票,voteCount变量变得非常糟糕,排名偏离正轨。我确信这样做更容易或更好。

orderedPlayersRank是一个数组,用votes对玩家进行排序,其中最好的是最后一个和最差的。所以排名第一的人应该始终位于orderedPlayersRank数组中。

global vars

let prevPlayerVotes = 0
let rankCount = 1
      //RANKING CODE    

      //orderedPlayersRank sorts players from highest votes to lowest

      orderedPlayersRank.map((player) => {
      this.database.child(player.id).transaction(function(player){
      if (player.votes >= prevPlayerVotes) {
          prevPlayerVotes = player.votes
          player.rank = rankCount
      } else if (player.votes < prevPlayerVotes) {
          rankCount++
          player.rank = rankCount
          prevPlayerVotes = player.votes
      } else {
          console.log("Rank calculation error.")
      }
      return player;
   })
})

这是我完整的upvote功能,仅供参考。我将上面的代码放在我对//ranking functionality评论的底部。在该位置,排名代码在任何有效投票时运行。我也会在downvote函数中使用相同的代码。

upvotePlayer(playerId) {
const players = this.state.players;

const orderedPlayersRank = _.orderBy(players, ['votes'], ['desc'])
if (this.state.user) {
    let ref = firebase.database().ref('/players/' + playerId + '/voters');
    ref.once('value', snap => {
        var value = snap.val()
        if (value !== null) {
            ref.child(this.uid).once('value', snap => {
                if (snap.val() === 0 || snap.val() == null) {
                    ref.child(this.uid).set(1);
                    this.database.child(playerId).transaction(function(player) {
                        if (player) {
                            player.votes++
                        }
                        return player;
                    })
                } else if (snap.val() === -1) {
                    ref.child(this.uid).set(1);

                    //Added vote balancing 
                    this.database.child(playerId).transaction(function(player) {
                        if (player) {
                            player.votes++
                                player.votes++
                        }
                        return player;
                    })
                } else if (snap.val() === 1) {
                    ref.child(this.uid).set(0);

                    //Added vote balancing
                    this.database.child(playerId).transaction(function(player) {
                        if (player) {
                            player.votes--
                        }
                        return player;
                    })
                } else {
                    console.log("Error in upvoting. snap.val(): " + snap.val())
                }
            })
        } else {
            ref.child(this.uid).set(1);
            this.alertUpVote()
            //Added vote balancing
            this.database.child(playerId).transaction(function(player) {
                if (player) {
                    player.votes++
                        console.log("Player added")
                }
                return player;
            })
        }
    });
//ranking functionality here
} else {
    this.alertNotLoggedIn()
    console.log("Must be logged in to vote.")
}
}

正如我所说,upvote功能正常。我正在寻找一些关于我正在努力争取的排名功能的建议。我感谢任何帮助,并可以提供任何其他相关代码

1 个答案:

答案 0 :(得分:1)

因此,如果数据在事务解决之前发生更改,则可以运行事务multiple times before completion。这可能导致范围之外的任何变量变得不同步(即rankCountprevPlayerVotes)。另一个原因可能是您正在循环orderedPlayersRank并为Promise的每次调用返回transaction。这将导致同时读取/修改prevPlayerRankrankCount,而不是按照我假设您的预期顺序读取/修改。

一种解决方案可能只是在列表中使用orderByChild('votes')并使用与检查前一个值配对的索引来确定显示时间的排名,或者在对投票进行更改时设置排名(通过Firebase函数,或观察者。)

实施例。 (Firebase功能)

export var rank = functions.database.ref('players/{playerId}/votes')
.onUpdate((change, context) => {
  // list by 'votes' in ascending order
  var orderedListRef = change.after.ref.root.child('players').orderByChild('votes')
  var oldVotes = change.before.val()
  var newVotes = change.after.val()
  var notChanged = 0
  var changeRank = 0
  // went higher in the list so bump every player passed by 1
  if (newVotes > oldVotes) {
    // Range: [oldVotes, newVotes]
    orderedListRef = orderedListRef.startAt(oldVotes).endAt(newVotes)
    changeRank = 1
    notChanged = newVotes
  } else {// went lower in the list so bump every player passed by -1
    // Range: [newVotes, oldVotes]
    orderedListRef = orderedListRef.startAt(newVotes).endAt(oldVotes)
    changeRank = -1
    notChanged = oldVotes
  }
  return orderedListRef.once('value')
  .then((ss) => {
    var promises = []
    var playersPassed = 0
    // IMPORTANT: must use `forEach` to ensure proper order
    ss.forEach((playerSS) => {
      if (playerSS.key === context.params.playerId) {
        return
      }
      playersPassed += 1
      if (playerSS.child('votes').val() === notChanged) {
        return
      }
      // use transaction to ensure proper number of bumps if multiple changes at once
      promises.push(playerSS.child('rank').ref.transaction((rank) => {
        return rank + changeRank
      }))
    })
    // use transaction to adjust rank by players passed
    promises.push(change.before.ref.parent.child('rank')
    .transaction((rank) => {
      return rank - playersPassed * changeRank
    }))
    return Promise.all(promises)
  })
})

初始化示例

export var initRank = functions.database.ref('players/{playerId}/votes')
.onCreate((snapshot, context) => {
  // list by 'votes' in ascending order
  return Promise.all([
    snapshot.ref.root
      .child('players')
      .orderByChild('votes')
      .startAt(snapshot.val())
      .once('value')
      .then((ss) => {
        return snapshot.ref.parent.child('rank').transaction((rank) => {
          if (rank) {
            return rank + ss.numChildren
          }
          return ss.numChildren
        })
      }),
    snapshot.ref.root
      .child('players')
      .orderByChild('votes')
      .endAt(snapshot.val()-1)
      .once('value')
      .then((ss) => {
        var promises = []
        ss.forEach((playerSS) => {
          promises.push(playerSS.child('rank').ref.transaction((rank) => {
            if (rank) {
              return rank + 1
            }
          })
        })
        return Promise.all(promises)
      })
  ])
})

通过这种方法,您需要将新创建的玩家的等级设置为最高等级。希望这有帮助!