monodb学习笔记(二)
1、mongodb的索引
索引包含单键索引和复合键索引, 单键索引是针对一个字段进行排序,而复合键索引可以对多个字段进行排序, 复合键索引只支持前缀子查询
索引操作
db.COLLECTION_NAME.getIndexes() --获取索引
db.COLLECTION_NAME.createIndex() --创建索引
db.COLLECTION_NAME.dropIndex() --删除索引
索引类型
单键索引
多键索引
复合键索引
索引的特性: 唯一性, 稀疏性,生存时间
索引合理性分析: explain()
创建索引
db.goods.createIndex({name: 1, price: -1}) --表示创建多键索引
注意:1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可
检测索引
db.goods.explain().find({name: 'apple'})
注意:在检测结果中如果是COLLSCAN,那么就表示是通过遍历来获取结果的, 如果是IXSCAN则表示是通过索引来获取的效果会比COLLSCAN来的好,但是第三种情况最佳
创建唯一性索引
db.goods.createIndex({name: 1}, {unique: true}) --表示创建唯一性索引
parse选项:对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds:指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。单位是秒
索引的其他操作
db.goods.getIndexes() --查看所有的索引集合
db.goods.dropIndex("索引名") --删除指定的索引
db.goods.dropIndexes() --删除所有的索引
2、mongodb复制集
优点: 高可用性, 数据安全, 分流/分工
复制集的特点:
- 主节点负责处理所有的写入请求
- 主节点(默认)和副节点都可以处理读取请求
- 副节点从主节点(或者符合条件的副节点)处复制数据
- 复制集中每个节点都会向其他节点发送心跳请求
- 每隔2秒发送一次, 超过10秒则请求超时(默认)
- 复制集中最多可以有50个节点
创建复制集的数量是至少3个
具体查看网上文档
3、数据库安全
启用验证 --修改mongodb的配置文件
在配置文件中开启权限验证
security:
authorization: enabled
启用身份验证 --先关闭mongo
net stop MongoDB // 停止服务
net start MongoDB // 开启服务
创建一个超级用户
db.createUser({user: "admin", pwd: "123456", roles: [{role: "root", db: "admin"}]})
删除用户
db.dropUser("admin") -- 表示删除指定的数据库用户
进行登录
mongo admin -u "admin" -p "123456"
--这里的root表示用户名, password表示密码
指定一个操作用户的操作步骤
> show dbs;
admin 0.000GB
config 0.000GB
even 0.000GB
local 0.000GB
use even
show users -- 查看当前数据库的所有用户
db.createUser({user: "even", pwd: "123456", roles: [{role: "dbOwner", db: "even"}]}) --创建该数据库的用户,只能操作该数据库
--使用新账号登录
mongo even -u "even" -p "123456"
也可以按以下流程进行登录
mongo --启动服务器
use admin --进行admin表
db.auth("root", "password") --进行身份验证
角色权限配置
Built-In Roles(内置角色):
1. 数据库用户角色:read、readWrite;
2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;
3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
4. 备份恢复角色:backup、restore;
5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
6. 超级用户角色:root 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
7. 内部角色:__system
具体角色:
Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
root:只在admin数据库中可用。超级账号,超级权限。
创建新角色
db.createRole({ role: “testRole”, privileges: [{ resource: { db: “”, collection: “” }, actions: [“enableSharding” ] }], roles: [“readWriteAnyDatabase”] })
db.createUser( { user: “testuser”, pwd: “123456”, roles: [ { role: “testRole”, db: “admin” } ] } )
4、使用mongoose操作数据库
安装数据库
yarn add mongoose
yarn add @types/mongoose --dev // mongoose的类型声明库
数据库的基本操作
import { connect, Model, model, Schema, Document } from 'mongoose'
// 使用mongoose连接数据库,这里使用的是密码连接
connect('mongodb://even:123456@127.0.0.1:27017/even')
// 声明数据库的document
interface userDocument extends Document {
name: string
age: number
}
// 声明Schema
const userSchema: Schema<userDocument> = new Schema({
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
}
})
//创建model, 注意第三个参数是数据库的真字表名
const userModel: Model<userDocument> = model('User', userSchema, 'user')
// 查询所有数据
userModel.find({}, { _id: 0, __v: 0 }, (err: any, doc: any) => {
if (err) {
console.error(err)
return
}
console.log(doc)
})
5、mongoose模块化以及预订义修饰符的使用
场景: 在models下新建三个文件,db.ts, user.ts, bag.ts
db.ts实现连接
import { connect } from 'mongoose'
export { Model, model, Schema, Document } from 'mongoose'
connect('mongodb://even:123456@127.0.0.1:27017/even', (err) => {
if (err) {
console.log(err)
return
}
console.log('数据库连接成功')
})
user.ts
import { Model, model, Schema, Document } from './db'
export interface UserDocument extends Document {
name: string
age: number
}
const UserSchema: Schema<UserDocument> = new Schema({
name: {
type: String,
unique: true,
required: true
},
age: {
type: Number,
required: true,
get(value) {
//值得一提的是,getter不会修改数据库中返回的数据,而是在获得doc对象后,再访问其中的属性时候才会生效
return value
},
set(value: any) {
if (typeof value === 'string') return 0
return value
}
}
})
export const UserModel: Model<UserDocument> = model('user', UserSchema, 'user')
注意: get定义后不会修改数据本身,而是在数据对象中添加了get的方法,只有在访问其中的属性的时候才会生效, 而set是在添加数据的时候,会对数据进行格式化, 如果需要设置普通索引,那么index: true, 如果需要设置唯一索引那么unique: true即可
bag.ts
import { Model, model, Schema, Document } from './db'
export interface BagDocument extends Document {
name: string
goods: string
count: number
}
const BagSchema: Schema<BagDocument> = new Schema({
name: {
type: String,
required: true
},
goods: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
export const BagModel: Model<BagDocument> = model('Bag', BagSchema, 'bag')
对数据的调用和使用
import { UserModel, UserDocument } from '../../models/user'
import { BagModel, BagDocument } from '../../models/bag'
const getUser = (): Promise<Array<number>> => {
return new Promise((resolve, reject) => {
UserModel.find({}, (err, doc) => {
if (err) return reject(err)
let ages = doc.map((item) => item.age)
resolve(ages)
})
})
}
const getBag = (): Promise<Array<BagDocument>> => {
return new Promise((resolve, reject) => {
BagModel.find({}, (err, doc) => {
if (err) return reject(err)
resolve(doc)
})
})
}
export const mongo = async () => {
let user = await getUser()
let bag = await getBag()
return {
user,
bag
}
}
6、mongoose下自定义静态方法
import { Model, model, Schema, Document } from './db'
export interface UserDocument extends Document {
name: string
age: number
}
// 自定义方法接口
interface IUserModel extends Model<UserDocument> {
findByName: (name: string, callBack: Function) => any
}
const UserSchema: Schema<UserDocument, IUserModel> = new Schema<UserDocument, IUserModel>({
name: {
type: String,
unique: true,
required: true
},
age: {
type: Number,
required: true
}
})
// 自定义方法实体
UserSchema.static('findByName', function myStaticMethod(name: string, callBack) {
this.find({ name }, (err: any, doc: any) => callBack(err, doc))
})
//暴露出去的方法,注意对model的类型声明
export const UserModel = model<UserDocument, IUserModel>('user', UserSchema, 'user')
注意:自定义内部扩展方法,mongoose官网暂没有相关的ts版本,使用UserSchema.method(name, funciton)进行定义,但是这个方法使用的较少
7、mongoose下进行数据较验
required : 表示这个数据必须传入
max: 用于 Number 类型数据,最大值
min: 用于 Number 类型数据,最小值
enum:枚举类型,要求数据必须满足枚举值 enum: ['0', '1', '2']
match:增加的数据必须符合 match(正则)的规则
maxlength:最大值
minlength:最小值
var UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
age: {
type: Number, // 是否必须的校验器
required: true, // 数字类型的最大值校验器
max: 120, // 数字类型的最小值校验器
min: 0
},
status: {
type: String, // 设置字符串的可选值
enum: ['0', '1', '2']
},
phone: {
type: Number,
match: /^\d{11}$/
},
desc: {
type: String,
maxlength: 20,
minlength: 10
}
})
自定义验证器
var UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
age: {
type: Number, // 是否必须的校验器
required: true, // 数字类型的最大值校验器
max: 120, // 数字类型的最小值校验器
min: 0
},
status: {
type: String, // 设置字符串的可选值
enum: ['0', '1', '2']
},
desc: {
type: String, // 自定义的验证器,如果通过验证返回 true,没有通过则返回 false
validate: function (desc) {
return desc.length >= 10
}
}
})
8、mongoose下聚合管道的使用
const check = () => {
return new Promise((resolve, reject) => {
UserModel.aggregate(
[
{ $match: { name: { $eq: 'even' } } },
{ $project: { name: 1, _id: 0, age: 1 } },
{
$lookup: {
from: 'bag',
let: { n: '$name' },
pipeline: [
{ $group: { _id: '$name', sum: { $sum: '$count' } } },
{ $match: { $expr: { $eq: ['$$n', '$_id'] } } }
],
as: 'info'
}
}
],
(err: any, doc: any) => {
if (err) {
return reject(err)
}
resolve(doc)
}
)
})
}
export const mongo = async () => {
const json = await check()
return json
}
注意:在使用objectid进行匹配的时候,使用官方的方法,mongoose.Types.objectId(id)