ITPub博客

首页 > 数据库 > > Mongo 数据库基础知识总结(持续更新)

Mongo 数据库基础知识总结(持续更新)

作者:浪花小雨滴 时间:2020-05-12 11:29:48 0 删除 编辑

一、MongoDB的下载及本地的安装

1.下载:Downloads for linux

2.安装路径: 如 E:\MongoDB

3.安装后的配置:(注意cd 命令不能换盘符,直接输入E: 即可换盘符)

在E:\MongoDB\下新建data文件夹

    在E:\MongoDB\data下新建db和log文件夹

    在E:\MongoDB\data\log下新建MongoDB.log文件

4.启动:

进入E:\MongoDB\bin ,输入:

mongod --dbpath E:\MongoDB\data\db

当输出信息出现如下日志时,代表安装成功:

2018-06-10T14:55:37.044+0800 I NETWORK  [initandlisten] waiting for connections on port 27017

5.设置为windows服务(总结)

可以通过如下命令启动:(当mongo不能运行时,也可以通过此命令在指定路径运行)

mongod.exe --logpath E:\mongodb\logs\mongodb.log --logappend --dbpath E:\mongodb\data --directoryperdb --serviceName MongoDB --install

也可以通过任务管理器启动:

Tips:当出现"特定服务错误代码100",可以删除data文件夹下的mongod.lock和storage.bson。

二、MongoDB的知识点

1.match.append("type", "test").append("picture.0",new Document("$exists",true));

.append("picture.0",new Document("$ne",null))

只選擇 type類型為"test"的员工,以及存在"picture"这一项的人员。

2. Mongodb正则表达式$regex操作符。

Java中的用法: new Document("$regex", Test).append("$options", "i");

Mongo中的用法: db.person.find({"BG":{$regex:"tEst",$options:"$i"}});


3.MongoDB的索引与查询优化

索引通常能够极大的提高查询的效率。在系统中使用查询时,应该考虑建立相关索引。但当需要修改数据时,由于需要增加相应字段索引的写入,所以修改数据时,时间会略微增加

单字段索引 : MongoDB默认为所有集合创建一个_id字段的单字段索引,该索引唯一,且 . 不能删除(_id为集合的主键)

索引的创建方法:

  db.test.ensureIndex({"name":1},{unique:false})
  db.test.createIndex({"name":1})

查询索引:

db.person.getIndexes() // 查询集合的索引信息

查询结果 :(??、)

对有索引的查询选择器进行解释:

db.test.find({name:'zhangsan'}).explain()
{
"user" : "BtreeCursor name_1",//表示该查询用到了索引
"isMultiKey" : false,//未使用多键复合索引
"n" : 10,            //查询选择匹配到的记录数量
"nscannedObjects" : 10,//执行查询扫描到的文档对象数量
"nscanned" : 10,      //扫描到的文档或索引总数
"nscannedObjectsAllPlans" : 10,//扫描文档总数在所有查询计划中
"nscannedAllPlans" : 10,      //在所有查询计划中扫描的文档或索条目的总数量
"scanAndOrder" : false,       //从游标取出查询到的数据时,是否对数据进行排序
"indexOnly" : false,
"nYields" : 0,                //产生的读锁数
"nChunkSkips" : 0,
"millis" : 0,                 //查询耗时(ms)
"indexBounds" : {
"name" : [
[
"wangwu",
"wangwu"
]
]
},
"server" : "raspberrypi:27017"
} 

删除索引

删除指定的索引dropIndex()

 db.COLLECTION_NAME.dropIndex("INDEX-NAME")

删除所有索引dropIndexes()

db.COLLECTION_NAME.dropIndexes()


复合索引

对多个字段同时添加索引,复合索引支持匹配多个字段的查询。

 db.test.ensureIndex({id:1,age:1})

查询索引结果:

 db.test.indexes.find()

