原文链接:MongoDB的学习--索引类型和属性

索引类型

MongDB的索引分为以下几种类型:单键索引、复合索引、多键索引、地理空间索引、全文本索引和哈希索引

单键索引(Single Field Indexes)

在一个键上创建的索引就是单键索引,单键索引是最常见的索引,如MongoDB默认创建的_id的索引就是单键索引。

例子:

{
"_id" : ObjectId(...),
"name" : "Alice",
"score" : 27
}

如果要在如上的文档中创建单键索引,语句如下:

db.users.ensureIndex( { "score" : 1 } )

其存储结构如下图:

如果想要在子文档的一个键上建立单键索引,其例子如下:

{
"_id": ObjectId(...),
"name": "John Doe",
"address": {
"street": "Main",
"zipcode": "53511",
"state": "WI"
}
}

结构如上,其创建语句如下:

db.users.ensureIndex( { "address.zipcode": 1 } )

如果想要在整个子文档上建立单键索引,其例子如下:

{
_id: ObjectId(...),
metro: {
city: "New York",
state: "NY"
},
name: "Giant Factory"
}

结构如上,其创建语句如下:

db.factories.ensureIndex( { metro: 1 } )

下面的语句能够使用其索引查找到上面的数据:

db.factories.find( { metro: { city: "New York", state: "NY" } } )

但再下面的这一条语句就查不到数据了,说明子文档的查找必须是精确匹配,包括子文档中的顺序:

db.factories.find( { metro: { state: "NY", city: "New York" } } )

复合索引(Compound Indexes)

在多个键上建立的索引就是复合索引。

例子:

{
"_id": ObjectId(...),
"userid": "aa1",
"category": ["food", "produce", "grocery"],
"location": "4th Street Store",
"score": 4
}

如果要在如上的文档中创建复合索引,语句如下:

db.products.ensureIndex( { "userid": -1, "score": 1 } )

userid是正序排列,score是逆序排列的。其存储结构如下图:

这个索引可以支持如下的排序:

db.products.find().sort( { userid: 1, score: -1 } );
db.products.find().sort( { userid: -1, score: 1 } );
db.products.find().sort( { userid: 1 } );
db.products.find().sort( { userid: -1 } );

不能支持如下的排序:

db.products.find().sort( { userid: 1, score: 1 } );
db.products.find().sort( { userid: -1, score: -1 } );
db.products.find().sort( { score: 1 } );
db.products.find().sort( { score: -1 } );

多键索引(Multikey Index)

如果在一个数组上面创建索引,MongoDB会自己决定,是否要把这个索引建成多键索引。

如果数据结构如下(两种):

{a: [1, 2], b: 1}
{a: 1, b: [1, 2]}

你可以创建{ a: 1, b: 1 },会是多键复合索引。

多键索引结构如下:

例子:

{
"_id" : ObjectId("..."),
"name" : "Warm Weather",
"author" : "Steve",
"tags" : [ "weather", "hot", "record", "april" ]
}

文档结构如上,如果你在tags上创建索引,就会创建出多键索引

如果文档结构如下:

{
"_id": ObjectId(...),
"title": "Grocery Quality",
"comments": [{
author_id: ObjectId(...),
date: Date(...),
text: "Please expand the cheddar selection."
}, {
author_id: ObjectId(...),
date: Date(...),
text: "Please expand the mustard selection."
}, {
author_id: ObjectId(...),
date: Date(...),
text: "Please expand the olive selection."
}]
}

创建{ "comments.text": 1 } 索引也会是多键索引,且在如下查找语句中有效:

db.feedback.find( { "comments.text": "Please expand the olive selection." } )

地理空间索引(Geospatial Indexes and Queries)

MongoDB支持几种类型的地理空间索引。其中最常用的是 2dsphere 索引(用于地球表面类型的地图)和 2d 索引(用于平面地图和时间连续的数据)。

1) 2dsphere

