从多个MongoDB集合中查找具有限制的文档,并使用Mongoose作为返回排序列表

时间:2015-08-19 11:51:31

标签: node.js mongodb mongoose

如果我有不同类型的文档,每个文档都在自己的集合中,是否有办法搜索所有集合中的帖子并将它们作为按日期戳等顺序排列的单个列表返回?

此外,我需要:

  • 能够从所有馆藏中确定我需要的帖子总数
  • 帖子应按相同标准订购 - 这意味着帖子数量将与每个集合不同
  • 能够以偏移量开始收集(比如,从201号邮政开始给我100个帖子)。

如果我将所有文档保存在同一集合中,这项任务将相当简单,但也需要一个动态的,基本上没有文档的模式,因为每个文档都会有很大不同,除了一些参数,如日期。

那么,有没有办法将我的文档保存在定义良好的模式中,每个模式都在不同的集合中,但仍然可以完成上述工作?

为了论证,这里的模式看起来如何划分:

var InstagramPostSchema = new Schema({
   date: Date,
   imageUrl: String,
   ...
})

var TwitterPostSchema = new Schema({
   date: Date,
   message: String,
   ...
})

如果我制作了一个通用模式,它可能看起来像这样:

var SocialPostSchema = new Schema({
   date: Date,
   type: String,
   postData: {}
})

首选方法是什么?

理想的方法是,如果我可以从公共基础模式编写继承的单独模式,但我对Mongoose和MongoDB不太熟悉,不知道是否存在' sa本地方式来做到这一点。

1 个答案:

答案 0 :(得分:2)

有一种很好的方法可以做到这一点,这也是一个更好的方法,并且对你的最终建议有一些偏好,并且它是使用鉴别器。

基本思想是存在一个具有公共属性的基础模式,甚至根本没有要为其定义主集合的属性。然后,每个其他模式都从该模式继承并共享相同的集合。

作为基本示范:

var async = require('async'),
    util = require('util'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

function BaseSchema() {

  Schema.apply(this,arguments);

  this.add({
    date: { type: Date, default: Date.now },
    name: { type: String, required: true }
  });
}

util.inherits(BaseSchema,Schema);

var socialPostSchema = new BaseSchema();

var instagramPostSchema = new BaseSchema({
  imageUrl: { type: String, required: true }
});

var twitterPostSchema = new BaseSchema({
  message: { type: String, required: true }
});

var SocialPost = mongoose.model('SocialPost', socialPostSchema ),
    InstagramPost = SocialPost.discriminator(
      'InstagramPost', instagramPostSchema ),
    TwitterPost = SocialPost.discriminator(
      'TwitterPost', twitterPostSchema );

async.series(
  [
    function(callback) {
      SocialPost.remove({},callback);
    },
    function(callback) {
      InstagramPost.create({
        name: 'My instagram pic',
        imageUrl: '/myphoto.png'
      },callback);
    },
    function(callback) {
      setTimeout(
        function() {
          TwitterPost.create({
            name: "My tweet",
            message: "ham and cheese panini #livingthedream"
          },callback);
        },
        1000
      );
    },
    function(callback) {
      SocialPost.find({}).sort({ "date": -1 }).exec(callback);
    }
  ],
  function(err,results) {
    if (err) throw err;
    results.shift();
    console.dir(results);
    mongoose.disconnect();
  }
);

输出:

[ { __v: 0,
    name: 'My instagram pic',
    imageUrl: '/myphoto.png',
    __t: 'InstagramPost',
    date: Wed Aug 19 2015 22:53:23 GMT+1000 (AEST),
    _id: 55d47c43122e5fe5063e01bc },
  { __v: 0,
    name: 'My tweet',
    message: 'ham and cheese panini #livingthedream',
    __t: 'TwitterPost',
    date: Wed Aug 19 2015 22:53:24 GMT+1000 (AEST),
    _id: 55d47c44122e5fe5063e01bd },
  [ { _id: 55d47c44122e5fe5063e01bd,
      name: 'My tweet',
      message: 'ham and cheese panini #livingthedream',
      __v: 0,
      __t: 'TwitterPost',
      date: Wed Aug 19 2015 22:53:24 GMT+1000 (AEST) },
    { _id: 55d47c43122e5fe5063e01bc,
      name: 'My instagram pic',
      imageUrl: '/myphoto.png',
      __v: 0,
      __t: 'InstagramPost',
      date: Wed Aug 19 2015 22:53:23 GMT+1000 (AEST) } ] ]

所以需要注意的是,即使我们定义了单独的模型甚至是单独的模式,所有项目实际上都在同一个集合中。作为鉴别器的一部分,存储的每个文档都有一个__t字段,描述了它的类型。

所以这里非常好的事情是:

  • 您可以将所有内容存储在一个集合中并一起查询所有对象

  • 您可以为每个架构分隔验证规则和/或在“基础”中定义事物,这样您就不需要多次写出来。

  • 对象通过附加模式“爆炸”到每个类型的模型中。这包括任何附加的方法。因此,当您创建或检索数据时,这些是第一类对象。

  • 如果您只想使用某种特定类型,例如“TwitterPost”,那么只需使用该模型,使用该模型“自动”过滤除执行任何查询操作的“推特”帖子以外的任何内容

将事物保存在一个集合中非常有意义,特别是如果您想尝试汇总不同类型的信息中的数据。

需要注意的是,尽管使用此模式可以使用完全不同的对象,但通常明智的做法是对操作有意义。这在跨不同类型的查询或聚合时特别有用。

因此,尽可能尝试将“遗留导入”数据转换为更“常见”的字段格式,并保留每种对象类型确实需要的唯一属性。

至于你问题的第一部分,你想用不同的限制来查询“每个集合”,然后对每个集合的整体结果进行排序,那么你也可以这样做。

有各种技术,但保留在MongoDB表单中,有nedb可用于存储组合结果并“排序”它们。所有这些都是以你习惯的方式完成的:

var async = require('async'),
    util = require('util'),
    mongoose = require('mongoose'),
    DataStore = require('nedb'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

function BaseSchema() {

  Schema.apply(this,arguments);

  this.add({
    date: { type: Date, default: Date.now },
    name: { type: String, required: true }
  });
}

util.inherits(BaseSchema,Schema);

var socialPostSchema = new BaseSchema();

var instagramPostSchema = new BaseSchema({
  imageUrl: { type: String, required: true }
});

var twitterPostSchema = new BaseSchema({
  message: { type: String, required: true }
});

var SocialPost = mongoose.model('SocialPost', socialPostSchema ),
    InstagramPost = SocialPost.discriminator(
      'InstagramPost', instagramPostSchema ),
    TwitterPost = SocialPost.discriminator(
      'TwitterPost', twitterPostSchema );

async.series(
  [
    function(callback) {
      SocialPost.remove({},callback);
    },
    function(callback) {
      InstagramPost.create({
        name: 'My instagram pic',
        imageUrl: '/myphoto.png'
      },callback);
    },
    function(callback) {
      setTimeout(
        function() {
          TwitterPost.create({
            name: "My tweet",
            message: "ham and cheese panini #livingthedream"
          },callback);
        },
        1000
      );
    },
    function(callback) {
      var ds = new DataStore();
      async.parallel(
        [
          function(callback) {
            InstagramPost.find({}).limit(1).exec(function(err,posts) {
              async.each(posts,function(post,callback) {
                post = post.toObject();
                post.id = post._id.toString();
                delete post._id;
                ds.insert(post,callback);
              },callback);
            });
          },
          function(callback) {
            TwitterPost.find({}).limit(1).exec(function(err,posts) {
              async.each(posts,function(post,callback) {
                post = post.toObject();
                post.id = post._id.toString();
                delete post._id;
                ds.insert(post,callback);
              },callback);
            });
          }
        ],
        function(err) {
          if (err) callback(err);
          ds.find({}).sort({ "date": -1 }).exec(callback);
        }
      );
    }
  ],
  function(err,results) {
    if (err) throw err;
    results.shift();
    console.dir(results);
    mongoose.disconnect();
  }
);

与之前相同的输出与最新的帖子排序第一,除了这次查询被发送到每个模型,我们只是从每个模型得到结果并将它们组合。

如果您更改查询输出并写入组合模型以使用“流”处理,那么您甚至可以获得基本相同的内存消耗,并且可以更快地处理并行查询的结果。