使用索引示例:

 db.test.find({id:{$lt:5},age:{$gt:12}})
 { "_id" : ObjectId("589835c41c85cb68725f739b"), "id" : 2, "name" : "wangwu", "age" : 11 }

解释执行示例:

db.test.find({id:{$lt:5},age:{$gt:12}}).explain()
{
"user" : "BtreeCursor id_1_age_1",
"isMultiKey" : false,
"n" : 2,
"nscannedObjects" : 2,
"nscanned" : 4,
"nscannedObjectsAllPlans" : 5,
"nscannedAllPlans" : 9,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"id" : [
[
-1.7976931348623157e+308,
5
]
],
"age" : [
[
12,
1.7976931348623157e+308
]
]
},
"server" : "raspberrypi:27017"
}

4 $and 与OR 的用法

Mongodb的原生语句:

  db.database.distinct( "BG",{ $and: [ { "BG": { $ne: "" } }, { "BG": { $ne: null } } ] } );

在Java的语句

Document whereDoc = new Document("BG",loginBg).append("$and",Array.asList(new Document("BU",new Document("$ne","")),new Document("BU",new Document("$ne",null))));

用下面方式做循环
List<Object> tem = new ArrayList<Object>();
		for(int i=0;i<siteList.size();i++) {
			tem.add( new Document("site",new Document("$regex",siteList.get(i)).append("$options", "i")));
		}	

(注意: 需要导入 import static java.util.Arrays.asList;)

需求:判断A表中的数组是否包含元素,然后将包含元素的数据取出,并选出B表中的数据

               List<String> empList = new ArrayList<String>();
               empList.add(roleEmpno);
               Document matchCor = new Document("adminGroup",new Document("$elemMatch",new Document("$in",empList))); 
               List<Document> corList = mongoTemplate.getDoc(mongoCollCor, matchCor);
               Set<String> corNameList = new HashSet<String>();
               BasicDBList condList1 = new BasicDBList(); 
               condList1.add(new BasicDBObject("company",new Document("$ne",null)));
               condList1.add(new BasicDBObject("company",new Document("$ne","")));
               BasicDBList condList = new BasicDBList();  
               if(corList!=null&&corList.size()>0) {
            	 for(Document doc:corList) {
            		if(!StringUtils.isBlank(doc.getString("corName"))){
            			corNameList.add(doc.getString("corName"));
            		}
            	 }
            	 for(String str:corNameList) {
                    condList.add(new BasicDBObject("company",str));      
            	 }
               } 
               match.append("$and", Arrays.asList(new Document("$or",condList),new Document("$and",condList1)));               

Mongodb的原生语句:

db.example.find({
    '$or':[
       {'$and':[{'example.a':{'$gt':1}},{'example.b':{'$gt':2}}]},
       {'$and':[{'example.c':{'$gt':3}},{'example.d':{'$gt':4}}]}
    ]
})

5 $unset 的用法,mongo查询某个字段是否存在,并删除记录里的这个字段

1.查询person表中,存在feature字段的记录信息:

    db.course.find( { "feature": { $exists: true } } )

2.删除person表中,所有的feature、picture字段

   db.course.update({"creator":"test"},{$unset:{"feature":true,"picture":true}},{multi:true});

删除person表中,存在hrPicture字段且为null的“hrPicture”字段

   db.course.update({"editor":"test"},{$unset:{"testPicture":{"$in":[null], "$exists":true}}},{multi:true});

3.根据条件往表里插入一个字段

  db.lecture.update({"creator":"kiosk"},{$set:{"feature":"PDF"}},{multi:true})

6 MongoDB中distinct的详细用法。

db.collection_name.distinct(field,query,options)

field -----指定要返回的字段(string)

query-----条件查询(document)

