Meteorのパブリッシュ/サブスクライブを理解する


84

のリストを表示する簡単なアプリを設定しましたProjectsautopublishすべてをクライアントに送信しないように、パッケージを削除しました。

 <template name="projectsIndex">    
   {{#each projects}}      
     {{name}}
   {{/each}}
 </template>

ときにautopublishオンにして、これはすべてのプロジェクトが表示されます:

if Meteor.isClient
  Template.projectsIndex.projects = Projects.find()

それを削除したら、さらに次のことを行う必要があります。

 if Meteor.isServer
   Meteor.publish "projects", ->
     Projects.find()
 if Meteor.isClient
   Meteor.subscribe "projects"
   Template.projectsIndex.projects = Projects.find()

それで、クライアント側のfind()方法はサーバー側から公開されたレコードのみを検索すると言うのは正確ですか?find()一度だけ電話するべきだと感じたので、つまずきました。

回答:


286

コレクション、出版物、購読はMeteorのトリッキーな領域であり、用語の混乱によって増幅されることがある頻繁な 混乱を避けるため、ドキュメントでより詳細に説明することができます

これがSachaGreifDiscoverMeteorの共著者)で、出版物と購読について1つのスライドで説明しています。

サブスクリプション

find()複数回呼び出す必要がある理由を正しく理解するには、コレクション、パブリケーション、およびサブスクリプションがMeteorでどのように機能するかを理解する必要があります。

  1. コレクションはMongoDBで定義します。Meteorはまだ関与していません。これらのコレクションは含まれていたデータベースのレコードを(もモンゴの両方で、「文書」と呼ばれると流星;例えば、更新仕様またはクエリセレクタは文書であるが、「文書」は、データベースレコードよりも一般的である、あまりにも- JavaScriptが含むオブジェクトfield: valueのペア)。

  2. 次に、Meteorサーバーコレクション を定義します。

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    これらのコレクションには、MongoDBコレクションからのすべてのデータが含まMyCollection.find({...})れており、それらを実行すると、カーソル(レコードのセット、それらを反復処理して返すメソッドを含む)が返されます。

  3. このカーソルは、(ほとんどの場合)レコードのセット「レコードセット」と呼ばれる)を公開(送信)するために使用されます。オプションで、これらのレコードから一部のフィールドのみを公開できます。クライアントがサブスクライブするのは(コレクションではなく)レコードセットです。公開は、新しいクライアントがサブスクライブするたびに呼び出される公開関数によって実行されます。この関数は、返すレコードを管理するためのパラメーターを受け取ることができます(たとえば、ユーザーID、そのユーザーのドキュメントのみを返す)。

  4. クライアントには、サーバーからのレコードの一部部分的にミラーリングするMinimongoコレクションがあります。「部分的に」あなたは通常、ページの読み込みを高速化するために、それが必要とするレコードだけクライアントに送信する、とのみ、それが必要なため、彼らは、「レコードの一部を」唯一のいくつかのフィールドの含めると、可能性があるためする権限を持っていますアクセス。

    Minimongoは本質的に、純粋なJavaScriptでのMongoのメモリ内の非永続的な実装です。これは、このクライアントが処理しているデータベースのサブセットのみを格納するローカルキャッシュとして機能します。クライアントでのクエリ(検索)は、サーバーと通信することなく、このキャッシュから直接提供されます。

    これらのMinimongoコレクションは最初は空です。彼らはによって満たされています

    Meteor.subscribe('record-set-name')
    

    呼び出します。サブスクライブするパラメーターはコレクション名ではないことに注意してください。これは、サーバーが呼び出しで使用したレコードセットの名前ですpublish。このsubscribe()呼び出しは、クライアントをレコードセット(サーバーコレクションのレコードのサブセット(最新の100件のブログ投稿など))にサブスクライブし、各レコードのフィールドのすべてまたはサブセット(titleおよびのみなどdate)を使用します。Minimongoは、受信レコードを配置するコレクションをどのようにして知るのですか?コレクションの名前になりますcollectionハンドラのパブリッシュに使用される引数addedchangedおよびremovedコールバックを、またはそれらの(最も時間の場合である)不足している場合、それはサーバー上のMongoDBのコレクションの名前になります。

レコードの変更

これがMeteorが非常に便利なところです。クライアントのMinimongoコレクションのレコード(ドキュメント)を変更すると、Meteorはそれに依存するすべてのテンプレートを即座に更新し、変更をサーバーに送り返します。変更をMongoDBに保存し、そのドキュメントを含むレコードセットをサブスクライブしている適切なクライアントに送信します。これはレイテンシー補正と呼ばれ、Meteorの7つのコア原則の1つです。

複数のサブスクリプション

さまざまなレコードを取得するサブスクリプションを多数持つことができますが、サーバー上の同じコレクションからのものである場合、それらはすべてクライアント上の同じコレクションになります_id。これは明確に説明されていませんが、Meteorのドキュメントによって暗示されています。

レコードセットをサブスクライブすると、サーバーにレコードをクライアントに送信するように指示します。クライアント店ローカルMinimongoのコレクションにこれらのレコードと同じ名前のcollection公開ハンドラのに使用される引数addedchangedおよびremovedコールバック。Meteorは、一致するコレクション名を使用してクライアントでMongo.Collectionを宣言するまで、着信属性をキューに入れます。

