# MongoDB

## 使用MongoDB

```
Mongo資料庫 > collection > document

SQL資料庫   >   資料表   > 資料列
```

使用MongoLab 當練習範例

!!注意，MongoLab於2016二月底改名為mLab，連結的資料庫路徑也改了

```
https://mlab.com/home
```

1.先註冊帳號

2.創建資料庫，選地區時記得選擇有免費的

3.創建database內的user

4.創建database內的collection

5.在collection內加入document

### 如何測試剛創好的資料庫?

使用Robomongo <https://robomongo.org/>

點選Download，選最下面的免費選項下載

1.下載完後開啟，先點選左上方的create

2.設定一下相關database url、port、username、password(database的，不是你帳戶的)

3.點選左下Test，如成功即可點選save，並進行connect

可參考<http://www.codedata.com.tw/database/mongodb-tutorial-1-setting-up-cloud-env/>

## 如何用Node.js連線到mLab?

> 注意：使用mLab註冊帳號後要去新增使用者，之後連線的url中的dbuser是你之後新增的使用者名稱跟密碼，不是輸入帳號

先使用npm 安裝

```
npm install mongodb
```

```
var mongo = require('mongodb');
var Server = mongo.Server;
var Db = mongo.Db;
```

設定server及資料庫

```
var server = new Server('ds013898.mlab.com',13898, {auto_reconnect : true});
var db = new Db('forclass', server);
```

填入database中user帳號及密碼(不是MongoLab的登入帳密)

```
db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")
      }else{
          console.log("client.authenticate error")
      };

    });
});
```

完成後如下，啟動server後如在terminal中出現connect success 即表示成功連線

```
var express = require('express');

////
var mongo = require('mongodb');
var Server = mongo.Server;
var Db = mongo.Db;

var server = new Server('ds013898.mlab.com',13898, {auto_reconnect : true});
var db = new Db('forclass', server);


db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")
      }else{
          console.log("client.authenticate error")
      };

    });
});

////
var app = express();





app.listen(8080);
```

## 開始操作資料庫

```
Relation DataBase                         MongoDB
------------------------------------------------------------
資料庫(Database)                           DataBase
資料表(Table)                              Collection
資料(Record/Row)                           Document
欄位(Column)                               Field
主索引(PK)                                 _id
function                                  function ( )
stored procedure                          mapreduce
```

1.先根據官方範例在MongoLab引入一個範例collection名為apple,而裡面的document如下

```
{
  "address": {
     "building": "1007",
     "coord": [ -73.856077, 40.848447 ],
     "street": "Morris Park Ave",
     "zipcode": "10462"
  },
  "borough": "Bronx",
  "cuisine": "Bakery",
  "grades": [
     { "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
     { "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
     { "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
     { "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
     { "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
  ],
  "name": "Morris Park Bake Shop",
  "restaurant_id": "30075445"
}
```

```
{
  "address": {
     "building": "1007",
     "coord": [ -73.856077, 40.848447 ],
     "street": "Morris Park Ave",
     "zipcode": "10462"
  },
  "borough": "test1",
  "cuisine": "banana",
  "grades": [
     { "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
     { "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
     { "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
     { "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
     { "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
  ],
  "name": "apple Shop",
  "restaurant_id": "12345"
}
```

接著回到index.js將code改成如下，應可看到query 出整個collection內容

```
var express = require('express');

////
var mongo = require('mongodb');
var Server = mongo.Server;
var Db = mongo.Db;

var server = new Server('ds013898.mlab.com',13898, {auto_reconnect : true});
var db = new Db('forclass', server);


db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")
       var cursor = db.collection('apple').find();

        cursor.each(function(err, doc) {

         console.log(doc);
        db.close();
   });
      }else{
          console.log("client.authenticate error")
      };

    });
});

////
var app = express(); 



app.listen(8080);
```

2.如果在find()，裡面放入參數，會query出所有符合的document

```
var cursor = db.collection('apple').find({ "borough": "Bronx" });
```