options-----其他的选项(document)

       /**
	 * 查询Collection,筛选出满足whereDoc条件的不同的distinctField字段值,
	 */
	public <T> List<T> getDocDistinctFiled(String collectionName, String distinctField ,Document whereDoc,Class<T> className) {
		List<T> results= new ArrayList<T>();
		try {
			MongoCollection<Document> collection= mongo.getCollection(collectionName);
			DistinctIterable<T> distincts = (DistinctIterable) collection.distinct(distinctField, whereDoc, className);
			MongoCursor<T> iter = distincts.iterator();
			while(iter.hasNext()){
				T a = (T) iter.next();
				results.add(a);
			}
		} catch(Exception e) {
			e.printStackTrace();
			System.out.println("MongoDB getDoc: "+ e.getMessage());
		}
		return results;
	}

7 mongo数据库的各种查询语句示例(比较全的)

上面是mongodb查询语句,对应下面是sql语句。
db.users.find()
    select * from users
db.users.find({"age" : 27}) 
    select * from users where age = 27
db.users.find({"username" : "joe", "age" : 27}) 
    select * from users where "username" = "joe" and age = 27
db.users.find({}, {"username" : 1, "email" : 1}) 
    select username, email from users;
db.users.find({}, {"username" : 1, "_id" : 0}) // no case 
   // 即时加上了列筛选,_id也会返回;必须显式的阻止_id返回
db.users.find({"age" : {"$gte" : 18, "$lte" : 30}}) 
    select * from users where age >=18 and age <= 30 // $lt(<) $lte(<=) $gt(>) $gte(>=)
db.users.find({"username" : {"$ne" : "joe"}}) 
    select * from users where username <> "joe"
db.users.find({"ticket_no" : {"$in" : [725, 542, 390]}}) 
    select * from users where ticket_no in (725, 542, 390)
db.users.find({"ticket_no" : {"$nin" : [725, 542, 390]}}) 
    select * from users where ticket_no not in (725, 542, 390)
db.users.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]}) 
    select * form users where ticket_no = 725 or winner = true
db.users.find({"id_num" : {"$mod" : [5, 1]}}) select * from users where (id_num mod 5) = 1
db.users.find({"$not": {"age" : 27}}) select * from users where not (age = 27)
db.users.find({"username" : {"$in" : [null], "$exists" : true}}) select * from users where username is null // 如果直接通过find({"username" : null})进行查询,那么连带"没有username"的纪录一并筛选出来
db.users.find({"name" : /joey?/i}) // 正则查询,value是符合PCRE的表达式
db.food.find({fruit : {$all : ["apple", "banana"]}}) // 对数组的查询, 字段fruit中,既包含"apple",又包含"banana"的纪录
db.food.find({"fruit.2" : "peach"}) // 对数组的查询, 字段fruit中,第3个(从0开始)元素是peach的纪录
db.food.find({"fruit" : {"$size" : 3}}) // 对数组的查询, 查询数组元素个数是3的记录,$size前面无法和其他的操作符复合使用
db.users.findOne(criteria, {"comments" : {"$slice" : 10}}) // 对数组的查询,只返回数组comments中的前十条,还可以{"$slice" : -10}, {"$slice" : [23, 10]}; 分别返回最后10条,和中间10条
db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"})  // 嵌套查询
db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe", "score" : {"$gte" : 5}}}}) // 嵌套查询,仅当嵌套的元素是数组时使用,
db.foo.find({"$where" : "this.x + this.y == 10"}) // 复杂的查询,$where当然是非常方便的,但效率低下。对于复杂查询,考虑的顺序应当是 正则 -> MapReduce -> $where
db.foo.find({"$where" : "function() { return this.x + this.y == 10; }"}) // $where可以支持javascript函数作为查询条件
db.foo.find().sort({"x" : 1}).limit(1).skip(10); // 返回第(10, 11]条,按"x"进行排序; 三个limit的顺序是任意的,应该尽量避免skip中使用large-number

8 Aggregate (复杂管道聚合aggregate)

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。


这里我们介绍一下聚合框架中常用的几个操作:

$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。

$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。

$limit:用来限制MongoDB聚合管道返回的文档数。

$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。

$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。

$group:将集合中的文档分组,可用于统计结果。

