mongodb 去重_看完这篇文章彻底入门MongoDB

7cff0bd696359728f5daed3af7a2b153.png

1、mongodb基本命令

1、查看当前数据库:db

2:查看所有数据库:show dbs / show databases

3、切换数据库: use +数据库名字

4、删除当前的数据库:db.dropDatabase()

2、集合(collection)的基本命令

1、展示特定数据库中所有集合:show collections

2、删除集合:db . 集合名 . drop()

3、向不存在的集合(collection)中第一次加入数据时,集合会被创建出来

4、手动创建集合db.createCollection(name,options)

如: db.createCollection(‘sub’,{capped:true,size:10})

参数capped:默认值为false,表示无上限,值为ture时表示设置上限

参数size:当capped值为true时,需要制定此参数,表示上限大小,当文档达到上限时候,讲会覆盖之前的数据,单位为字节。

3、数据类型

1、ObjectID:文档ID

2、String:字符串

3、Boolean:布尔值

4、Integer:整数

5、Double:存储浮点值

6、Arrays:数组或者列表,多个值存储到一个键

7、Object:用于嵌入式的文档,即一个值一个文档

8、Null:存储Null值

9、Timestamp:时间戳,表示从1970-1-1到现在的总秒数

10、Date:存储当前日期或者时间的UNIX时间格式

注意:

· 每个文档都有一个属性为:_id,保证每个文档的唯一性,

· 可以自己去设置_id插入文档,如果没有提供,那么MongoDB为每个文档提供一个独特的_id,类型为objectID

· objectID是一个12字节的十六进制数:

前4个字节为当前的时间戳

接下来3个字节为机器ID

接下来2个字节为MongoDB的服务进程id

最后3个字节是简单的增量值

4、基本操作

1、插入数据 save和insert的区别

db.collection.insert({}) 插入的数据,如果_id已经存在,则报错

db.collection.save({}) 插入的数据,如果_id已经存在,则会更新

2、简单查询 db.collection名.find()

3、更新 db.集合名称.update(<query>,<update>,{multi:<boolean>})

参数:query:查询条件

参数update: 更新操作符,更新成什么

参数multi:可选,默认是false,表示只更新找到的第一条记录,值为true表示把满足条件的文档全部更新

例子:

db.collection.update({name:’xiaoming’},{name:’xiaohua’ })

结果是:数据中除了_id,只剩下了name字段,其他的字段没有了

db.collection.update({name:’xiaoming’},{$set:{name:’xiaohua’} })

结果是:数据中只更新了name字段,其他的字段正常显示

db.collection.update({},{$set:{name:’xiaohua’} },{multi:true}) 更新全部

4、删除 db.集合名称.remove(<query>,{justOne:<boolean>})

参数query:可选,删除的文档的条件

参数justOne:可选,如果设为true或者1,则只删除一条,默认为false,表示删除多条(默认删除满足所有条件的记录)

5、高级查询

1、find() :查询所有

2、findOne() : 查询,只返回第一个 db.集合名称.findOne({})

3、pretty() :将结果格式化 db.集合名称.findOne({}).pretty()

6、运算符

1、比较运算符

· 等于:默认就是等于,没有运算符

· 小于:$lt

· 小于等于: $lte

· 大于: $gt

· 大于等于 : $gte

· 不等于 : $ne

例子: db.集合名称.find({ age:{ $gte: 18 } })

2、范围运算符

$in ,$nin 判断是否在某个范围内

如:查询年龄为18,28,38的学生

db.集合名称.find( { age: { $in[18,28,38] } } ) 

3、逻辑运算符

and : 同时满足条件

db.集合名称.find( { age:18,name:’xiaoming ’} ) 

or:使用$or

例子:查询年龄大于18,或者姓名为false的学生

db.集合名称.find( { $or:[{age:{$gt:18}} , { gender:false }] } ) 

4、mongodb支持正则表达式

例子:查找sku属性以adb开头

db.products.find({sku:/^abc/})

例子:查找sku属性以789结尾

db.products.find({sku:{/$regex:’789$’/}})

7、高级操作

1、limit 和 skip

limit() : 用于读取指定数量的文档

db.stu.find().limit(2)

skip() : 用于跳过指定数量的文档

db.stu.find.skip(2)

同时使用

db.stu.find.skip(5).limit(4)

2、自定义查询 $where

使用$where后面写一个函数,返回满足条件的数据

查询年龄大于30的学生

db.stu.find( {
   $where : function () {
      return this.age>30;   //this 指定是每一条
  }
} )

3、投影

在查询到的返回结果中,只选择必要的字段

db.集合名称.find({ } , { 字段名称:1,... })

参数为字段与值,值为1表示显示,值为0则不显

特殊:对于_id 列默认是显示的,如果不显示需要明确设置为0

db.stu.find({ } , { _id:0,name:1 } )

4、排序

sort() :用于对集进行排序

db.集合名称.find().sort( { 字段1:1,... } )

参数1:升序排列, -1为降序排列

5、统计个数

count() : 统计结果集的文档条数

两种书写方式

