MongoDB

来源:互联网 发布:解析json 编辑:程序博客网 时间:2024/06/18 12:49

1传统数据库技术回顾

数据库就是存储数据的,那么存储数据就用txt就行了啊,为什么要有数据库?

理由之1: 数据库有行、列的概念,数据有关系,数据不是散的。
老牌数据库,比如MySQL、SQL Server、Oracle、Access。这些数据库,我们管他们叫做结构型数据库。为什么?因为每个表中,都有明确的字段,每行记录,都有这些字段。不能有的行有,有的行没有。
这里写图片描述

理由之2:数据库能够提供非常方便的接口,让增删改查操作变得简单
我们的老牌数据库,都无一例外的使用SQL语言,管理数据库。
SQL就是structure query language。
比如,查询所有女生: SELECT * FROM step1 WHERE xingbie = ‘女’;
再比如,查询所有女生,并且年龄20~24之间,且在北京:

SELECT * FROM step1 WHERE xingbie = '女' AND nianling < 24 AND nianling >= 20 AND xianzaisuozaidi = '北京';

理由之三:数据库不能自己玩儿,要给向PHP、.net、jsp等语言提供接口。
用php这些语言,能够向数据库之中增删改查。

老牌数据库,都是结构型数据库,现在出了什么问题?
比如,我们现在想往一个已经有1000条数据的数据库中增加一个字段“高中信息”。
这里写图片描述
之前已经存在的数据,实际上不需要增加这个字段。因为这些用户已经填写完毕表单了,不需要再手机高中信息了。我们的意图就是在今后注册的用户,需要填写高中信息。但是,我们刚才说了,所谓的字段,是表的一个结构。所有的行都必须拥有,不能有的行有这个字段,有的行没有这个字段。
可想而知,大数据时代,数据库中有100万条数据都算少的。我们如果要动字段,时间太长。
所以,字段这个东西,太不灵活。

数据不灵活。一个字段,需要是同样类型的数据。不能一行记录是文本,一行记录是数字。

非结构型数据库NoSQL应运而生。
NoSQL是个怪胎,无法挑战老牌数据库,但是在大数据时代有自己的意义。

2 NoSQL

NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

非结构型数据库。没有行、列的概念。用JSON来存储数据。
集合就相当于“表”,文档就相当于“行”。
这里写图片描述

1.数据库 DataBase

● 数据库中存储众多集合。
● 数据库最终会变为文件系统里面的文件,而数据库名字就是相应的文件名,所以数据库的命名,应该遵守操作系统的文件名命名规范。
● 数据库命名不能是admin、local、config

2.集合 Collections

集合就是一组文档,相当于“表”。
这里写图片描述
集合中可以存储完全不同结构的文档。
这里写图片描述

3.文档

NoSQL中,最小的“数据条目”,不是“行”,而是“文档”。
文档就是键值对的一个集合,实际上表达方式和JSON一样。
这里写图片描述

文档就是JSON,但是要比JSON多了一些限制:
● 每个文档必须有一个特殊的键 _id ,这个键在集合中必须是唯一的。
● 文档中的所有键不能重复;大小写不同的键,视为不同的键。
● 文档中键的命名,不能含有.和$,其他不限,甚至可以用中文命名、阿拉伯数字。
● 文档中值的类型,比JavaScript中多了一些,比如日期、ObjectId()、正则表达式。
● 文档给程序员看的时候,是JSON的表示模式,但是实际存储的时候,是BSON方式,即用二级制方式存储。

4.总结NoSQL的特点和优势

我们总结NoSQL数据库在以下的这几种情况下比较适用:
1、数据模型比较简单;
2、需要灵活性更强的IT系统;
3、对数据库性能要求较高;
4、不需要高度的数据一致性;

我们看,有些系统,特别需要筛选。比如,筛选出所有女生大于20岁的。那么SQL型数据库,非常擅长!因为它有行、列的概念。
但是,有些系统,真的不需要进行那么多的筛选,比如站内信。站内信只需要存储就好了。不需要筛选。那么NoSQL的。

NoSQL不是银弹,没有资格挑战老牌数据库,还是特定情况下,是适合的。

