MangoDB高级应用
mongodb高级应用
author:simplewu
聚合
聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。
//统计员工总数 db.emp.aggregate([{$count:"countname"}]) //或者 db.emp.find().count()
$group
使用$group是对筛选的数据进行分组。类似于mysql中的group by关键字。
//根据员工gender来分组并且统计数量 db.emp.aggregate([{$group : {_id : "$gender", count: {$sum : 1}}}])
说明:
- 这里_id是表示分组的字段,名字是固定的。
- count表示聚合生成列的名称。
- $sum表示聚合函数。
- 1统计的值,其他聚合函数也可以是字段。
聚合表达式
表达式 | 描述 | 实例 |
---|---|---|
$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"}}}]) |
这些聚合函数基本上与mysql,oracle中效果是一致的。
索引
所以这东西学习过数据库的都知道是不可缺少的,当然我们的mangodb也是有的。
索引通常能够极大的提高查询的效率,如果没有索引,mongodb在读取数据时必须扫描集合中的每个文档,并选取那些符合查询条件的记录。
创建索引语法:
db.collection.createindex(keys, options) /* key:你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。如果多个字段使用,隔开 */ db.emp.createindex({"name":1}) #创建单列索引 db.emp.createindex({"name":1,"age":-1}) #创建多列索引。
索引的常见操作:
//查看集合索引 db.emp.getindexes() //查看集合索引大小 db.emp.totalindexsize() //删除集合所有索引 db.emp.dropindexes() //删除集合指定索引 db.emp.dropindex("索引名称")
索引的种类、
在mysql里面索引有许多种类当然我们的mongodb中也有很多种类:id索引、单键索引、多键索引、复合索引、过期索引、全文索引。
id索引
id索引也称为主键索引,是我们创建一个集合时,自动创建的索引。
集合的默认排序是按照id来进行排序的。在mongodb中id是根据objectid()来生成的,这个顺序是以时间撮来进行生成。
单键索引
单键索引是最普通的索引。
和id索引不同,单键索引不会自动创建,需要我们手动创建。
db.col.createindex({"name":1})//创建单列索引,对name列创建索引
多键索引
多键索引和单键索引创建形式相同,区别在于字段的值。
单键索引:值是一个单一的值,例如:字符串,数字或者日期。
多键索引:值有多个记录,例如:数组。
db.emp.createindex({"name":1,"age":-1})//创建多列索引,对name和age创建索引
复合索引
当我们的查询条件不只一个时,就需要建立符合索引。符合索引是在多个列上同时创建索引。
db.col.createindex({"name":1,"age":-1}) //创建复合索引。
索引的命名
默认情况下,索引的命名是列+1或者-1,这种方式不是很方面记忆,而且删除是也不太方面。这时候我们就需要为索引创建一个名称。
//创建索引并命名为ix_name。 db.students.createindex({name:-1},{name:"ix_name"})
唯一索引
我们可以为索引添加一个唯一性,从而保存该列的数据不允许重复。
//创建索引并命名为ix_name。 db.students.createindex({name:-1},{name:"ix_name",unique:true})
过期索引
过期索引:就是在一段时间后会自动过期的索引。在索引过期后,相应的数据也会被删除。
适合存储一些希望一段时间后会失效的数据,比如用户登录信息,存储的日志等。
db.collections.createindex({time:1},{expireafterseconds:10})
过期索引的一些限制:
- 过期索引的值必须是指定的时间类型,必须使用isodate或者isodate数组,不能使用时间撮,否则不会被自动删除。
- 如果指定的是isodate数组,则按照最小时间删除。
- 过期索引不能是复合索引。
- 删除时间是有一定的误差,由于删除过程是由后台程序每60秒跑一次,而且删除数据也需要一定的时间。所以存在误差。
全文索引
当要对一篇文章中的文本内容进行搜索的时候,这个时候可以考虑全文索引。全文索引可以加快检索内容关键字的效率。全文索引只能对字符串或者字符串数组有效。
//创建全文索引 db.students.createindex({name:"text",info:"text"})
使用全文索引
创建好全文索引后,我们就可以来使用全文索引,使用全文索引需要使用$text和$search两个运算符。
//查找全文索引中包含了zhangsan的文档。 db.students.find({$text:{$search:"zhangsan"}}) //查找全文索引中包含了zhangsan或者zhangsanfeng的文档。 db.students.find({$text:{$search:"zhangsan zhangsanfeng"}}) //查找全文索引中包含了zhangsan,但不包含zhangsanfeng的文档。 db.students.find({$text:{$search:"zhangsan -zhangsanfeng"}}) //查找全文索引中包含了zhangsan和zhangsanfeng的文档。 db.students.find({$text:{$search:"\"zhangsan\" \"zhangsanfeng\""}})
全文索引的相似度
我们在百度中搜索时,经常会看到和我们关键字匹配度越高的,排行就越靠前。在mongodb中,我们还可以返回查询结果的相似度,与sort一起使用效果会更好。
使用方式:在find后面跟上{score:{$meta:"textscore"}}
db.students.find({$text:{$search:"zhangsan"}},{score:{$meta:"textscore"}}) .sort({score:{$meta:"textscore"}})
全文索引的限制
- 每次查询只能指定一个text。
- text操作符不能出现在$nor查询中。
- 查询中如果包含了text则hint将不再起作用。
- mongodb的全文索引对中文支持不是很好。
索引的注意事项
索引像一把双刃剑,用得好可以提高查询效率,如果用不好可能会导致性能的降低。
- $where和$exists完全不能走索引
- ne取反操作效率很低
- $not、$nin$or、$in
explain执行计划
索引的性能如何,我们可以通过explain执行计划来进行分析,从而使索引的性能达到最优。
explain的使用方式非常简单,我们只需要在执行的find()命令后添加一个explain()方法即可。
db.students.find().explain();
文档之间的关系
很多时候数据库中的数据不是单独存在的,数据和数据之间会有一些相互之间的联系。我们mongodb可以配置这种数据之间的关系。
文档之间的关系
- 一对一(one to one)
- 一对多(one to many)
- 多对一(many to one)
- 多对多(many to many)
每种关系又可以有两种方式来实现。
嵌入式:嵌套在一个document文档中。
引用式:通过外键引用的方式来实现。
java操作mongodb
下载mongodb驱动http://mongodb.github.io/mongo-java-driver/
<dependency> <groupid>org.mongodb</groupid> <artifactid>mongodb-driver</artifactid> <version>3.9.0</version> </dependency>
连接数据库,你需要指定数据库名称,如果指定的数据库不存在,mongo会自动创建数据库。
public static void main( string args[] ){ try{ // 连接到 mongodb 服务 mongoclient mongoclient = new mongoclient( "localhost" , 27017 ); // 连接到数据库 mongodatabase mongodatabase = mongoclient.getdatabase("students"); system.out.println("connect to database successfully"); }catch(exception e){ system.err.println( e.getclass().getname() + ": " + e.getmessage() ); } }
我们可以使用 com.mongodb.client.mongodatabase 类中的createcollection()来创建集合
我们可以使用com.mongodb.client.mongocollection类的 insertmany() 方法来插入一个文档。
我们可以使用 com.mongodb.client.mongocollection 类中的 find() 方法来获取集合中的所有文档。
你可以使用 com.mongodb.client.mongocollection 类中的 updatemany() 方法来更新集合中的文档。
要删除集合中的第一个文档,首先你需要使用com.mongodb.dbcollection类中的 findone()方法来获取第一个文档,然后使用remove 方法删除。
个人测试结果,基本操作:
package com.simple.nosql.mongodb; import java.util.arrays; import java.util.hashmap; import java.util.list; import java.util.map; import org.bson.document; import org.junit.before; import org.junit.test; import com.mongodb.mongoclient; import com.mongodb.client.finditerable; import com.mongodb.client.mongocollection; import com.mongodb.client.mongocursor; import com.mongodb.client.mongodatabase; import com.mongodb.client.result.deleteresult; import com.mongodb.client.result.updateresult; /** * mangodb基本操作 * @author simplewu * */ public class test1 { private mongoclient mongoclient = null; /** * 获取mongodb客户端连接 * * @return */ public mongoclient getmongoclient() { this.mongoclient = new mongoclient("localhost", 27017); return this.mongoclient; } private mongodatabase mongodatabase = null; /** * 连接数据库,你需要指定数据库名称,如果指定的数据库不存在,mongo会自动创建数据库。 */ public mongodatabase createconnection() { getmongoclient();// 获取客户端连接 // 连接到数据库 mongodatabase = mongoclient.getdatabase("students"); system.out.println("connect to database successfully"); return this.mongodatabase; } @before public void before() { createconnection(); } /** * 我们可以使用 com.mongodb.client.mongodatabase 类中的createcollection()来创建集合 */ @test public void createcollection() { // 创建集合 mongodatabase.createcollection("employee"); system.out.println("集合创建成功"); } /** * 获取集合 */ public mongocollection<document> getcollection() { // 获取指定名称集合 mongocollection<document> collection = mongodatabase.getcollection("employee"); system.out.println("集合获取成功"); return collection; } /** * 插入文档 */ @test public void testinsert() { // 获取集合 mongocollection<document> collection = getcollection(); map<string, object> map = new hashmap<>(); map.put("_id", 1); map.put("name", "aa"); map.put("email", "aa@gmail.com"); map.put("info", "my name is aa"); document document = new document(map); collection.insertone(document); // 插入多个文档 document document1 = new document(); document1.append("_id", 2); document1.append("name", "bb"); document1.append("email", "bb@gmail.com"); document1.append("info", "my name is bb"); document document2 = new document(); document2.append("_id", 3); document2.append("name", "cc"); document2.append("email", "cc@gmail.com"); document2.append("info", "my name is cc"); list<document> list = arrays.aslist(document1, document2); collection.insertmany(list); system.out.println("插入文档成功"); } /** * 检索文档 */ @test public void testfind() { // 获取集合 mongocollection<document> collection = getcollection(); // 检索所有文档 finditerable<document> iterable = collection.find(); //获取结果集 mongocursor<document> cursor = iterable.iterator(); while (cursor.hasnext()) { system.out.println(cursor.next()); } // 获取name=aa的文档 /* * document document = new document(); document.append("name", "aa"); * finditerable<document> iterable = collection.find(document); * select(iterable); */ } /** * 更新文档 */ @test public void update() { // 获取集合 mongocollection<document> collection = getcollection(); //创建需要更新的条件 document document = new document("name", "aa"); //创建需要更新的内容 document upd = new document("name", "aa-aa"); //更新文档 document document1 = new document("$set", upd); updateresult result = collection.updateone(document, document1); system.out.println("更新成功"); } /** * 指定条件查询 */ @test public void eqltgt(){ mongocollection<document> collection = getcollection(); //创建条件 document check = new document("$eq", 1); //指定判断列 document document1 = new document("_id",check); finditerable<document> iterable = collection.find(document1); select(iterable); } /** * 删除文档 */ @test public void delete() { // 获取集合 mongocollection<document> collection = getcollection(); document document = new document("name", "bb"); deleteresult result = collection.deleteone(document); system.out.println("删除数量 : " + result.getdeletedcount()); } public void select(finditerable<document> iterable) { mongocursor<document> cursor = iterable.iterator(); while (cursor.hasnext()) { system.out.println(cursor.next()); } } }
上一篇: 域名在朋友圈内分享需要ICP备案