Node_MongoDB

weixin_33763244發表於2016-06-06

基本使用

概念

SQL和NoSQL

clipboard.png

文件 Document

clipboard.png

文件就是鍵值對的一個集合,實際上表達方式和JSON一樣
文件就是JSON,但是要比JSON多了一些限制:

  • 每個文件必須有一個特殊的鍵 _id, 這個鍵在集合中必須是保證唯一性.
  • 文件中鍵的命名,不能含有.$
  • 文件中值的額型別,比原生JavaScript更多, 比如:日期,ObjectId(),正規表示式
  • 文件是給使用者看的,是JSON的表示模式,但實際儲存的時候,是BSON方式(用二進位制方式儲存)

集合 Collections

集合就是一組文件(document),相當於“表”
集合中可以儲存完全不同的結構的文件

clipboard.png

資料庫 DataBase

  • 資料庫中儲存眾多的集合
  • 資料庫最終會變成檔案系統裡的檔案,而資料庫名字就是相應的檔名。資料庫的命名應該遵守作業系統的檔案命名規範。
  • 資料庫命名不能是admin,local,config

mongod

mongo 使用資料庫

mongod 開機

mongoimport 匯入資料

mongod --dbpath c:\mongodata
// --dbpath 引數,表示資料庫的存放位置,資料夾必須事先建立
// mongoDB 有真實的物理檔案,對應一個個資料庫。
db.help(); // 在 mongodb 客戶端中獲得幫助

clipboard.png

db.stats(); 顯示資料庫名稱、集合數目,以及資料庫中的文件

clipboard.png

MongoDB資料模型

MongoDB 中的資料模式非常靈活。同一集合中的文件不需要具有同一欄位或結構,集合文件的公用欄位可能包含不同型別的資料。

設計 MongoDB 模式注意問題

  • 根據使用者需求來設計模式。
  • 如果想一起使用物件,請將這些物件合併到一個文件中,否則要將它們分開(但是要確保不需要連線)。
  • 經常複製資料(但要有一定限度),因為與計算時間相比,硬碟空間顯得非常便宜。
  • 在寫入時進行連線,而不能在讀取時連線。
  • 針對經常發生的用例來設計模式。
  • 在模式中實現複雜的聚合。

  • 資料庫中儲存眾多集合。
  • 資料庫最終會變為檔案系統裡面的檔案,而資料庫名字就是相應的檔名,所以資料庫的命名,應該遵守作業系統的檔名命名規範。
  • 資料庫命名不能是adminlocalconfig

基本命令列操作

列出所有資料庫

show dbs;

使用某個資料庫

use students;
// 如果想新建,也是use, use一個不存在的庫名,就是建立一個資料庫。  

檢視當前所在庫

db

集合沒有檢視,需要插入資料之後,能夠看到。 如果真的想建立剛才未存在的資料庫,那麼必須插入一個資料。不需要建立集合。
例如:

db.student.insert({'name': 'xiaoming', 'age': 23});    
//student就是所謂的集合(collections)。集合中儲存著很多json(document)。 
//db. 一個未知的集合名字,這個集合將自動建立。    

mongodb增刪改查

建立資料庫

use + 資料庫名稱 的方式來建立資料庫。use 會建立一個新的資料庫,如果該資料庫存在,則返回這個資料庫。

use mydb

使用命令 db 檢查當前選定的資料庫。

db

使用命令 show dbs 來檢查資料庫列表。

show dbs

剛建立的資料庫(mydb)沒有出現在列表中。為了讓資料庫顯示出來,至少應該插入一個文件。

刪除資料庫

db.dropDatabase();  
// 刪除當前所在的資料庫

匯入外部資料

mongoimport --db test --collection restaurants --dorp --file ddd.json
// --db test 想往那個資料庫中匯入
// --collection restaurants  想往那個集合中匯入
// --dorp 把集合清空  表示資料庫中有預設資料,覆蓋原有資料。  
// --file ddd.json 使用的是哪個檔案.
mongoimport --db student --collection class1 --dorp --file a.json    

