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

标签 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: {}
})

执行此操作的首选方法是什么?

理想的方法是,如果我可以编写继承自公共(public)基础架构的单独架构,但我对 Mongoose 和 MongoDB 不够熟悉,不知道是否有本地方法可以做到这一点.

最佳答案

有一个很好的方法可以做到这一点,它也更好一些,并且比您的最终建议有一些好处,那就是使用鉴别器。

基本思想是,有一个具有公共(public)属性的基本架构,甚至根本没有属性,您可以从中定义主集合。然后,每个其他架构都继承该架构并共享相同的集合。

作为基本演示:

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”),则使用该模型“自动”过滤掉执行的任何查询操作中除“twitter”帖子之外的任何其他内容,只需使用该模型即可。

将内容保存在一个集合中非常有意义,特别是当您想尝试聚合不同类型的信息中的数据时。

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

因此,在可能的情况下,尝试将“旧导入”数据转换为更“通用”的字段格式,并仅保留每种对象类型真正需要的唯一属性。

<小时/>

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

技术有很多种,但保持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();
  }
);

与之前的输出相同,最新的帖子首先排序,只不过这次向每个模型发送了一个查询,我们只是从每个模型中获取结果并将它们组合起来。

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

关于node.js - 使用 Mongoose 从多个 MongoDB 集合中查找有限制的文档并作为返回排序列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32094731/

相关文章:

mongodb - 库存文件 - child 组 - Ansible 中

node.js - MongoDB:按地理位置给定区域和最大点数的集群文档?

arrays - Mongoose 在 find().where() 中使用数组

javascript - 我如何在路由模型nodejs中获取获取的记录

node.js - 502 Bad Gateway 在 Elastic Beanstalk 上部署 Express Generator 模板

javascript - Puppeteer 阻止元素在不 sleep /等待的情况下出现

javascript - 只向前端发送指定的javascript文件

java - 将初始(批量)数据导入 RESTful 系统的最佳做法是什么?

模型上的 Node.js 和 mongoose module.exports 与 Schema

node.js - ERR_CONNECTION_RESET 将大文件上传到 Amazon S3 时出错