舉例:\
假設有個inventory collection裡面有三個資料\`

```
{ _id: 5, type: "food", item: "aaa", ratings: [ 5, 8, 9 ] }
{ _id: 6, type: "food", item: "bbb", ratings: [ 5, 9 ] }
{ _id: 7, type: "food", item: "ccc", ratings: [ 9, 5, 8 ] }
```

執行 db.inventory.find( { ratings: \[ 5, 8, 9 ] } )

將返回

```
{ "_id" : 5, "type" : "food", "item" : "aaa", "ratings" : [ 5, 8, 9 ] }
```

### 移除document

```
var cursor = db.collection('apple').remove({});
```

### 新增document

```
var cursor = db.collection('apple').insert({
    title: 'web課程', 
    description: 'test ',
    by: 'eason',
    url: 'hi',
    tags: ['hello'],
    likes: 200
});
```

### 運用operator

以下參考至<http://www.runoob.com/mongodb/mongodb-operators.html>

先輸入三組document

```
db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")

    db.collection('apple').insert({
    title: 'web課程', 
    description: 'test ',
    by: 'eason',
    url: 'hi',
    tags: ['hello'],
    likes: 50
});
        db.collection('apple').insert({
    title: 'web課程', 
    description: 'test ',
    by: 'eason',
    url: ['hi'],
    tags: 'hello',
    likes: 100
});
         db.collection('apple').insert({
    title: 'web課程', 
    description: 'test ',
    by: 'eason',
    url: true,
    tags: ['hello'],
    likes: 200
});

      }else{
          console.log("client.authenticate error")
      };

    });
});
```

接著改成查詢的code如下

```
db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")
       var cursor = db.collection('apple').find({likes : {$gt : 100}});

        cursor.each(function(err, doc) {

         console.log(doc);
     db.close();
   });
      }else{
          console.log("client.authenticate error")
      };

    });
});
```

## $gt代表大於

## $lt代表小於

## $gte大於等於

## $lte小於等於

### 使用type操作

```
db.open(function(err, client) {
    client.authenticate('forclass1', 'test123', function(err, success) {
        if(success){
        console.log("connect success")
       var cursor = db.collection('apple').find({url : {$type : 8}});

        cursor.each(function(err, doc) {

         console.log(doc);
     db.close();
   });
      }else{
          console.log("client.authenticate error")
      };

    });
});
```

type的值，數字對照表

```
Double    1     
String    2     
Object    3     
Array    4     
Binary data    5     
Object id    7     
Boolean    8     
Date    9     
Null    10     
Regular Expression    11     
JavaScript    13     
Symbol    14     
JavaScript (with scope)    15     
32-bit integer    16     
Timestamp    17     
64-bit integer    18
```

## 使用limit()

如果為參數1代表讀到一個document，如果為五代表讀前五個document

```
var cursor = db.collection('apple').find().limit(1);
```

## 使用skip()

與limit相反，跳過skip參數個document，都是從前面往後數

```
var cursor = db.collection('apple').find().skip(2);
```

## 使用sort()

根據name的值去排列，而不是根據document的index順序

```
var cursor = db.collection('apple').find().sort({"likes":-1})
```

## 比較這兩個Find()

```
var cursor = db.collection('apple').find({},{likes:1, _id: 0});
var cursor = db.collection('apple').find({likes : {$gt : 100}},{likes:1, _id: 0});
```

發現find()的第一個參數代表:我們要從哪個地方去找東西

第二個參數代表:從那個地方要找那些東西出來

第二個參數中物件的值只有0和1，指定其他數和指定1的效果相同，0為不顯示

參考至:

中文:

<http://calvert.logdown.com/posts/159792-sql-to-mongodb-mapping-chart>

英文:官方doc:

<https://docs.mongodb.org/manual/reference/method/db.collection.createIndex/#db.collection.createIndex>

英文:官方github.io:

<http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findOne>

### 如果是下載到local端啟用

```
1.先到你的Mongo資料庫下bin的外面創建資料夾
   2.cd到bin裡面把路徑複製
   3.使用admin開起cmd在cd到剛複製的路徑
   4.執行mongod --dbpath ../資料夾名稱/
   5.即可使用robomongo連線
```

1.建造database

```
use 資料庫名稱  //如不存在即會創建新的
```

ps:用Robomongo執行以上指令如發現沒出現，需要點選新連線，才會出現

2.建造collection

```
db.createCollection("apple")
```

## Node.js 連接

```
var MongoClient = require('mongodb').MongoClient
  , assert = require('assert');

// Connection URL
var url = 'mongodb://localhost:27017/myproject';
// Use connect method to connect to the Server
MongoClient.connect(url, function(err, db) {
  assert.equal(null, err);
  console.log("Connected correctly to server");

  db.close();
});
```

## Mongo有新增的連接方法，參考下面文章

主要是寫關於require('mongodb').Db和require('mongodb').MongoClient的區別\
(其告知MongoClient為較新的方法，推薦使用)

<http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html>

## 使用Mongoose

> mongoose query 的物件沒辦法直接修改，要用 lean() 後才可修改，且 lean 可增加許多 query 效能

<https://stackoverflow.com/a/68553745/4622645>

> 如出現連線字串後加入/\<db name> 出現 auth error 請參考以下模板連線方式：
>
> <https://github.com/EasonWang01/Nodejs-server-API-boilerplate>

介紹

```
Schema  ：  描述數據結構