建立集合(表)

db.class1   
//如果集合不存在,幫你建立集合

使用 show collections 來檢視建立了的集合。

show collections
// 沒有資料,剛建立的 集合 並不顯示出來。

插入資料

db.class1.insert({'name': 'ting','age': 21}); 

檢視資料(文件)

db.class1.find({});   //db.class1.find();  
//find() 方法會以非結構化的方式來顯示所有文件。
db.class1.find().pretty();
//用格式化方式顯示結果

//在 find() 方法中,如果傳入多個鍵,並用逗號(,)分隔它們,那麼 MongoDB 會把它看成是 AND 條件。    
db.class1.find({'key1': 'val1', 'key2': 'val2'}); 

//若基於 OR 條件來查詢文件,可以使用關鍵字 $or。  //或 的條件
db.class1.find({$or: [{key1: val1, key2: val2}]});

clipboard.png

排序

db.class.find().sort({'sex': 1});  //1 升序

修改資料

// update暗含查詢,修改誰,修改結果  update預設只會修改第一個document,需要第三個引數支援: {'multi': true}
db.class1.update({'name': 'xiaoming'},{$set: {'age': 16}});
// 批量修改
db.class1.update({'score.math': '60'},{$set: {'age': 20}}, {'multi': true}); 
db.class1.update({},{$set: {'age': 20}},{'multi': true});  //第一個引數為空,表示全部資料修改。
// 替換document, 沒有使用$set關鍵字,完整替換
db.class1.uodate({'name': 'xiaoming'},{'age': 12});

刪除資料

db.student1.drop(); // 刪除集合 (籃子都刪除)
db.studnet1.remove({}) //刪除全部 (籃子還存在)
db.student1.remove({'score.math': 80}); //會刪除全部匹配得到的
db.student1.remove({'score.math': 80},{justOne: true}); //刪除匹配的第一個

nodeJs 使用 mongodb

// 安裝 mongodb
npm install mongodb

nodejs 操作 mongodb

// mongo db 驅動
va r MongoClient = require('mongodb').MongoClient;
//資料庫的地址  /student 表示資料庫
// 假如資料庫不存在,沒有關係,程式會自動建立資料庫
var url = 'mongodb://localhsot:27017/student';
// 建立連線
MongoClient.connect(url,function( err,db ){
// db 引數就是連線上資料庫 實體 所有資料庫操作都建立在 實體上。        
  if( err ){
    console.log('資料庫連線失敗');
    return ;
  }
  console.log('資料庫成功連線');
  
  db.collection('student').insertOne({
      'name': 'mlln',
      'age': 23
  },function( err,result ){
      if (err) {
          console.log('插入失敗');
          return ;
      }
      console.log(result);
      res.send('1');
      db.close();
  });
});

// cmd 中檢視資料 比較多 可以使用 it  檢視下一頁

插入資料

db.collection('class1').isnertOne({'name': 'xiaoming', 'age': 21},function( err,result ){
    if( err )
        console.log('資料插入失敗');
    console.log('資料插入成功');    
});

查詢資料

var cousor = db.collection('class1').find({});  
// find({}) //可以輸出查詢條件。find({'score.math': 70});

// 遍歷遊標
cousor.each(function( err,doc ){
    if( doc != null ){
        console.log(doc);  // 輸出每條記錄
    }
});
var cursor = db.collection('class1').find({'score.math': {$gt: 30}});
var cursor = db.collection('class1').find({ 'score.math': {$gt: 20} , 'score.cha': {$lt : 70}});
var cursor = db.collection('class1').find({ $or: [ {'score.math': {$gt: 50}}, {'scror.cha': {$lt: 70}} ] }); 
// 排序   // 1 正序 , -1 反序
var cursor = db.collection('class1').find({}).sort({'score.math': 1});

更新

覆蓋匹配的記錄 //updateOne();

db.collection('class1').updateOne({'name': 'mm'}, {'age': 23},function( err,result ){
    if( err )
        console.log('修改失敗');
    console.log('修改成功');    
});

