资讯专栏INFORMATION COLUMN

Mongoose 之 Population 使用

timger / 2034人阅读

摘要:使用可以实现在一个中填充其他的。表示关联注意被关联的的必须是和才有效。类型的时,格式如为表示不填充,为时表示填充。以键值对的形式表示。回调函数,接收两个参数,错误和填充完的。参考数据库的最简单实现使用之原文链接

Mongoose 是 MongoDB 的 ODM(Object Document Mapper)

什么是ODM? 其实和ORM(Object Relational Mapper)是同类型的工具。都是将数据库的数据转化为代码对象的库,使用转化后的对象可以直接对数据库的数据进行CRUD(增删改查)

MongoDB 是文档型数据库(Document Database),不是关系型数据库(Relational Database)。而Mongoose可以将 MongonDB 数据库存储的文档(documents)转化为 javascript 对象,然后可以直接进行数据的增删改查。

因为MongoDB是文档型数据库,所以它没有关系型数据库joins(数据库的两张表通过"外键",建立连接关系。) 特性。也就是在建立数据的关联时会比较麻烦。为了解决这个问题,Mongoose封装了一个Population功能。使用Population可以实现在一个 document 中填充其他 collection(s)document(s)

在定义Schema的时候,如果设置某个 field 关联另一个Schema,那么在获取 document 的时候就可以使用 Population 功能通过关联Schema的 field 找到关联的另一个 document,并且用被关联 document 的内容替换掉原来关联字段(field)的内容。

接下来分享下:Query#populate Model#populate Document#populate的用法

先建立三个SchemaModel:

var mongoose = require("mongoose");
var Schema   = mongoose.Schema;

var UserSchema = new Schema({
    name  : { type: String, unique: true },
    posts : [{ type: Schema.Types.ObjectId, ref: "Post" }]
});
var User = mongoose.model("User", UserSchema);

var PostSchema = new Schema({
    poster   : { type: Schema.Types.ObjectId, ref: "User" },
    comments : [{ type: Schema.Types.ObjectId, ref: "Comment" }],
    title    : String,
    content  : String
});
var Post = mongoose.model("Post", PostSchema);

var CommentSchema = new Schema({
    post      : { type: Schema.Types.ObjectId, ref: "Post" },
    commenter : { type: Schema.Types.ObjectId, ref: "User" },
    content   : String
});
var Comment = mongoose.model("Comment", CommentSchema);

在上述的例子中,创建了三个 Models:UserPostComment

User 的属性 posts,对应是一个 ObjectId 的数组。ref表示关联Post(注意: 被关联的model的 type 必须是 ObjectId, Number, String, 和 Buffer 才有效)。

Post的属性 postercomments 分别关联UserComment

Comment的属性 postcommenter 分别关联PostUser

三个 Models 的关系:一个 user--has many-->post。一个 post--has one-->user,has many-->comment。一个 comment--has one-->post 和 user。

创建一些数据到数据库:

// 连接数据库
mongoose.connect("mongodb://localhost/population-test", function (err){
    if (err) throw err;
    createData();
});

function createData() {

    var userIds    = [new ObjectId, new ObjectId, new ObjectId];
    var postIds    = [new ObjectId, new ObjectId, new ObjectId];
    var commentIds = [new ObjectId, new ObjectId, new ObjectId];

    var users    = [];
    var posts    = [];
    var comments = [];

    users.push({
        _id   : userIds[0],
        name  : "aikin",
        posts : [postIds[0]]
    });
    users.push({
        _id   : userIds[1],
        name  : "luna",
        posts : [postIds[1]]
    });
    users.push({
        _id   : userIds[2],
        name  : "luajin",
        posts : [postIds[2]]
    });

    posts.push({
        _id      : postIds[0],
        title    : "post-by-aikin",
        poster   : userIds[0],
        comments : [commentIds[0]]
    });
    posts.push({
        _id      : postIds[1],
        title    : "post-by-luna",
        poster   : userIds[1],
        comments : [commentIds[1]]
    });
    posts.push({
        _id      : postIds[2],
        title    : "post-by-luajin",
        poster   : userIds[2],
        comments : [commentIds[2]]
    });

    comments.push({
        _id       : commentIds[0],
        content   : "comment-by-luna",
        commenter : userIds[1],
        post      : postIds[0]
    });
    comments.push({
        _id       : commentIds[1],
        content   : "comment-by-luajin",
        commenter : userIds[2],
        post      : postIds[1]
    });
    comments.push({
        _id       : commentIds[2],
        content   : "comment-by-aikin",
        commenter : userIds[1],
        post      : postIds[2]
    });

    User.create(users, function(err, docs) {
        Post.create(posts, function(err, docs) {
            Comment.create(comments, function(err, docs) {
            });
        });
    });
}

