MongoDB入门

数据库服务器本质上是一个用于存储数据的服务器;数据库服务器中能够放多个数据库。javascript

1、3个概念

数据库(database)html

数据库是一个仓库;能够在仓库中放多个集合。
复制代码

集合(collection)java

集合相似于数据;在集合中能够存放多个文档。
复制代码

文档(document)mongodb

文档是数据库中的最小单位,咱们存储操做的内容都是文档。
复制代码

3个概念的关系数据库

在MongoDB中,数据库和集合都不须要手动建立,当咱们建立文档时,若是文档所在的集合或者数据库不存在会自动建立数据库。json

2、数据库操做(database)

启动数据库命令api

mongo
复制代码

查询全部数据库数组

show dbs;
//or
show databases;
复制代码

进入(建立)数据库服务器

//进入(建立)my_test数据库
use my_test;
复制代码

若是my_test数据库存在,则直接进入数据库;mongoose

若是my_test不存在,则建立并进入数据库。

3、集合操做(collection)

新建集合

//新建集合:number
db.createCollection('number')
复制代码

查看集合List

show collections;
复制代码

删除集合

/** * @collectionName [集合名] */
db.collectionName.drop();
复制代码

4、1.文档操做——新增

友情提示:

文档——增、删、改、查的案例是基于以下的基础上:

​ 新建一个数据库todos,新建一个集合todo

增删改查(CRUD)官网API地址

insertOne()

插入一条数据

/** * @todo [集合名] * @obj {Object} [要插入的数据] * * @TODO: * 只能插入一条 */
db.<collection>.insertOne(obj);
复制代码

Example:

db.todo.insertOne({ name: 'a1', age: 10 })
//查询todo集合中的全部文档
db.todo.find();
//结果:{ "_id" : ObjectId("5cf64b69743e692169bbdbf7"), "name" : "a1", "age" : 10 }
复制代码

insertMany()

插入多条数据

/** * @arr {Array|Object} [要插入的多条数据] */
db.<collection>.insertMany(arr)
复制代码

Example:

db.todo.insertMany([ { name: 'a2', age: 11 }, { name: 'a3', age: 12 } ]);
db.todo.find();
/** 结果: { "_id" : ObjectId("5cf64e57743e692169bbdbfa"), "name" : "a1", "age" : 10 } { "_id" : ObjectId("5cf64e5e743e692169bbdbfb"), "name" : "a2", "age" : 11 } { "_id" : ObjectId("5cf64e5e743e692169bbdbfc"), "name" : "a3", "age" : 12 } */
复制代码

insert()

插入多条or插入单条

/** * @data {Object|Array} [要插入的单条或者多条数据] * * @TODO: * * 即2个方法的合集 */
db.todo.insert(data);
复制代码

Example: 同上面insertOne()insertMeny()的用法相同。

关于_id

当插入一个文档没有指定_id,则文档会根据时间戳自动生成一个_id的值。

若是指定了_id,则使用_id做为ID。

4、2.文档操做——查询

find()

返回全部符合条件的数据;

/** * @obj {Object} [须要筛选的条件] * @return {Array} [符合条件值的集合] */
db.todo.find(obj)
复制代码

Example:

查询name = a1的数据

db.todo.find({ name: 'a1' });
// { "_id" : ObjectId("5cf67ae40f133022c604d5b8"), "name" : "a1", "age" : 10 }
复制代码

findOne()

返回符合条数据中的第一条数据;

Example:

/** * @obj {Object} [须要筛选的条件] * @return {Object} [符合条件的值] */
db.todo.find(obj)
复制代码

count()

查询符合条件的数据有多少条;

Example:

/** * @obj {Object} [须要筛选的条件] * @return {Number} [符合条件的值] */
db.todo.find(obj).count();
复制代码

4、3.文档操做——修改

update()

替换1个或者n个文档的对象;

修改1个或者n个文档的对象的值;

删除1个或者n个文档的对象的键;

