当前位置: 首页 > 知识库问答 >
问题:

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

姬选
2023-03-14

我有upvote和downvote函数,它们正在执行事务,并正确地操作我的体育球员名字数据库中的票数。

投票结果为1和-1。然后进行数学运算,将玩家的票数总计,并将其作为投票放入数据库

每次投票时,我都希望一个函数或一段代码来查看players中的所有名字,并为每个名字分配一个数字,描述他们在数据库中每个人中的排名(根据他们的投票从多到少)(即。詹姆斯有10张支持票和0张支持票(票=10),他排名第一。约翰有10张赞成票和1张反对票(票=9),排名第二。如果我投约翰一票,我应该刷新页面,看到他们是1平。这在一定程度上适用于我下面的当前代码,但是一旦我开始通过输入添加更多的名字,并且做一些向上投票、向下投票和收回我的投票,voteCount变量就会变得异常,排名也会偏离轨道。我相信有一个更容易或更好的方法来做这件事。

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 functionaly注释中。在该位置,无论何时投出有效的一票,排名代码都将运行。我也会在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个答案

公良育
2023-03-14

因此如果数据在事务解析之前发生更改,则事务可以在完成之前运行多次。这会导致作用域外的任何变量不同步(即rankcountprevplayervots)。另一个原因可能是您在OrderedPlayersRank上进行循环,并为对Transaction的每次调用返回一个Promise。这将导致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)
      })
  ])
})

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

 类似资料:
  • 我想通过像delete*这样的查询删除整个节点,其中user_id=“-KTruPWrYO9WFj-TF8Ft”如何在firebase上实现?

  • 如果子“contacts”的值为“9aIMkiMa0bSuMLjUk3R5bLpnoQS2”,我想从子“Group”中检索文本“Private Group”。我还想检查父子“问题帖子”的其他子节点是否具有相同的值。我一直在努力,但没有成功。这是我一直在使用的代码,但它只产生了错误。

  • 问题内容: 我正在尝试读取xml文件,例如: 这是我到目前为止的代码: 这是我尝试编写此代码的尝试,怎么说都不成功,这就是我开始赏金的原因。这是http://pastebin.com/huKP4KED。 赏金更新: 我确实真的尝试了好几天,但现在没想到会这么难,我会接受有用的链接/书籍/教程,但更喜欢代码,因为我昨天需要这样做。 这是我需要的: 关于上面的xml: 我需要获取标题ID的值 temp

  • 我想到了这个结构来将数据写入Firebase服务器。然而,我在实际代码中有复杂的情况,以这种方式编写结构。 null 这是我试图更新节点的函数。我在这里的思考过程是--我首先将计划标题和UID设置为要创建的自动ID。然后,我尝试引用auto ID创建的“键”,然后尝试存储all_images和all_dates,并使用对childByAutoID调用创建的相同键的引用来存储,然后更新这些值。 对不

  • 我想在Android Studio中获取isSeen节点的值。M6F2mJjQ8uGPXAynZzV是消息的id,并将消息更改为消息。我想在适配器类中获得这个值。

  • 我想递归地连接一个节点的参数值和它的父节点的相同参数值。 例如,如下: 应该成为 我试过了 有什么问题吗?