这两种类型的物体似乎彼此如此接近,两者都感觉多余。 两个模式和模型有什么意义?
答案 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中,模式是我们对数据建模,描述数据结构,默认值和验证的地方,然后我们采用该模式并从中创建模型,模型基本上就是包装器模式,它使我们能够与数据库进行实际交互,以便创建,删除,更新和读取文档。
让我们根据模式创建模型。
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。