全文索引
全文索引的建立方法跟之前的单键索引,复合索引,比较相似
单键索引:db.articles.ensureIndex({key:'text'})
复合索引:db.articles.ensureIndex({key_1:'text',key_2:'text'})
全文索引:db.articles.ensureIndex({"$**":"text"})
全文索引中,我们可能存在不同的字段,比如api,比如问答,比如公告,mongod中存储的表结构是不固定的
创建全文索引
> db.articles.ensureIndex({'article':'text'})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
成功后,我们看下如何使用全文查询,在这之前先插入几条简单的数据
db.articles.find({$text:{$search:'coffee'}})
db.articles.find({$text:{$search:'aa bb cc'}})
db.articles.find({$text:{$search:'aa bb -cc'}})
db.articles.find({$text:{$search:'"aa" "bb" "cc"'}})
使用全文索引跟使用其他索引不一样,我们不再需要字段的名字,而是直接使用$text,$search进行查询,这个时候有个问题,如果创建了两个全文索引,它查的是哪一个,这个问题在mongodb中不会发生,
因为在mongodb中,每个数据集合只能创建一个全文索引
模糊查找包含某个字段
> db.articles.find({$text:{$search:'aa'}})
{ "_id" : ObjectId("5b638940be4539ecd263d2d2"), "author" : "luxun", "title" : "背影", "article" : "aa bb rr gg" }
{ "_id" : ObjectId("5b63893abe4539ecd263d2d1"), "author" : "luxun", "title" : "背影", "article" : "aa bb cc dd ee" }
{ "_id" : ObjectId("5b63894bbe4539ecd263d2d3"), "author" : "luxun", "title" : "背影", "article" : "aa bb hh oo dssd hlk" }
看到返回结构有三个包涵aa
我们查个只有第二个包含的
> db.articles.find({$text:{$search:'rr'}})
{ "_id" : ObjectId("5b638940be4539ecd263d2d2"), "author" : "luxun", "title" : "背影", "article" : "aa bb rr gg" }
可以看到只有第二条文档返回了
模糊查找多个字段
有时候我们查询时,关键词不只有一个,可能多个,在mongodb中,可以非常简单的将他们分开
> db.articles.find({$text:{$search:'aa bb cc'}})
{ "_id" : ObjectId("5b638940be4539ecd263d2d2"), "author" : "luxun", "title" : "背影", "article" : "aa bb rr gg" }
{ "_id" : ObjectId("5b63893abe4539ecd263d2d1"), "author" : "luxun", "title" : "背影", "article" : "aa bb cc dd ee" }
{ "_id" : ObjectId("5b63894bbe4539ecd263d2d3"), "author" : "luxun", "title" : "背影", "article" : "aa bb hh oo dssd hlk" }
我们看到都返回了,这里是或查询,包含aa或者包含bb,或者包含cc都会返回
不包含某个字段
有时候我们查询,想要不包含某个字符串,可以用负号
> db.articles.find({$text:{$search:'aa bb -cc'}})
{ "_id" : ObjectId("5b638940be4539ecd263d2d2"), "author" : "luxun", "title" : "背影", "article" : "aa bb rr gg" }
{ "_id" : ObjectId("5b63894bbe4539ecd263d2d3"), "author" : "luxun", "title" : "背影", "article" : "aa bb hh oo dssd hlk" }
包含 aa 或者 bb 并且不包含cc
可以看到,返回了两条,包含aa bb,但是不包含cc的数据
与/并 查找
我们的aa bb cc是或的查找方式,有的时候我们想用与的方式查找,就需要将字符串用引号引起来
> db.articles.find({$text:{$search:'"aa" "bb" "cc"'}})
{ "_id" : ObjectId("5b63893abe4539ecd263d2d1"), "author" : "luxun", "title" : "背影", "article" : "aa bb cc dd ee" }
或
> db.articles.find({$text:{$search:"\"aa\" \"bb\" \"cc\""}})
{ "_id" : ObjectId("5b63893abe4539ecd263d2d1"), "author" : "luxun", "title" : "背影", "article" : "aa bb cc dd ee" }
可以看到只返回一条包含aa bb cc的一条信息
全文索引用于在文档中搜索文本,我们也可以使用正则表达式来查询字符串,但是当文本块比较大的时候,正则表达式搜索会非常慢,而且无法处理语言理解的问题(如 entry 和 entries 应该算是匹配的)。使用全文索引可以非常快地进行文本搜索,就如同内置了多种语言分词机制的支持一样。创建索引的开销都比较大,全文索引的开销更大。创建索引时,需后台或离线创建。
被索引的数据的默认语言决定了如何解析词根以及忽略停止词的规则。被索引数据的默认语言是英语。如果希望指定一个不同的语言,在创建全文索引时使用 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"})
地理位置索引
地理空间索引(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 支持 Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon、Geometry 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"})
java 查询
Double longtitude=subjectContentValue.get(0);
Double lantitude=subjectContentValue.get(1);
BasicDBObject query = new BasicDBObject();
query.put("loc",
new BasicDBObject("$nearSphere",
new BasicDBObject("$geometry",
new BasicDBObject("type","Point")
.append("coordinates",new double[]{longtitude,lantitude}))
.append("$maxDistance",distance)
));
if(queryRange!=null&&queryRange.intValue()==0){//本场景
query.put("source_id",sourceId);
}
getTimeLimitCriteria(query,timeRange);
System.out.println(query.toString()+"_"+collectionName);
logger.info("nearSphere:{}",query.toString());
DBCursor cursor= dataMongoTemplate.getCollection(collectionName).find(query);
List<String> list = new LinkedList<>();
while (cursor.hasNext()) {
DBObject dbObject=cursor.next();
list.add((String)dbObject.get(objectProperty));
}
return list;
哈希索引
简单地说,哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
优势:
1 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据。
劣势
1 如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索。
2 哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询)。
3 哈希索引也不支持多列联合索引的最左匹配规则。