2dsphere 允许使用GeoJSON格式(http://www.geojson.org)指定点、线和多边形。

点可以用形如[longitude, latitude]([经度, 纬度])的两个元素的数组表示:

{
"name" : "New York City",
"loc" : {
"type" : "Point",
"coordinates" : [50, 2]
}
}

线可以用一个由点组成的数组来表示:

{
"name" : "Hudson River",
"loc" : {
"type" : "LineString",
"coordinates" : [[0, 1], [0, 2], [1, 2]]
}
}

多边形是由线组成的数组来表示:

{
"name" : "New England",
"loc" : {
"type" : "Polygon",
"coordinates" : [[[0, 1], [0, 2], [1, 2], [0, 1]]]
}
}

2dsphere 支持 PointMultiPointLineStringMultiLineStringPolygonMultiPolygonGeometry Collection

loc 字段的名字可以是任意的,但是其中子对象是由 GeoJSON 指定的,不能改变。

在 ensureIndex 中使用 2dsphere 选项就可以创建一个地理空间索引:

db.world.ensureIndex({"loc": "2dsphere"})

可以使用多种不同的地理空间查询:交集(intersection)、包含(within)以及接近(nearness)。查询时,需要将希望查找的内容指定为形如 {"$geometry":geoJsonDesc} 的 GeoJSON 对象。

交集(intersection),使用 $geoIntersects 操作符:

var place = {
"type" : "Polygon",
"coordinates" : [[[0, 1], [0, 3], [50, 2], [0, 1]]]
}
db.world.find({"loc" : {"$geoIntersects" : {"$geometry" : place}}})

会查找出所有与 place 有交集的文档。

包含(within),使用 $within 或者 $geoWithin 操作符:

db.world.find({"loc" : {"$within" : {"$geometry" : place}}})

接近(nearness),使用 $near 或者 $geoNear 操作符:

var place = {
"type" : "Point",
"coordinates" : [0, 3]
}
db.world.find({"loc" : {"$near" : {"$geometry" : place}}})

place 必须是个点,$near 是唯一一个会对查询结果进行自动排序的地理空间操作符,$near 的返回结果是按照距离由近及远排序的。

2) 2d

2d 索引用于扁平化表面,而不是球体表面,否则极点附近会出现大量的扭曲变形。

文档中使用包含两个元素的数组表示 2d 索引字段,不是 GeoJSON 的格式。

{
"name": "Water Temple",
"tile": [32, 22]
}

2d 索引只能对点进行索引。可以保存一个由点组成的数组,但是它只会被保存为由点组成的数组,不会被当成线。特别是对 $within 查询来说,数组中的某个点在查询范围内,该文档就会被找出。

在 ensureIndex 中使用 2d 选项就可以创建一个地理空间索引,也可以在其中设置最大最小边界值和精度。默认情况下,最大值和最小值的范围是[ -180 , 180 ),精度是26位的精度,大致相当于2英尺或60厘米的精度:

db.places.ensureIndex({"tile" : "2d"}, {"min" : -90, "max" : 90, "bits" : 20})

这会创建一个180*180大小的空间索引。

2d 索引的查询比 2dsphere 简单许多,可以直接使用$near 和 $within,而不必带有 $geometry 子对象:

db.places.find({"tile": {"$near": [20, 21]}}).limit(10)

如果不加 limit,默认最多返回100条。

$within 可以查询出某个形状里的所有文档,可以是矩形($box)、圆形($center)或者多边形($polygon)。

db.places.find({"tile": {"$within": {"$box": [[0, 0], [30, 30]]}}})

$box 接收两个元素,第一个元素是矩形的左下角坐标,第二个元素是矩形的右上角坐标。

db.places.find({"tile": {"$within": {"$center": [[30, 30], 10]}}})

$center 也接收两个元素,第一个元素是圆心点的坐标,第二个是圆的半径。

db.places.find({"tile": {"$within": {"$polygon": [[0, 0], [30, 30], [0, 25]]}}})

$polygon 接收多个点组成的数组,用来指定多边形。

不管是 2dsphere 索引还是 2d 索引,都可以和其他字段一起组成复合索引:

