首页 文章

为什么Mongoose有模式和模型?

提问于
浏览
80

这两种类型的物体似乎彼此如此接近,两者都感觉多余 . 有两个模式和模型有什么意义?

4 回答

  • 55

    简单来说,

    model 是一个数据对象模型,就像您在MVC设计模式中找到的那样 . 它定义了 Structure 数据库中必须存储哪种数据以及数据具有什么类型的关系 .

    schema 就像 database schema ,定义将存储在数据库中的内容 .

  • 1

    回答这类问题的最简单方法通常是举个例子 . 在这种情况下,有人已经为我做了:)

    看看这里:

    http://rawberg.com/blog/nodejs/mongoose-orm-nested-models/

    EDIT: 原帖(如评论中所述)似乎不再存在,所以我在下面复制它 . 它是否应该返回,或者它刚刚移动,请告诉我 .

    它给出了在mongoose中使用模型中的模式的正确描述以及为什么要这样做,并且还向您展示了如何通过模型推送任务,而模式是关于结构等的 .

    Original Post:

    让我们从一个在模型中嵌入模式的简单示例开始 .

    var TaskSchema = new Schema({
        name: String,
        priority: Number
    });
    
    TaskSchema.virtual('nameandpriority')
        .get( function () {
            return this.name + '(' + this.priority + ')';
        });
    
    TaskSchema.method('isHighPriority', function() {
        if(this.priority === 1) {
            return true;
        } else {
            return false;
        }
    }); 
    
    var ListSchema = new Schema({
        name: String,
        tasks: [TaskSchema]
    });
    
    mongoose.model('List', ListSchema);
    
    var List = mongoose.model('List');
    
    var sampleList = new List({name:'Sample List'});
    

    我创建了一个新的 TaskSchema 对象,其中包含任务可能具有的基本信息 . Mongoose virtual attribute设置为方便地组合任务的名称和优先级 . 我这里只指定了一个getter,但也支持虚拟setter .

    我还定义了一个名为 isHighPriority 的简单任务方法来演示方法如何使用此设置 .

    ListSchema 定义中,您将注意到如何将tasks键配置为保存 TaskSchema 对象的数组 . 任务键将成为DocumentArray的一个实例,它提供了处理嵌入式Mongo文档的特殊方法 .

    现在我只将 ListSchema 对象传递给mongoose.model并将TaskSchema保留了 . 从技术上讲,没有必要将 TaskSchema 变成正式模型,因为我们不会将它保存在它自己的集合中 . 稍后我将向您展示如果您这样做不会造成任何伤害,它可以帮助您以相同的方式组织所有模型,尤其是当它们开始跨越多个文件时 .

    使用 List 模型设置,让我们添加几个任务并将它们保存到Mongo .

    var List = mongoose.model('List');
    var sampleList = new List({name:'Sample List'});
    
    sampleList.tasks.push(
        {name:'task one', priority:1}, 
        {name:'task two', priority:5}
    );
    
    sampleList.save(function(err) {
        if (err) {
            console.log('error adding new list');
            console.log(err);
        } else {
            console.log('new list successfully saved'); 
        }
    });
    

    我们的 List 模型( simpleList )实例上的tasks属性就像常规JavaScript数组一样,我们可以使用push向它添加新任务 . 需要注意的重要事项是将任务添加为常规JavaScript对象 . 这是一个微妙的区别,可能不会立即直观 .

    您可以从Mongo shell验证新列表和任务已保存到mongo .

    db.lists.find()
    { "tasks" : [
        {
            "_id" : ObjectId("4dd1cbeed77909f507000002"),
            "priority" : 1,
            "name" : "task one"
        },
        {
            "_id" : ObjectId("4dd1cbeed77909f507000003"),
            "priority" : 5,
            "name" : "task two"
        }
    ], "_id" : ObjectId("4dd1cbeed77909f507000001"), "name" : "Sample List" }
    

    现在我们可以使用 ObjectId 来拉出 Sample List 并迭代其任务 .

    List.findById('4dd1cbeed77909f507000001', function(err, list) {
        console.log(list.name + ' retrieved');
        list.tasks.forEach(function(task, index, array) {
            console.log(task.name);
            console.log(task.nameandpriority);
            console.log(task.isHighPriority());
        });
    });
    

    如果你运行最后一段代码,你会收到一个错误,说嵌入文档没有方法 isHighPriority . 在当前版本的Mongoose中,您无法直接访问嵌入式架构上的方法 . 有一个open ticket来修复它,在向Mongoose谷歌集团提出问题后,manimal45发布了一个有用的解决方案,现在就可以使用了 .

    List.findById('4dd1cbeed77909f507000001', function(err, list) {
        console.log(list.name + ' retrieved');
        list.tasks.forEach(function(task, index, array) {
            console.log(task.name);
            console.log(task.nameandpriority);
            console.log(task._schema.methods.isHighPriority.apply(task));
        });
    });
    

    如果您运行该代码,您应该在命令行上看到以下输出 .

    Sample List retrieved
    task one
    task one (1)
    true
    task two
    task two (5)
    false
    

    考虑到这种解决方法,让我们将_2711320变成一个Mongoose模型 .

    mongoose.model('Task', TaskSchema);
    
    var Task = mongoose.model('Task');
    
    var ListSchema = new Schema({
        name: String,
        tasks: [Task.schema]
    });
    
    mongoose.model('List', ListSchema);
    
    var List = mongoose.model('List');
    

    TaskSchema 定义与之前相同,因此我将其删除了 . 一旦它变成模型,我们仍然可以使用点表示法访问它的底层Schema对象 .

    让我们创建一个新列表并在其中嵌入两个Task模型实例 .

    var demoList = new List({name:'Demo List'});
    
    var taskThree = new Task({name:'task three', priority:10});
    var taskFour = new Task({name:'task four', priority:11});
    
    demoList.tasks.push(taskThree.toObject(), taskFour.toObject());
    
    demoList.save(function(err) {
        if (err) {
            console.log('error adding new list');
            console.log(err);
        } else {
            console.log('new list successfully saved'); 
        }
    });
    

    当我们将Task模型实例嵌入到List中时,我们在它们上面调用 toObject 将它们的数据转换为 List.tasks DocumentArray 期望的纯JavaScript对象 . 以这种方式保存模型实例时,嵌入的文档将包含 ObjectIds .

    完整的代码示例是available as a gist . 希望这些解决方案有助于在Mongoose继续发展的过程中顺利完成任务 . 我仍然是Mongoose和MongoDB的新手,所以请随时在评论中分享更好的解决方案和技巧 . 快乐的数据建模!

  • 2

    Schema 是一个对象,它定义将存储在MongoDB集合中的任何文档的结构;它使您可以为所有数据项定义类型和验证器 .

    Model 是一个对象,使您可以轻松访问命名集合,从而允许您查询集合并使用模式验证保存到该集合的任何文档 . 它是通过组合Schema,Connection和集合名称创建的 .

    最初由Valeri Karpov表达,MongoDB Blog

  • 45

    我没有解释为什么Mongoose决定要求开发人员同时提供Schema和Model变量 . 他们已经消除了一个框架的例子开发人员需要定义数据模式是django - 开发人员在models.py文件中编写他们的模型,并将其留给框架来管理模式 . 考虑到我对django的经验,首先想到他们为什么这样做的原因是易于使用 . 也许更重要的是DRY(在更改模型时必须记住更新架构 - django会为你做到这一点!Rails还为你管理数据架构 - 开发人员不编辑直接架构,但通过定义操纵架构的迁移来更改它 .

    我可以理解Mongoose将模式与模型分开的一个原因是您希望从两个模式构建模型的实例 . 这样的场景可能会引入比值得管理更多的复杂性 - 如果您有两个由一个模型管理的模式,为什么它们不是一个模式?

    也许原始问题更像是传统关系数据库系统的遗留物 . 在世界NoSQL / Mongo世界中,模式可能比MySQL / PostgreSQL更灵活,因此更改模式是更常见的做法 .

相关问题