如何从 Meteor collection 发布两个随机项目?

How do I publish two random items from a Meteor collection?

我正在制作一个应用程序,其中 collection 中的两个随机内容会显示给用户。每次用户刷新页面或点击按钮时,她都会得到另一对随机物品。

例如,如果 collection 是水果,我想要这样的东西:

apple vs banana

peach vs pineapple

banana vs peach

下面的代码适用于服务器端,除了随机对仅生成一次这一事实外,它可以正常工作。在服务器重新启动之前,该对不会更新。我明白这是因为 generate_pair() 只被调用了一次。我试过从 Meteor.publish 函数之一调用 generate_pair() 但它只是有时有效。其他时候,我没有得到任何项目(错误)或只有一个项目。

我不介意发布整个 collection 并从客户端随机选择项目。如果 Items 有 30,000 个条目,我只是不想让浏览器崩溃。

总而言之,有没有人知道如何从出现在客户端的 collection 中获取两个随机项目?

var first_item, second_item;

// This is the best way I could find to get a random item from a Meteor collection
// Every item in Items has a 'random_number' field with a randomly generated number between 0 and 1
var random_item = function() {
  return Items.find({
    random_number: {
      $gt: Math.random()
    }
  }, {
    limit: 1
  });
};

// Generates a pair of items and ensure that they're not duplicates.
var generate_pair = function() {
  first_item = random_item();
  second_item = random_item();

  // Regenerate second item if it is a duplicate
  while (first_item.fetch()[0]._id === second_item.fetch()[0]._id) {
    second_item = random_item();
  }
};

generate_pair();

Meteor.publish('first_item', function() {
  return first_item;
});

// Is this good Meteor style to have two publications doing essentially the same thing?
Meteor.publish('second_item', function() {
  return second_item;
});

您的方法的问题在于,在客户端中使用相同的参数(在这种情况下没有参数)一遍又一遍地订阅相同的发布只会让您只订阅一次服务器端逻辑,这是因为 Meteor 正在优化其内部 Pub/Sub 机制。

要真正丢弃之前的订阅并让服务器端发布代码重新执行并发送两个新的随机文档,您需要在发布中引入一个无用的随机参数,您的客户端代码将订阅结束然后使用随机数转到出版物,每次您都会取消订阅并重新订阅新的随机文档。

这是此模式的完整实现:​​

server/server.js

function randomItemId(){
  // get the total items count of the collection
  var itemsCount = Items.find().count();
  // get a random number (N) between [0 , itemsCount - 1]
  var random = Math.floor(Random.fraction() * itemsCount);
  // choose a random item by skipping N items
  var item = Items.findOne({},{
    skip: random
  });
  return item && item._id;
}

function generateItemIdPair(){
  // return an array of 2 random items ids
  var result = [
    randomItemId(),
    randomItemId()
  ];
  //
  while(result[0] == result[1]){
    result[1] = randomItemId();
  }
  //
  return result;
}

Meteor.publish("randomItems",function(random){
  var pair = generateItemIdPair();
  // publish the 2 items whose ids are in the random pair
  return Items.find({
    _id: {
      $in: pair
    }
  });
});

client/client.js

// every 5 seconds subscribe to 2 new random items
Meteor.setInterval(function(){
  Meteor.subscribe("randomItems", Random.fraction(), function(){
    console.log("fetched these random items :", Items.find().fetch());
  });
}, 5000);

您需要 meteor add random 才能使用此代码。

Meteor.publish 'randomDocs', ->
  ids = _(Docs.find().fetch()).pluck '_id'
  randomIds = _(ids).sample 2
  Docs.find _id: $in: randomIds

这是另一种方法,使用优秀的 publishComposite 包来填充本地(仅限客户端)集合中的匹配项,这样它就不会与主集合的其他用途发生冲突:

if (Meteor.isClient) {
  randomDocs = new Mongo.Collection('randomDocs');
}

if (Meteor.isServer) {
  Meteor.publishComposite("randomDocs",function(select_count) {
    return {
      collectionName:"randomDocs",
      find: function() {
        let self=this;
        _.sample(baseCollection.find({}).fetch(),select_count).forEach(function(doc) {
          self.added("randomDocs",doc._id,doc);
        },self);
        self.ready();
      }
    }
  });
}

in onCreated: this.subscribe("randomDocs",3);
(then in a helper): return randomDocs.find({},{$limit:3});