①:db.collection.find( {条件} ).count()
②:db.collection.count({条件})

6、消除重复

distinct() : 对数据进行去重

db.集合名称.distinct(‘去重字段’,{条件})

例子:db.stu.distinct(‘hometown’, { age: {$gt: 18}})

注意:当sort,skip,limit一起使用时,无论其位置变化,总是先sort再skip,最后limit

8、数据的备份和恢复

备份的语法(cmd命令)

mongodump -h dbhost -d dbname -o dbdirectory

-h : 服务器地址,也可以指定端口号

-d : 需要备份的数据库名称

-o : 备份的数据存放位置,此目录中存放着备份出来的数据

例子: mongodump -h 192.168.196.128:27017 -d test1 -o ~/Desktop/test1bak

数据恢复

恢复语法:

mongorestore -h dbhost -d dbname -dir dbdirectory

-h : 服务器地址,也可以指定端口号

-d : 需要恢复的数据库实例

-dir : 备份数据所在位置

9、聚合命令(难点)

聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组,过滤等功能,然后经过一系列的处理,输出相应的结果。

db.集合名称.aggregate( { 管道: { 表达式 } } )

常用管道:

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

$match : 过滤数据,只输出符合条件的文档,类似于find()

$project : 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果

$sort : 讲输入文档排序后输出

$limit : 限制聚合管道返回的文档数

$skip : 跳过指定数量的文档,并返回余下的文档

$unwind : 将数组类型的字段进行拆分

mongodb表达式

语法: 表达式 : ‘$列名’

$sum : 计算综总和,$sum:1 表示以一倍计数

$avg : 计算平均值

$min:计算最小值

$max :计算最大值

$push : 在结果文档中插入值到一个数组中

$first : 根据资源文档的排序获取第一个文档数据

$last: 根据资源文档的排序获取最后一个文档数据

$group

· 将集合中的文档分组,可用于统计结果

· _id 表示分组的依据(也就是依照哪个字段进行分组),使用某个字段的格式为 ‘$ 字段’

例1:统计男生、女生的总人数,按照gender分组,并计算每组平均年龄

db.stu.aggregate(
 { $group :
   {
     _id: ‘$gender’,
     count:{ $ sum :1}, //计算总数,1:表示每一行计数1,这样总数就是每一组的总条数
     avg_age:{ $avg: ‘$age’}   //计算平均值
   }
  }
)

v2-662a2fef97d8d57043edd549c9b8c0a8_b.png

· group by null 将集合中所有的文档分为一组

例2:求学生总人数和平均年龄

db.stu.aggregate(
  {$group :{ 
    _id:null,
    count:{$sum :1},
    avg_age:{ $avg: ‘$age’}
  }}
)

$project

输出的结果中如果不想显示_id的话,就要用到 $project

在这里加一个管道,$group输出的结果

db.stu.aggregate(
  {$group :{ _id:’$gender’,count:{$sum :1},avg_age:{ $avg: ‘$age’}}},
  {$project:{ gender:’$_id’,count:’$count’,avg_age:’$ave_age’}}
)

如果不想显示_id , 就写入_id:0, 正常显示的字段就写入 count:1

8b7358618ae7aefb24cd5ddbb2938cbd.png

$match

· 用于过滤数据,只输出符合条件的文档

· 使用MongoDB的标准查询操作

· 与find的区别:match是管道命令,能将结果交给后一个管道,但是find不可以

例1:查询年龄大于20的学生

db.stu.aggregate(
  {$match : {age:{$gt : 20} }}
)

例2:选择年龄大于20的学生,观察男性和女性有多少人

db.stu.aggregate(
  {$match : {age:{$gt:20}}},
  {$group:{ _id:’$gender’,count:{$sum : 1} }},
  {$project:{ gender:’$_id’, count:1 , _id:0}}
)

例3:小练习

{“country”: “china”,”province”:”sh”,”userid”:”a”}
{“country”: “china”,”province”:”sh”,”userid”:”b”}
{“country”: “china”,”province”:”sh”,”userid”:”a”}
{“country”: “uk”,”province”:”sh”,”userid”:”c”}
{“country”: “china”,”province”:”bj”,”userid”:”da”}
{“country”: “china”,”province”:”bj”,”userid”:”fa”}

要求:统计出每个country/province下的userid的数量(同一个userid只统计一次)

db.stu.aggregate(
 //去重,通过将country、province和userid三个属性同时进行分类,那么一样的就会分到一组,然后输出,就达到了去重的作用
 {$group:{ _id:{ country:”$country”,province:”$province”,userid:”$userid” } }},
 {$group:{ _id:{ country:”$country”,province:”$province”}, count:{ $sum:1} }},
{$project:{_id:0,country:”$_id.country”,province:”$_id.province”, count:1 }}
)

$sort

· 将输入文档排序后输出

例1:查询学生信息,按照年龄升序

db.stu.aggregate(
  { $sort: { age:1 }}
)

例2:查询男生、女生人数、按照人数降序

db.stu.aggregate(
 {$group:{ _id:”$gender”,count:{$sum:1} }}
 {$sort:{ count:-1 }}
)

