MongoDB是一个基于分布式文件存储的文档式数据库.其由C++编写, 旨在为Web应用提供可扩展的高性能数据存储解决方案.
MongoDB中每条数据记录被作为一个文档存储,文档由集合(collection)进行管理, 每个数据库(db)下包含多个集合.
这与关系型数据库记录,数据表,数据库的关系类似, 但是同一个collection下的文档可以存储格式不同的数据,更加灵活.
首先我们在Ubuntu上安装MongoDB:
sudo apt-get install mongodb
MongoDB的数据需要存储在dbpath目录下, MongoDB默认以/data/db
作为dbpath.
该目录不会在安装过程中自动创建, 需要手动chua创建:
sudo mkdir -p /data/db
启动MongoDB服务:
mongod
使用dbpath参数指定目录:
mongod --dbpath path
使用--rest
参数启动Web管理界面支持.
MongoDB Shell是MongoDB自带的交互式Javascript shell:
mongo
因为这是个js shell可以在这里用js进行交互操作.
MongoDB默认用27017端口提供服务, Web界面的端口比服务端口多1000, 即28017端口.
在浏览器中访问http://localhost:28017
即可打开MongoDB的Web界面.
MongoDB中最顶层的容器为数据库(db), 使用show dbs命令查看所有数据库:
> show dbs
local 0.078125GB
use指令用于切换到指定数据库, 当该数据库不存在时则自动创建:
> use test
switched to db test
成功切换到数据库后, db对象代表当前数据库:
> db
test
调用dropDatabase()
删除数据库:
> db.dropDatabase()
{ "dropped" : "test", "ok" : 1 }
访问db对象的成员为集合, 访问一个不存在的集合将会自动创建:
> db.user
test.user
若要在创建时指定参数可以使用db.createCollection(name, options)
调用drop()
方法清空集合:
> db.user.drop()
true
当集合中不含文档时会被自动删除.
文档操作
文档是MongoDB中数据的单位, 每个文档是采用BSON(Binary JSON)存储的字典.
文档由集合进行管理,
添加文档
使用insert()
方法添加文档:
> doc = ({username:'filey', password:'1234'});
{ "username" : "filey", "password" : "1234" }
> db.user.insert(doc)
save()
方法用于保存文档更改, 当文档不存在时自动创建:
> db.user.insert(doc)
> db.user.save(doc)
> db.user.insert(doc)
E11000 duplicate key error index: test.user.$_id_ dup key: { : ObjectId('57d77ba9a78dc753c19b1cc0') }
> db.user.save(doc)
查找文档
find()
方法可以查看集合中的文档:
> db.user.find()
{ "_id" : ObjectId("57d77b87a78dc753c19b1cbf"), "username" : "first", "password" : "1234" }
{ "_id" : ObjectId("57d77ba9a78dc753c19b1cc0"), "username" : "second", "password" : "1234" }
可以根据文档中的属性值进行查询:
> db.user.find({'username':first})
{ "_id" : ObjectId("57d77b87a78dc753c19b1cbf"), "username" : "first", "password" : "1234" }
pretty()
方法可以优化显示:
> db.user.find().pretty()
{
"_id" : ObjectId("57d77b87a78dc753c19b1cbf"),
"username" : "first",
"password" : "1234"
}
{
"_id" : ObjectId("57d77ba9a78dc753c19b1cc0"),
"username" : "second",
"password" : "1234"
}
与SQL类似, MongoDB的查询支持更复杂的条件:
功能 | MongoDB | SQL |
---|---|---|
等于 | find({attr:val}) | where attr = val |
小于 | find({attr:{\(lt:val}}) | where attr < val | | 小于等于 | find({attr:{\)lte:val}}) | where attr <= val |
大于 | find({attr:{\(gt:val}}) | where attr > val | | 大于等于 | find({attr:{\)gte:val}}) | where attr >= val |
不等于 | find({attr:{$ne:val}}) | where attr != val |
可以发现除了最简单的等于外,其它的表达式val都是用字典表示, 字典中的多个键表示与的关系:
find(attr:{$gt:lower, $gt:upper})
上述表达式等价于: where attr > lower and attr < upper
. or条件的表达:
find({
attr: {
$or: [
{'<': upper},
{'&ne', bidden}
]
}
})
$type
用于根据BSON中的数据类型进行筛选:
find({attr:{$type:1}})
上面的查询限定了attr
的类型为double.
数字与类型之间的对照见下表:
Double
String
Object
Array
Binary Data
Undefined (已废弃)
ObjectId
Boolean
Date
Null
删除文档
remove(query, [option])
方法用于删除文档, query与find的参数相同, 用于表达查询条件.
option是描述选项的字典:
db.collection.remove(
query,
{
justOne: false,
writeConcern: {}
}
)
justOne为true只删除查询到的第一个结果, 否则删除所有结果; writeConern表示日志的级别.
使用{}
表示删除所有文档:
db.collection.remove({})
更新文档
除了save方法可以强制更新文档外, update方法可以实现更灵活的文档更新:
db.collection.update(query, update, [option])
query是表达查询条件的字典, 与find相同.
update为描述更新操作的字典, 其key有$set
和$inc
等分别代表设置,添加键值对等操作.
option为表示选项的字典:
{
upsert: false,
multi: false,
writeConcern: {}
}
upsert为true表示在文档不存在时新建文档, 默认为false.
multi为false时只修改查询到的第一个文档,否则修改查询到的所有文档, 默认为false.
writeConcern表示日志的级别.
limit, skip 与 sort
limit方法用于限制结果集中文档的数量:
db.collection.find().limit(num)
num表示返回结果集中前num个文档, 若num大于总数则返回整个结果集.
skip用法与limit类似:
db.collection..find().skip(num)
num表示跳过结果集中前num个文档, 返回后面的文档.
limit和skip都是与顺序有关的查询操作, 所以我们需要排序方法和它们配合使用.
sort方法用于进行排序:
db.collection.find().sort({key1:1, key2:-1,})
key表示作为排序依据的属性, 1表示升序排列, -1表示降序排列.
在key1相同的情况下, 按照key2的规则进行排序.
默认情况下按照objectId进行升序排列.
aggregate
聚合(aggregate)操作用于获得集合或结果集的和,平均值等统计量.
db.collection[.find()].count(query)
count接受一个代表查询规则的字典, 返回满足条件的文档的个数.可以对集合或者结果集应用该方法.
db.collection.distinct(key)
distinct函数返回所有key指定属性的列表并删除重复:
> db.user.distinct('username')
[ "first", "second" ]
distinct只能对集合使用, 不能对结果集使用.
上面两个函数都只能进行及其简单的聚合操作, aggregate
方法可以完成复杂的聚合操作.
db.collection.aggregate(ops)
ops参数为列表, 其中的元素为代表一次聚合操作的字典. 第一次操作的结果作为第二次操作的输入, 如此形成聚合管道.
首先准备一个测试集合;
> db.user.insert({username:'first', score:98})
> db.user.insert({username:'first', score:88})
> db.user.insert({username:'second', score:58})
> db.user.insert({username:'third', score:98})
> db.user.insert({username:'forth', score:95})
执行聚合管道操作:
db.user.aggregate([
{ $match:
{score: {$gte: 60}}
},
{ $group:
{ _id: '$username', avg: {$avg: '$score'} }
}
])
首先执行match操作, 筛选score>=60的文档:
{username:'first', score:98}
{username:'first', score:88}
{username:'third', score:98}
{username:'forth', score:95}
group操作进一步过滤上述结果集, 按照username分组, 计算每组score的平均值输出为avg:
"result" : [
{
"_id" : "forth",
"avg" : 95
},
{
"_id" : "third",
"avg" : 98
},
{
"_id" : "first",
"avg" : 93
}
],
"ok" : 1
}
上述语句等价于SQL语句:
select avg(score) as "avg"
from db.user
where score >= 60;
以上文的键值对$match:{score: {$gte: 60}}
为例,$match
称为管道操作符, {score: {$gte: 60}
称为管道表达式.
下面介绍一些常用的管道操作:
$project
投影, 选择特定属性.其表达式为字典, 键为属性名, 值为1的键被保留,0和未指定的被过滤:
db.user.aggregate({
$project: {
_id: 1,
}
})
结果:
{
"result" : [
{
"_id" : ObjectId("57db67663364fa058d0ac911")
},
],
"ok": 1
}
$match
匹配, 筛选特定文档.match的管道表达式与find的query字典相同.
db.user.aggregate({
$match: {
score: {$gte: 60},
}
})
结果:
{
"result" : [
{
"_id" : ObjectId("57db67663364fa058d0ac911"),
"username" : "first",
"score" : 98
},
{
"_id" : ObjectId("57db676e3364fa058d0ac912"),
"username" : "first",
"score" : 88
},
],
"ok" : 1
}
$limit
限制文档的个数.管道表达式为正整数:
db.user.aggregate({$limit: 2 })
$skip
跳过指定个数的文档, 管道表达式为正整数:
db.user.aggregate({$skip: 2 })
$sort
对文档按照指定的属性排序, 管道表达式与sort()方法参数一致.
db.user.aggregate({
$sort: {score: 1, username: 1}
})
$unwind
将含有多值属性的文档拆分为多个只有单值属性的文档:
>db.article.insert(
{
name: 'First to MonogoDB',
author: 'finley',
tags: ['technology', 'database', 'NoSQl']}
)
>db.article.aggregate({$unwind: '$tags'})
$unwind
的管道表达式为$
加字段名组成的字符串.
结果:
{
"result" : [
{
"_id" : ObjectId("57db79f8dbf8a742aa6bc85f"),
"name" : "First to MonogoDB",
"author" : "finley",
"tags" : "technology"
},
{
"_id" : ObjectId("57db79f8dbf8a742aa6bc85f"),
"name" : "First to MonogoDB",
"author" : "finley",
"tags" : "database"
},
{
"_id" : ObjectId("57db79f8dbf8a742aa6bc85f"),
"name" : "First to MonogoDB",
"author" : "finley",
"tags" : "NoSQl"
}
],
"ok" : 1
}
unwind的目标字段必须为数组, 否则会产生错误:
exception: $unwind: value at end of field path must be an array
$group
用于分组并进行统计:
db.user.aggregate(
{ $group:
{ _id: '$username', avg: {$avg: '$score'} }
}
)
结果:
"result" : [
{
"_id" : "forth",
"avg" : 95
},
{
"_id" : "third",
"avg" : 98
}
],
"ok" : 1
}
关系表达式中, _id
键是必须的, 指定用于分组的属性, 属性值相同的文档将被分为一组.
其余键用于指定结果中属性的名称, 其值指定属性的值.
可以使用的集函数包括:
$avg
$sum
$max
$min
$first
$last
$push
将所选属性合并到一个array中
注意使用$
加字段名组成的字符串来指定进行统计的属性.
索引
索引可以大幅提高查询数据的效率:
db.collection.ensureIndex(index, option)
index为一个词典, 其中键为属性名, 值为1或-1.1代表按升序建立索引, -1代表按降序建立索引.
索引可以建立在单一字段上, 或者在多个字段上建立复合索引.
option为指定可选参数的字典:
key | type | description | default |
---|---|---|---|
background | bool | true: 建立索引时阻塞数据库, false: 后台建立 | false |
unique | bool | 索引字段是否唯一 | false |
dropDups | bool | 创建唯一索引时是否自动删除重复文档 | false |
name | string | 索引的名称 | 根据索引规则自动创建 |
sparse | bool | 对集合中不存在该字段的文档不创建索引, 使其无法被索引字段查询到 | false |
expireAfterSeconds | integer | 失效时间 | |
v | indexvision | 索引版本号 | 自动 |
weights | document | 索引权重值 | |
default_language | string | 对于文本索引, 决定词典规则和停用词 | en |