MongoDB是NoSQL数据库系统中比较流行的数据库之一。它也是最接近关系型数据库的,一个数据库可以包含多个集合(Collection),类似于关系数据库中的表;而每个集合中可以存储一组由列标识的记录,列是可以自由定义的,非常灵活,这就类似于关系数据库表中的每一条记录。
MongoDB Database常用命令
- 连接数据库
mongo --host xx.xx.xx.xx -u 用户名 -p 密码 --authenticationDatabase admin
- Help查看命令提示
db.help();
db.yourCollection.help();
- 显示所有数据库
show dbs;
- 切换/创建数据库
use XXX;
- 查看当前数据库
db;
db.getName();
- 删除当前使用的数据库
db.dropDatabase();
- 显示当前db状态
db.stats();
- 当前db的版本
db.version();
- 查看当前db的连接服务器机器地址
db.getMongo();
MongoDB Collection集合命令
- 创建一个集合(在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。)
// 创建成功会显示{"ok": 1}
db.createCollection("log", {capped : true, size : 5242880, max : 5000 }); // 此命令将创建一个名为log的集合,该集合的最大大小为 5 MB,最多 5000 个文档。
// 判断集合是否为定容量
db.collName.isCapped();
2. 得到指定名称的集合
db.getCollectionName("log");
- 得到当前db的所有集合
show collections;
db.getCollectionNames();
- 显示当前db所有集合索引的状态
db.printCollectionStats();
- 查询当前集合的数据条数
db.yourCollection.count();
- 查看当前集合数据空间大小
db.yourCollection.dataSize();
- 获取当前集合所在的db
db.yourCollection.getDB();
- 获取当前集合的状态
db.yourCollection.stats();
- 获取集合总大小
db.yourCollection.totalSize();
- 获取集合储存空间大小
db.yourCollection.storageSize();
- 集合重命名
db.yourCollection.renameCollection("newName");`
- 删除当前集合
db.yourCollection.drop();
查询
- 查询所有记录
db.yourCollection.find();
db.yourCollection.find().pretty(); // 使用易读的方式读取数据
- 查询某列组成的数据(去重)
db.yourCollection.distinct("name"); // 若多个name值为“张三”, 只会查出一个张三
- 按条件查询
db.yourCollection.find({ name: "张三"' }); // 查询name为“张三”的行(document)
db.yourCollection.find({ age: { <: 22 } }); // 查询age值小于22的行
db.yourCollection.find({ tags: { $in: ['男', '女'] } }); // 与查询数组中指定的值中的任何一个匹配
// 其他 $gt 大于、$gte大于等于、$lte小于等于、$ne不等于、$nin 与查询数组中指定的值中的任何一个都不匹配
- 字符模糊匹配
db.yourCollection.find({ name: /mongo/ });
// 相当于%%
select * from userInfo where name like ‘%mongo%';
- 查询指定列数据
db.yourCollection.find({}, { name: 1, age: 1 });
// 相当于 select name, age from yourCollection
- 按条件查询指定列数据
db.yourCollection.find({ age: { $lt: 20 }}, { name: 1, age: 1 })
- 排序
db.yourCollection.find().sort({ age: 1 }); // 升序
db.yourCollection.find().sort({ age: -1 }); // 降序
- 查询前5条数据
db.yourCollection.find().limit(5);
- 查询10条以后的数据
db.yourCollection.find().skip(10);
- 查询在5-10之间的数据
db.yourCollection.find().limit(10).skip(5);
- or(与)查询
db.yourCollection.find({ $or: [{ age: 22}, { age: 25 }]}); // age = 22 或 age = 25
- 查询第一条数据
db.yourCollection.findOne();
db.yourCollection.find().limit(1);
- 查询某个结果集的记录条数
db.yourCollection.find({ age: { $gte: 25 } }).count();
- 使用aggreate聚合,进行分类统计 Mongoose: aggregate聚合 $group使用说明
db.yourCollection.aggregate([{ $group: { _id: "$tag", count: {$sum: 1}}}]) // 按照tag进行分组,获取每种类别以及每种类别数量
- 使用populate进行连表查询–Population 可以自动替换document中的指定字段,替换内容从其他collection获取。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const personSchema = new Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{
type: Schema.Types.ObjectId,
ref: 'Story' // ref选项告诉Mongoose在填充的时候使用哪个Model填充,这里使用Story Model,所有存储再次的_id都必须是Story Model中document的`_id`.
}]
});
const storySchema = new Schema({
author: {
type: Schema.Types.ObjectId,
ref: 'Person'
},
title: String,
fans: [{
type: Schema.Types.ObjectId,
ref: 'Person'
}]
});
// 创建Story和Person两个Model
let Story = mongoose.model('Story', storySchema);
let Person = mongoose.model('Person', personSchema);
// 添加文档
let author = new Person({
name: 'astar',
age: 18
}); // mongoose将会自动生成_id
await author.save();
let story = new Story({
title: '故事集',
author: author._id
});
await story.save();
// 查询文档,使用populate填充story字段
let story = await Story.findOne({ title: '故事集' }).populate('author');
// 可选返回字段
let story = await Story.findOne({ title: '故事集' }).populate('author', ['name']); // 返回的author字段包含了_id和name
// 若不想返回_id,可使用String类型, 空格分割,其中`-_id`表示不返回_id
let story = await Story.findOne({ title: '故事集' }).populate('author', 'name age -_id'); // 返回的author字段包含了name和age
// 填充多个字段
let story = await Story.findOne({ title: '故事集' }).populate('author').populate('fans'); // 如果对同一字段 populate() 两次,只有最后一次生效。
populate({ objParam }) - 更丰富的选项
objParam
path
:需要 populate 的字段。populate
:多级填充。select
:从 populate 的文档中选择返回的字段。model
:用于 populate 的关联 model。如果没有指定,populate 将根据 schema 中定义的 ref 字段中的名称查找 model。可指定跨数据库的 model。match
:populate 连表查询的条件,符合条件的会用文档替换 _id,不符合条件的会用 null 替换 _id。options
:populate 查询的选项。- sort:排序。
- limit:限制数量。
Story.
find(...).
populate({
path: 'fans',
match: { age: { $gte: 20 } },
select: 'name -_id',
options: {
limit: 5
}
})
// 多级填充
// 查询 friends 的 friends
Author.findOne({ name: 'dora' }).populate({
path: 'friends',
populate: { path: 'friends' }
});
跨数据库填充
跨数据库不能直接通过 schema 中的 ref 选项填充,但是可以通过 objParam 中的 model 选项显式指定一个跨数据库的 model。
let eventSchema = new Schema({
name: String,
conversation: ObjectId // 注意,这里没有指定 ref!
});
let conversationSchema = new Schema({
numMessages: Number
});
let db1 = mongoose.createConnection('localhost:27000/db1');
let db2 = mongoose.createConnection('localhost:27001/db2');
// 不同数据库的 Model
let Event = db1.model('Event', eventSchema);
let Conversation = db2.model('Conversation', conversationSchema);
// 显示指定 model
let doc = await Event.find().populate({
path: 'conversation',
model: Conversation
});
使用refPath进行动态引用
let userSchame = new Schame({
name: String,
connextions: [{
kind: String,
item: {
type: ObjectId,
refPath: 'connections.kind' // 根据kind字段判断使用哪个collection来填充item
}
}]
});
...
User.
findOne(...).
populate('connections.item')
虚拟值填充(4.5.0新功能)
var PersonSchema = new Schema({
name: String,
band: String
});
var BandSchema = new Schema({
name: String
});
BandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // Find people where `localField`
foreignField: 'band', // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false
});
var Person = mongoose.model('Person', PersonSchema);
var Band = mongoose.model('Band', BandSchema);
/**
* Suppose you have 2 bands: "Guns N' Roses" and "Motley Crue"
* And 4 people: "Axl Rose" and "Slash" with "Guns N' Roses", and
* "Vince Neil" and "Nikki Sixx" with "Motley Crue"
*/
Band.find({}).populate('members').exec(function(error, bands) {
/* `bands.members` is now an array of instances of `Person` */
});
要记得虚拟值默认不会被 toJSON() 输出。如果你需要填充的虚拟值显式在依赖 JSON.stringify() 的函数 (例如 Express 的 res.json() function)中打印, 需要在 toJSON 中设置 virtuals: true 选项。
// Set `virtuals: true` so `res.json()` works
var BandSchema = new Schema({
name: String
}, { toJSON: { virtuals: true } });
如果你使用了填充保护,要确保 select 中包含了 foreignField。
Band.
find({}).
populate({ path: 'members', select: 'name' }).
exec(function(error, bands) {
// Won't work, foreign field `band` is not selected in the projection
});
Band.
find({}).
populate({ path: 'members', select: 'name band' }).
exec(function(error, bands) {
// Works, foreign field `band` is selected
});
在中间件中使用填充
你可以在 pre 或 post 钩子中使用填充。 如果你总是需要填充某一字段,可以了解一下mongoose-autopopulate 插件。
// Always attach `populate()` to `find()` calls
MySchema.pre('find', function() {
this.populate('user');
});
// Always `populate()` after `find()` calls. Useful if you want to selectively populate
// based on the docs found.
MySchema.post('find', async function(docs) {
for (let doc of docs) {
if (doc.isPublic) {
await doc.populate('user').execPopulate();
}
}
});
// `populate()` after saving. Useful for sending populated data back to the client in an
// update API endpoint
MySchema.post('save', function(doc, next) {
doc.populate('user').execPopulate(function() {
next();
});
});
修改、添加、删除数据
- 添加行
db.yourCollection.insert({name: 'aaa'})
db.yourCollection.save({ name: 'name', age: 20 });
save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。
insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。
3.2 版本之后新增了 db.collection.insertOne() 和 db.collection.insertMany()。
- 修改行
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>, // 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi: <boolean>, // 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern: <document> // 可选,抛出异常的级别。
}
)
db.yourCollection.update({ age: 25 }, { $set: { name: '张三' } }, false, true); // 相当于:update yourCollection set name = ‘changeName’ where age = 25;
db.yourCollection.update({name: ‘Lisi’}, {$inc: {age: 50}}, false, true); // 相当于:update yourCollection set age = age + 50 where name = ‘Lisi';
db.yourCollection.update({name: ‘Lisi’}, {$inc: {age: 50}, $set: {name: ‘hoho’}}, false, true); // 相当于:update yourCollection set age = age + 50, name = ‘hoho’ where name = ‘Lisi';
db.yourCollection.update({name: ‘Lisi’}, {$pull: {age: 50}, $set: {name: ‘hoho’}}, false, true);
- 删除行
db.yourCollection.remove({ age: 25 });
db.yourCollection.remove({}); // 删除所有数据
- 查询修改删除
db.yourCollection.findAndModify({
query: {age: {$gte: 25}},
sort: {age: -1},
update: {$set: {name: ‘a2′}, $inc: {age: 2}},
remove: true
});
- 数组元素的删除、添加
db.collection.update(<query>, { $pull: { 'group': { groupName: 'friends' } }}); // 删除group数组中groupName为friends的数据
db.collection.update(<query>, { $addToSet: { 'group': { groupName: 'newFriends'} }}); // 在group数组中增加groupName为newFriends的数据
MongoDB索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
- 创建索引
db.yourCollection.createIndex({name: 1}); // 1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。
db.yourCollection.createIndex({"name":1,"age":-1}); // 也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)
- 查询当前聚集集合所有索引
db.yourCollection.getIndexes();
- 查看总索引记录大小
db.yourCollection.totalIndexSize();
- 读取当前集合的所有index信息
db.yourCollection.reIndex();
- 删除指定索引
db.yourCollection.dropIndex(“name_1″);
- 删除所有索引
db.yourCollection.dropIndexes();
MongoDB用户相关
- 添加一个用户
db.createUser({ user: 'username', pwd: 'xxx', roles: [{role: 'readWrite', db: 'dbName'}]});
- 数据库认证、安全模式(登录)
db.auth("ray", "123456");
- 显示当前所有用户
show users;
- 删除用户
db.removeUser("userName");
【参考】