MongoDB入门

10 篇文章 0 订阅

MongoDB

一种文档数据库,由C++编写,数据存储结构为BSON

数据类型

数据类型描述
String字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean布尔值。用于存储布尔值(真/假)。
Double双精度浮点值。用于存储浮点值。
Min/Max keys将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Arrays用于将数组或列表或多个值存储为一个键。
Timestamp时间戳。记录文档修改或添加的具体时间。
Object用于内嵌文档。
Null用于创建空值。
Symbol符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID对象 ID。用于创建文档的 ID。
Binary Data二进制数据。用于存储二进制数据。
Code代码类型。用于在文档中存储 JavaScript 代码。
Regular expression正则表达式类型。用于存储正则表达式。

对比结构化数据库

结构化数据库NOSQL数据库名称
databasedatabase数据库
rowcollection数据库表/集合
columnfield字段/域
indexindex索引
joins/表连接/mongodb不支持(3.2后可以通过聚合)
primary keyprimary key主键/mongodb自动将_id设为主键

常用操作

启动服务

## 在windows下
## 启动服务:管理员命令下
mongod --dbpath D:mongoDB\data\db
## Linux下
## 进入bin目录,启动服务
./mongod --config /usr/local/mongodb/etc/mongodb.conf
## 进入控制台
mongo

help	#:查看帮助指令

数据库

建库规范

  • 不能是空字符串("")。
  • 不得含有’ '(空格)、.、$、/、\和\0 (空宇符)。
  • 应全部小写。
  • 最多64字节
show dbs			# 展示所有的数据库
use tablename		# 切换到某个库
db					# 查看当前库
db.dropDatabase()	# 删除当前库

集合(表)

一般不用特意去创建集合,在insert时指定就可以。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

建集合规范

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
创建集合
db.createCollection(name, options)
	# 参数options:
		#capped	Boolean	true:固定集合,当到达最大值会覆盖最早的文档。(为true时,size也要指定)
		#size:	字节数
		#max:	包含文档的最大数量

# 举例
db.createCollection('collection')
db.createCollection("coll", { capped: true, autoIndexId : true, size: 6142800, max : 10000})
固定集合
# 创建固定集合
# 它的自然顺序保证与文档插入顺序一致。
# 大小固定,一旦超过大小,最老的数据将被删除,新数据将被添加到末端。(栈)
# 可用于日志数据 或 归档数据
db.createCollection("coll_new",{capped: true, size: 20480, max: 100})
# 使用stats()检查集合大小
db.coll_new.stats()
# 自然排序
db.coll.find().sort({$natural:-1}).limit(10)
查看集合
show collections
删除集合
db.collection.drop()

文档(数据)

所有存储在集合中的数据都是以BSON结构存储的。
BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON。

插入操作
# 单条插入; document 为一份文档数据,可以先定义
document = ({'title':'标题','name':'xzy'});

db.COLLECTION_NAME.insert(document)
~~db.COLLECTION_NAME.save(document)~~ 
db.COLLECTION_NAME.insertOne(document)
db.COLLECTION_NAME.replaceOne(document)
# 多条插入
db.collection.insertMany(
    [ <document 1> , <document 2>, ... ],
    {
        writeConcern: <document>,	 # 写入策略	默认为1 要求写入确认;0:不要求写入确认;-1:忽略网络错误;2:要求以写入到副本集的主服务器和一个备用服务器;majority:要求已写入到副本集中的大多数服务器中。
        ordered: <boolean>			 # 是否按顺序写入 默认是true
    }
)

注意:save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。
insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。

