web-dev-qa-db-ja.com

Mongooseにスキーマとモデルの両方があるのはなぜですか?

2種類のオブジェクトは互いに非常に近いため、両方を持つことは冗長に感じられます。 bothスキーマとモデルを持つことのポイントは何ですか?

85
Randomblue

多くの場合、このタイプの質問に答える最も簡単な方法は、例を使用することです。この場合、誰かがすでに私のためにそれを行っています:)

ここを見てください:

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

EDIT:元の投稿(コメントで述べたように)はもう存在しないようですので、以下に再現します。万が一戻った場合、または移動したばかりの場合はお知らせください。

これは、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 仮想属性 は、タスクの名前と優先度を便利に組み合わせるためにセットアップされます。ここではゲッターのみを指定しましたが、仮想セッターもサポートされています。

また、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)のタスク属性は、通常の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グループに質問を投げかけた後、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オブジェクトにアクセスできます。

新しいリストを作成し、その中に2つのタスクモデルインスタンスを埋め込みましょう。

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を呼び出して、データをList.tasksDocumentArrayが必要です。この方法でモデルインスタンスを保存すると、埋め込みドキュメントにはObjectIdsが含まれます。

完全なコード例は 要点として利用可能 です。これらの回避策が、Mongooseの開発が進むにつれて物事をスムーズに進めることを願っています。私はまだMongooseとMongoDBにかなり慣れていないので、コメントでより良いソリューションとヒントを共有してください。ハッピーデータモデリング!

57
Adam Comerford

Schemaは、MongoDBコレクションに保存されるドキュメントの構造を定義するオブジェクトです。すべてのデータ項目のタイプとバリデーターを定義できます。

Modelは、名前付きコレクションに簡単にアクセスできるようにするオブジェクトです。コレクションにクエリを実行し、スキーマを使用して、保存したドキュメントを検証できますコレクション。スキーマ、接続、およびコレクション名を組み合わせて作成されます。

もともとValeri Karpovのフレーズ MongoDBブログ

47

受け入れられた答えが実際に提起された質問に答えるとは思わない。答えは説明しませんwhy Mongooseは、開発者がスキーマとモデル変数の両方を提供することを要求することにしました。データスキーマを定義するためにdeveloperの必要性を排除したフレームワークの例はDjangoです。開発者はモデルをmodels.pyファイルに書き込み、それをフレームワークに任せますスキーマを管理します。私がDjangoで経験したことを考えると、なぜ彼らがこれを行うのかについて頭に浮かぶ最初の理由は、使いやすさです。おそらくもっと重要なのは、DRY(繰り返さないでください)原則です。モデルを変更するときにスキーマを更新する必要はありません。Djangoが自動的に行います! Railsはデータのスキーマも管理します。開発者はスキーマを直接編集するのではなく、スキーマを操作する移行を定義することでスキーマを変更します。

Mongooseがスキーマとモデルを分離することを理解できた理由の1つは、2つのスキーマからモデルを構築するインスタンスです。このようなシナリオでは、管理するよりも複雑さが増す可能性があります。1つのモデルで管理される2つのスキーマがある場合、なぜそれらは1つのスキーマではないのでしょうか。

おそらく、元の質問は、従来のリレーショナルデータベースシステムの遺物である可能性があります。世界NoSQL/Mongoの世界では、おそらくスキーマはMySQL/PostgreSQLよりも少し柔軟であるため、スキーマの変更はより一般的な方法です。

4
johnklawlor

簡単に言えば、

モデルは、MVCデザインパターンで見られるようなデータオブジェクトモデルです。構造データベースに保存するデータの種類とタイプを定義します。データが持つ関係。

schemadatabase schema、データベースに保存されるものの定義。

1