$limit和$skip

· $limit : 限制聚合管道返回的文档数

例1:查询2条学生信息

db.stu.aggregate({ $limit:2 })

· $skip : 跳过指定数量的文档,并返回余下的文档

例子2:查询从第3条开始的学生信息

db.stu.aggregate({ $skip:2 })

例3:统计男生、女生人数、按照人数升序、取第二条数据

db.stu.aggregate(
  { $group:{ _id:”$gender”,count:{ $sum:1 } } },
  { $sort:{count:1 } },
  { $skip:1 },
  {$limit:1},
)

注意:先写skip,在写limit

$unwind

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

语法:db.集合名称.aggregate( {$unwind: “$字段名称” } )

例1:

db.stu.insert({ _id:1,item:’t-shirt’, size:[ ‘S’,’M’,’L’ ] })
db.stu.aggregate( {$unwind:’$size’} )

输出结果

{ _id:1,item:’t-shirt’, size:‘S’ }
{ _id:1,item:’t-shirt’, size:‘M’ }
{ _id:1,item:’t-shirt’, size:‘L’ }

练习:数据库找那个有一条数据:{“username”:”Alex”,”tags”:[“C++”,”Js”,”Java”]},如何获取tags列表的长度?

思路:

①:聚合查询,通过$match查找到这条数据

②:$unwind将tags拆分

③:$group获取数据的总条数

db.stu.aggragate(
  {$match:{ username:”Alex” }},
  {$unwind:”$tags”},
{$group:{ _id:null, count:{$sum:1} } }
)

$unwind的注意事项

如果数据库中有以下数据
{“_id”:1,“item”:”a”,”size”:[“S”,”M”,”L”]}
{“_id”:2,“item”:”b”,”size”:[ ]}
{“_id”:3,“item”:”c”,”size”:”M”} 
{“_id”:4,“item”:”d”}
{“_id”:5,“item”:”e”,”size”:null}
运行:db.stu.aggregate({ $unwind:’$size’ })
结果
{“_id”:1“item”:”a”,”size”:”S”} 
{“_id”:1,“item”:”a”,”size”:”M”} 
{“_id”:1,“item”:”a”,”size”:”L”} 
{“_id”:3,“item”:”c”,”size”:”M”} 

而其他三条数据并没有返回,被删除了,那我们如何让它们正常返回呢?

就需要另一个参数:preserveNullAndEmptyArrays,当为true时,表示保留属性为空的文档

用法:

db.stu.aggregate(
  {$unwind:{
    path:’$字段名称’,
    preserveNullAndEmptyArrays:<boolean>  //防止数据丢失
  }}
)

10、创建索引

1、索引:提升查询速度

语法:db.集合.ensureIndex( { 属性:1 } ), 1表示升序,-1表示降序
具体操作:db.t255.ensureIndex({name:1})

1或者-1,基本没什么差别,只有当需要排序的时候才有区别,比如,当降序的时候-1比较快,反之,1比较快

mongodb通过explain(“executionStats”)来检测查询的速度,输出结果中的属性executionTimeMillis值是查询所需要的的时间,单位为毫秒

测试:插入10万条数据到数据库中

for(i=0;i<100000;i++){db.t255.insert({name:’test’+i,age:i})}

建立索引之前

db.t255.find().explain(“executionStats”)

e25b948773d2367acd28a51c7692fff4.png

建立索引后

db.t255.ensureIndex({name:1})

2c6561bf0a132825533491f6d6afa708.png

第一个是mongodb默认以_id为索引,第二个是我们建立的索引

db.t255.find().explain(“executionStats”)

查询速度是0毫秒,这说明建立索引前后差距是很大的

f49dc5374c6fe6ac0b47f25d33dc37f5.png

· 在默认情况下索引字段的值可以相同

· 创建唯一索引(索引的值是唯一的),这样可以去重,也是建立爬虫去重的思路之一

db.t1.ensureIndex({name:1},{“unique”:true})

简单讲一下爬虫数据去重,实现增量式爬虫
· 使用数据库建立关键字段(一个或者多个)建立索引进行去重
思路一: 根据url地址进行去重
 ① url地址对应的数据不会变的情况,url地址能够唯一判别一条数据的情况
 ② 首先,将url存到redis中,拿到url之后,判断url在redis的url集合中是否存在,
   如果存在,说明该url已经请求过了,不再请求,如果不存在,说明该url没被请求过,
   把url存入redis的集合中
 ③ 这方面有一个很厉害的算法:布隆过滤器
思路二:根据数据本身进行去重
 ①选择特定的字段,使用加密算法(md5,sha1)将字段进行加密,生成字符串,
   存入redis的集合中
 ②后续新来一条数据,同样的方法进行加密,如果得到的字符串在redis中存在,
   说明数据存在,对数据进行更新,否则说明数据不存在,直接插入

· 通过多个字段来判断数据的唯一性的时候可以建立联合索引

db.t1.ensureIndex({name:1},{age:1})

· 查看当前集合所有索引

db.t1.getIndexes()

· 删除索引

db.t1.dropIndex({‘索引名称’: 1})
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值