/** * @filterObj {Any|Object} [筛选条件] * @newObj {Any} [新的值] * $set {Object} [修改对象的值] * $unset {Object} [删除对象的键] * @config {Object} [配置项] * * multi {Boolean} [是否修改多个对象][default:false] * @TODO: * * 默认值修改筛选条件的第一个对象 */
db.<collection>.update(filterObj, newObj, config);
复制代码

Example:

//将age=10的所有数据的name改成a14 
db.todo.update(
    {
        //筛选出age=10的数据
        age: 10
    }, 
    { 
        //将原数据中的name设置为'a14'
    	$set: { 
        	name: 'a14'
        }
    }, 
    { 
        //修改多个数据
    	multi: true
    }  
)

//删除掉 第一条name=a14的这条数据的name属性
db.todo.update(
	{ 
    	name: 'a14'
    },
    {
        $unset: {
            name: 'a14'
        }
    }
)
复制代码

$unset$set是MongoDB的文档操做符;

更多的内容请查阅:MongoDB官网操做符

updateOne()

更新一条数据;

updateMany()

更新多条数据;

replaceOne()

替换第一条数据;

4、4.文档操做——删除

@TODO:

​ 在实际开发中,这个操做并不经常使用,由于实际上全部的删除操做知识改变了数据的状态,保证不会被输出,可是实际上数据仍是存在于数据库中。

remove()

删除多个文档或第一个;

deleteOne()

删除第一个;

deleteMany()

删除多个;

Example All Api

/** * 默认:删除符合条件全部的文档 @removeOne: true; 只删除查询条件的第一个 **/
db.<collection>.remove(query, removeOne);

//删除多个
db.<collection>.deleteOne(query);

//删除多个
db.<collection>.deleteMany(query);
复制代码

4、5.文档操做——练习题

  • 习题的知识点汇总:

比较操做符:

$gt:大于

$gte:大于等于

$lt:小于

$lte:小于等于

$eq:等于,必需要全等;

修改/新增/删除操做符

$set:修改[value];新增{[key]: [value]}

unset: 删除[key]

添加操做符

$push:向数组中添加一个值

$addToSet:向数组中添加一个值(被添加进来的值在这个数组中必须不存在)

方法

limit(n):取出n条数据;

skip(m):跳过第m条以前的数据,即从m+1条开始取。

Example

/** * [分页] * @page {Number} [当前页] * @page_size {Number} [每页条数] * * @return {Object} * @data {Array} [符合条件的数据] * @total {Number} [总数量] */
function getDataList(page, page_size) {
    var skip = ( page - 1 ) + page_size;
    var data = db.<collection>.find({/*筛选条件*/});
    return {
        data: data.limit(page_size).skip(skip),
        total: data.count()
    };
}

复制代码
  • 习题:

  • 答案:

//1
use my_test

//2
db.user.insert({ username: 'sunwukong' });

//3
db.user.find();

//4
db.user.insert({ username: 'zhubajie' });

//5
db.user.find();

//6
db.user.find().count();

//7
db.user.find({ username: 'sunwukong' })

//8
db.user.update(
    {
        username: 'sunwukong'
    },
    {
        $set: {
            address: 'huaguoshan'
        }
    },
    {
        //修改多个
        multi: true
    }
)

//9
db.user.update(
    {
        username: 'zhubajie'
    },
    {
        $set: {
            username: 'tangseng'
        }
    },
    {
        multi: true
    }
)

//10
db.user.update(
    {
        username: 'sunwukong',
    },
    {
        $unset: {
            // 这里的值随便填写,主要是给:`$unset`添加一个键,准备删除。
            address: 'delete'
        }
    },
    {
        multi: true
    }
)

//11
db.user.update(
    {
        username: 'sunwukong'
    },
    {
        $set: {
            hobby: {
                cities: ['beijing', 'shanghai', 'shenzhen'],
                movies: ['sanguo', 'hero']
            }
        }
    },
    {
        multi: true
    }
)

