web-dev-qa-db-ja.com

1つのイベントを繰り返し監視する代わりにクエリを使用して、ソーシャルネットワークアプリの投稿の取得を高速化する

/ posts/id /(post info)のようなソーシャルネットワークの投稿オブジェクトにつながるキーの配列があります

投稿を読み込むとき、observeSingleEventOfType(.Value)メソッドを使用して/ posts/0を読み込み、次に/ posts/1などを読み込みます。

lazyTableViewを使用して一度に30をロードしますが、非常に遅いです。 JSONツリーのデータを再構築する必要がある場合でも、クエリメソッドの1つを使用したり、高速化する別の方法を使用したりする方法はありますか。

私はParseからアプリを再実装していますが、これまでの経験は非常に良好でした。これだけが、私が少し立ち往生していることです。助けてくれてありがとう!

編集:

func loadNext(i: Int) { 

    // check if exhists
    let ideaPostsRef = Firebase(url: "https://APPURL")

    ideaPostsRef.childByAppendingPath(i.description).observeSingleEventOfType(.Value, withBlock: {
        (snapshot) in

        if i % 29 == 0 && i != 0 && !self.hitNull { return }
            // false if nil
            // true if not nil
        if !(snapshot.value is NSNull) {
            let postJSON  = snapshot.value as! [String: AnyObject]
            print("GOT VALID \(postJSON)")
            let post = IdeaPost(message: postJSON["message"] as! String, byUser: postJSON["user"] as! String, withId: i.description)
            post.upvotes = postJSON["upvotes"] as! Int
            self.ideaPostDataSource.append(post)
            self.loadNext(i + 1)
        } else {
            // doesn't exhist
            print("GOT NULL RETURNING AT \(i)")
            self.doneLoading = true
            self.hitNull = true
            return
        }
    }
}

この再帰的な関数は、基本的にキーベースiの値をfirebaseから取得して実行します。 NSNULLの場合、それが最後にロードできるポストであることを認識し、二度とロードしません。 NSNULLがヒットしなくてもi % 29 == 0の場合は、ベースケースとして返されるため、一度にロードされるのは30の投稿のみです(インデックスは0)。 doneLoadingtrueに設定すると、プロパティオブザーバーを使用してtableView.reloadData()が呼び出されます。

ここに、私がフェッチしている配列がどのように見えるかのサンプルがあります

"ideaPosts" : [ {
    "id" : 0,
    "message" : "Test",
    "upvotes" : 1,
    "user" : "Anonymous"
  }, {
    "id" : 1,
    "message" : "Test2",
    "upvotes" : 1,
    "user" : "Anonymous"
  } ]
86
Big_Mac

更新:この質問も AskFirebaseエピソードでカバーするようになりました

Firebaseから多くのアイテムをロードするのに時間がかかる必要はありません。リクエストをパイプライン処理できるからです。しかし、コードがこれを不可能にしているため、実際には最適なパフォーマンスが得られません。

コードでは、サーバーにアイテムを要求し、そのアイテムが返されるのを待ってから、次のアイテムをロードします。次のような簡略化されたシーケンス図で:

Your app                     Firebase 
                             Database

        -- request item 1 -->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
        <-  return item  1 --  r  n
                                  g
        -- request item 2 -->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
                               r  n
        <-  return item  2 --     g
        -- request item 3 -->
                 .
                 .
                 .
        -- request item 30-->
                               S  L
                               e  o
                               r  a
                               v  d
                               e  i
                               r  n
                                  g
        <-  return item 30 --

このシナリオでは、ラウンドトリップ時間の30倍+ディスクからデータをロードするのにかかる時間の30倍を待っています。 (簡単にするために)往復に1秒かかり、ディスクからアイテムを読み込むのに少なくとも30秒(1 + 1)= 60秒かかると言います。

Firebaseアプリケーションでは、すべての要求(または少なくとも妥当な数の要求)を一度に送信すると、パフォーマンスが大幅に向上します。

Your app                     Firebase 
                             Database

        -- request item 1 -->
        -- request item 2 -->  S  L
        -- request item 3 -->  e  o
                 .             r  a
                 .             v  d
                 .             e  i
        -- request item 30-->  r  n
                                  g
        <-  return item  1 --     
        <-  return item  2 --      
        <-  return item  3 --
                 .
                 .
                 .
        <-  return item 30 --

再び1秒のラウンドトリップと1秒のロードを想定すると、30 * 1 + 1 = 31秒を待っています。

そのため、すべてのリクエストは同じ接続を経由します。その場合、get(1)get(2)get(3)、およびgetAll([1,2,3])の唯一の違いは、フレームのオーバーヘッドです。

動作を実演するためのjsbin を設定しました。データモデルは非常に単純ですが、違いを示しています。

function loadVideosSequential(videoIds) {
  if (videoIds.length > 0) {
    db.child('videos').child(videoIds[0]).once('value', snapshot => {
      if (videoIds.length > 1) {
        loadVideosSequential(videoIds.splice(1), callback)
      }
    });
  }
}

function loadVideosParallel(videoIds) {
  Promise.all(
    videoIds.map(id => db.child('videos').child(id).once('value'))
  );
}

比較のために、64個のアイテムを順番に読み込むにはシステム上で3.8秒かかりますが、パイプラインで読み込むには(Firebaseクライアントがネイティブに行うように)600msかかります。正確な数値は接続(遅延と帯域幅)によって異なりますが、パイプラインバージョンは常に大幅に高速化されるはずです。

111