7.MongoDB系列之聚合框架、特殊索引和集合类型

1. 聚合框架

1.1 管道阶段和可调参数

聚合框架基于管道的概念。他由多个阶段组成,每个阶段都会提供一组按钮或可调参数。每个阶段对其输入执行不同的数据处理任务,并生成文档已作为输出传递到下一阶段。

1.2 阶段常见操作

匹配(match)、投射(project)、排序(sort)、跳过(skip)、限制(limit)

如以下命令包括此五阶段

db.getCollection('users').aggregate([
	{$match: {age: 42}},
	{$sort: {username: 1}},
	{$skip: 10},
	{$limit: 5},
        // 投射其实就是文档的展示信息控制
	{$project: {_id: 0, username: 1}}
])

1.2.1 $unwind对数组进行展开

// 展开前
db.food.find({})
_id     fruit
1	(Array) 3 Elements
2	(Array) 3 Elements
3	(Array) 3 Elements
// 展开后
db.food.aggregate([
        // $fruit表示取字段fruit展开
	{$unwind: "$fruit"}
])
_id     fruit
1	apple
1	banana
1	peach
2	apple
2	kumquat
2	orange
3	cherry
3	banana
3	apple

1.2.2 数组表达式

// 首先插入两个文档
db.blog.insert({'title': 'mongo', 'comments': [{'vote': 100, 'info': 'good'}, {'vote': 110, 'info': 'ok'}]});
db.blog.insert({'title': 'es', 'comments': [{'vote': 90, 'info': 'ok'}, {'vote': 80, 'info': 'bad'}]});

过滤出数组中点赞数大于100的文档

// input表示将要过滤的字段$取字段值 as定义变量 $$用来引入表达式中定义的变量
db.blog.aggregate([
	{$match: {}},
	{$project: {_id: 0, title: 1, comments: {$filter: {input: "$comments", as: 'comment', cond: {$gte: ['$$comment.vote', 110]}}}}}
])

查询结果如下:

{
    "title": "mongo",
    "comments": [
        {
            "vote": 110,
            "info": "ok"
        }
    ]
}
{
    "title": "es",
    "comments": [ ]
}

1.3 分组简介

// 先插入文档
db.blog.insert({'title': 'es', 'comments': [{'vote': 80, 'info': 'ok'}, {'vote': 70, 'info': 'bad'}]});
// _id为分组的字段,多个可以{title: '$title', 'comments': '$comments'}
// num名称自定义,可以$sum求和 $avg平均等
db.getCollection('blog').aggregate([
	{$group: {_id: 'title', num: {$sum: 1}}}
])

分组结果如下:

{
    "_id": "title",
    "num": 3
}
db.deal_price.aggregate([
	{$match: {'city': {'$in': ['东莞', '佛山', '深圳', '长沙', '中山', '广州', '珠海', '韶关', '合肥', '清远', '惠州']}, 'trade_date': {'$gte': ISODate('2022-06-19')}, 'avg_price': {'$ne': null} }},
	{$project: {_id: 0, city: 1, region: 1, district_name: 1, avg_price: 1}},
	{$group: {_id: {city: '$city', region: '$region', 'district_name': '$district_name'}, avg_price: {$avg: '$avg_price'}}}
])
  • 按月聚合输出
db.district_stat.aggregate([
	{$match: {city: '北京', weekend: {$gte: ISODate('2022-11-01')}}},
	{$project: {_id: 0, city: 1, avg_price: 1, year_month: {$dateToString: {format: "%Y%m", date: "$weekend"}}}},
	{$group: {_id: {city: '$city', year_month: '$year_month'}, avg_price: {$avg: '$avg_price'}}}
])

2. 了解应用程序动态

2.1 查看当前操作

mongos> db.currentOp()
{
        "inprog" : [
                {
                        "shard" : "study",
                        "type" : "op",
                        "host" : "2bffe09ec303:27019",
                        "desc" : "ReplBatcher",
                        "active" : true,// 操作是否正在执行
                        "opid" : "study:3427",// 操作的唯一标识符
                        "numYields" : 0,// 操作释放锁允许其他操作进行的次数
                        "locks" : {// 描述操作获取锁的类型

                        },
                        "waitingForLock" : false,// 操作当前是否处于阻塞并等待结果
                },
                {
                        ...
                        "secs_running" : NumberLong(0),// 操作执行时间(秒)
                        "microsecs_running" : NumberLong(239),// 操作执行时间(微秒)
                        "op" : "command", // 操作类型      
                }          
        ],
        "ok" : 1,
        ...
}

2.1.1 寻找有问题操作

client字段可以跟踪旧版本或者漏洞版本的软件信息

2.1.2 终止操作

// 传入opid,只有当操作让出时,才能终止操作
> db.killOp('study:2')

2.1.3 假象

任何在local.oplog.rs上长时间运行的请求及任何回写监听命令都可以被忽略。
MongoDB会长时间运行若干请求,最常见是复制线程和用于分片的回写监听器。

2.1.4 防止幻象操作

