文档数据库MongoDB

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: [
        	{'&lt': upper},
            {'&ne', bidden}
        ]
    }
})

$type用于根据BSON中的数据类型进行筛选:

find({attr:{$type:1}})

上面的查询限定了attr的类型为double.

数字与类型之间的对照见下表:

  1. Double

  2. String

  3. Object

  4. Array

  5. Binary Data

  6. Undefined (已废弃)

  7. ObjectId

  8. Boolean

  9. Date

  10. 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
posted @ 2016-10-07 15:41  -Finley-  阅读(1723)  评论(0编辑  收藏  举报