db.world.ensureIndex({"name": 1, "loc": "2dsphere"})

全文索引(Text Indexes)

全文索引用于在文档中搜索文本,我们也可以使用正则表达式来查询字符串,但是当文本块比较大的时候,正则表达式搜索会非常慢,而且无法处理语言理解的问题(如 entry 和 entries 应该算是匹配的)。使用全文索引可以非常快地进行文本搜索,就如同内置了多种语言分词机制的支持一样。创建索引的开销都比较大,全文索引的开销更大。创建索引时,需后台或离线创建。

{
"_id" : ObjectId("55a0e30427c9370e525032e9"),
"content" : "This morning I had a cup of coffee.",
"about" : "beverage",
"keywords" : [
"coffee"
]
}
{
"_id" : ObjectId("55a0e31027c9370e525032ea"),
"content" : "Who doesn't like cake?",
"about" : "food",
"keywords" : [
"cake",
"food",
"dessert"
]
}

文档如上所示,在 content 上创建全文索引:

db.article.ensureIndex({"content": "text"})

使用全文索引查询内容:

db.article.find({"$text": {"$search": "coffee"}})

如果要在所有字符串的键上进行文本搜索,请使用通配符 ($**) 来索引所有的包含字符串的键。创建了一个 article 中所有文档的所有键的字符串进行索引的索引,且命名为 TextIndex:

db.article.ensureIndex({"$**": "text"}, {"name": "TextIndex"})

被索引的数据的默认语言决定了如何解析词根以及忽略停止词的规则。被索引数据的默认语言是英语。如果希望指定一个不同的语言,在创建全文索引时使用 default_language 选项。

支持如下语言(不支持中文,至少在 2.6 的版本中是这样的):

  • da or danish
  • nl or dutch
  • en or english
  • fi or finnish
  • fr or french
  • de or german
  • hu or hungarian
  • it or italian
  • nb or norwegian
  • pt or portuguese
  • ro or romanian
  • ru or russian
  • es or spanish
  • sv or swedish
  • tr or turkish

注:如果您将语言指定为值 "none" ,那么 text search 会使用简单的分词器,没有停止词也没有取词根处理。

db.quotes.ensureIndex({"content": "text"}, {"default_language": "spanish"})

哈希索引(Hashed Index)

哈希索引可以支持相等查询,但是哈希索引不支持范围查询。您可能无法创建一个带有哈希索引键的复合索引或者对哈希索引施加唯一性的限制。但是,您可以在同一个键上同时创建一个哈希索引和一个递增/递减(例如,非哈希)的索引,这样MongoDB对于范围查询就会自动使用非哈希的索引。

db.active.ensureIndex({"a": "hashed"})

上面的操作将会在 active 的 a 键上创建一个哈希索引。

索引属性

MongDB的索引属性有以下几种:TTL索引、唯一索引和稀疏索引。

TTL索引(TTL Indexes)

TTL索引是一种特殊索引,通过这种索引MongoDB会过一段时间后自动移除集合中的文档。这对于某些类型的信息来说是一个很理想的特性,例如机器生成的事件数据、日志、会话信息等,这些数据都只需要在数据库中保存有限时间。

TTL索引有如下限制:

  • 它不支持复合索引 。

  • 被索引键必须是日期类型的数据。

  • 如果这个键存储的是一个数组,且在索引中有多个日期类型的数据(和一篇文档关联),那么当其中最低 (比如,最早)过期阀值得到匹配时,这篇文档就会过期失效了。

TTL索引不能保证过期数据会被立刻删除。在文档过期和MongoDB从数据库中删除文档之间,可能会有延迟。删除过期数据的后台任务 每隔60秒 运行一次。所以,在文档过期 之后 和 后台任务运行或者结束 之前 ,文档会依然存在于集合中。删除操作的持续实际取决于您的 mongod 实例的负载。因此,在两次后台任务运行的间隔间,过期数据可能会继续留在数据库中超过60秒。在其他方面,TTL索引是普通索引,并且如果可以的话,MongoDB会使用这些索引来匹配任意查询。

