【mongoDB】Mongooseチートシート 

##Mongooseとは
どんな種類のデータを保存できるかを定義する**「スキーマ」**を持つモデルを構築できる。
モデルはjavasrciptにおけるクラスのようなもの。
Mongooseはモデルを使って、データベースクエリの組織化を行う。

##よく使うクエリメソッド

```js
find : マッチするドキュメントの配列を返す。

Human.find({name:"taro" }, (err, result)=> {
  //処理結果
});
```

findOne : 配列でなく一つのドキュメントを返す。　例) Human.findOne({name: "Taro"})
findById : ObjectIdeで一致するオブジェクトを返す。 例)Human.findOne("kjfkajjdfkjagkja")
remove : データーベースにあるドキュメントを削除する。
find().pretty()　:  .pretty()をつけることにより結果がインデントされる。
```
これらのクエリはプロミスを返すので、結果処理には then や catchでつなげる必要がある。

##Node.jsアプリケーションにMongooseを設定する。

```js
//mongooseをロード
const mongoose = require("mongoose");
//データーベース接続を設定
mongoose.connect(
  "mongodb://<username>:<password>@localhost:27017/<dbname>",
  {userNewParser:true}
);

mongoose.set("useCreateIndex", true);

//データーベースをdb変数に代入
const db = mongoose.connection;

//接続メッセージをログに出力する
db.once("open",()=>{
  console.log("success connect");
});
```

##スキーマを作る

mongoose.Schemaメソッドのコンストラクタにパラメーター（オブジェクト）を与えることによって、スキーマオブジェクトを構築できる。

```js
const mongoose = require("mongoose");
const humanSchema = new mongoose.Schema({
  name:String,
  age:Number
});
```

スキーマのデータ型について
https://mongoosejs.com/docs/schematypes.html
####仮想属性を設定する
```js
const humanSchema = new mongoose.Schema({
  name:String,
  age:Number
});

humanSchema.virtual("info").get(function () { //実際の属性としてはデーターベースに保存されないが、インスタンスから取り出せる。
  return `${this.name} ${this.age}`;
});
```


####バリデーションを設定する

```js
"use strict";

const mongoose = require("mongoose");

const humanSchema = new mongoose.Schema({
  name: {  //nameのバリューにオブジェクトで渡す。
    type: String,
    required: true,  //必須
    unique: true     //ユニーク
  },
  age: {
    type: Number,
    min: [18, "Too short"],//最小値エラーメッセージ を設定
    max: 99 //最大値
  }
});

module.exports = mongoose.model("Human", humanSchema);
```

##スキーマをモデルに適用する。

```js
const Human = mongoose.model("Human", humanSchema)
//mongoose.modelメソッドに("モデル名", スキーマ)を引数に与えて、実体化してHumanに格納する。
```

####エクスポート
```js
module.exports = mongoose.model("Human", humanSchema);
```

##スキーマにインスタンスメソッドを設定する。(getInfoと言う名のメソッドを設定)

```js
humanSchema.methods.getInfo = function () {
  return `Name: ${this.name} Age: ${this.age}`;
};
```



##モデルからオブジェクトを作る。

####new → saveの方法

```js
let human1 = new Human({ //newキーワードでオブジェクトを生成。
  name: "Taro",
  age:20
});
human1.save((error, savedDocument) => {  //saveメソッドで保存
  //引数に、エラー処理、または返されるデーターの処理をコールバック関数で渡す。
  if (error){
   console.log(error)
  }
  console.log(savedDocument);
```

####createの方法

```js
Human.create({
  name: "Taro",
  age:20
  },
  function(error,savedDocument){
  if (error){
   console.log(error)
  }
  console.log(savedDocument);
  }
};
//createは、newとsaveを一気に行う。(パラメータ, コールバック)が引数。
```

####フックを設定する。

```models/human.js
humanSchema.pre("save", function(next) {
  //saveされる前に実施する処理
  //この中でのthisは呼び出し元、つまりhumanSchemaが格納される。
  next();
}
```

##モデルを組織化する

モデルファイルにスキーマとモデルを定義する。

```models/human.js
const mongoose = require("mongoose");

const humanSchema = mongoose.Schema({ //mongoose.Schemaメソッドでスキーマを作る
  name:String,
  age:Number
});

module.exports = mongoose.model("Human", humanSchema); 
//mongoose.modelメソッドでスキーマをHumanモデルに適用しエクスポート
```
他のファイルからHumanモデルを呼び出す。

```main.js
const Human = require("./models/human");
```

##モデル間のリレーションを設定する
```js
"use strict";

const mongoose = require("mongoose");

const humanSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
    unique: true
  },
  age: {
    type: Number,
    min: [18, "Too short"],
    max: 99 //最大値
  }
  father: { //Fatherモデルに所属//単数形
    type: mongoose.Schema.Types.ObjectId, //外部キー
    ref: "Father" //参照モデル
    }
  children: [{ //Childモデルを所有//複数形//配列リテラルで囲む
    type: mongoose.Scheme.Types.ObnectId,
    ref: "Child"
    }]
});