数据查询
db.coll.find()[.pretty()]	# 查看所有数据,.pretty()为了数据格式化
db.coll.findOne()			# 获取一条
db.coll.find().count()		# 获取数量
条件查询
# 相当于 where name = 'xzy'
db.coll.find({'name':'xzy'})
db.coll.find({"_id":ObjectId("5fb23fe3c33b909f22d5a31e")})  
# 找出所有存在name字段的数据 并返回name属性
db.coll.find({'name':{$exists:1}},{name:1})	
# AND操作    where name = 'xzy' and age = 23
db.coll.find({'name':'xzy','age':23});
# OR操作	where name='xzy' or age>=25
# OR操作 必须先满足第一个条件,在满足第二个条件,所有子句都支持使用自己的索引
db.coll.find(
	{$or:[
		{'name':'xzy'},
		{'age':{$gte:25}}
		]
	}
)

# 模糊查询 name 包含 xzy
db.coll.find({name:/xzy/})
# 已 jay开头
db.coll.find({name:/^jay})
# 已 jay结尾
db.coll.find({name:/jay$/})
  • (>) 大于 - $gt
  • (<) 小于 - $lt
  • (>=) 大于等于 - $gte
  • (<= ) 小于等于 - $lte
  • 不等于 - $ne
# where name = 'xzy' and age < 20
db.coll.find({'name':'xzy','age':{$lt:20}});
# where title='MongoDB' AND (age>=20 or url='www.xx.com')
db.mycol.find({'title':'MongoDB',$or:[{'age':{$gte:20}},{'url':'www.xx.com'}]})
  • (in) - $in
  • (not in) - $nin
  • $all 与in操作类似 但是in只需要满足()中的一个,而all需要满足()中的所有。
# where name in ('xzy','Jay')
db.coll.find({'name':{$in:['xzy','Jay']}})
# where name not in ('xzy','Jay')
db.coll.find({'name':{$nin:['xzy','Jay']}})
# 只有当 favs 里同时包含['play','b-ball']才会被查询出来
db.coll.find({'favs':{$all:['play','b-ball']}})
# slice 需要数组 
# 接收两个参数
# 如果只有一个参数,则为数据项总和
# 如果有两个参数,那么第一个参数定义偏移,第二个参数定义限制
# 第一个参数可以为 负数 代表从后往前
db.coll.find({'name':'xzy','favs':{$slice:[0,2]}})

简单的字段去重:

db.coll.distinct('name')
# 输出结果为:
['xzy','jay','zqy']

分组查询

db.coll.group({
	key: {name:true},		# 指定希望用哪个键对结果分组
    initial: {Total: 0},	# 元素开始统计的起始基数
    reduce: function(items,prev){prev.Total += 1}	# 分组操作
})	
# 还有另外三个参数可选
# keyf: 如果不希望使用文档中已有的键对结果进行分组,可以使用该参数替代
# cond:添加额外的语句条件
# finalize:指定一个函数,用于最终结果返回之前。

案例:
# 使用idCard 和 idName 进行分组查询
db.coll.group({
    key: {idCard:true,idName:true},
    initial:{count:0},
    reduce:function(items,prev){
        prev.count += 1;
    }
});
结果:
{
	"idCard" : "990101197901180002",
	"idName" : "Jay",
	"count" : 1
}
...
分页查询

limit、skip

# 取前5条
db.coll.find().limit(5)
# 跳过5条
db.coll.find().skip(5)
# 结合以上两个函数就可以实现分页。
db.coll.find().skip(0).limit(5)	# 第一页
db.coll.find().skip(5).limit(5)	# 第二页
排序
# 根据id排序 1为升序 -1为降序
db.coll.find().sort({_id:1})
# 年龄大于20 且根据年龄倒序
db.coll.find({'age':{$gt:20}}).sort({age:-1})

## 自然顺序 根据插入库的顺序排序
db.coll.find().sort({$natural:-1}).limit(10)
高级查询
管道、聚合

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $ match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。
# $project : 可以控制字段的输出顺序 以及是否输出
db.coll.aggregate({
    $project:{
        _id : 0,
        idName:1,
        idCard:1,
        phone:1,
        favs:1
    }
})

可以看个案例:

# $match 要在 $project 之前 
db.coll.aggregate(
{
    $match:{
        age:{$gt:20}
    }
},
{
    $project:{
        _id : 0,
        idName:1,
        idCard:1,
        phone:1,
        favs:1
    }
},
{
    $skip:0
},
{
    $limit:2
},
{
    $sort:{
        phone:1
    }
}
)