何を説明していないと、あなたは何が起こるかですしていない明示的に使用しaddedchangedそしてremovedほとんどの時間である- 、またはすべてのハンドラを公開します。この最も一般的なケースでは、コレクション引数は(当然のことながら)ステップ1でサーバー上で宣言したMongoDBコレクションの名前から取得されます。ただし、これは、異なる名前の異なるパブリケーションとサブスクリプションを持つことができることを意味します。レコードは、クライアントの同じコレクションに入れられます。レベルに至るまで、トップレベルのフィールドで、文書を横にして、クライアント上のクライアントワーク側に別のトップレベルのフィールドを出荷する機能を公開- 、流星は、サブスクリプションが重複することができるような、文書間の和集合を実行するために世話をしますコレクションは2セットのフィールドの和集合

例:クライアント上の同じコレクションを埋める複数のサブスクリプション

BlogPostsコレクションがあります。これは、サーバーとクライアントの両方で同じように宣言しますが、実行方法は異なります。

BlogPosts = new Mongo.Collection('posts');

クライアントでBlogPostsは、以下からレコードを取得できます。

  1. 最新の10のブログ投稿へのサブスクリプション

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. 現在のユーザーの投稿へのサブスクリプション

    // server
    Meteor.publish('posts-current-user', function publishFunction() {
      return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
      // this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId
    }
    Meteor.publish('posts-by-user', function publishFunction(who) {
      return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
    }
    
    // client
    Meteor.subscribe('posts-current-user');
    Meteor.subscribe('posts-by-user', someUser);
    
  3. 最も人気のある投稿へのサブスクリプション

これらのドキュメントはすべて、サーバー上のコレクションをposts介してMongoDBのコレクションから取得BlogPostsされ、最終BlogPosts的にクライアント上のコレクションに格納されます。

これで、find()複数回呼び出す必要がある理由を理解できました。2回目はクライアントで、すべてのサブスクリプションのドキュメントが同じコレクションに含まれ、必要なものだけをフェッチする必要があるためです。たとえば、クライアントで最新の投稿を取得するには、サーバーからのクエリをミラーリングするだけです。

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});

これにより、クライアントがこれまでに受信したすべてのドキュメント/レコード(上位の投稿とユーザーの投稿の両方)にカーソルが戻ります。(Geoffreyに感謝します)。


10
これは素晴らしい。おそらく言及する価値があるのはBlogPosts.find({})、両方のパブリケーションをサブスクライブした後にクライアントで行うとどうなるかです。つまり、現在クライアントにあるすべてのドキュメント/レコードのカーソルが、トップの投稿とユーザーの投稿の両方に返されます。質問者がこれに混乱したSOに関する他の質問を見たことがあります。
ジェフリーブース

3
これは素晴らしい。ありがとう。さらに、Meteor.users()コレクションはクライアント側で自動公開されるため、少し混乱します。上記の回答に少し追加して、users()コレクションを示すことはできますか?
Jimmy MG Lim

3
当初の要求よりもはるかに多いとしても、@ DVGはこのすばらしい記事を受け入れられた答えとしてマークする必要があると思います。ダンに感謝します。
physiocoder 2014

1
おかげで@DanDascalescu、私にとって多くをクリアした素晴らしい説明、あなたの説明を読んだ後に「コレクション」についての流星のドキュメントをフォローするとき、私BlogPostsはコレクションではないと思う唯一のこと、「挿入」、「更新」のようなメソッドを持つその返されたオブジェクト"..etcであり、実際のコレクションはpostsクライアントとサーバーにもあります。
UXE 2014年

4
購読したレコードセットのみを呼び出すことはできますか?のように、Minimongo dbをローカルでクエリする代わりに、JavaScriptでレコードセットを直接取得することは可能ですか?
ジミーノット2014

27

はい、クライアント側のfind()は、Minimongoのクライアントにあるドキュメントのみを返します。ドキュメントから:

クライアントで、Minimongoインスタンスが作成されます。Minimongoは本質的に、純粋なJavaScriptでのMongoのメモリ内の非永続的な実装です。これは、このクライアントが処理しているデータベースのサブセットのみを格納するローカルキャッシュとして機能します。クライアントでのクエリ(検索)は、サーバーと通信することなく、このキャッシュから直接提供されます。

あなたが言うように、publish()はクライアントが持つドキュメントを指定します。


1

ここでの基本的な経験則はpublishsubscribed変数名はクライアント側とサーバー側で同じである必要があります。

MongoDBとクライアント側のコレクション名は同じである必要があります。

名前の付いたコレクションのパブリッシュおよびサブスクライブを使用していると仮定するとemployees、コードは次のようになります。


サーバ側

ここでは、varキーワードの使用はオプションです(このキーワードを使用して、コレクションをこのファイルに対してローカルにします)。

CollectionNameOnServerSide = new Mongo.Collection('employees');   

Meteor.publish('employeesPubSub', function() { 
    return CollectionNameOnServerSide.find({});     
});

クライアント側の.jsファイル

CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');

Template.templateName.helpers({
  'subcribedDataNotAvailable' : function(){
        return !employeesData.ready();
    },
   'employeeNumbers' : () =>{
       CollectionNameOnClientSide.find({'empId':1});
  }
});

クライアント側の.htmlファイル

ここでは、subcribedDataNotAvailableヘルパーメソッドを使用して、クライアント側でデータの準備ができているかどうかを確認できます。データの準備ができている場合は、employeeNumbersヘルパーメソッドを使用して従業員番号を出力します。

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>

0
// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.