db.token.ensureIndex({"lastUpdated": 1}, {"expireAfterSecs": 60*60*24})

token 超过24小时就会被删除掉。

唯一索引(Unique Indexes)

唯一索引可以拒绝保存那些被索引键的值已经重复的文档。

db.members.ensureIndex({"user_id": 1}, {unique: true})

默认情况下,MongoDB索引的 unique 属性是 false 。如果对复合索引施加唯一性的限制,那么MongoDB就会强制要求复合值的唯一性,而不是分别对每个单独的值要求唯一。

唯一性的限制是针对一个集合中不同文档的。也即,唯一索引可以防止 不同 文档的被索引键上存储相同值,但是它不禁止同一篇文档在被索引键存储的数组里存储的元素或者内嵌文档是相同的值。在同一篇文档存储重复数据的情况下,重复的值只会被存入索引一次。

例如,一个集合有一个唯一索引 a.b :

db.collection.ensureIndex({"a.b": 1 }, {unique: true})

假如在集合中没有其他的文档的 a.b 键的值是 5 ,那么唯一索引将会允许将以下文档插入集合:

db.collection.insert({a: [{b: 5}, {b: 5}]})

如果一篇文档不包含唯一索引的被索引键,那么索引默认会为该文档存储一个null值。由于唯一性的限制,MongoDB将只允许有一篇可以不包含被索引键。如果超过一篇文档不包含被索引键或没有值,那么会抛出键重复(duplicate key)错误导致索引创建失败。可以组合使用唯一性和稀疏索引的特性来过滤那些包含null值的文档以避免这个错误。

稀疏索引(Sparse Indexes)

稀疏索引会跳过所有不包含被索引键的文档。这个索引之所以称为 “稀疏” 是因为它并不包括集合中的所有文档。与之相反,非稀疏的索引会索引每一篇文档,如果一篇文档不含被索引键则为它存储一个null值。

db.addresses.ensureIndex({"xmpp_id": 1}, {"sparse": true})

如果一个索引会导致查询或者排序的结果集是不完整的,那么MongoDB将不会使用这个索引,除非用户使用 hint() 方法来显示指定索引。例如,查询 { x: { $exists: false } } 将不会使用 x 键上的稀疏索引,除非显示的hint。

2dsphere (version 2), 2d 和 text 这些索引总是稀疏的。

只要一篇文档里有至少一个被索引键,稀疏且只包含有递增/递减索引键的复合索引就会索引这篇文档。

至于稀疏且包含有地理索引键(例如 2dsphere, 2d)以及递增/递减索引键的复合索引,只有地理索引键的存在与否能决定一篇文档是否被索引。

至于稀疏且包含了全文索引键和其他递增/递减索引键的复合索引,只有全文索引键的存在与否能决定是否索引该文档。

一个稀疏且唯一的索引,可以防止集合中的文档被索引键中出现重复值,同时也允许多个文档里不包含被索引键。

参考:MongoDB 2.6 中文文档