相当于
select idName,idCard,phone,favs from coll where age>20 limit 0,2 order by phone asc

分组

# 案例一:
db.coll.aggregate([
    {$group:{
        _id:{
        	# groupName 分组名 可自定义 
            groupName:'$idCard'
        },
        total:{
        	# 计数器 
            $sum:1
        }
    }}
])

db.coll.aggregate([{$group:{_id:{groupName:'$idCard'},total:{$sum:1}}}])
# 相当于
select idCard as groupName,count(1) as total where coll group by idCard

# 案例二:

db.coll.aggregate([
    {$match:{
        idName:{$ne:''}
    }},
    {
    	$group:{
    		# _id 不可改为别的
    		_id: "$idName",
    		ageTotal:{$sum:'$age'}
    	}
    },
    {$match:{ageTotal : {$gte:20}}}
])
相当于
select idName as _id,count(age) as ageTotal from coll where idName <> '' group by idName having ageTotal >= 20
  • $group是固定的,表示这里一个分组聚合操作。
  • _id表示需要根据哪一些列进行聚合,其实一个JSON对象,其key/value对分别表示结果列的别名以及需要聚合的的数据库列。
  • ageTotal表示聚合列的列名。
  • $sum表示要进行的聚合操作,后面的1表示每次加1。
聚合表达式
表达式描述实例
$sum计算总和db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ s u m : " sum:" sum:"price"}}])
$avg计算平均值db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ a v g : " avg:" avg:"price"}}])
$min获取最小值db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ m i n : " min:" min:"price"}}])
$max获取最大值db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ m a x : " max:" max:"price"}}])
$push在结果文档中插入一个值到一个数组中db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ p u s h : " push:" push:"price"}}])
$addToSet在结果文档中插入一个值到一个数组中,但不创建副本db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",num_tutorial:{ a d d T o S e t : " addToSet:" addToSet:"price"}}])
$first根据资源文档排序获取第一个文档的数据db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",price_first:{ f i r s t : " first:" first:"price"}}])
$last根据资源文档的排序获取最后一个文档db.coll.aggregate([KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"price",price_last:{ l a s t : " last:" last:"price"}}])
# 获取最大最小的年龄
db.coll.aggregate([
    {
        "$group":{
            _id:1,
            maxAge: {"$max":"$age"},
            minAge: {"$min":"$age"}
        }
    }
])

# 时间处理 格式化
db.coll.aggregate([
    {
        $project:{
            "date":{
                "$dateToString":{
                    "format":"%Y-%m-%d %H:%M:%S",
                    "date":"$date"
                }
            },"idName":1,"idCard":1,"age":1
        }
    },
    {
        $match:{"date":{$ne:null}}
    }
])

SQLNOSQL
where$match
group by$group
having$match
select$project
order by$sort
limit$limit
sum()$sum
count()$sum
join$lookup(v3.2新增)
length()$size
isNull()$ifNull
更新数据
db.collection.update(    
	<query>, 
	<update>, 
	{       
		upsert: <boolean>,   
		multi: <boolean>,  
		writeConcern: <document>
	}
)
  • query:对那些数据进行操作; 类似sql update查询内where后面的
  • update:对这些数据做什么操作;update的对象和一些更新的操作符(如 , , ,inc…)等,也可以理解为sql update查询内set后面的
  • upsert(可选):如果不存在update记录,是否将其作为记录插入。true为插入
  • multi(可选):是否更新多条记录,默认是false
  • writeConcern(可选):抛出异常的级别
# 危险的操作写法
# 将name为zy的第一条数据 覆盖为 age:25
db.coll.update({name:'zy'},{'age':25})
# 将age为444的一条数据覆盖为 ...
db.coll.update({'age':444},
{
    'idName':"Kobe",
    'idCard':'990101197901180008',
    'age':44,
    'favs':['magic','b-ball','sing','play'],
    'phone':'11011000001',
    'star':true
})

