Priorityを使ったページングについてご紹介します
Priorityとは
データに対して設定する優先度のことで、
queryOrderedByPriorityを使用してデータを並べ替えることができます。
また、設定する優先度は数値または文字列のみで、並べ替えは以下のルールで行われます。
- 優先度を持たない子(デフォルト)が最初に来ます。
- 優先度として数値を持つ子が次に来ます。該当する子は優先度の数値で昇順に並べ替えられます。
- 優先度として文字列を持つ子が最後に来ます。該当する子は優先度で辞書順に並べ替えられます。
- 2 つの子が同じ優先度を持つ場合(ともに優先度を持たない場合も含む)、キーで並べ替えられます。数値キーが最初に来て(数値で昇順に並べ替えられます)、残りのキーが続きます(辞書順で昇順に並べ替えられます)。
優先度の値に使用できるのは、数値たは文字列だけです。数値の優先度は IEEE 754 倍精度浮動小数点数として保存され、並べ替えられます。キーは常に文字列として保存され、数値として扱われるのは 32 ビット整数として解析できるときだけです。
ページングUnixTimeを使うので、数値のみ利用します。
デフォルトのデータの並び順
Firebaseコンソール上でデータを1から順に追加すると以下のようになります。
UnixTimeを元に出した値をpriorityに設定する
データを書き込む際には、TimestampMaxから時間を引いた値を使用します。
Priorityは値が小さいほど優先度が高い仕様のため、TimestampMax - unixtimeをすることで、値は小さくなります。
struct FIRPriority {
static let maxPriority:Int64 = 0
static let timestampMax:Int64 = 9999999999999 // UnixTimeより大きい値
func getPriority() -> Double {
return Double(FIRPriority.timestampMax - Date.currentTimeMillis())
}
}
新着取得方法
Priority順に並び替え先頭からLimit分だけ取得するようにします。
最後のoffsetは次のページングする際に利用するので変数で保持しておきます。
let limit:UInt = 20
var offset = NSNumber(value: 0)
FIRDatabase.database().reference(withPath: "path")
.queryOrderedByPriority() // priority順に並び替え
.queryStarting(atValue: offset)
.queryLimited(toFirst: limit)
.observeSingleEvent(of: .value, with: { snapshot in
guard let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] else {
return
}
offset = snapshot.last?.priority as! NSNumber
})
ページング
1つ前で説明した新着取得方法とほぼ同じですが、offsetの部分が違います。
let limit:UInt = 20
var offset = NSNumber(value: 0)
FIRDatabase.database().reference(withPath: "path")
.queryOrderedByPriority() // priority順に並び替え
.queryStarting(atValue: NSNumber(value: offset.int64Value + 1)) // 最後に取得したデータの次Priorityを設定
.queryLimited(toFirst: limit)
.observeSingleEvent(of: .value, with: { snapshot in
guard let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] else {
return
}
offset = snapshot.last?.priority as! NSNumber
})
新着監視方法
新着のみをイベントで受け取りたい場合、
これから書き込まれた時に設定される未来のpriority値を範囲として監視すれば良いので
以下のようになります。
FIRDatabase.database().reference(withPath: "path")
.queryOrderedByPriority() // priority順に並び替え
.queryStarting(atValue: FIRPriority.maxPriority) // 0から
.queryEnding(atValue: NSNumber(value: offset.int64Value + 1)) // 最新データのpriorityに+1したもの
.observe(.childAdded, with: { snapshot in
var addedCount = 0
if let data = snapshot.value as? NSDictionary {
// MEMO:Dict to Object
addedCount += 1
}
print(addedCount)
})
まとめ
共通化などする余地はありますが、基本的な取得方法はこのようになります。
注意
- こちらにもあります通り、REST APIは結果の並び替えに対応していないです。
- offset+1しているところで、書き込み頻度がミリ秒単位の場合、priorityが被ることによりデータが抜けたりする可能性があります。