3 MongoDB

1. MongoDB基本安装

官网:https://www.mongodb.com/
手册:https://docs.mongodb.org/manual/
win7系统需要安装补丁,KB2731284。
这里写图片描述

此时,我们看一下装好的文件夹:
C:\Program Files\MongoDB\Server\3.0\bin 加入到系统的path环境变量中(这样就可以全局使用MongoDB命令了)
这里写图片描述

2. MongoDB基本使用

那么我们就能在系统的任何盘符,使用mongo命令了:
使用数据库: mongo
开机: mongod
导入数据: mongoimport

1.先开一个CMD:

开机命令:
这里写图片描述

–dbpath就是选择数据库文档所在的文件夹。(c:\mongo是自己在c盘下建立mongo,也就是说我们要自己创建数据库文档所在的文件夹)
也就是说,mongoDB中,真的有物理文件,对应一个个数据库。U盘可以拷走。
一定要保持,开机这个CMD不能动了,不能关,不能ctrl+c。 一旦这个cmd有问题了,数据库就自动关闭了。

2.再开一个CMD:

输入
这里写图片描述

那么,运行环境就是mongo语法了。
列出所有数据库:

show dbs 

使用某个数据库

use 【数据库名字】

如果想新建数据库,也是use。use一个不存在的,就是新建。(例如新建use hcd)

查看当前所在数据库

db

插入数据:
这里写图片描述
student就是所谓的集合。集合中存储着很多json。
student是第一次使用,集合将自动创建。

3.实例:

1号cmd
开机:

mongod --dbpath D:\mongo

2号cmd

mongo       //连接show dbs     //查看数据库use hcd     //新建hcd (如果你不插入数据,hcd的数据库是不会被创建的)switched to db hcddb          //查看当前数据库hcddb.student.insert({"name":"hcd"});     //数据库中新建student集合,并插入数据WriteResult({ "nInserted" : 1 })show collections                   //显示所有的集合studentsystem.indexesdb.student.find()               //查看student集合的数据{ "_id" : ObjectId("599c2fc210caa6deb9584e47"), "name" : "hcd" }

3. MongoDB视图软件 MongoVUE的使用

安装后打开
1.创建连接(要确定本地的mongoDB已经打开)
这里写图片描述
首先现按 +号(没有直接弹出框的话点左上角Connect按钮) 然后弹出窗口,name:自己想起的连接数据库的名字(不是数据库的名字),Server:127.0.0.1(本地服务地址),然后点击Save按钮

2.观察
假如:我们创建连接名字为first,数据库名字为test,集合名字books,集合中有两个文档,
按钮Tree View Table View Text View为查看文档的方式
这里写图片描述

4 MongoDB数据库的使用

(注意:这里是cmd的命令行方式,不用特别记住,还有Node方式)
可以查看手册https://docs.mongodb.org/manual/
这里写图片描述

1 插入数据

插入数据,随着数据的插入,数据库创建成功了,集合也创建成功了。

db.student.insert({"name":"xiaoming"});

我们不可能一条一条的insert。所以,我们希望用sublime在外部写好数据库的形式,然后导入数据库:
注意:mongoimport和mongo是同级的,不能在连接mongo后再执行:mongoimport命令

mongoimport --db test --collection restaurants --drop --file primer-dataset.json

-db test 想往哪个数据库里面导入
–collection restaurants 想往哪个集合中导入
–drop 把集合清空
–file primer-dataset.json 哪个文件

这样,我们就能用sublime创建一个json文件,然后用mongoimport命令导入,这样学习数据库非常方便。

例如:

mongoimport --db hcd --collection student --drop /c/Users/Administrator/Desktop/a.json

将数据存放在hcd数据库中的student的集合中,然后将json拖入命令工具中就行了

2 查找数据

查找数据,用find。find中没有参数,那么将列出这个集合的所有文档

db.restaurants.find()                                   //(列出restaurants集合的所有数据)

精确匹配:

db.student.find({"score.shuxue":70});                  // {a:{b:‘c’}}  就用“a.b”:c表示11    