# 修改 idName=xiaolili 满足 age=15 的一条数据 
db.coll.update({age:15},{$set:{idName:"xiaolili"}})
# 修改 idName=xiaolili 满足 age=15 的所有数据
# 总共4个参数,第四个参数表示有多条数据符合筛选条件的话是否全部更改,默认为0(false)只改第一条,改为1(true)后表示全部更改 ;第3个参数1(true)表示如果没有符合条件的记录是否新增一条记录,1(true)表示新增,0(false)表示不新增
db.coll.update({age:15},{$set:{name:"xiaolili"}},1,1)
# set age=age-1 
db.coll.update({'name':'xzy'},{$inc:{age:-1}},false,1)
# set age=age+1 where age>10
db.coll.update( { "age" : { $gt : 10 } } , { $inc : { "age" : 1} },false,false )
删除数据
db.collection.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)
  • query(可选):删除文档的条件
  • justOne(可选):如果设为 true 或 1,则只删除一个文档。
  • writeConcern(可选):抛出异常的级别
# 删除所有
db.coll.remove({})
# 条件删除
db.coll.remove({'name':'xzy','age':24})	

索引

额外开销

每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。

索引不能被以下的查询使用:

  • 正则表达式及非操作符,如 $nin, $not, 等。
  • 算术运算符,如 $mod, 等。
  • $where 子句
# 创建索引
db.collection.createIndex(keys,options)	
# 获取所有索引
db.collection.getIndexes()	
# 删除所有的索引
db.colletion.dropIndexes()
# 删除指定索引
db.collection.dropIndex("索引名称")	

# 复合索引,唯一,后台创建
db.coll.createIndex({"idName":1,"age":-1},{unique:true,background: true})
# 180s 后自动删除
db.coll.createIndex({"createDate": 1},{expireAfterSeconds: 180,background:true})

# 使用explain 检验是否使用了索引
db.mongoTest.find({"idName":"Jay"}).explain();

option参数表如下:

参数Type描述
backgroundBoolean建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false
uniqueBoolean是否唯一。默认false
namestring索引名称,如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
sparseBoolean对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSecondsinteger指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v索引号版本
weightsdocument权重,1~99999之间,表示该索引相对于其他索引字段的得分权重。
default_languagestring对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_overridestring对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.

Java操作MongoDB

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

实体类注解

注解说明
@Document标识当前实体是一个mongodb实体 @Document(collection = “myColl”) 也可以用这个标识集合
@Id标识主键 只能存在一个
@Indexed标识索引
@CompoundIndex标识联合索引
@Field对当前字段的额外内容进行定义,主要是用来定义集合中字段实际名称
@Transient标识此字段为java属性而非mongodb字段
@DBRef将此字段同另外一个mongodb的文档进行关联

实体类

@Data
// 指定某个collection
@Document(collection = "mongoTest")
public class UserBo implements Serializable {
    @Id
    private String id;
    private String idCard;
    private String idName;
    private String phone;
    private Integer age;
    private String[] favs;
    @Transient
    private String note;

    private Date date;
    private String dateString;
}

CRUD