如果MongoDB中请求发生堆积,呢么写操作将堆积在操作系统的套接字缓冲区中。当终止正在进行的写操作时,就会开始处理缓冲区中的写操作。即使客户端停止发送写操作,MongoDB也会处理那些写入缓冲区的操作。
防止幻像写入的最好方法是执行写入确认机制。

2.2 计算大小

2.2.1 文档

获取文档大小

> Object.bsonsize({_id:ObjectId()})
> Object.bsonsize(db.users.findOne())

2.2.2 集合

// 查看整个集合的信息
> db.movies.stats()
// 以TB为单位获取集合的统计数据
> db.big.stats(1024*1024*1024*1024)

2.2.3 数据库

// 查看整个数据库的信息
> db.stats()

2.3 使用mongotop和mongostat

# mongotop -h localhost:27039
2022-07-24T11:43:55.304+0800    connected to: mongodb://localhost:27039/

                  ns    total    read    write    2022-07-24T11:43:56+08:00
      local.oplog.rs      1ms     1ms      0ms
   admin.system.keys      0ms     0ms      0ms
  admin.system.roles      0ms     0ms      0ms
admin.system.version      0ms     0ms      0ms
    config.actionlog      0ms     0ms      0ms
       config.chunks      0ms     0ms      0ms
  config.collections      0ms     0ms      0ms
    config.lockpings      0ms     0ms      0ms
       config.mongos      0ms     0ms      0ms
     config.settings      0ms     0ms      0ms
# mongostat -h localhost:27039
// insert query update delete getmore command 每秒操作发生次数简单统计
// flushes 将数据刷新到磁盘的次数
// vsize 所使用虚拟内存数量
// res 正在使用内容大小
// qr|qw 读操作和写操作的队列大小
// ar|aw 有多少活跃的客户端
// net_in net_out 网络传入传出字节数
// conn 连接数
insert query update delete getmore command dirty used flushes vsize  res qrw arw net_in net_out conn      set repl                time
    *0    *0     *1     *0       2     7|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  3.89k   79.6k   16 configRS  SEC Jul 24 11:52:41.199
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0   524b   74.5k   16 configRS  SEC Jul 24 11:52:42.202
    *0    *0     *0     *0       0     4|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.06k   76.2k   16 configRS  SEC Jul 24 11:52:43.200
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0   731b   75.5k   16 configRS  SEC Jul 24 11:52:44.200
    *0    *0     *0     *0       0     3|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.06k   76.0k   16 configRS  SEC Jul 24 11:52:45.200
    *0    *0     *0     *0       1     3|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.84k   76.0k   16 configRS  SEC Jul 24 11:52:46.199
    *0    *0     *0     *0       0     3|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.06k   76.0k   16 configRS  SEC Jul 24 11:52:47.200
    *0     2     *0     *0       0     2|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.37k   75.9k   16 configRS  SEC Jul 24 11:52:48.199
    *0    *0     *0     *0       0     3|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0  1.06k   76.0k   16 configRS  SEC Jul 24 11:52:49.200
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0 1.93G 154M 0|0 1|0   731b   75.4k   16 configRS  SEC Jul 24 11:52:50.200

3. 特殊索引和集合类型

3.1 地理空间索引及全文搜索

与Elasitcsearch一样,MongoDB同样支持地理空间索引及全文搜索,由于选型常用ES而非MongoDB此处略过

3.2 TTL索引

首先先了解下固定集合,其类似于循环队列 ,当满的时候,最旧的文档会被覆盖。而TTL索引允许允许为每个文档设置一个超时时间,当一个文档过期时就会被删除。这种类型的索引对于类似会话保存这样的缓存场景非常有用。

//  设置TTL索引,过期时间为24小时
db.getCollection('sessions').createIndex({'lastUpdated': 1}, {'expireAfterSeconds': 60*60*24})

3.3 使用GridFS存储文件

GridFS是MongoDB存储大型二进制文件的一种机制

优点:

  • 使用GridFS能够简化技术栈, 如果项目已在使用MongoDB,呢么可以使用GridFS代替独立的文件存储工具
  • GridFS可以利用MongoDB已经设置好的复制或自动分片机制,因此实现故障转移与横向扩展容易些
  • GridFS没有在同一个目录下存储大量文件的问题

缺点:

  • 性能比较低。不如 从文件系统访问文件速度快
  • 修改文档,需要先删除,后重新保存。因为MongoDB会将文件作为多个文档进行存储,无法对同一文件的所有块进行枷锁

3.3.1 GridFS存储示例

可以使用mongofile进行上传查看

mongofiles --uri mongodb://admin:admin@127.0.0.1:27017/study?authSource=admin -l ok.txt put ok.txt

2022-07-09T16:11:52.620+0800    connected to: mongodb://[**REDACTED**]@127.0.0.1:27017/study?authSource=admin
2022-07-09T16:11:52.750+0800    added gridFile: ok.txt

3.3.2 GridFS底层机制

GridFS背后的理念是将大文件分割为多个,并将每个块作为独立的文档进行存储。文档的结构如下