数据的准备就绪后,接下来就是探索populate方法:

1. Query#populate

什么Query? Query(查询),可以快速和简单的从MongooDB查找出相应的 document(s)。 Mongoose 封装了很多查询的方法,使得对数据库的操作变得简单啦。这里分享一下populate方法用法。

语法:

**`Query.populate(path, [select], [model], [match], [options])`**

参数:

path

  类型:StringObject
  String类型的时, 指定要填充的关联字段,要填充多个关联字段可以以空格分隔。
  Object类型的时,就是把 populate 的参数封装到一个对象里。当然也可以是个数组。下面的例子中将会实现。

select

  类型:ObjectString,可选,指定填充 document 中的哪些字段。
  Object类型的时,格式如:{name: 1, _id: 0},为0表示不填充,为1时表示填充。
  String类型的时,格式如:"name -_id",用空格分隔字段,在字段名前加上-表示不填充。详细语法介绍 query-select

model

  类型:Model,可选,指定关联字段的 model,如果没有指定就会使用Schemaref

match

  类型:Object,可选,指定附加的查询条件。

options

  类型:Object,可选,指定附加的其他查询选项,如排序以及条数限制等等。

填充Userposts字段:

//填充所有 users 的 posts
User.find()
    .populate("posts", "title", null, {sort: { title: -1 }})
    .exec(function(err, docs) {
        console.log(docs[0].posts[0].title); // post-by-aikin
    });

//填充 user "luajin"的 posts
User.findOne({name: "luajin"})
    .populate({path: "posts", select: { title: 1 }, options: {sort: { title: -1 }}})
    .exec(function(err, doc) {
        console.log(doc.posts[0].title);  // post-by-luajin
    });

//这里的 populate 方法传入的参数形式不同,其实实现的功能是一样的,只是表示形式不一样。

填充Postpostercomments字段:

Post.findOne({title: "post-by-aikin"})
    .populate("poster comments", "-_id")
    .exec(function(err, doc) {
        console.log(doc.poster.name);           // aikin
        console.log(doc.poster._id);            // undefined

        console.log(doc.comments[0].content);  // comment-by-luna
        console.log(doc.comments[0]._id);      // undefined
    });

Post.findOne({title: "post-by-aikin"})
    .populate({path: "poster comments", select: "-_id"})
    .exec(function(err, doc) {
        console.log(doc.poster.name);           // aikin
        console.log(doc.poster._id);            // undefined

        console.log(doc.comments[0].content);  // comment-by-luna
        console.log(doc.comments[0]._id);      // undefined
    });

//上两种填充的方式实现的功能是一样的。就是给 populate 方法的参数不同。
//这里要注意,当两个关联的字段同时在一个 path 里面时, select 必须是 document(s)
//具有的相同字段。


//如果想要给单个关联的字段指定 select,可以传入数组的参数。如下:

Post.findOne({title: "post-by-aikin"})
    .populate(["poster", "comments"])
    .exec(function(err, doc) {
        console.log(doc.poster.name);          // aikin
        console.log(doc.comments[0].content);  // comment-by-luna
    });

Post.findOne({title: "post-by-aikin"})
    .populate([
        {path:"poster",   select: "-_id"},
        {path:"comments", select: "-content"}
    ])
    .exec(function(err, doc) {
        console.log(doc.poster.name);          // aikin
        console.log(doc.poster._id);           // undefined

        console.log(doc.comments[0]._id);      // 会打印出对应的 comment id
        console.log(doc.comments[0].content);  // undefined
    });
2. Model#populate

Model(模型),是根据定义的 Schema 编译成的抽象的构造函数。models 的实例 documents,可以在数据库中被保存和检索。数据库所有 document 的创建和检索,都通过 models 处理。

语法:

**`Model.populate(docs, options, [cb(err,doc)])`**

参数:

docs

  类型:DocumentArray。单个需要被填充的 doucment 或者 document 的数组。