多个条件:

db.student.find({"score.shuxue":70 , "age":12})

大于条件:

db.student.find({"score.yuwen":{$gt:50}});

寻找所有年龄是9岁,或者11岁的学生

db.student.find({$or:[{"age":9},{"age":11}]});

查找完毕之后,打点调用sort,表示升降排序。

db.restaurants.find().sort( { "borough": 1, "address.zipcode":-1 } )//这里面表示先按照borough来升序排列,如果borough相同,则按照address.zipcode降序排列

3 修改数据

修改里面还有查询条件。你要该谁,要告诉mongo。
默认情况下更改第一个匹配的元素
查找名字叫做小明的,把年龄更改为16岁:

db.student.update({"name":"小明"},{$set:{"age":16}});

查找数学成绩是70,把年龄更改为33岁:

db.student.update({"score.shuxue":70},{$set:{"age":33}});

更改所有匹配项目:”
By default, the update() method updates a single document. To update multiple documents, use the multi option in the update() method.

db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});

完整替换,不出现$set关键字了:

db.student.update({"name":"小明"},{"name":"大明","age":16});

4 删除数据

默认情况下情况满足的都会删除

db.restaurants.remove( { "borough": "Manhattan" } )

By default, the remove() method removes all documents that match the remove condition. Use the justOne option to limit the remove operation to only one of the matching documents.
加上justOne: true表示只删除满足条件的第一个元素

db.restaurants.remove( { "borough": "Queens" }, { justOne: true } )

删除restaurants集合下的所有数据

db.restaurants.remove( {} )

5 Node.js操作MongoDB

首先:
Node.js安装MongoDB
npm install mongodb

API可以查看手册https://docs.mongodb.org/manual/
这里写图片描述
或者直接查看http://mongodb.github.io/node-mongodb-native/2.2/quick-start/quick-start/

案例1:
访问127.0.0.1/都会往数据库写入组对象

var express = require('express');var app = express();//在Node中引入mongodb模块var MongoClient = require('mongodb').MongoClient;//设置要链接的数据库URL,myproject为我们数据库的名字var url = 'mongodb://localhost:27017/myproject';//使用数据库连接服务app.get('/',function(req,res){    // 回调函数中,err为错误,db为数据库    MongoClient.connect(url, function(err, db) {      //在数据库中简历集合'documents'      var collection = db.collection('documents');      //向集合中插入一组文档      collection.insertMany([        {a : 1}, {a : 2}, {a : 3}      ], function(err, result) {             console.log("Inserted 3 documents into the collection");      });      //关闭数据库,正规的就是每一次操作都要打开和闭合数据库      db.close();    });})app.listen(3000);

结果:
这里写图片描述

04—案例01
案例2:
目录:
这里写图片描述
列出所有数据库中的数据
index.ejs:

<!doctype html><html lang="en"><head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">        table,tr,td{            border:1px solid black;            border-collapse: collapse;        }        tr:nth-child(2n){            background-color: pink;        }    </style></head><body>    <h1>学生管理系统</h1>    <a href="/add">添加学生</a>    <table>        <tr>            <th>姓名</th>            <th>年龄</th>            <th>数学分数</th>            <th>语文分数</th>        </tr>        <% for(var i = 0 ; i < result.length ; i++){%>            <tr>                <td><%= result[i].name %></td>                <td><%= result[i].age %></td>                <td><%= result[i].score.yuwen %></td>                <td><%= result[i].score.shuxue %></td>            </tr>        <%}%>    </table></body></html>

add.ejs:

<!doctype html><html lang="en"><head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">    </style></head><body>       <h1>学生管理系统</h1>    <form action="/tijiao" method="get">        <p>            姓名: <input type="text" name="name"/>        </p>        <p>            年龄: <input type="text" name="age"/>        </p>        <p>            数学成绩: <input type="text" name="shuxuechengji"/>        </p>        <p>            语文成绩: <input type="text" name="yuwenchengji"/>        </p>        <p>            <input type="submit"/>        </p>    </form></body></html>

Node.Js:

