スポーツ選手の名前のデータベースでトランザクションを実行し、投票数を正しく操作する賛成票と反対票の関数があります。
投票は1と-1としてキャストされます。次に、計算が行われ、プレーヤーの投票数が合計され、データベースに次のように配置されます。votes
投票が行われるたびに、関数またはコードですべての名前を調べてplayers
、データベース内の全員のランクを表す番号を各名前に割り当てます(投票の多いものから少ないものへ)(つまり、ジェームズは10票と0票(votes = 10
)、ランク1です。ジョンは10票と1票()でランク2です。ジョンに賛成したvotes = 9
場合は、ページを更新して、1で結ばれていることを確認する必要があります。以下の現在のコードで学位を取得していますが、入力を介して名前を追加し、賛成票、反対票、撤回を行うと、voteCount変数はすべて奇抜になり、ランクはコースから大きく外れます。これを行うためのより簡単な、またはより良い方法。
OrderedPlayersRankは、プレーヤーをvotes
、最初に最高、最後に最悪で並べ替える配列です。したがって、私の#1ランクの人は、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;
})
})
参考までに、これが私の完全な賛成関数です。私は上記のコードを//ranking functionality
下の方にコメントがあるところに置いています。その場所では、有効な投票が行われるたびにランキングコードが実行されます。反対票関数にも同じコードを入れます。
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.")
}
}
私が言ったように、賛成票機能はうまく機能しています。苦労しているランキング機能についてのアドバイスを探しています。私はどんな助けにも感謝し、他の関連するコードを提供することができます
したがって、トランザクションが解決される前にデータが変更された場合、トランザクションは完了する前に複数回実行できます。これにより、スコープ外の変数(rankCount
およびprevPlayerVotes
)が同期しなくなる可能性があります。もう1つの理由は、をループしてorderedPlayersRank
、Promise
への呼び出しごとにを返すことである可能性がありますtransaction
。これによりprevPlayerRank
、rankCount
期待どおりに順番にではなく、同時に読み取り/変更されます。
1つの解決策はorderByChild('votes')
、リストで使用し、前の値をチェックすることと組み合わせてインデックスを使用して、表示時にランクを決定するか、投票に変更が加えられたときにランクを設定することです(Firebase Functionまたはウォッチャーのいずれかによって)。
例 (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)
})
])
})
このアプローチでは、新しく作成されたプレーヤーのランクを最高ランクに設定する必要があります。お役に立てれば!
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加