为什么Mongoose有模式和模型?

时间:2012-02-03 10:31:21

标签: mongodb mongoose

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

6 个答案:

答案 0 :(得分:56)

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

看看这里:

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

编辑:原帖(如评论中所述)似乎已不复存在,因此我将在下方复制。如果它回来了,或者它刚搬了,请告诉我。

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

原帖:

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

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定义中,您将注意到如何配置任务键以保存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 Google Group提出问题之后,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

考虑到这种解决方法,让我们将TaskSchema变成一个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的新手,所以请随时在评论中分享更好的解决方案和技巧。快乐的数据建模!

答案 1 :(得分:47)

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

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

最初由Valeri Karpov表达,MongoDB Blog

答案 2 :(得分:3)

我不认为接受的答案实际上回答了提出的问题。答案并没有解释为什么 Mongoose决定要求开发人员同时提供Schema和Model变量。他们已经消除了开发人员定义数据模式的需求的框架示例是django - 开发人员在models.py文件中编写他们的模型,并将其留给框架管理架构。考虑到我对django的经验,首先想到他们为什么这样做的原因是易于使用。也许更重要的是DRY(不要重复自己)原则 - 当你改变模型时,你不必记得更新架构 - django会为你做的! Rails还为您管理数据模式 - 开发人员不直接编辑模式,但通过定义操作模式的迁移来更改模式。

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

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

答案 3 :(得分:1)

简单来说,

模型是一种数据对象模型,就像您在MVC设计模式中找到的那样。它定义了结构哪种数据必须存储在数据库中?数据有什么类型的关系。

架构就像 #include <stdio.h> void main(void) { char in[1000]; printf("read these lines: "); while (1) { if (scanf("%*[^a-zA-Z]") == EOF) break; if (scanf("%1000[a-zA-Z]", in) == EOF) break; printf("%1000s\n", in); } } ,即将存储在数据库中的定义。

答案 4 :(得分:0)

要了解原因?您必须了解猫鼬到底是什么?

好吧,猫鼬是MongoDB和Node JS的对象数据建模库,提供了更高级别的抽象。因此,这有点像Express和Node之间的关系,因此Express是常规Node之上的抽象层,而Mongoose是常规MongoDB驱动程序之上的抽象层。

对象数据建模库只是我们编写Javascript代码的一种方法,然后将其与数据库进行交互。因此,我们可以只使用常规的MongoDB驱动程序来访问数据库,就可以了。

但是我们改用Mongoose,因为它为我们提供了许多现成的功能,从而可以更快,更简单地开发应用程序。

因此,Mongoose的一些功能为我们提供了用于对数据和关系进行建模的模式,轻松的数据验证,简单的查询API,中间件等等。

在Mongoose中,模式是我们对数据建模,描述数据结构,默认值和验证的地方,然后我们采用该模式并从中创建模型,模型基本上就是包装器模式,它使我们能够与数据库进行实际交互,以便创建,删除,更新和读取文档。

enter image description here

让我们根据模式创建模型。

const tourSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'A tour must have a name'],
    unique: true,
  },
  rating: {
    type: Number,
    default: 4.5,
  },
  price: {
    type: Number,
    required: [true, 'A tour must have a price'],
  },
});
//tour model
const Tour = mongoose.model('Tour', tourSchema);

根据常规,型号名称的首字母必须大写。

让我们创建我们使用猫鼬和模式创建的模型的实例。另外,与我们的数据库进行交互。

const testTour = new Tour({ // instance of our model
  name: 'The Forest Hiker',
  rating: 4.7,
  price: 497,
});
 // saving testTour document into database
testTour
  .save()
  .then((doc) => {
    console.log(doc);
  })
  .catch((err) => {
    console.log(err);
  });

因此同时使用schama和modle猫鼬会使我们的生活更轻松。

答案 5 :(得分:0)

将模型视为模式的包装器。模式定义了您的文档的结构,您可以期待什么样的属性,以及它们的数据类型(字符串、数字等)。模型提供了一种在模式上执行 CRUD 的接口。在 FCC 上查看此 post