$sort:将输入文档排序后输出。

$geoNear:输出接近某一地理位置的有序文档。

db.person.aggregate( [
  { $match :  { "emp_no":"18266", "hrPicture":{"$ne":null} } },
  {$project : {  site : 1 , methodName : 1 , card_id : 1 , edit_time : 1 }},
  { $sort: {"edit_time":-1}},
  { $skip : 100 },
  { $limit : 115 },
] ,
  {allowDiskUse: true}   //mongodb聚合内存不足解决方案:当sort数据量过大
                           ,超过其memory时,需要设置其字段
);

//对应的Java代码

Document project = new Document("type",1);
//統計數量
List<Bson> aggregates = new ArrayList<Bson>();
int count =(int)mongoTemplate.getDocNum(mongoCollName, whereDoc);;
page.setTotalPages(count%page.getPageSize()>0?(count/page.getPageSize()+1):count/page.getPageSize());
page.setTotalRows(count);
aggregates.add(new Document("$match",whereDoc));
aggregates.add(Aggregates.project(project));
aggregates.add( new Document("$sort", new Document("create_time",1)));
aggregates.add(new Document("$skip", skip));
aggregates.add(new Document("$limit",  page.getPageSize()));
List<Document> logDocList = mongoTemplate.getDoc(mongoCollName, aggregates);

public List<Document> getDoc(String collectionName, List<Bson> aggregates) {
		List<Document> docs = new ArrayList<Document>();
		try {
			MongoCollection<Document> collection = mongo
					.getCollection(collectionName);

			AggregateIterable<Document> cursor = collection
					.aggregate(aggregates).allowDiskUse(true);

			for (Document doc : cursor) {
				docs.add(doc);
			}
		} catch (Exception e) {
			StringWriter errors = new StringWriter();
			e.printStackTrace(new PrintWriter(errors));
			Log.info("====== MongoDB getDoc " + errors);
			System.out.println("MongoDB getDoc: " + e.getMessage());
		}
		return docs;
	}

9 Mongo中可以JS脚本

print(new Date());
for (var i=0;i<300000;i++) {
  db.log.insert({
    "site" : "Test_"+i, 
    "status" : NumberInt(1), 
    "description" : "??????", 
    "creator" : "Test_"+i, 
    "trigger_time" : new Date(), 
    "create_time" : new Date(), 
    })
}
print(new Date()); //便于查看程序执行的时间

10 $in与$nin的使用($nin有点特殊)

$nin操作符

语法: { field: { $nin: [value1,value2 ... valueN]} }

说明

$nin 顾名思义即not in,$nin 操作符输出符合以下条件的文档:
field字段的值不在指定数组[value1,value2 … valueN]内 或
field字段在文档中根本不存在。

首先先看一个查询语句:db.inventory.find( { qty: { $nin: [ 5, 15 ] } } )

上面这个查询输出inventory数据库文档中qty字段的值既不等于5也不等于15的文档,没有qty这个字段的文档同样也会被输出。

当field字段是一个数组时,那么 $nin 操作符将输出满足这样条件的文档,即在field数组中的任意一个元素都不在[value1,value2 … valueN]这个指定数组内。

再看一个查询:

1db.test.update({tags:{$nin:["appli","school"]}},{$set:{sale:false}})

上面这个实例,假设test数据库包含的文档中 tags 是个数组,那么当 tags 字段里面的元素的值都不在[“appli”,”school”]这个数组中,或者文档中根本没有 tags 这个字段,则将用 update 更新操作符将文档中的 sale 设置为false

$in操作符

$in可以查询一个键的多个值

我们想查询喜欢dancing和swimming和的人,可以得到如下结果

db.user.find({"hobby":{"$in":["dancing","swimming"]}},{"_id":0})
{ "hobby" : "swimming", "gender" : "female" }
{ "hobby" : "dancing", "gender" : "male" } 

