关系型数据库中有1对1、1对多、多对1、多对多关系,MongoDB也一样。
MongoDB用BSON组织数据,这一点要灵活很多。实例:一个人有多个地址。我们可以有两张表来存放,一个user表、一个address表。user和address是一对多关系。
看user表中的一个document
{
"_id":ObjectId("72355788888abcf289bc9cc4"),
"name": "sunyang",
"contact": "12345678",
"age": "27"
}
对应的address
{
"_id":ObjectId("52ffc4a5d85242602e000000"),
"building": "changan road No.123",
"pincode": 123456,
"city": "Beijing",
"state": "China"
}
{
"_id":ObjectId("72355788888abcf289bc9cc4"),
"name": "sunyang",
"contact": "12345678",
"age": "27",
"address":[
{
"_id":ObjectId("52ffc4a5d85242602e000000"),
"building": "changan road No.123",
"pincode": 123456,
"city": "Beijing",
"state": "China"
} ,
{
"_id":ObjectId("52ffc4a5d85242602e000001"),
"building": "changan road No.123",
"pincode": 123456,
"city": "Beijing",
"state": "China"
}
]
}
这样数据保存在一个文档中,查询起来很方便
db.users.findOne({"name":"sunyang"},{"address":1})
{
"_id":ObjectId("72355788888abcf289bc9cc4"),
"name": "sunyang",
"contact": "12345678",
"age": "27",
"address_ids":[
ObjectId("52ffc4a5d85242602e000000"),
ObjectId("52ffc4a5d85242602e000001")
]
}
这种方式需要两次查询,先查ObjectId,再查地址。
>var result = db.users.findOne({"name":"sunyang"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})
跨数据库引用的场景
需要用到三个参数
{ $ref : , $id : , $db : }
实例
{
"_id":ObjectId("1111222222333333333"),
"address": {
"$ref": "address_home",
"$id": ObjectId("11111111111111111111"),
"$db": "address"},
"contact": "123456",
"age": "27",
"name": "sunyang"
}
查找方式
>var user = db.users.findOne({"name":"sunyang"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":ObjectId(dbRef.$id)})
这种命名空间类型,明显是在数据库内部维护了一个树形结构,通过类似占位符的形式取值。
语法
db.collection.createIndex(keys, options)
实例
db.col.createIndex({"title":1,"description":-1})
db.values.createIndex({open: 1, close: 1}, {background: true})
1:升序
-1:降序
options包括
background 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。
unique 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparse 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
查看集合索引
db.col.getIndexes()
查看集合索引大小
db.col.totalIndexSize()
删除集合所有索引
db.col.dropIndexes()
删除集合指定索引
db.col.dropIndex("索引名称")
这个网上也有很多解释,有一些明显是错误的。这个覆盖索引跟oracle索引一样,会走fast range scan。
举例
>db.users.createIndex({gender:1,user_name:1})
我要按gender搜索user_name,并排除_id值,这时候就直接从索引上取数据。(因为_id会默认返回)
>db.users.find({gender:"M"},{user_name:1,_id:0})
mongodb说,索引是创建在内存中,所以相当于读缓存,会很快。但索引都在内存中不会对内存造成影响吗?
索引字段是个数组不能走覆盖索引。(这个概念是比较新的,因为在oracle、mysql是不可能存在这种情况)
>db.users.find({gender:"M"},{user_name:1,_id:0}).explain()
可以查看的字段
indexOnly: 字段为 true ,表示我们使用了索引。
cursor:因为这个查询使用了索引,MongoDB 中索引存储在B树结构中,所以这是也使用了 BtreeCursor 类型的游标。如果没有使用索引,游标的类型是 BasicCursor。这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合(系统自动创建,由于存储索引信息,这个稍微会提到)来得到索引的详细信息。
n:当前查询返回的文档数量。
nscanned/nscannedObjects:表明当前这次查询一共扫描了集合中多少个文档,我们的目的是,让这个数值和返回文档的数量越接近越好。
millis:当前查询所需时间,毫秒数。
indexBounds:当前查询具体使用的索引。
如下查询实例指定了使用 gender 和 user_name 索引字段来查询:
>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})
考虑如下集合
{
"address": {
"city": "Los Angeles",
"state": "California",
"pincode": "123"
},
"tags": [
"music",
"cricket",
"blogs"
],
"name": "Tom Benzamin"
}
tags字段是一个集合,这里可以看做是用户标签,在这个上建索引,相当于在集合所包含的所有元素上建联合索引。
>db.users.ensureIndex({"tags":1})
使用
>db.users.find({tags:"cricket"})
如何要筛选的条件,在另一个关系表里,比如商品表和商品详情表,首页搜索条件在详情表里,这时候可以在商品表里创建子文档索引。
感觉这种索引有些复杂,在实际开发中可能创建主表索引并关联查询比较好。
实例:
>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})
一旦创建索引,我们可以使用子文档的字段来检索数据:
>db.users.find({"address.city":"Los Angeles"})
和oracle一样,字段位置不影响分析器是否走索引的判断。
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。
有些情况不会走索引,这和oracle思想是一样的,比如对字段进行算术运算、正则表达式、$where 子句。(这里好像和oracle有些不同,where条件不走索引,find()方法走索引)
集合中索引不能超过64个
索引名的长度不能超过128个字符
一个复合索引最多可以有31个字段
类似sql中的count(*)
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
聚合的表达式
$sum 计算总和。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg 计算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])
管道操作
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit:用来限制MongoDB聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。
实例:
db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);
这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate(
{ $project : {
_id : 0 ,
title : 1 ,
author : 1
}});
$match实例
db.articles.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
$ match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。
$skip实例
db.article.aggregate(
{ $skip : 5 });
经过$skip管道操作符处理后,前五个文档被"过滤"掉。(分页)
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
接下来的 3 个字节是机器标识码
紧接的两个字节由进程 id 组成 PID
最后三个字节是随机数
形式上这个和oracle中的rowid差不多,但是里面保存的信息多。
创建ObjectId
>newObjectId = ObjectId()
从ObjectId中获取时间戳
>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()
转换成字符串
>new ObjectId().str
注:根据http://www.runoob.com/mongodb/ 整理学习