流星,一对多关系和仅将字段添加到发布中的客户端集合?



有人能看到这段代码中可能有什么错误吗?基本上,我想检查当前登录的用户是否共享了帖子,并在客户端集合中添加一个临时字段:isCurrentUserShared。

这在第一次加载新页面并从现有共享中填充时有效,或者在加载页面后第一次向共享集合添加或删除记录时有效。

1) isSharedByMe只更改状态1次,那么回调仍然会根据console.log进行调用,但在我第一次添加或删除记录后,isSharedByMe不会在Posts集合中更新。它第一次工作。

2) 为什么回调会被连续调用两次,即向Sharescollection添加1条记录会触发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运算符,并在记录中保留一个postId或shareId数组。

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) 您询问Phrases集合,但发布函数永远不会向该集合发布任何内容,因为所有added调用都发送到名为"posts"的minimongo集合。

2) 您询问了"Reposts"集合,但没有任何代码使用该名称,因此不清楚您指的是什么。不过,添加到"Posts"集合的每个元素都将在"Shares"集合上创建一个新的观察者,因为它调用checkSharedId()。每个观察者都会尝试在客户端的"posts"集合中添加和更改文档。

3) 与点2相关,mySharedHandle.stop()只会停止checkSharedId()创建的最后一个观察器,因为每次运行checkSharedId()时都会覆盖句柄。

4) 如果"Shares"的观察者发现一个具有IN_postId的文档,它会尝试将一个具有该id的文档发送到minimongo"posts"集合。IN_postId是从您在"Posts"集合上的查找中传递的,其观察者也试图将不同的文档发送到客户端的"Posts"集合。您希望在具有该_id的客户端上使用哪个文档?您看到的一些错误可能是由Meteor试图忽略重复添加的请求引起的。

从这一切来看,我认为你可能会更好地将其分解为两个发布函数,一个用于"帖子",一个为"共享",以利用流星默认行为发布游标。任何连接都可以在必要时在客户端上完成。例如:

//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;来更正此问题,从而将光标返回到结果的开头。

最新更新