修改欄位中的值

db.collection.('class1').updateOne({'name': 'mlln'}, { $set: {'age': 24} }, function( err,result ){
    if ( err ) 
        console.log('修改失敗');
    console.log('修改成功');    
});

替換記錄 //replaceOne();

db.collection('class1').replaceOne({'name': 'mlln'}, {'name': 'm1', 'age': 20});    

刪除資料

db.collection('class1').deleteMany({'age',25},function( err,reslut ){
    if( err )
        console.log('刪除失敗');
    console.log('資料刪除成功');   
});

DAO層封裝

mongooose 物件與資料對應,產生ODM思想 , 一個物件new出來之後,資料庫中就存在。


// db模組封裝了所有對資料庫的常用操作
var MongoClient = require('mongodb').MongoClient;
var setting = require('../settings');

// 連線資料庫
function _connectDB(callback) {
  var url = setting.dburl;  // 配置檔案中讀取
  MongoClient.connect(url, function (err, db) {
    callback(err, db);
  });
}

// 插入資料
// 往那個集合中增加,增加什麼,增加之後的事情。
exports.insertOne = function (collectionName, json, callback) {
  _connectDB(function (err, db) {
    if (err) {
      callback(err, null);
      db.close(); // 關閉資料庫
      return;
    }

    db.collection(collectionName).insertOne(json, function (err, result) {
      callback(err, result);
      db.close(); // 關閉資料庫
    });
  });

};

// 查詢資料
// 在那個集合查詢,查什麼,分頁設定,查完之後做什麼
exports.find = function (collectionName, json, args, callback) {
  if (arguments.length == 3) {
    callback = args;
    args = { 'pageamount': 0, 'page': 0 }
  } else if (arguments.length == 4) {
  } else {
    throw new Error('find引數是3個或者四個');
  }

  var result = [];
  // 應該省略的條數
  var skipNum = args.pageamount * args.page;
  // 數目限制
  var limitNum = args.pageamount;

  _connectDB(function (err, db) {
    var cursor = db.collection(collectionName).find(json).skip(skipNum).limit(limitNum);
    cursor.each(function (err, doc) {
      // each 遍歷的時候存在問題,callback 匹配到一條,就執行一次,
      // callback 意義上是隻執行一次,使用陣列解決
      if (err) {
        callback(err, null);
        db.close(); // 關閉資料庫
        return;
      }
      if (doc != null) {
        result.push(doc); //放入結果陣列
      } else {
        // 遍歷結束,沒有更多文件
        callback(null, result);
        db.close(); // 關閉資料庫
      }
    });
  });

};

// 刪除
exports.deleteMany = function (collectionName, json, callback) {
  _connectDB(function (err, db) {
    db.collection(collectionName).deleteMany(json, function (err, result) {
      callback(err, result);
      db.close(); // 關閉資料庫
    });
  });
};

// 修改
exports.updateMany = function (collectionName, json1, json2, callback) {
  _connectDB(function (err, db) {
    if (err) {
      callback(err, null);
      return false;
    }
    db.collection(collectionName).updateMany(json1, json2, function (err, result) {
      callback(err, result);
      db.close();
    });
  });
};

mongoDB分頁

// limit() 表示讀取的條數    
// skip() 表示略過的條數  

limit和skip配合使用就是分頁查詢。
假如,第一個是page=0,每頁10條,所以當前頁的查詢語句

db.student.find().limit(10).skip(page*10);

資料總數
在shell中,找尋資料總數