{
  // 块唯一ID
  "_id": ObjectId("..."),
  // 块在文件中的相对位置
  "n": 0,
  // 块所包含的二进制数据
  "data": BinData("..."),
  // 此块所属文件元数据的文档ID
  "files_id": ObjectId("...")
}

每个文件的元数据保存在一个单独的集合中,默认情况下是fs.files,可以执行db.fs.files.find({})查看结果

{
    // 文件的唯一ID
    "_id": ObjectId("62c93848cf13162ba9c71cad"),
    // 文件总字节数
    "length": NumberLong("2"),
    // 组成文件的每个块的大小
    "chunkSize": NumberInt("261120"),
    // 上传时间
    "uploadDate": ISODate("2022-07-09T08:11:52.747Z"),
    "filename": "ok.txt",
    "metadata": { }
}

欢迎关注公众号算法小生或沈健的技术博客shenjian.online

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
mongodb-orm简介Mongodb ORM是基于java的ORM框架,简化了SDK的使用,使代码变得更清晰、简单。 与Ibatis类似,将查询、执行语句封装在xml,与代码隔离。简称MQL。 项目使用加入mongodb orm的支持包1. 添加jar包或maven支持<dependency>     <groupId>com.mongodborm</groupId>     <artifactId>mongodb-orm</artifactId>     <version>0.0.1-RELEASE</version> </dependency>2. 初始化mongodb templet        spring初始化<bean id="mongoTemplet" class="com.mongodb.client.MongoClientTemplet">     <property name="factory">         <bean class="com.mongodb.client.MongoORMFactoryBean">             <property name="dataSource">                 <bean class="com.mongodb.client.MongoDataSource">                     <property name="nodeList" value="127.0.0.1:27017" />                     <property name="dbName" value="your db name" />                     <property name="userName" value="user name" />                     <property name="passWord" value="password" /> <!-- 可使用默认值 --> <property name="connectionsPerHost" value="" />                     <property name="threadsAllowedToBlock" value="" />                     <property name="connectionTimeOut" value="" />                     <property name="maxRetryTime" value="" />                     <property name="socketTimeOut" value="" />                 </bean>             </property>             <property name="configLocations">                 <list>                     <value>classpath:mql/mongo-mql.xml</value>                 </list>             </property>         </bean>     </property> </bean>        代码初始化    try {       Resource resource =  new ClassPathResource("mongo-mql.xml");           MongoORMFactoryBean factory = new MongoORMFactoryBean();       factory.setConfigLocations(new Resource[]{resource});       factory.init();          MongoClientTemplet templet = new MongoClientTemplet();       templet.setFactory(factory);       templet.init();     } catch(Exception e) {       e.printStackTrace();     }编写MQLMapping<mapping id="model" class="test.mongodborm.Model">         <property column="_id" name="id" />         <property column="name" name="name" />         <property column="time" name="time" value="0" />         <property column="status" name="status" /> </mapping> <mapping id="extendModel" class="test.mongodborm.Model" extends="model">     <property column="newProperty" name="newProperty" /> </mapping>  select<select id="queryModelList" collection="test_sample">     <query class="java.lang.String">         <property column="name" name="${value}" />     </query>     <field mapping="model" />     <order>         <property column="time" value="desc" />     </order> </select> update/findAndModify<update id="updateModel" collection="test_sample">     <query class="test.mongodborm.Model$Child">         <property column="name" name="name" ignoreNull="true" />         <property column="time" operate="gte" value="0" type="number" />         <property column="status" operate="in">             <list type="number">0,1</list>         </property>     </query>     <action class="java.util.Map">         <property column="name" name="name" operate="set" />         <property column="status" operate="set" />     </action> </update>有嵌套的查询<select id="queryModelList3" collection="test_sample">     <query class="java.lang.String">         <property column="_id" value="${value}" />         <property column="time" value="0" type="number" />     </query>     <field class="java.util.Map">         <property column="name" name="name" />         <property column="parent" name="parent">             <value class="test.mongodborm.Model$Parent">                 <property column="name" name="name" />                 <property column="child" name="child">                     <value class="test.mongodborm.Model$Child">                         <property column="name" name="name" />                         <property column="time" name="time" value="0" />                     </value>                 </property>                 <property column="data" name="data">                     <value class="java.util.Map">                         <property column="title" name="title" />                         <property column="content" name="content" />                     </value>                 </property>             </value>         </property>         <property column="data" name="data">             <value class="java.util.Map">                 <property column="title" name="title" />                 <property column="content" name="content" />             </value>         </property>     </field>     <order class="java.util.Map">         <property column="time" name="time" value="desc" />     </order> </select>Templet用法Model model = mongoTemplet.findOne("queryModelList", "yuxiangping"); List<Model> list = mongoTemplet.findOne("queryModelList", ""); Model model = new Model(); model.setTime(1L); Map<String, String> action = new HashMap<String, String>(); action.put("name", "yuxiangping-update"); int update = mongoT emplet.update("updateModel", model, action);        更多的使用方法参见 sample.xml 标签:Mongodb
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法小生Đ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值