module.exports = mongoose.model("Human", humanSchema);
```

####実際にドキュメント同士を関連づける

```js
//所有の場合
human1.children.push(child1);
human1.save();
 //human1インスタンスのchildrenプロパティにchild1ドキュメントを追加
//もしくは、直接ObjectIDを追加する。
human1.children.push("オブジェトID");
human1.save();

//所属の場合
human1.father = father1;
human1.save();
```

####関連付いたドキュメントを取得する。

```js
Human.populate(human1, "children"); //human1に紐づくchildドキュメントを連結してオブジェクト化する。
```
####親を指定して、関連づいたドキュメントを取得する。

```js
Human.findOne({ name: "taro" }).populate({"children" }).exec((error, data) => {
  console.log(data);
}
)
```

##データベース内のドキュメントを検索する

```js
const Human = require("./models/human");

Human.findOne({name:"Taro"}).where("age":20);
//findOne(合致するものを取得).where(絞り込み条件);
```
###クエリを実行する

```js
let query = Human.findOne({name:"Taro"}).where("age":20);

query.exec((error, data)=>{  //execで実行し、処理を引数にとる。返るデータはdataに渡す。
  if(data){
    console.log(data.name)
  }
);
```

##検索とアップデートを同時に行う

```js
 <モデル>.findByIdAndUpdate(<id>, {
      $set: <オブジェクト>//$setのバリューの値でアップデート
    })
```

##検索と削除を同時に行う

```js
<モデル>.findByIdAndRemove(<id>)
```



##プロミスを使うための設定

```js
mongoose.Promise = global.Promise
```

##シードモジュールの作成

```seed.js
const mongoose = require("mongoose"); //モジュールロード

const Human = require("./models/Human.js");
//保存するモデルの読み込み必要

mongoose.connect(  //接続設定
  "mongodb://localhost:27017/my_db",
  {userNewParser:true}
);

mongoose.set("useCreateIndex", true);

mongoose.connection;

const humans = [ //シードデーター
  {name:"Taro",
   age:20
  },
  {name:"Ken",
   age:30
  },
  {name:"Jiro",
   age:40
  }
];

Human.deleteMany() //既存のデータをクリア
  .exec()
  .then(() => {
     console.log("Empty"); //確認用ログ
  });

let commands = []; //シーダー配列用

humans.forEeach((c)=> { //commands配列にシーダーをループで全て追加
  commands.push(Human.create({
    name:c.name,
    email:c.age
    }));
});

Promise.all(commands) //全てのプロミス解決後の処理
  .then(r => {
    console.log(JSON.stringify(r));
    mongoose.connection.close();
  })
  .catch(error => {
    console.log($error);
});
```