var express = require("express");var MongoClient = require('mongodb').MongoClient;var app = express();//设置模板引擎app.set("view engine","ejs");//数据库连接的地址,最后的斜杠后面表示数据库名字var shujukuURL = 'mongodb://localhost:27017/itcast';app.get("/",function(req,res){    //先连接数据库,对数据库的所有操作,都要写在他的回调函数里面。    MongoClient.connect(shujukuURL, function(err, db) {        if(err){            res.write("数据库连接失败");            return;        }        //查询数据库,遍历所有数据到docs        db.collection('teacher').find({}).toArray(function(err, docs){            if(err){                res.write("遍历错误");                return;            }            res.render("index",{                "result" : docs            });            db.close();        });    });});app.get("/add",function(req,res){    res.render("add");});app.get("/tijiao",function(req,res){    //得到参数    var name = req.query.name;    var age = req.query.age;    var yuwenchengji = req.query.yuwenchengji;    var shuxuechengji = req.query.shuxuechengji;    MongoClient.connect(shujukuURL, function(err, db) {        if(err){            console.log("数据库连接失败");            return;        }//将学生的数据存到数据库中        db.collection("teacher").insertOne({            "name" : name,            "age" : age,            "score" : {                "shuxue" : shuxuechengji,                "yuwen" : yuwenchengji            }        },function(err,result){            if(err){                console.log("数据库写入失败");                return;            }            res.send("恭喜,数据已经成功插入");            //关闭数据库            db.close();        });    });});app.listen(3000);

结果:
第一次访问http://127.0.0.1:3000/:
这里写图片描述
添加学生http://127.0.0.1:3000/add,并且填写相关的信息:
这里写图片描述
这时我们查看数据库:
这里写图片描述
这时我们再一次查看http://127.0.0.1:3000/会发现:
这里写图片描述
04—案例02

6封装DAO

开发DAO:J2EE开发人员使用数据访问对象(DAO)设计模式把底层的数据访问逻辑和高层的商务逻辑分开.实现DAO模式能够更加专注于编写数据访问代码.
使用我们自己的DAO模块,来实现数据库插入。代码变得简单。

我们这里把常用的增删改查,都封装成为module。(将API进行了简单的封装)
目录结构:
这里写图片描述
db.js就是我们要封装的js:
(下面的查找中包含了分页和总数,可以在7中查看)

//这个模块里面封装了所有对数据库的常用操作var MongoClient = require('mongodb').MongoClient;var settings = require("../settings.js");//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库//封装成为内部函数function _connectDB(callback) {    var url = settings.dburl;   //从settings文件中,都数据库地址    //连接数据库    MongoClient.connect(url, function (err, db) {        if (err) {            callback(err, null);            return;        }        callback(err, db);    });}//插入数据exports.insertOne = function (collectionName, json, callback) {    _connectDB(function (err, db) {        db.collection(collectionName).insertOne(json, function (err, result) {            callback(err, result);            db.close(); //关闭数据库        })    })};//查找数据,找到所有数据。args是个对象{"pageamount":10,"page":10}为第3个参数,为了防止有的时候//可能会不传args而导致的传入的参数只有3个,这时第3个参数不再是args而是callbackexports.find = function (collectionName, json, C, D) {    if (arguments.length == 3) {        //那么参数C就是callback,参数D没有传。        var callback = C;        var skipnumber = 0;        //数目限制        var limit = 0;    } else if (arguments.length == 4) {        var callback = D;        var args = C;        //应该省略的条数,从第0页开始        var skipnumber = args.pageamount * args.page || 0;        //数目限制        var limit = args.pageamount || 0;        //排序方式        var sort = args.sort || {};    } else {        throw new Error("find函数的参数个数,必须是3个,或者4个。");        return;    }    //连接数据库,连接之后查找所有    _connectDB(function (err, db) {        db.collection(collectionName).find(json).skip(skipnumber).limit(limit).sort(sort).toArray(function(err, docs){            if (err) {                callback(err, null);                db.close(); //关闭数据库                return;            }                    callback(null, result);            db.close(); //关闭数据库        })    });}//删除exports.deleteMany = function (collectionName, json, callback) {    _connectDB(function (err, db) {        //删除        db.collection(collectionName).deleteMany(            json,            function (err, results) {                callback(err, results);                db.close(); //关闭数据库            }        );    });}//修改exports.updateMany = function (collectionName, json1, json2, callback) {    _connectDB(function (err, db) {        db.collection(collectionName).updateMany(            json1,            json2,            function (err, results) {                callback(err, results);                db.close();            });    })}//获得数据的总数exports.getAllCount = function (collectionName,callback) {    _connectDB(function (err, db) {        db.collection(collectionName).count({}).then(function(count) {            callback(count);            db.close();        });    })}

