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


92

2つのタイプのオブジェクトは互いに非常に接近しているように見えるため、両方を持っていることは冗長に感じられます。スキーマとモデルの両方を持つことのポイントは何ですか?

回答:


61

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

ここを見てください:

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

また、isHighPriorityメソッドがこのセットアップでどのように機能するかを示すために呼び出される単純なタスクメソッドも定義しました。

ListSchema定義しますが、タスクキーが配列の保持するように構成されている様子がわかりますTaskSchemaオブジェクトを。DocumentArrayタスクキーは、埋め込まれたMongoドキュメントを処理するための特別なメソッドを提供するインスタンスになります。

今のところ、ListSchemaオブジェクトをmongoose.modelに渡し、TaskSchemaは省略しました。TaskSchema独自のコレクションに保存することはないので、技術的にはを正式なモデルに変換する必要はありません。後で私はあなたがそれが何も害を及ぼさない方法をあなたに示します、そしてそれが特に複数のファイルにまたがり始めるとき、同じようにすべてのモデルを整理するのを助けることができます。

Listモデルのセットアップのは、それにカップルのタスクを追加し、モンゴに保存してみましょう。

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シェルから、新しいリストとタスクが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.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'); 
    }
});

TaskモデルのインスタンスをListに埋め込むのでtoObject、データをList.tasks DocumentArrayが期待しているプレーンなJavaScriptオブジェクトに変換するように要求します。この方法でモデルインスタンスを保存すると、埋め込まれたドキュメントにが含まれますObjectIds

完全なコード例は要旨として入手できます。うまくいけば、Mongooseが開発を続けている間、これらの回避策が物事をスムーズにするのに役立ちます。MongooseとMongoDBはまだかなり新しいので、コメントでより良い解決策やヒントを共有してください。ハッピーデータモデリング!


3
(この場合のように)リンクが機能しなくなる可能性があるため、SOに投稿された質問への回答として裸のリンクを送信しないことをお勧めします。リンクする記事の関連セクションを少なくともコピー/貼り付けして引用します。
Behrang Saeedzadeh 2014年

1
完了-それはまだGoogleのキャッシュに残っていたので、比較的単純でした
Adam Comerford 2014年

1
記録では、埋め込みドキュメントメソッドの問題が修正されました:github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota

5
私は誰かのパレードに雨を降らせようとしているわけではありませんが、この答えはチュートリアルのように読みます。それほどまで票を持つにもかかわらず、私は次の答えははるかに役に立った:stackoverflow.com/a/22950402/26331を
aaaidan

2
私はその答えを見ました(そしてそれを支持しました)。これはその2年前に答えられ、受け入れられました。2015年2月以降、質問のコメントで参照した回答へのリンクがあり、自分でリンクする必要がないと感じました。
Adam Comerford、2015

54

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

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

MongoDBブログ、Valeri Karpovによる元のフレーズ


5

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

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

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


スキーマ対モデルでは十分ではないかのように、対応するTypeScriptインターフェース維持しようとすると、さらに重複が発生し、GraphQLスキーマを作成するとさらに重複します。
Dan Dascalescu

0

理由を理解するには?あなたは実際にマングースが何であるかを理解する必要がありますか?

まあ、mongooseは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);

慣習によれば、モデル名の最初の文字は大文字にする必要があります。

mongooseとスキーマを使用して作成したモデルのインスタンスを作成してみましょう。また、データベースと対話します。

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);
  });

したがって、スケマとマングースの両方を持つと、私たちの生活が楽になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.