首页 > 数据库 > > Mongo 数据库基础知识总结(持续更新)
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 270175.设置为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。
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-number8 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/ ,如需转载,请注明出处,否则将追究法律责任。