options

  类型:Object。以键值对的形式表示。
  keys:path select match model options,这些键对应值的类型和功能,与上述Query#populate方法的参数相同。

[cb(err,doc)]

  类型:Function,回调函数,接收两个参数,错误err和填充完的doc(s)

填充Postpostercomments字段以及commentscommenter字段:

Post.find({title: "post-by-aikin"})
    .populate("poster comments")
    .exec(function(err, docs) {

        var opts = [{
            path   : "comments.commenter",
            select : "name",
            model  : "User"
        }];

        Post.populate(docs, opts, function(err, populatedDocs) {
            console.log(populatedDocs[0].poster.name);                  // aikin
            console.log(populatedDocs[0].comments[0].commenter.name);  // luna
        });
    });
3. Document#populate

Document,每个 document 都是其 Model 的一个实例,一对一的映射着 MongoDB 的 document。

语法:

**`Document.populate([path], [callback])`**

参数:

path

  类型:StringObject。与上述Query#populate`方法的 path 参数相同。

callback

  类型:Function。回调函数,接收两个参数,错误err和填充完的doc(s)

填充Userposts字段:

User.findOne({name: "aikin"})
    .exec(function(err, doc) {

        var opts = [{
            path   : "posts",
            select : "title"
        }];

        doc.populate(opts, function(err, populatedDoc) {
            console.log(populatedDoc.posts[0].title);  // post-by-aikin
        });
    });

博文涉及的完整例子在 gist 上。(ps: gist 被已墙了。)

参考

mongoose-api-docs

数据库的最简单实现

Mongoose 使用之 Population

原文链接

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/18758.html

相关文章

  • [转]mongodb中的populate方法

    摘要:使用可以实现在一个中填充其他的。表示关联注意被关联的的必须是和才有效。封装了很多查询的方法,使得对数据库的操作变得简单啦。这里分享一下方法用法。类型的时,格式如为表示不填充,为时表示填充。类型,可选,指定附加的查询条件。 Mongoose 是 MongoDB 的 ODM(Object Document Mapper)。 什么是ODM? 其实和ORM(Object Relational...

    ranwu 评论0 收藏0
  • 【译】《精通使用AngularJS开发Web App》(三)--- 深入scope,继承结构,事件系

    摘要:比如,我们可以监听事件由实例发出,然后在任何浏览器中就是变化的时候都会得到通知,如下所示每一个作用域对象都会有这个方法,可以用来注册一个作用域事件的侦听器。这个函数所扮演的侦听器在被调用时会有一个对象作为第一个参数。 上一篇:【译】《精通使用AngularJS开发Web App》(二) 下一篇:【译】《精通使用AngularJS开发Web App》(四) 书名:Mastering W...

    wind5o 评论0 收藏0
  • 4000字详细说明,推荐20个好用到爆的Pandas函数方法

    摘要:往期回顾教程实现社交网络可视化,看看你的人脉影响力如何转载字概括精髓,必知必会例注意字归纳总结带你做数据预处理新闻字张图,一键生成炫酷的动态交互式图表分享收藏点赞在看 今天分享几个不为人知的pandas函数,大家可能平时看到的不多,但是使用起来倒是非常的方便,也能够帮助我们数据分析人员大...

    2501207950 评论0 收藏0
  • mongodb操作mongoose(一)

    摘要:更重要的是它将返回的字符串包装成了对象,这在操作时要比直接使用字符串方便许多。今天就利用的官方文档对它的标准操作进行简单的分析。 NOSQL存储作为一种与关系性数据库完全不同的存储方式,提供了灵活,大型,敏捷,可扩展的存储模式,并且在有着复杂的数据情形下性能完全不逊色于RDBMS,而作为NOSQL中流行程度较高的数据库,mongodb有着上手容易,数据查询方式多样等诸多特点,所以也深受...

    KavenFan 评论0 收藏0
  • Python遗传算法框架DEAP-Creating Types

    摘要:是一个遗传算法框架,这里是它的简介。最小化问题使用负值的的最大化问题用正值。略种群种群横线个体。这个种群是直接使用和函数来初始化的。个体之间分布在网格中每个格子包含一个个体。调用将会返回一个种群,个体是使用两个索引可获得的。 DEAP是一个python遗传算法框架,这里是它的简介。DEAP documentation今天整理一下DEAP的概览,大体了解一下它的流程。初学,不严谨,仅作为...

    Channe 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<