Java中的代码

   List<String> empnoList = new ArrayList<String>();
   empnoList.add(loginedEmpno);
   match.append("adminGroup", new Document("$in",empnoList));

11 mongodb中sort慢的优化

1. 需要注意,sort skip 和limit 是按照先后顺序执行的, 为了确保数据,一般都是sort().skip().limit()

2. 创建对应的index比如,createIndex({{saleDate:-1, RMBDealPrice:-1}}), 注意,sort的参数(key) 需要按照index的创建顺序传入,否则index不生效。

db.log.find({"type" : "daily"}).sort({"create_time":-1}).skip(10000).limit(15);

db.log.aggregate( [
  { $match :  {"type" : "daily"} },
  {$project : {  type:1, mac: 1 , content : 1 , edit_time : 1 }},
  { $sort: {"create_time":1}},
  { $skip : 27854 },
  { $limit :27875},
] ,
  {allowDiskUse: true}
);

12 异常:Query failed with error code 96 and error message 'Executor error during find command: OperationFailed: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.

处理方式:	
db.yourCollection.createIndex({<field>:<1 or -1>})</field>
扩大排序内存的限制,例如扩大到320M
db.adminCommand({setParameter:1, internalQueryExecMaxBlockingSortBytes:335544320})

13 _id和ObjectId

MongoDB 中存储的文档必须有一个"_id" 键。这个键的值可以是任何类型的,默认是个ObjectId 对象。在一个集合里面,每个文档都有唯一的"_id" 值,来确保集合里面每个文档都能被唯一标识。如果有两个集合的话,两个集合可以都有一个值为123 的"_id" 键,但是每个集合里面只能有一个"_id" 是123 的文档.

前4 个字节是从标准纪元开始的时间戳,单位为秒。这会带来一些有用的属性。时间戳,与随后的. 5 个字节组合起来,提供了秒级别的唯一性。由于时间戳在前,这意味着ObjectId 大致会按照插入的顺序排列。这对于某些方面很有用,如将其作为索引提高效率,但是这个是没有保证的,仅仅是“大致”。

如果插入文档的时候没有"_id" 键,系统会自动帮你创建一个。可以由MongoDB 服务器来做这件事情,但通常会在客户端由驱动程序完成。因为:虽然ObjectId 设计成轻量型的,易于生成,但是毕竟生成的时候还是产生开销。在客户端生成体现了MongoDB 的设计理念:能从服务器端转移到驱动程序来做的事,就尽量转移。这种理念背后的原因是,即便是像MongoDB 这样的可扩展数据库,扩展应用层也要比扩展数据库层容易得多。将事务交由客户端来处理,就减轻了数据库扩展的负担。

14 explain(),语句分析工具

MongoDB 3.0之后,explain的返回与使用方法与之前版本有了很大的变化,介于3.0之后的优秀特色和我们目前所使用给的是3.0.7版本,本文仅针对MongoDB 3.0+的explain进行讨论。3.0+的explain有三种模式,分别是:queryPlanner、executionStats、allPlansExecution。现实开发中,常用的是executionStats模式

db.getCollection('person').find({"age":{"$lte":2000}}).explain("executionStats")

15

要想不扫描全表,索引当然是必须的,不然怎么知道其他数据里的ctime就一定不大于找出来的那几条数据呢。至于MySQL里,你怎么就知道它没有扫描全表呢?MySQL是的数据存储是紧密的,它扫描的速度要比Mongo快很多,所有在无索引的情况下MySQL很占优势。Mongo的一大优势在于查询方式丰富,而要展现Mongo这一特点,合理必要的索引是必不可少的。


16 MongoDB中对数组元素进行查询

查询

MongoDB中根据数组子元素进行匹配,有两种方式。

1:使用 “[数组名].[子元素字段名]” 的方式进行匹配。
2:使用 “[数组名]” $elemMatch { [子元素字段名] }的方式。