//12
db.user.update(
    {
        username: 'tangseng'
    },
    {
        $set: {
            hobby: {
                movies: ['A Chinese Odyssey', 'King of comedy']
            }
        }
    },
    {
        multi: true
    }
)

//13
db.user.find(
    {
        'hobby.movies': 'hero'
    }
)

//14
db.user.update(
    {
        username: 'tangseng'
    },
    {
        // 操做符:$push能够向数组中添加数据
        // 操做符: $addToSet也能够;区别是`$addToSet`对于重复数据不添加;$push则不会。
        $push: {
            'hobby.movies': 'Intersterllar'
        }
    }
)

//15
db.user.remove(
    {
        'hobby.cities': 'beijing'
    }
)

//16
db.user.drop();

//17.插入20,00条数据
var data = [];

for(var i = 1; i <= 1000*20; i++) {
        data.push( { num: i } )
}

db.numbers.insert(data) //建立numbers集合并插入2w条数据

//18.查询num = 500的数据
numbers.find({ num: 500 })
//or
numbers.find({ num: { $eq: 500 } })
//区别:
// 前者会匹配数组中值为500的数据;
// 后者只匹配num=500的数据;

//19.查询num>5000的文档
numbers.find({ num: { $gt: 5000 })

//20.查询num>=19996的文档
numbers.find({ num: { $gte: 19996 })

//21.查询num<30的文档
numbers.find({ num: { $lt: 30 })

//22.查询 40 < num < 50 的文档
numbers.find({ num: { $lt: 50, $gt: 40 } })

//23.查询集合中前10条数据
numbers.find().limit(10)

//24.查询集合中第11-20条数据
numbers.find().limit(10).skip(10)

//25.查询第21-30条数据
numbers.find().limit(10).skip(20)

复制代码

5、1.文档间的关系

一对一(one to one)

一我的 对应 一个身份证号码

db.human.insert({
    name: 'david',
    idcard: {
        idcard: 123465789102345678
    }
})
复制代码

一对多(one to many)/ 多对一(many to one)

一个帐户 对应 多个订单

/** 用户david有订单 d一、d2,可使用d一、d2查询到商品信息; 商品经过user_id能够查询到用户; 一个订单只能对应一个用户,一个用户能够拥有多个订单。 */
db.users.insert({
    _id: 1,
    name: 'david',
    //订单
    orders: ['d1', 'd2']
})

db.orders.insert([
    {
        user_id: 1,
        id: 'd1',
        name: '商品1'
    },
    {
        user_id: 1,
        id: 'd1',
        name: '商品1'
	}
])
复制代码

多对多(many to many)

多个老师对应多个学生

/**

*/

db.teachers.insert([
    {
        tid: 't1',
        name: 't1',
        students: ['s1', 's2']
    },
    {
        tid: 't2',
        name: 't2',
        students: ['s1', 's2', 's3']
    },
    {
        tid: 't3',
        name: 't3',
        students: ['s1']
    }
])

db.students.insert([
    {
        sid: 's1',
        name: 's1
        students: ['t1', 't2']
    },
    {
        sid: 's2',
        name: 's2',
        students: ['t1', 't2', 't3']
    },
    {
        sid: 's3',
        name: 's3',
        students: ['t3']
    }
])
复制代码

5、2.文档间的关系——习题

知识点概括:

$or:或操做符,详细用法见29题答案

$inc:自增操做符,详细用法见33题答案

原始数据:

//dept.json
/* 1 */
{
    "_id" : ObjectId("5cf7c7c0ffd6ea864b5a22cc"),
    "deptno" : 10.0,
    "dname" : "财务部",
    "loc" : "北京"
}

/* 2 */
{
    "_id" : ObjectId("5cf7c7c0ffd6ea864b5a22cd"),
    "deptno" : 20.0,
    "dname" : "办公室",
    "loc" : "上海"
}

/* 3 */
{
    "_id" : ObjectId("5cf7c7c0ffd6ea864b5a22ce"),
    "deptno" : 30.0,
    "dname" : "销售部",
    "loc" : "广州"
}

/* 4 */
{
    "_id" : ObjectId("5cf7c7c0ffd6ea864b5a22cf"),
    "deptno" : 40.0,
    "dname" : "运营部",
    "loc" : "深圳"
}

//emp.json
/* 1 */
{
    "_id" : ObjectId("5cf7ca79ffd6ea864b5a22d0"),
    "empno" : 7369.0,
    "ename" : "林冲",
    "job" : "职员",
    "mgr" : 7902.0,
    "hiredate" : ISODate("1980-12-16T16:00:00.000Z"),
    "sal" : 1200.0,
    "depno" : 20.0
}

/* 2 */
{
    "_id" : ObjectId("5cf7ca79ffd6ea864b5a22d1"),
    "empno" : 7499.0,
    "ename" : "孙二娘",
    "job" : "销售",
    "mgr" : 7698.0,
    "hiredate" : ISODate("1981-02-19T16:00:00.000Z"),
    "sal" : 1600.0,
    "comm" : 300.0,
    "depno" : 30.0
}

/* 3 */
{
    "_id" : ObjectId("5cf7ca79ffd6ea864b5a22d2"),
    "empno" : 7521.0,
    "ename" : "扈三娘",
    "job" : "销售",
    "mgr" : 7698.0,
    "hiredate" : ISODate("1981-02-19T16:00:00.000Z"),
    "sal" : 800.0,
    "comm" : 500.0,
    "depno" : 30.0
}

/* 4 */
{
    "_id" : ObjectId("5cf7ca79ffd6ea864b5a22d3"),
    "empno" : 7566.0,
    "ename" : "卢俊义",
    "job" : "经理",
    "mgr" : 7839.0,
    "hiredate" : ISODate("1981-02-19T16:00:00.000Z"),
    "sal" : 2975.0,
    "depno" : 20.0
}

/* 5 */
{
    "_id" : ObjectId("5cf7ca79ffd6ea864b5a22d4"),
    "empno" : 7654.0,
    "ename" : "潘金莲",
    "job" : "销售",
    "mgr" : 7839.0,
    "hiredate" : ISODate("1981-02-19T16:00:00.000Z"),
    "sal" : 2975.0,
    "depno" : 20.0
}

复制代码

问题:

问题答案:

var emp = db.getCollection('emp')
var dept = db.getCollection('dept')

//33.为全部工资低于1000的员工增长工资400元
emp.update({ sal: { $lte: 1000 } }, { $inc: { sal: 400 } })

//32.查询全部mgr为7698的全部员工
emp.find({ mgr: 7698 });

//31.查询销售部全部员工
var cwbId = dept.findOne({ dname: '销售部' }).deptno
emp.find({depno: cwbId})

//30.查询财务部的全部员工
var cwbId = dept.findOne({ dname: '财务部' }).deptno
emp.find({depno: cwbId})

//29.工资 >= 2500 或者 <= 1000
emp.find({ $or: [{sal: { $gte: 2500 }}, { sal: { $lte: 1000 } }] })

//28.工资1000-2000
emp.find({ sal: { $lte: 2000, $gte: 1000 } })

//27.工资<= 2000
emp.find({ sal: { $lte: 2000 } });
复制代码

6、排序和投影

排序

sort()指定查询文档的筛选条件; sort()limit()skip()排序部分前后,由于总会先进行排序。

1:升序排列

-1:降序排列

默认:若是不指定sort(),则按照_id排序,而_id = 时间戳 + 机器码组成,因此实际上默认是按照建立时间排序。

Example

//按照 工资 降序 排列
emp.find().sort({ sal: -1 })

//先按照 工资(sal) 升序;若是工资相同,再按照编号(empno) 降序排列
emp.find().sort({ sal: 1, empno: -1 })
复制代码

映射

指定查询时,须要返回的字段。

1:容许映射;

0:禁止映射;

//返回 工资(sal) >= 1250 的员工姓名
emp.find({ sal: { $gte: 1250 } }, { ename: 1 })
/*_id会自动加上 {"_id" : ObjectId("5cf7ca79ffd6ea864b5a22d1"),"ename" : "孙二娘"}, {"_id" : ObjectId("5cf7ca79ffd6ea864b5a22d2"),"ename" : "扈三娘"} {"_id" : ObjectId("5cf7ca79ffd6ea864b5a22d3"),"ename" : "卢俊义"} {"_id" : ObjectId("5cf7ca79ffd6ea864b5a22d4"),"ename" : "潘金莲"} */

//若是想忽略_id,能够尝试:
emp.find({ sal: { $gte: 1250 } }, { ename: 1, _id: 0 })
复制代码

7、1.操做MongoDB的库——Mongoose

Mongoose是一个对象文档模型(ODM)库,他对Node原生的MongoDB模块进行了进一步的优化封装,并提供了更多的功能。

为何要使用mongoose?

  • 能够为文档建立一个模式结构(Schema|约束)
  • 能够对模型中的对象/文档进行验证
  • 数据能够经过类型转换转换为对象模型
  • 可使用中间件来应用业务逻辑挂钩
  • 比Node原生的MongoDB驱动更容易

7、2.mongoose——3个概念

Schema(模式对象)

Schema对象定义约束数据库中的文档结构

Model

Model对象做为集合中的全部文档的表示,至关于MongoDB数据库中的集合collection

Document

Document表示集合中的具体文档,至关于集合中的一个具体文档。

建立顺序:先有Schema约束Model,在用Model操做Document

7、3.Mongoose——第一个DEMO

链接数据库,并插入第数据

const mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect(
  'mongodb://localhost:27017/mongoose_test', {useNewUrlParser: true}, function () {
  console.log('数据库链接成功')
});

var stuSchema = new Schema({
  name: String,
  age: Number,
  gender: {
    type: String,
    default: 'woman'
  },
  address: String
})

var StuModel = mongoose.model('student', stuSchema)

StuModel.create({
  name: '孙悟空',
  age: 18,
  gender: 'man',
  address: '花果山'
}, function (err) {
  if(!err) {
    console.log('插入成功')
  }
})

StuModel.create({
  name: '白骨精',
  age: 18,
  address: '盘丝洞'
}, function (err) {
  if(!err) {
    console.log('插入成功')
  }
})
复制代码

7、4.使用mongoose的Model进行增删改查

Model的增/删/改/查(CRUD)

.count(): 查询数量

.create(): 新增

.find(): 查询

.update(): 修改

.remove(): 删除

const mongoose = require('mongoose');
const Schema = mongoose.Schema

let datas = [
  { name: '白骨精', age: 19, address: '盘丝洞'},
  { name: '孙悟空', age: 20, address: '花果山'},
  { name: '猪八戒', age: 21, address: '高老庄'},
  { name: '沙和尚', age: 22, address: '流沙河'}
]

const userSchema = new Schema({
  name: String,
  age: Number,
  sex: {
    type: String,
    default: 'man'
  },
  address: String
})

const userModel = mongoose.model('users', userSchema)

//查询数量
userModel.count({}, function(err, count) {
  if(!err) {
    console.log(count)
  }
})

//写入数据
userModel.create(datas, function(err) {
  if(!err) {
    console.log('写入成功')
  }
})

/** * [查询] * model.find(conditions, [projection], [options], [callback]) * @conditions {Object} [筛选条件] * @projection {Object|String} [投影] * * { name: 1, _id: 0 } * * 'name -_id' * @options {Object} [查询选项:skip limit] * @callback {Function} [回调函数] * */
userModel.find({}, function (err, datas) {
  if(!err) {
    console.log( datas )
  }
})


/** * [修改] * model.update(conditions, docs, [options], [callback]) * @conditions {Object} [筛选条件] * @docs {Object|String} [修改的值] * @options {Object} [查询选项:skip limit] * @callback {Function} [回调函数] * */
userModel.update({ name: '孙悟空' }, { $set: { age: 20 } }, function (err) {
  if(!err) {
    console.log( '修改为功' )
  }
})

/** * [删除] * model.remove(conditions, [callback]) * @conditions {Object} [筛选条件] * @callback {Function} [回调函数] * */
userModel.remove({  }, function (err) {
  if(!err) {
    console.log( '删除成功' )
  }
})
复制代码

7、5.使用mongoose的Document进行增删改

Document是Model的一个实例,经过Model查询到的结果都是Document

Document的增/删/改(CUD)

const mongoose = require('mongoose');
const Schema = mongoose.Schema

mongoose.connect('mongodb://localhost:27017/mongoose_test', { useNewUrlParser: true }, function (err) {
  if(!err) {
    console.log('数据库链接成功')
  } else {
    console.log('数据库链接失败' + err)
  }
})

const userSchema = new Schema({
  name: String,
  age: Number,
  sex: {
    type: String,
    default: 'man'
  },
  address: String
})

const userModel = mongoose.model('users', userSchema)

let datas = [
  { name: '白骨精', age: 19, address: '盘丝洞'},
  { name: '孙悟空', age: 20, address: '花果山'},
  { name: '猪八戒', age: 21, address: '高老庄'},
  { name: '沙和尚', age: 22, address: '流沙河'}
]

//插入一条数据
var user = new userModel({
  name: '玉帝',
  age: 10000,
  sex: 'woman',
  address: '天庭'
})
user.save(function (err, product) {
  if(!err) {
 console.log( '保存成功' )
}
})

// 修改数据1
userModel.findOne({}, function(err, doc) {
  if(!err) {
    doc.update({ $set: { sex: 'woman' } }, function (err) {
      if(!err) {
        console.log('修改为功')
      }
    })
  }
})
// 修改数据2
userModel.findOne({}, function(err, doc) {
  console.log( doc.toJSON() )
  console.log( doc.toObject() )
  console.log( doc.toString() )
  if(!err) {
    doc.age = 80
    doc.save(function(err) {
      if(!err) {
        console.log('修改为功')
      }
    })
  }
})

// 删除数据
userModel.findOne({ name: '玉帝' }, function(err, doc) {
  if(!err) {
    doc.remove(function (err) {
      if(!err) {
        console.log('删除成功')
      }
    })
  }
})
复制代码

关于更多的mongooseapi请参阅 mongoose官网文档

7、6.mongoose模块化处理

为何要模块化?

在没有模块化以前,链接数据库,建立SchameModel、数据CRUD都是在index.js文件中进行的,可是当一个应用有各类路由的时候,须要在各个页面操纵各类Model,这样在每一个页面操纵Model的时候很是的困难,所以须要模块化解决这个问题。

实施模块化

未整理以前的目录:

/index.js
复制代码

模块化以后的目录:

/models
    /user.js
/schame
    User.js
index.js
mongoose_init.js
复制代码

内容以下:

/schames/User.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema

const userSchema = new Schema({
  name: String,
  age: Number,
  sex: {
    type: String,
    default: 'man'
  },
  address: String
})

module.exports = userSchema
复制代码

/models/user.js

const mongoose = require('mongoose');
const User = require('../schames/User')

const user = mongoose.model('users', User)

module.exports = user
复制代码

mongoose_init.js

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mongoose_test', { useNewUrlParser: true }, function (err) {
  if(!err) {
    console.log('数据库链接成功')
  } else {
    console.log('数据库链接失败' + err)
  }
})
复制代码

index.js

require('./mongoose_init')
const user = require('./models/user')

user.find({}, function(err, data) {
  if(!err) {
    console.log(data) //查询成功
  }
})
复制代码
相关文章
相关标签/搜索