db.student.stats().count; // 獲得collections總數 // 需要向上取整。
db.student.find().count();
// find() ,有分頁邏輯
exports.find = function( collectionName,json,args,callback ){
  if ( arguments.length == 3 ) {
    callback = args;
    args = {'pageamount': 0, 'page': 0}
  } else if ( arguments.length == 4 ) {
  } else {
    throw new Error('find引數是3個或者四個');
    return false;
  }

  // 應該省略的條數
  var skipNum = args.pageamount * args.page;
  //數目限制
  var limitNum = args.pageamount;

  _connectDB(function ( err,db ) {
    var cursor = db.collection(collectionName).find(json).skip(skipNum).limit(limitNum);
    cursor.each(function ( err,doc ) {
      // each 遍歷的時候存在問題,callback 匹配到一條,就執行一次,
      // callback 意義上是隻執行一次,使用陣列解決
      if ( err ) {
        callback(err,null);
        return ;
      }
      if ( doc != null ) {
        result.push( doc ); //放入結果陣列
      } else {
        //遍歷結束,沒有更多文件
        callback(null,result);
      }
    });
  });
}; 

cookie & session

cookie

需要cookie-parser中介軟體支援

  • HTTP是無狀態協議。簡單地說,當你瀏覽了一個頁面,然後轉到同一個網站的另一個頁面,伺服器無法認識到,這是同一個瀏覽器在訪問同一個網站。每一次的訪問,都是沒有任何關係的。
  • Cookie是一個簡單到爆的想法:當訪問一個頁面的時候,伺服器在下行HTTP報文中,命令瀏覽器儲存一個字串;瀏覽器再訪問同一個域的時候,將把這個字串攜帶到上行HTTP請求中。
  • 第一次訪問一個伺服器,不可能攜帶cookie。 必須是伺服器得到這次請求,在下行響應報頭中,攜帶cookie資訊,此後每一次瀏覽器往這個伺服器發出的請求,都會攜帶這個cookie。

特點

  • cookie是不加密的,使用者可以自由看到;
  • 使用者可以刪除cookie,或者禁用它
  • cookie可以被篡改
  • cookie可以用於攻擊
  • cookie儲存量很小。未來實際上要被localStorage替代,但是後者IE9相容。

作用:
記錄使用者的資訊,購買習慣,猜你喜歡。

express中的cookie, res負責設定cookie, req負責識別cookie。
cookie是沒有跨域限制

cookie使用

var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.get('/',function ( req,res ) {
    // maxAge 在express 是以毫秒為單位
    res.cookie('xihao','tfboys',{
        'maxAge': 9000,
        httpOnly: true
    });
    res.send(req.cookies);
});
app.listen(80);

猜你喜歡

頁面中獲取原先的值,push到陣列中,然後再次設定回來。供首頁訪問。

var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.get('/',function ( req,res ) {
    // maxAge 在express 是以毫秒為單位
    res.send('猜你喜歡' + req.cookies.mudidi );
});
app.get('/gonglve',function ( req,res ) {
    var mudidi = req.query.mudidi;
    //記錄使用者的喜好
    //讀取使用者的喜好,然後把新的資料push設定新的資料
    var mudidiArr = req.cookies.mudidi || [];
    mudidiArr.push( mudidi );
    res.cookie('mudidi',mudidiArr,{
        'maxAge': 9000,
        httpOnly: true
    });
    res.send(mudidi + '旅遊攻略');
});
app.listen(80);

session

需要express-session中介軟體支援

會話,session並不是天生的就有的技術,而是依賴cookie。
用於登陸。當一個瀏覽器禁用cookie的時候,登陸效果消失,或者使用者清除了cookie,登陸也消失。
session比cookie不一樣的地方,session下發的是亂碼,並且伺服器自己快取一些東西。下次瀏覽器的請求,帶著亂碼上來。此時與快取(快取是存在伺服器記憶體中,一旦伺服器關了,快取就消失)進行比較,看看匹配的是哪一個。
所以,一個亂碼,可以對應無限大的資料。(理論上),但是受記憶體限制。
任何語言中,session的使用,是“機理透明”的,它幫你設定cookie的.

clipboard.png

var express = require('express');
var app = express();
var session = require('express-session');
app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: true }
}));


app.get('/',function( req,res ){
    if ( req.session.login == '1' ) {
        res.send( '歡迎'+ req.session.username +'的登陸' );
    } else {
        res.send('沒有成功登陸');
    }
});

