簡體   English   中英

Meteor,一對多關系並僅在發布中向客戶端集合添加字段?

[英]Meteor, One to Many Relationship & add field only to client side collection in Publish?

任何人都可以看到此代碼中可能出現的問題,基本上我想檢查當前登錄用戶是否共享了帖子並向客戶端集合添加了一個臨時字段:isCurrentUserShared。

這在第一次加載新頁面並從現有共享中填充時,或者在加載頁面時僅在第一次添加或刪除記錄到Shares集合時第一次起作用。

1)isSharedByMe只更改狀態1次,然后仍然按照console.log調用回調,但是在第一次添加或刪除記錄后,isSharedByMe在Posts集合中沒有更新。 它第一次工作。

2)為什么連續兩次調用回調,即將一條記錄添加到Sharescollection觸發2次調用,如console.log所示。

Meteor.publish('posts', function() {

    var self = this;
    var mySharedHandle;


    function checkSharedBy(IN_postId) {
        mySharedHandle = Shares.find( { postId: IN_postId, userId: self.userId }).observeChanges({

            added: function(id) {
                console.log("   ...INSIDE checkSharedBy(); ADDED: IN_postId = " + IN_postId );
                self.added('posts', IN_postId, { isSharedByMe: true });
            },

            removed: function(id) {
                console.log("   ...INSIDE checkSharedBy(); REMOVED: IN_postId = " + IN_postId );
                self.changed('posts', IN_postId, { isSharedByMe: false });
            }
        });
    }


    var handle = Posts.find().observeChanges({

        added: function(id, fields) {
            checkSharedBy(id);
            self.added('posts', id, fields);
        },

        // This callback never gets run, even when checkSharedBy() changes field isSharedByMe.
        changed: function(id, fields) {
            self.changed('posts', id, fields);
        },

        removed: function(id) {
            self.removed('posts', id);
        }
    });

    // Stop observing cursor when client unsubscribes
    self.onStop(function() {
        handle.stop();
        mySharedHandle.stop();
    });

    self.ready();
});

就個人而言,我會以一種非常不同的方式,通過使用$ in運算符,並在記錄中保留一個postIds或shareIds數組。

http://docs.mongodb.org/manual/reference/operator/query/in/

我發現發布函數在保持簡單時效果最好,如下所示。

Meteor.publish('posts', function() {
    return Posts.find();
});
Meteor.publish('sharedPosts', function(postId) {
    var postRecord = Posts.findOne({_id: postId});
    return Shares.find{{_id: $in: postRecord.shares_array });
});

我不知道這會讓你在多大程度上解決你的實際問題,但我會從你的代碼和你問的問題開始。

1)你詢問一個短語集合,但發布功能永遠不會向該集合發布任何內容,因為所有added調用都發送到名為“posts”的minimongo集合。

2)您詢問“Reposts”集合,但是沒有任何代碼使用該名稱,因此不清楚您所指的是什么。 添加到'Posts'集合中的每個元素雖然會調用checkSharedId()但會在'Shares'集合上創建一個新的觀察者。 每個觀察者都會嘗試在客戶的“帖子”集合中添加和更改文檔。

3)與第2點相關, mySharedHandle.stop()只會停止checkSharedId()創建的最后一個觀察者,因為每次運行checkSharedId()checkSharedId()覆蓋句柄。

4)如果您的“分享”觀察者發現了一個帶有IN_postId的文檔,它會嘗試將帶有該_id的文檔發送到minimongo'posts'集合。 IN_postId從您在“帖子”集合中的查找傳遞,其觀察者也嘗試向客戶端的“帖子”集合發送不同的文檔。 你想在客戶端使用哪個doc _id? 您看到的一些錯誤可能是由Meteor試圖忽略重復添加的請求引起的

從這一切開始,我認為你最好將其分為兩個發布功能,一個用於'Posts',一個用於'Shares',以利用流星默認行為發布游標。 然后可以在必要時在客戶端上完成任何連接。 例如:

//on server
Meteor.publish('posts', function(){
  return Posts.find();
});

Meteor.publish('shares', function(){
  return Shares.find( {userId: this.userId }, {fields: {postId: 1}} );
});

//on client - uses _.pluck from underscore package
Meteor.subscribe( 'posts' );
Meteor.subscribe( 'shares');

Template.post.isSharedByMe = function(){  //create the field isSharedByMe for a template to use
  var share = Shares.findOne( {postId: this._id} );
  return share && true;
};

使用observeChanges連接發布的備用方法。 未經測試的代碼,我不清楚它比上面更簡單的方法有很多優勢。 因此,在上述內容破壞或成為性能瓶頸之前,我會按上述方式執行此操作。

Meteor.publish("posts", function(){
  var self = this;
  var sharesHandle;
  var publishedPosts = [];
  var initialising = true;  //avoid starting and stopping Shares observer during initial publish

  //observer to watch published posts for changes in the Shares userId field
  var startSharesObserver = function(){
    var handle = Shares.find( {postId: {$in: publishedPosts}, userId === self.userId }).observeChanges({

      //other observer should have correctly set the initial value of isSharedByMe just before this observer starts.
      //removing this will send changes to all posts found every time a new posts is added or removed in the Posts collection 
      //underscore in the name means this is undocumented and likely to break or be removed at some point
      _suppress_initial: true,

      //other observer manages which posts are on client so this observer is only managing changes in the isSharedByMe field 
      added: function( id ){
        self.changed( "posts", id, {isSharedByMe: true} );
      },

      removed: function( id ){
        self.changed( "posts", id, {isSharedByMe: false} );
      }
    });
    return handle;
  };

  //observer to send initial data and always initiate new published post with the correct isSharedByMe field.
  //observer also maintains publishedPosts array so Shares observer is always watching the correct set of posts.  
  //Shares observer starts and stops each time the publishedPosts array changes
  var postsHandle = Posts.find({}).observeChanges({
    added: function(id, doc){
      if ( sharesHandle ) 
        sharesHandle.stop();
      var shared = Shares.findOne( {postId: id});
      doc.isSharedByMe = shared && shared.userId === self.userId;
      self.added( "posts", id, doc);
      publishedPosts.push( id );
      if (! initialising)
        sharesHandle = startSharesObserver();
    },
    removed: function(id){
      if ( sharesHandle ) 
        sharesHandle.stop();
      publishedPosts.splice( publishedPosts.indexOf( id ), 1);
      self.removed( "posts", id );
      if (! initialising)
        sharesHandle = startSharesObserver();
    },
    changed: function(id, doc){
      self.changed( "posts", id, doc);
    }
  });

  if ( initialising )
    sharesHandle = startSharesObserver();

  initialising = false;
  self.ready();

  self.onStop( function(){
    postsHandle.stop();
    sharesHandle.stop();
  });
});

myPosts是一個游標,所以當你調用forEach時,它會循環顯示結果,添加你想要的字段,但最后會在結果列表的末尾。 因此,當你返回myPosts ,沒有什么可以循環通過,所以fetch()會產生一個空數組。

你應該能夠通過添加myPosts.cursor_pos = 0;來糾正這個問題myPosts.cursor_pos = 0; 在返回之前,從而將光標返回到結果的開頭。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM