如何从Meteor集合中发布两个随机项?

时间:2015-05-30 07:52:49

标签: mongodb random meteor

我正在创建一个应用程序,其中向用户显示集合中的两个随机内容。每次用户刷新页面或点击按钮时,她都会得到另一组随机的项目。

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

  

apple vs banana

     

桃子与菠萝

     

香蕉与桃子

下面的代码是针对服务器端的,除了随机对只生成一次之外,它的工作原理。在重新启动服务器之前,该对不会更新。我理解这是因为generate_pair()只被调用一次。我尝试从其中一个generate_pair()函数调用Meteor.publish,但它有时只能运行。其他时候,我没有任何项目(错误)或只有一个项目。

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

总而言之,有没有人对如何从客户端出现的集合中获取两个随机项目有任何想法?

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;
});

3 个答案:

答案 0 :(得分:3)

你的方法的问题是在客户端中反复订阅具有相同参数的同一个发布(在这种情况下没有参数)只会让你只订阅一次到服务器端的逻辑,这是因为Meteor正在优化其内部发布/订阅机制。

要真正丢弃以前的订阅并获取服务器端发布代码以重新执行并发送两个新的随机文档,您需要为您的出版物引入一个无用的随机参数,您的客户端代码将一遍又一遍地订阅随机编号的出版物,每次您取消订阅并重新订阅新的随机文件。

以下是此模式的完整实现:​​

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才能使此代码生效。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

这是另一种方法,使用优秀的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});