猫鼬为什么同时具有模式和模型?


92

这两种类型的对象似乎彼此非常接近,以至于两者都显得多余。什么是具有点架构和模式?

Answers:


61

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

在这里看看:

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

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

它给出了在猫鼬模型中使用架构的恰当描述,以及您为什么要这样做,并向您展示了当架构涉及结构等时如何通过模型推送任务。

原始帖子:

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

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使用任务可能具有的基本信息创建了一个新对象。设置了猫鼬虚拟属性以方便地组合任务的名称和优先级。我在这里只指定了一个吸气剂,但也支持虚拟的吸气剂。

我还定义了一个简单的任务方法,isHighPriority以演示方法如何在此设置下工作。

ListSchema定义中,您将注意到如何配置task键以容纳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)实例上的task属性的工作方式类似于常规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中,您不能直接访问嵌入式架构上的方法。有一张开放的票证可以解决它,向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.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

完整的代码示例可作为参考。希望随着猫鼬的不断发展,这些变通办法可以使事情顺利进行。我对Mongoose和MongoDB还是很陌生,所以请随时在评论中分享更好的解决方案和技巧。快乐的数据建模!


3
通常建议不要提交裸露的链接作为对SO中发布的问题的答案,因为该链接可能会停止工作(在这种情况下)。至少复制/粘贴并引用您链接到的文章的相关部分。
Behrang Saeedzadeh 2014年

1
做了-它仍然在谷歌的缓存,这样比较简单
亚当科默福德

1
作为记录,嵌入式文档方法问题已得到修复:github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota

5
我并不想在任何人的游行上都下雨,但是这个答案更像是一个教程:回答如何,但不回答为什么。尽管得票数较少,但我发现以下答案很有帮助:stackoverflow.com/a/22950402/26331
aaaidan 2015年

2
我已经看到了这个答案(并投票赞成),在此之前的两年中,这个答案已经被接受。很高兴能找到一个更好的答案,任何人都不会下雨,而且自2015年2月以来,该问题的评论中一直存在指向您引用的答案的链接,因此我不认为自己需要将此链接
亚当·科默福德

54

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

模型是使您可以轻松访问命名集合的对象,允许您查询集合并使用Schema来验证保存到该集合的任何文档。它是通过组合架构,连接和集合名称来创建的。

最初由MongoDB博客的 Valeri Karpov表达


5

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

我能理解Mongoose会将架构和模型分开的一个原因是,您想从两个架构中构建模型的实例。这种情况可能会带来比值得管理的更为复杂的复杂性-如果您有两个由一个模型管理的模式,为什么它们不是一个模式?

最初的问题也许更多地是传统关系数据库系统的遗迹。在NoSQL / Mongo世界中,架构可能比MySQL / PostgreSQL灵活一些,因此更改架构是更常见的做法。


好像模式与模型还不够,重复自己,在尝试维护匹配的TypeScript接口时会遇到更多重复,在创建GraphQL模式时会遇到更多重复。
Dan Dascalescu

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猫鼬会使我们的生活更轻松。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.