找出数组中, 具有 qid=1并且reorderFlag=0的记录
查询数组内同一条记录同时满足2个条件的语句:
{ "qList.qid": 1, "qList.reorderFlag": 0}
{ "qList": { $elemMatch: { "qid": 1, "reorderFlag": 0} } }

17 因数据库一次查询数据量过大导致的内存溢出问题

可以通过分页查询去优化,每次查询的总数,然后循环取出结果

业务代码:

    int totalNumber = faceDao.getTotalNumber(parseDate); //计算总数
    int pageSize = 1000;                                 //设置每页的数量
    int page = (int)(totalNumber/pageSize)+1;            
    int skip =;
    for (int i=;i<page;i++) {
        skip = i*pageSize;
        List<Person> personList = 取出数据;   
        // 把查询出来的集合插入到sqlite中
        if (personList != null) {
            // 遍历查询出来的list
            for (int j = ; j < personList.size(); j++) {
               业务代码
            }
       }
    }

数据层代码:

   @Override
   public List<Person> get(Date date,int skip,int pageSize) {
    	List<Person> personList = null ;  
    	Query query = new Query(new Criteria().andOperator(where("").is(""),where("").exists(true),where("edit_time").gt(date)))
                .skip(skip)
                .limit(pageSize);    	         
    	try {
    		personList = mongoTemplate.find(query, Person.class);
		} catch (Exception e) {
			e.printStackTrace();
		}    	
		return personList;	
    }

18 MongoDB查询大于某个时间,小于某个时间,在某一段时间范围

例如查询ct>=2012.12.7 且et<=2012.12.7:可翻译为

    "ct":{$gte:new Date(2012,11,7)},"et":{$lte:new Date(2012,11,7)}

19 MongoDB中的那些个update修改器: $inc $set $unset $push $pull

$inc : 在原有基础上在增加多少 {$inc:{"score":-20}}
$set : 此前我们已经提到过 $set 的用法和特性(没有就自动添加一条)了
$unset : 用来删除Key(field)的,{$unset:{"english_name" : 1}} 就是删除 "english_name" 这个field相当于关系型数据库中删除了字段
$push : 它是用来对Array (list)数据类型进行 增加 新元素的
$push 是在 Array(list) 的尾端加入一个新的元素 {$push : {"test_list" : 6}}
$pull : 就是指定删除Array中的某一个元素
$pop : 指定删除Array中的第一个或最后一个元素,{$pop:{"test_list" : -1}} -1代表最前面,1代表最后边


20 mongodb--数组元素的插入

$addToSet:向数组中添加元素,若数组本身含有该元素,则不添加,否则,添加,这样就避免了数组中的元素重复现象;

$push:向数组尾部添加元素,但它不管数组中有没有该元素,都会添加

db.class.update({name: "class1"}, {$addToSet:{student:{name: "zhaoliu", age: 17}}});

updateDoc = new Document("$push",new Document("admin_group",adminGroupDoc))
		.append("$set", new Document("editor",creator).append("edit_time",new Date()));

21 mongodb查询不区分大小写

对应的查询命令:

db.COLLECTION.find({ "FIELD":  {'$regex':'VALUE','$options':'i'}});
//FIELD是要查询的属性,如“email”,VALUE是要查询的属性值

对应的Java语句

// SSH 框架中
whereDoc.append("site",new Document("$regex", site).append("$options", "i"));
// SpringBoot框架
public UserPO findUserByAccount(String account) {
    Criteria usernameCriteria = Criteria.where("username").is(account);
    Criteria emailCriteria = Criteria.where("email").regex(account, "i");//邮箱不区分大小写
    Criteria mobileCriteria = Criteria.where("mobile").is(account);
    Query query = new Query().addCriteria(new Criteria().orOperator(usernameCriteria, emailCriteria, mobileCriteria));
    return template.findOne(query, UserPO.class);
}

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3129544/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: MongoDB 聚合
请登录后发表评论 登录
全部评论

注册时间:2018-05-16

  • 博文量
    4259
  • 访问量
    40075