Node.js:

var express = require("express");var app = express();var db = require("./model/db.js");//插入数据,使用我们自己封装db模块,就是DAO。app.get("/charu",function(req,res){    //三个参数,往哪个集合中增加,增加什么,增加之后做什么    db.insertOne("teacher",{"name":"小红"},function(err,result){        if(err){            console.log("插入失败");            return;        }        res.send("插入成功");    });});//查找app.get("/du",function(req,res){    //这个页面现在接受一个page参数。    var page = parseInt(req.query.page);  //express中读取get参数很简单    //查找4个参数,在哪个集合查,查什么,分页设置,查完之后做什么    db.find("canguan",{},{"pageamount":6,"page":page},function(err,result){        if(err){            console.log(err);        }        res.send(result);        console.log(result.length);    });});//删除app.get("/shan",function(req,res){    var borough = req.query.borough;    db.deleteMany("canguan",{"borough":borough},function(err,result){       if(err){           console.log(err);       }        res.send(result);    });});//修改app.get("/xiugai",function(req,res){    db.updateMany(        "canguan",      //集合名字        {            "borough":"Manhattan"       //改什么        },        {            $set: { borough: "北京" }     //怎么改        },        function(err,result){   //改完之后做什么            if(err){                console.log(err);            }            res.send(result);        }    );});app.listen(3000);

05—案例02
更多的可以查看
05—案例03

7将查到的数据分页,数据总数

分页,想想我们的百度百家Ajax案例,当时调用了百度的JSON,有一个参数叫做page=3,生成的JSON不一样。

这个就是分页,就是我们想寻找所有的新闻,但是是位于第3页的新闻。那么有两种做法:
1) 错误的做法: 就是讲所有的result都读取到数组,然后进行数据操作,进行分页;
2) 正确的做法: 就是真的在数据库中,只读取这么多内容。

错误的,我们试图每次都读取全部数据,但是这样开销很大。

 var a = [];  db.find("student",{},function(err,result){      for(var i = 10 * page ; i < 10 * (page + 1) ; i++){          a.push(result[i]);      }      res.send(a);  });

所以,mongodb提供了傻傻的两个函数。
limit() 表示找几条数据
skip() 表示基础条数,展示的是skip()基础上的limit()条
这里写图片描述

假如第一页是page=0。每页10条,所以当前页的查询语句:

db.student.find({}).limit(10).skip(page*10)

数据总数怎么得到?
shell中可以查看:

db.student.stats().count;

05—案例02

8索引index

数据库中,根据一个字段的值,来寻找一个文档,是很常见的操作。比如根据学号来找一个学生。
这个学号,是唯一的,只要有学号,就能唯一确认一个学生的文档。学号这个属性,就非常适合建立索引,这样一来,查找学生就变得简单了。

1.这个语句,能够查看检索的过程:

db.student.find({"name":"user888"});

2.学生的姓名是唯一的,为了快速的进行检索,所以就把name属性建立成为“索引”:

db.student.createIndex({"name":1});

这样,今后通过name寻找student文档的时候,速度非常快。因为能够快速的从索引表中,找到这个文档。
缺点就是插入每条数据的时候,时间变慢了,效率低了。但是换回来的就是寻找的速度快了。

3.索引这个属性对应的属性值,所有的文档都不能相同:

db.members.createIndex( { "user_id": 1 }, { unique: true } );

那么”user_id”不能相同
07—案例shuoshuo

githup地址:https://github.com/haochangdi123/cleanUP-Node.js