Model   ：  由Schema生成的模型

Entity  ：  由Model創建的實體
```

開始使用

1\.

```
var mongoose = require('mongoose');
mongoose.connect('mongodb://user:pass@host:port/dbs');
```

(可點選mLab的tools標籤，看相關連線資料)\
如何抓取連線時的錯誤

```
mongoose.connect('mongodb://forclass1:test123@ds013898.mlab.com:13898/forclass',function(err){
    if(err){throw err};
});
```

如何抓取正確連線到資料庫的訊息

```
db.once('open', function() {
  console.log("connect mongo")
});
```

如何抓取連線後執行時的錯誤

```
db.on('error', console.error.bind(console, 'connection error:'));
```

完整

```
var mongoose = require("mongoose");
mongoose.connect('mongodb://forclass1:test123@ds013898.mlab.com:13898/forclass',function(err){
    if(err){throw err};
});
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("connect mongo")
});
```

(存入資料時如collection名稱不存在則會自動建立)

2\.

定義model(這裡省略先定義schema，直接定義在MODEL內)

第一個參數為collection的名稱

```
var Cat = mongoose.model('Cat', {
  name: String,
  friends: [String],
  age: Number,
});
```

3.存入資料(產生實體)

```
var kitty = new Cat({ name: 'Zildjian', friends: ['tom', 'jerry']});
kitty.age = 3;
```

4.使用save才真的存入

```
kitty.save(function (err) {
  if (err) // ...
  console.log('meow');
});
```

ps:如果存入資料的欄位不在schema內則不會存入

ps:如省略某些欄位沒寫，則不會顯示，亦可正常存入

```
var mongoose = require('mongoose');
mongoose.connect('mongodb://forclass1:test123@ds013898.mlab.com:13898/forclass');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("connect");
});
var Cat = mongoose.model('Cat', {
  name: String,
  friends: [String],
  age: Number,
});


var kitty = new Cat({ name: 'Zildjian', friends: ['tom', 'jerry']});
kitty.age = 3;

//使用save方法後才會存入
kitty.save(function (err) {
  if (err) // ...
  console.log('meow');
});
```

## 使用Promise

```
var User = mongoose.model('ac', new mongoose.Schema({
    name:{type: String, unique: true},
    password:String
}));
var list = new User({name:"s", password:"123456"});
list.save()
.then(a => console.log(a))
.catch(err => console.log(err));
```

### 注意：記得res.end回傳資料要先`JSON.stringify`

## 查詢資料

```
var Cat = mongoose.model('Cat', {
  name: String,
  friends: [String],
  age: Number,
});
Cat.find({},function(err,doc){

    console.log(doc);
});
```

得知必須使用先前定義好Model才能查找

但如果改成下面呢?

```
var Cat = mongoose.model('Cat', {});
Cat.find({},function(err,doc){

    console.log(doc);
});
```

發現一樣可查找，而上面的例子我們將Schema留空，於是我們知道，可以只提供collection的參數即可，後面Schema參數如果不寫會報錯，但其可接受空的物件當參數。

#### 其他查找方法和原生相似

```
Cat.find({},{_id:1},function(err,doc){

    console.log(doc);
});
```

find()

1.第一個參數為要搜尋哪些document

2.第二個參數為要顯示document內的那些資料(1代表要，0代表不要)

3.第三個參數為一個function(err,doc)\
，讀取到的資料會顯示在doc這

```
var find = Cat.find({},{time:1,_id:0},function(err,doc){
res.render("home",{text:doc});

});
```

### 我們也可先定義Schema在把他compile到model內

```
var kittySchema = mongoose.Schema({
    name: String
});

var Kitten = mongoose.model('cats', kittySchema);
```

這樣和上面直接將schema在model內定義是相同的，不同之處在於有了例外定義的Schema，我們可以幫Schema指定方法

但記得使用methods函式指定方法的話，要放在model實例化之前

```
kittySchema.methods.speak = function () {
  var greeting = this.name
    ? "Meow name is " + this.name
    : "I don't have a name";
  console.log(greeting);
}
```

完整版

```
var mongoose = require('mongoose');
mongoose.connect('mongodb://forclass1:test123@ds013898.mlab.com:13898/forclass');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("connect");
});
var kittySchema = mongoose.Schema({
    name: String
});

kittySchema.methods.speak = function () {
  var greeting = this.name
    ? "Meow name is " + this.name
    : "I don't have a name";
  console.log(greeting);
}
//////////method要定義在model實例化之前
var Kitten = mongoose.model('cats', kittySchema);

var fluffy = new Kitten({ name: 'fluffy' });
fluffy.speak(); // "Meow name is fluffy"
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://easonwang.gitbook.io/class/mongodb.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