app.get('/login',function ( req,res ) {
    req.session.login = '1';
    req.session.username = 'mlln';
    res.send('你已經成功登陸');
});
app.listen(80)

加密

md5 加密 是函式型加密。每次加密的結果相同,沒有隨機位 //永遠不要用明文寫密碼。
特點:

  • 不管加密的文字,多長多短,永遠都是32位英語字母、數字混合。
  • 那怕只改一個字,密文都會大變。
  • MD5沒有反函式的可能。 網上的破解工具,都是字典模式,通過列出明-密對應的字典找到明碼。
  • MD5常用於版本校驗。可以比對兩個檔案、軟體是否完全一致。

node中,自帶了一個模組 crypto模組,負責加密。

//建立hash
var md5 = crypto.createHash('md5');  //建立一個hash對應
//然後update 和  digest
var password = md5.update(fields.password).digest('base64');

console.log( md52( md52('123123').slice(11,7) + md52('123123') ) );
function md52( mingma ) {
    var md5 = crypto.createHash('md5');
    var password = md5.update(mingma).digest('base64');
    return password;
}

GM影像

gm軟體

只要伺服器需要處理圖片,那麼這個伺服器就需要安裝graphicsmagick軟體 , 還有其它的影像處理軟體。ImageMagick 。
GM軟體API
GM-API
nodejs 使用graphicsmagick。需要第三方npm的gm包
GM包執行環境 連結描述

nodejs縮圖製作

var fs = require('fs');
var gm = require('gm');
gm('./axin.jpg')
    .resize(50, 50,"!")
    .write('./axin.jpg', function (err) {
        if (err) {
            console.log(err);
        }
});

nodejs頭像裁切

gm("./axin.jpg").crop(141,96,152,181).write("./2.jpg",function(err){
    //141  96 是寬高 。  152  181是座標
});

索引

index
資料庫中,根據一個欄位的值,來尋找一個文件,是很常見的操作。

檢視檢索的過程

db.student.find({'name': 'username33'}).explain();

// 過程顯示    
{
  "cursor" : "BasicCursor",
  "isMultiKey" : false,
  "n" : 2,
  "nscannedObjects" : 19999,
  "nscanned" : 19999,
  "nscannedObjectsAllPlans" : 19999,
  "nscannedAllPlans" : 19999,
  "scanAndOrder" : false,
  "indexOnly" : false,
  "nYields" : 0,
  "nChunkSkips" : 0,
  "millis" : 16,
  "indexBounds" : {

  },
  "server" : "YOS-01409231922:27017"
} 

為了快速進行檢索,可以把唯一欄位,建立成"索引".
每個collection是已經有一個索引(預設索引_id),再建立另外索引會具備兩條索引。

建立索引:

db.student.createIndex({ 'name': 1 }); // 1 表示的是正向。

索引的優缺點:
好處:通過索引查詢的速度會更快,因為可以從索引表中找到,當前個文件。
壞處:當資料庫中的資料不存在的時候,已經建立的索引會存在。以後插入的資料會變慢。 損失的插入的時間。得到的是查詢的時間。

複合索引

db.student.createIndex({ 'name': 1, 'address': -1 }); // 1 表示的是正向。 -1 表示負向

先以第一個條件排序,然後相同的時候以第二個條件再進行排序。(插入資料之前是一條預設的索引,索引建立之後是3條索引)

db.student.createIndex({ 'name': 1 }, {unique: true}); // unique 所有的collection都不能相同,如果相同會報錯。

適用範圍:網站並沒有大量插入的操作。(例如:同一時段並沒有大量的人在註冊)

DAO層中新增init函式使用索引:

function init() {
  // 對資料庫進行一個初始化
  _connectDB(function (err, db) {
    if (err) {
      console.log(err);
      return;
    }
    // 建立索引
    db.collection('users').createIndex(
      { 'username': 1 },
      null,
      function (err, results) {
        if (err) {
          console.log(err);
          return;
        }
        console.log('索引建立成功');
      }
    );
  });
}