MongoDB的学习--索引类型和属性(转)的更多相关文章

  1. MongoDB的学习--索引类型和属性

    索引类型 MongDB的索引分为以下几种类型:单键索引.复合索引.多键索引.地理空间索引.全文本索引和哈希索引 单键索引(Single Field Indexes) 在一个键上创建的索引就是单键索引, ...

  2. MongoDB的学习--索引

    索引可以用来优化查询,而且在某些特定类型的查询中,索引是必不可少的.为集合选择合适的索引是提高性能的关键. 先来mock数据 for (i = 0; i < 1000000; i++) { db ...

  3. MongoDB数据模型和索引学习总结

    MongoDB数据模型和索引学习总结 1. MongoDB数据模型: MongoDB数据存储结构: MongoDB针对文档(大文件採用GridFS协议)採用BSON(binary json,採用二进制 ...

  4. [转载]MongoDB开发学习(2)索引的基本操作

    索引能够极大的提高查询的效率.在数据库中简历索引必不可少. 在MongoDB中可以很轻松的创建索引. 默认索引_id_ 开启MongoDB服务器,创建数据库cnblogs,创建集合Users .(关于 ...

  5. [Spring学习笔记 2 ]装配各种类型的属性 map,list,array,null,properties

    一.spring Ioc容器补充(1) Spring Ioc容器 DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入field(注解) ...

  6. 【Hibernate学习笔记-5.2】使用@Temporal修饰日期类型的属性

    作者:ssslinppp       1. 摘要 关于日期类型,Java和数据库表示的方法不同: Java:只有java.util.Date和java.util.Calender两种: 数据库:dat ...

  7. MongoDB的学习--文档的查询

    继续关于<MongoDB权威指南>记录,今天的内容是文档的查询~~ MongoDB官网地址:http://www.mongodb.org/ 我使用的是MongoDB 2.4.8 find函 ...

  8. Mysql几种索引类型的区别及适用情况

    如大家所知道的,Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE. 那么,这几种索引有什么功能和性能上的不同呢? FULLTEXT 即为全文索引,目前只有MyI ...

  9. 双刃剑MongoDB的学习和避坑

    双刃剑MongoDB的学习和避坑 MongoDB 是一把双刃剑,它对数据结构的要求并不高.数据通过key-value的形式存储,而value的值可以是字符串,也可以是文档.所以我们在使用的过程中非常方 ...

随机推荐

  1. mysql源码解读之事务提交过程(一)

    mysql是一种关系型数据库,关系型数据库一个重要的特性就是支持事务,这是区别于no-sql产品的一个核心特性.当然了,no-sql产品支持键值查询,不能支持sql语句,这也是一个区别.今天主要讨论下 ...

  2. [g2o]一个备忘

    g2o使用的一个备忘 位姿已知,闭环的帧已知,进行图优化. #include "stdafx.h" #include <vector> #include "P ...

  3. V-rep学习笔记:机器人逆运动学数值解法(The Jacobian Transpose Method)

    机器人运动学逆解的问题经常出现在动画仿真和工业机器人的轨迹规划中:We want to know how the upper joints of the hierarchy would rotate ...

  4. RAC集群时间同步服务

    集群时间同步服务在集群中的两个 Oracle RAC 节点上执行以下集群时间同步服务配置.Oracle Clusterware 11g 第 2 版及更高版本要求在部署了 Oracle RAC 的集群的 ...

  5. 洛谷 [P2825] 游戏

    二分图匹配的匈牙利算法 这道题,如果没有硬石头的限制,那么就与ZJOI 2007矩阵游戏完全一样,但是如果有了硬石头的限制,我们就不能将整行整列作为元素建图,我们可以以硬石头为边界,将每一行.每一列分 ...

  6. (转)Vue种key的作用

    https://blog.csdn.net/qq_41861679/article/details/80659278 https://cn.vuejs.org/v2/api/#key 其实不只是vue ...

  7. js原生函数

    arguments:代表所有的形参的集合: 可以通过arguments: cosole.log(arguments):打印所有参数 console.log(arguments[i]);可以通过访问下标 ...

  8. Nginx实现url请求不区分大小写

    原文地址:http://blog.linuxeye.com/382.html 如果你将跑在Windows下的项目(如:php)迁移到Linux下,由于Windows操作系统中,文件名是不区分大小写的: ...

  9. [转载]Ubuntu 安装 万能五笔 输入法

    原文地址:Ubuntu 安装 万能五笔 输入法作者:庖丁解牛 paul@paul-desktop:~/scripts$ cat ins-ibus-wnwb.sh #!/bin/sh set -e cd ...

  10. Jmeter参数的AES加密使用

    在Jmeter日常实践中,大家应该都遇到过接口传参需要加密的情况.以登陆为例,用户名和密码一般都需要进行加密传输,在服务端再进行解密,这样安全系数会更高,但在使用jmeter进行接口测试的时候,怎样发 ...