Mongodb 为什么Mongoose既有模式又有模型?

Mongodb 为什么Mongoose既有模式又有模型?,mongodb,mongoose,Mongodb,Mongoose,这两种类型的对象似乎彼此非常接近,以至于两者都感觉多余。同时拥有模式和模型有什么意义?编辑:尽管这对很多人都很有用,正如评论中提到的,它回答了“如何”而不是为什么。谢天谢地,这个问题的原因也在其他地方得到了回答。这一点在评论中已经联系了一段时间,但我意识到很多人在阅读时可能不会走那么远 通常,回答这类问题的最简单方法是举个例子。在这种情况下,有人已经为我做了:) 请看这里: 编辑:原始帖子(如评论中所述)似乎不再存在,因此我在下面复制它。如果它回来了,或者它刚搬走,请告诉我 它对在mongoo

这两种类型的对象似乎彼此非常接近,以至于两者都感觉多余。同时拥有模式和模型有什么意义?

编辑:尽管这对很多人都很有用,正如评论中提到的,它回答了“如何”而不是为什么。谢天谢地,这个问题的原因也在其他地方得到了回答。这一点在评论中已经联系了一段时间,但我意识到很多人在阅读时可能不会走那么远

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

请看这里:

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

它对在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设置为方便地组合任务的名称和优先级。我在这里只指定了一个getter,但也支持虚拟setter

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

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

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

使用
列表
模型设置,让我们向其中添加几个任务,并将它们保存到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'); 
    }
});
我们的
列表
模型(
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
来调出
示例列表
,并迭代它的任务

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中,您不能直接访问嵌入模式上的方法。在向猫鼬谷歌集团提出这个问题后,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
定义与以前相同,因此我将其省略。一旦它变成一个模型,我们仍然可以使用点符号访问它的底层模式对象

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

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'); 
    }
});
当我们将任务模型实例嵌入到列表中时,我们对它们调用
toObject
,将它们的数据转换为
列表.tasks
所期望的普通JavaScript对象。以这种方式保存模型实例时,嵌入的文档将包含
objectid


完整的代码示例如下所示。随着猫鼬的不断发展,希望这些解决办法能帮助事情顺利进行。我对Mongoose和MongoDB还很陌生,所以请在评论中分享更好的解决方案和技巧。快乐数据建模

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

Model是一个对象,它使您能够轻松访问命名集合,允许您查询集合并使用架构验证保存到该集合的任何文档。它是通过组合架构、连接和集合名称创建的


瓦莱里·卡尔波夫(Valeri Karpov)最初的措辞是,

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

我可以理解的一个原因是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);
  });