zoukankan      html  css  js  c++  java
  • mongoose 文档(七) Population

    在mongoose中没有join但有时我们仍然想引用其他collection的document,population由此而来。

    population是自动将document中指定path替换为其他collection的document的过程。我们能迁移document、多个document、简单对象、多个简单对象或者是查询返回的所有对象。

    var mongoose = require('mongoose')
      , Schema = mongoose.Schema
      
    var personSchema = Schema({
      _id     : Number,
      name    : String,
      age     : Number,
      stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
    });
    
    var storySchema = Schema({
      _creator : { type: Number, ref: 'Person' },
      title    : String,
      fans     : [{ type: Number, ref: 'Person' }]
    });
    
    var Story  = mongoose.model('Story', storySchema);
    var Person = mongoose.model('Person', personSchema);

     到目前为止我们已经创建了2个Model。Person model的stories域已经设置到一个ObjectIds数组。ref选项告诉mongoose在填充的时候使用哪个Model,在我们的例子是Story model。我们存储在这里的所有 _id 必须是来自 Story Model 的document _id。我们同时还声明了 Story _creator 属性为Number,和在personSchema里的_id的类型一样。匹配_id和ref的类型非常重要。

    注意: ObjectId, Number, String, 和 Buffer可以用来作为 ref。

    保存 ref

    保存对于其他document的ref的工作方式和保存属性相同,只是分配_id值。

    var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
    
    aaron.save(function (err) {
      if (err) return handleError(err);
      
      var story1 = new Story({
        title: "Once upon a timex.",
        _creator: aaron._id    // assign the _id from the person
      });
      
      story1.save(function (err) {
        if (err) return handleError(err);
        // thats it!
      });
    });

    Population

     到目前为止,我们还没有做什么太大的不同。我们只是创造了一个Person,一个Story。现在,让我们来看看使用查询生成器填充我们的story’s_creator:

    Story
    .findOne({ title: 'Once upon a timex.' })
    .populate('_creator')
    .exec(function (err, story) {
      if (err) return handleError(err);
      console.log('The creator is %s', story._creator.name);
      // prints "The creator is Aaron"
    });

    填充的路径不再设置到原来的_id,在返回结果之前通过单独查询它们的值将会被从数据库返回的mongoose document取代。

    ref数组的工作方式相同。在query上调用populate方法就会返回一个document数组取代原来的_ids。

    mongoose >= 3.6 exposes the original _ids used during population through the document#populated() method.

    Setting Populated Fields

    在Mongoose >= 4.0,你也可以手动填充一个域。

    Story.findOne({ title: 'Once upon a timex.' }, function(error, story) {
      if (error) {
        return handleError(error);
      }
      story._creator = aaron;
      console.log(story._creator.name); // prints "Aaron"
    });

    注意这只对单个ref有效。目前不能手动填充ref数组。

    Field 选择

    如果我们只想要一些填充document的特定的字段?这能通过传递一般的字段名语法作为第二个参数给populate方法来完成。

    Story
    .findOne({ title: /timex/i })
    .populate('_creator', 'name') // only return the Persons name
    .exec(function (err, story) {
      if (err) return handleError(err);
      
      console.log('The creator is %s', story._creator.name);
      // prints "The creator is Aaron"
      
      console.log('The creators age is %s', story._creator.age);
      // prints "The creators age is null'
    })

    填充多个 path

    如果我们想同时填充多个path怎么办?

    Story
    .find(...)
    .populate('fans _creator') // 用空格分隔 path 名
    .exec()

    在 mongoose >= 3.6,我们能传递一个由空格分隔的字符串组成的path名给populate。在3.6之前你必须多次执行populate() 方法。

    Story
    .find(...)
    .populate('fans')
    .populate('_creator')
    .exec()

    查询条件和其他选项

    如果我们想根据年龄填充fans数组,只选择他们的名字并返回最多5个人呢?

    Story
    .find(...)
    .populate({
      path: 'fans',
      match: { age: { $gte: 21 }},
      select: 'name -_id',
      options: { limit: 5 }
    })
    .exec()
    • path: 以空格分隔的引用字段的名称
    • select: 填充引用 document 中的哪些字段
    • match: 可选,指定附加的查询条件
    • model: 可选,指定引用的 model
    • options: 可选,指定附加的其他查询选项,如排序以及条数限制等等

    Refs to children

    如果我们使用aaron对象,我们不能获得stories列表。这是因为还没有story对象被‘压栈’到 aaron.stories。

    这里有两种观点。首先,让aaron知道哪个stories是他的就好了。

    aaron.stories.push(story1);
    aaron.save(callback);

    这使我们能够执行一个查找和填充组合:

    Person
    .findOne({ name: 'Aaron' })
    .populate('stories') // only works if we pushed refs to children
    .exec(function (err, person) {
      if (err) return handleError(err);
      console.log(person);
    })

    值得商榷的是我们是否要两套指针,因为他们可能会不同步。相反,我们能跳过填充并准确地 find()我们感兴趣的story。

    Story
    .find({ _creator: aaron._id })
    .exec(function (err, stories) {
      if (err) return handleError(err);
      console.log('The stories are an array: ', stories);
    })

    修改 ref

    现在,我们有一个story的_creator不正确。我们可以就像其它mongoose属性设置一样修改refs:

    var guille = new Person({ name: 'Guillermo' });
    guille.save(function (err) {
      if (err) return handleError(err);
      
      story._creator = guille;
      console.log(story._creator.name);
      // prints "Guillermo" in mongoose >= 3.6
      // see https://github.com/Automattic/mongoose/wiki/3.6-release-notes
      
      story.save(function (err) {
        if (err) return handleError(err);
        
        Story
        .findOne({ title: /timex/i })
        .populate({ path: '_creator', select: 'name' })
        .exec(function (err, story) {
          if (err) return handleError(err);
          
          console.log('The creator is %s', story._creator.name)
          // prints "The creator is Guillermo"
        })
      })
    })

     从query population返回的document变为功能齐全,可删除的、可保存的document,除非lean选项被指定。要将它们和子文档搞混了。当调用它的删除方法时要小心,因为你将从数据库中删除它,而不仅仅是数组。

    填充现有document

    如果我们有一个现有的mongoose document并想填充它的一些path,mongoose >= 3.6 支持document#populate()方法。

    填充多个现有document

    如果我们有一个或多个文件,甚至是简单的对象(像mapReduce 输出),我们可以在 mongoose >= 3.6 使用Model.populate()方法来填充它们。document#populate() 和 query#populate() 就是用这个方法更新document。

    Populating across multiple levels

    你有一个记录着用户好友的user schema。

    var userSchema = new Schema({
      name: String,
      friends: [{ type: ObjectId, ref: 'User' }]
    });

    填充让你获得用户的好友列表,但如果你想要用户的朋友的朋友呢?指定populate选项来告诉mongoose以用户所有的朋友来填充friend数组:

    User.
      findOne({ name: 'Val' }).
      populate({
        path: 'friends',
        // Get friends of friends - populate the 'friends' array for every friend
        populate: { path: 'friends' }
      });

    Populating across Databases

    比如说你有一个代表event的shema和一个代表conversation的shema。每一个event都有一个对应的conversation线程。

    var eventSchema = new Schema({
      name: String,
      // The id of the corresponding conversation
      // Notice there's no ref here!
      conversation: ObjectId
    });
    var conversationSchema = new Schema({
      numMessages: Number
    });

    同时,假设event和conversation存储在单独的MongoDB实例。

    var db1 = mongoose.createConnection('localhost:27000/db1');
    var db2 = mongoose.createConnection('localhost:27001/db2');
    
    var Event = db1.model('Event', eventSchema);
    var Conversation = db2.model('Conversation', conversationSchema);

    在这种情况下,你将无法正常populate()。因为populate()不知道使用哪个model,conversation 域永远是空。可是,你能确指定该mode

    Event.
      find().
      populate({ path: 'conversation', model: Conversation }).
      exec(function(error, docs) { /* ... */ });

    这可被称为“跨数据库填充”,因为它使你能够通过MongoDB数据库甚至是通过MongoDB实例填充。

  • 相关阅读:
    Some Depth Theory
    Hom和$otimes$如何把我绕晕
    A natrual way to introduce homotopy groups
    指数多项式的Galois群计算
    Trianglated Category and Derived Categories
    最近的代数课上的一些灵魂提问
    致青年朋友的一封信 莫洛亚
    一个函数证明题
    游客的故事
    14. 运算符重载(二)
  • 原文地址:https://www.cnblogs.com/surahe/p/5188211.html
Copyright © 2011-2022 走看看