@Autowired
private MongoTemplate mongoTemplate;
新增
@RequestMapping("/insertOne")
public String insertOne(@RequestBody UserBo bo){
    if(bo==null){
        return "fail";
    }
    bo.setDate(new Date());
    // 使用save 方法,当主键冲突时,会覆盖
    //mongoTemplate.save(bo);
    // 使用insert 方法 推荐使用insert方法,当主键冲突时,会报错
    mongoTemplate.insert(bo);
    return "succ";
}
删除
@RequestMapping("/delOne")
public String delOne(@RequestBody UserBo bo){
    Query query = new Query(Criteria.where("id").is(bo.getId()));
    // 查找并删除
    // mongoTemplate.findAllAndRemove(query,UserBo.class);
    DeleteResult res = mongoTemplate.remove(query, UserBo.class);
    System.out.println("删除了:"+res.getDeletedCount()+"条");
    return "succ";
}
更新
@RequestMapping("/updateOne")
public String updateOne(@RequestBody UserBo bo){
    Query query = new Query();
    query.addCriteria(Criteria.where("_id").is(bo.getId()));

    Update up = new Update();
    if(bo.getIdName()!=null){
        up.set("idName",bo.getIdName());
    }
   // 修改单条
    UpdateResult res = mongoTemplate.upsert(query,up,UserBo.class);
    // mongoTemplate.updateFirst(query,up,UserBo.class);
    // 批量更新
    // mongoTemplate.updateMulti(query,up,UserBo.class);
    System.out.println("修改了:"+res.getModifiedCount()+"条");
    return "suc";
}
查询
@RequestMapping("/queryInfo")
public String queryInfo(@RequestBody UserBo bo){
	Query query = new Query();
	// 全量查询
    List<UserBo> list = mongoTemplate.findAll(UserBo.class);
	// 去重获取字段 获取不同的idName
    List<UserBo> disList = mongoTemplate.findDistinct(query,"idName", UserBo.class,UserBo.class);
}
条件查询
@RequestMapping("/queryInfo")
public String queryInfo(@RequestBody UserBo bo){
	Query query1 = new Query();
	// where age <> '' and age is not null
	// 注意 .ne("") 要放在 .ne(null) 前面
	List<UserBo> testList = mongoTemplate.find(query1.addCriteria(Criteria.where("age").ne("").ne(null)),UserBo.class);
	
	Query query2 = new Query();
	// in 可传多个对象 也可传集合
	List<UserBo> testList2 = mongoTemplate.find(query2.addCriteria(Criteria.where("age").ne("").ne(null))
        .addCriteria(Criteria.where("favs").in("kill","work")),UserBo.class);	

	// OR查询
	Query orQuery = new Query();
	// where age <> '' and age is not null and age>200 and idName like '%魈%' and (id = 'NO007' OR id = 'NO008')
    List<UserBo> userList = mongoTemplate.find(orQuery.addCriteria(Criteria.where("age").ne("").ne(null).gt(200))
    	// 模糊查询
        .addCriteria(Criteria.where("idName").regex(".*"+"魈"+".*"))
        // OR 操作
        .addCriteria(new Criteria().orOperator(Criteria.where("id").is("NO007"),Criteria.where("id").is("NO008")))
        ,UserBo.class);
	
	// 排序 分页 查询
	Query query = new Query();
	// where age <> '' and age is not null order by age asc limit 5,5
	List<UserBo> sortList = mongoTemplate.find(
		query.with(Sort.by(Sort.Order.asc("age"))).addCriteria(Criteria.where("age").ne("").ne(null)).limit(5).skip(5),
        UserBo.class);
}
聚合查询
@RequestMapping("/queryInfo")
public String queryInfo(@RequestBody UserBo bo){
	// 基本的聚合查询
	Aggregation aggregation = Aggregation.newAggregation(
		// project 需要展示的字段
	    Aggregation.project("idName","idCard","age"),
	    // 条件查询
	    Aggregation.match(Criteria.where("age").ne("").ne(null).gte(20)),
	    // 分组 as:别名
	    Aggregation.group("idName").count().as("count"),
	    // 排序
	    Aggregation.sort(Sort.by(Sort.Order.desc("idName")))
	);
	AggregationResults<UserBo> results = mongoTemplate.aggregate(aggregation, UserBo.class, UserBo.class);
    List<UserBo> aggList = results.getMappedResults();

	// 日期处理 格式化
	Aggregation agg = Aggregation.newAggregation(Aggregation.match(Criteria.where("date").ne(null)),
            Aggregation.project("date").andExpression("{$dateToString:{format:'%Y-%m-%d %H:%M:%S',date:'$date'}}").as("dateString")
            );
    AggregationResults<UserBo> aggResults = mongoTemplate.aggregate(agg,UserBo.class,UserBo.class);
    List<UserBo> aggregationList =aggResults.getMappedResults();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值