Mongo
设计应用
使用ensureIndex()创建索引
db.users.ensureIndex({
"username:1
})
通常。在一个特定的集合,不应该拥有两个以上的索引
索引的值是按照一定顺序排列的,因此,使用索引键对文档进行排序非常快。然而,只有在首先使用索引键进行排序时,索引才有用。
复合索引就是建立在多个字段上的索引
db.users.ensureIndex({
"age": 1,
"username:1
})
通常来说,如果mongodb使用索引进行查询,那么查询结果文档通常就是按照索引顺序排序的
如果对查询结果的范围做了限制,那么mongo在几次匹配之后就可以不在扫描索引,在这种情况下,将排序键放在第一位时一个和好的策略。
可以通过hint来强制使用某个特定的索引
在多个键上建立的索引就是复合索引
索引使用的方向,与排序方向相同即可,注意,相互反转(在每个方向上*-1)的索引时等价的{“age”:1,”username”:-1}适用的查询和{“age”-1,”username”1}是完全一样的
只有基于多个查询条件进行排序时,索引方向才是你叫重要的,如果只是基于单一索引键进行排序
如果你的查询只需要查找索引中包含的字段,那就根据没必要获取实际的文档。当一个索引包含用户请求的所有字段,可以认为这个索引覆盖了本次查询。在实际中,应该使用覆盖索引,而不是获取文档
为了确认查询只使用索引就可以完成,应该使用投射来指定不要返回_id字段
如果在覆盖索引上执行explain(),indexOnly字段的值要设为true
复合索引具有双重功能,而且对不同的查询可以表现出不同的索引。
如果有一个拥有n个键的索引,难免你同时得到了所有这n个键的前缀组成的索引。
$where查询和检查一个键是否存在的查询完全无法使用索引
$ne查询可以使用索引,但并不是很有效,因为必须要查看所有索引的条目
$nin就总是要进行全表扫描
设计多个字段的索引时,应该将会用于精确匹配的字段防到索引的前面,将用于范围匹配的字段放到最后
mongo允许对嵌套字段和数组建立索引,嵌套对象和数组字段可以与符合索引中顶级字段一起使用。
可以在嵌套文档的键上建立索引,方式和正常的键一样。
例如:
{
"username:"sid",
"loc":{
"ip":"1.2.3.4"
"city":"xxx"
"state":"xxx"
}
}
需要在loc的某一个字段建立索引,以便提高这个字段的查询速度
db.users.ensureIndex({
"loc.city":1
})
对数组建立索引,可以高效的搜索数组中的特定元素
对于索引的键,如果这个键在文档中是一个数组,那么这个索引就会呗还标记为多键索引,多键索引可能会比非多键索引慢一些,可能会友多个索引条目指向同一个文档,因此在返回结果时必须要先去除重复的内容
基数就是集合中某个字段拥有不同值的数量,一般来说,应该在基数比较高的键上建立索引,或者至少应该吧基数高的键放在复合索引的前面
explain()能够提供大量的查询相关的信息。对于任意查询,都可以在最后添加一个explain()调用
字段说明:
唯一索引可以确保集合的每一个文档都有唯一值
如果向保证同文档的“username”键都拥有不同的值,那么可以创建一个唯一索引
db.users.ensureIndex(
{
"username":1
},
{
"unique":true
}
)
创建符合唯一索引时,单个键的值可以相同,但所有键的组合值必须时唯一的
在已有的集合创建唯一索引时可能会失败,因为集合中肯能已经存在重复值了,通常需要先对已有的数据进行处理,在极少数情况下,可能希望直接删除重复的值,创建索引时使用dropDups选项,如果遇到重复的值,第一个会被保留,之后的重复文档都会呗删除
db.users.ensureIndex(
{
"username":1
},
{
"unique":true,
"dropDups";true
}
)
所有的数据库索引信息都存储在system.indexes集合中,这个是一个保留集合,不能在其中插入或者删除文档,直蹦通过ensureIndex或者dropIndexes对其进行操作
创建一个索引之后,可以执行db.collectionName.getIndexes()查询给定集合上的所有索引信息
mongo中普通的集合是动态的,可以自动增长,但是固定集合,固定集合需要事先创建好,而却他的大小时固定的。固定集合的行为类似于循环队列,如果已经满了,最老的文档会被删除,新插入的文档会占据这块空间
不同于普通集合,固定集合必须在使用前显示创建,可以使用create命令创建固定集合,使用createCollection函数
创建一个名为my_collection大小为10000字节的固定集合
db.createCollection("my_collection",{
"capped":true,
"size":10000
})
限制固定集合中的文档的数量
db.createCollection("my_collection",{
"capped":true,
"size":10000,
"max":100
})
创建固定集合还可以将已有的某个常规集合转换成固定集合,使用convertToCapped命令
db.runCommand("convertToCapped","test","size":10000)
对于固定排序,自然排序就是文档从旧到新排序的,当然也可以按照从新到旧的顺序排序
db.my_collection.find().sort({
"$natural":-1
})
允许为每一个文档设置一个超市时间,一个文档到达预设置的老化程度之后就会呗删除
在ensureIndex中指定expireAlterSecs选项就可以创建一个TTL索引
db.foo.ensureIndex(
{
"lastUpdate":1
},
{
"expireAlterSecs":60*60*24
}
)
在lastUpdate字段上建立了一个ttl索引,如果一个文档的lastUpdate字段存在并且它的值时日期类型,当服务器时间比文档的lastUpdate字段的时间晚expireAlterSecs秒时,文档就会呗删除
mongo每分钟对ttl索引进行一次清理,所以不应该依赖以秒为单位保证索引的存活状态
mongo支持几种类型的地理空间索引,其中常用的时2dsphere索引和2d索引
可以使用多种不同类型的地理空间查询:交集、包含、以及接近。查询时,需要将希望查找的内容制定为形如{“$geometry”:geoJsonDesc}的GeoJson对象
例如:可以使用$geoIntersects操作符找出与查询位置相交的文档
var eastVillage={
"type":"xxx",
"coordinates":{
[-73.9917900,40.7264100],
[-73.9917900,40.7264100],
[-73.9917900,40.7264100],
}
}
db.open.street.map.find({
"loc":{
"$geoIntersects":{
"$geometry":eastVillage
}
}
})
使用”$within”查询完全包含在某个区域的文档
db.open.street.map.find({
"loc":{
"$within":{
"$geometry":eastVillage
}
}
})
使用”$near“查询附近的位置
db.open.street.map.find({
"loc":{
"$near":{
"$geometry":eastVillage
}
}
})
$near是唯一一个会对查询结果进行自动排序的地理空间操作符,返回结果时按照距离由近及远排序的
shell下使用mongofiles 命令即可
对聚合框架可以对集合中的文档进行变化和组合,可以用多个构件创建一个管道,用于对一连串的文档进行处理,包括筛选、投射、分组、排序、限制、跳过 将一系列操作分别传给aggregate()函数即可
db.articles.aggregate(
{
"$project":{
"author:1
}
},
{
"$group":{
"_id":"$auhtor",
"count":{
"$sum":1
}
}
},
{
"$sort":{
"count":-1
}
},
{
"$limit":5
}
)
用于对文档集合进行筛选,之后就可以在筛选得到的文档子集做聚合
可以从文档中提取字段,可以重命名字段
只包含一个author字段
db.articles.aggregate({
"$project":{
"author":1,
"_id":0
}
})
将投射过的字段进行重命名,将”_id”在返回结果中重命名为”userId”
db.users.aggregate(
{
"$project":{
"userId":"$_id",
"_id":0
}
}
)
算术表达式可用于操作数值,指定一组数值,就可以使用这个表达式进行操作了
将”salary“和”bonus“字段的值相加
db.employees.aggregate(
{
"$project":{
"todayPay:{
"$add":["$salary","$bonus"]
}
}
}
)
操作符的语法:
布尔表达式
控制语句
将文档依据特定字段的不同值进行分组
拆分可以将数组中的每一个值拆分为单独的文档
如果希望在查询中得到特定的子文档,先使用“unwind”得到所有子文档,再使用“match”得到想要的文档
根据任何字段或多个字段进行排序
接受一个数字n,返回结果集中的前n个文档
接受一个数字m,丢弃结果集中的钱n个文档
map函数使用特定的emit函数返回要处理的值,emit会给mapreduce一个键和一个值
map=function(){
for (var key in this){
emit(key,{
count:1
})
}
}
reduce=function(key,emits){
total=0;
for (var i in emits){
total+=emit[i].count;
}
return {
"count":total
};
}
mr=db.runCommand(
{
"mapreduce":"foo",
"map":map,
"reduce":reduce
}
)
操作相关元信息
返回集合中文档的数量
db.foo.count({"x";2})
用来找出给定键的所有不同值,使用时必须指定集合和键
db.runCommand(
{
"distinct":"people",
"key":"age"
}
)
选定分组所依据的键进行分组,然后对分组内的文档进行聚合得到结果文档
db.runCommand(
{
"ns":"stocks",
"key":"day",
"inital":{
"time":0
},
"$reduce":funcion(doc,prev){
if (doc.time>prev.time){
prev.price=doc.price;
price.time=doc.time;
}
}
}
)
完成器用于精简从数据库传到用户的数据
分组所依据 的条件非常复杂,需要定义一个函数来决定文档分组所依据的键
定义分组函数就要用到keyf键,使用keyf的group命令
db.posts.group(
{
"ns":"posts",
"$keyf":function(x){
return x.category.toLowerCase();
},
"initializer":...
}
)
决定何时采用范式化何时采用反范式化需要根据自己的应用程序的实际情况仔细权衡
一般来说,数据生成越频繁,就越不应该将这些数据内嵌到其他文档中
如果内嵌字段或者内嵌字段数量时无限增长的,那么应该将这些内容保存在单独的集合中,使用引用的方式进行访问
如果某些字段时文档数据的一部分,那么需要将这些字段内嵌到文档中
如果在查询文档时经常需要将需要将某个字段排除出去,那么这个字段应该放在另外的集合中
内嵌数据与引用数据的比较:
更适合内嵌 | 更适合引用 |
---|---|
子文档较小 | 子文档较大 |
数据不会定期改变 | 数据经常改变 |
最终数据一致即可 | 中间阶段的数据必须一致 |
文档数据小幅增加 | 文档数据大幅增加 |
数据通常需要执行二次查询才能获得 | 数据通常不包含在结果中 |
快速读取 | 快速写入 |
需要在写入效率更高的模式与读取更高的模式之间权衡
注: