Priorityを使ったページングと新着監視の手法

Priorityを使ったページングについてご紹介します

Priorityとは

データに対して設定する優先度のことで、
queryOrderedByPriorityを使用してデータを並べ替えることができます。
また、設定する優先度は数値または文字列のみで、並べ替えは以下のルールで行われます。

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)
    })

まとめ

共通化などする余地はありますが、基本的な取得方法はこのようになります。

注意