项目中使用mongodb也有5个多月了,总结一下MongoTemplate的聚合查询

以项目中的一个走访表作为例子

在对应项目中引入对应版本的spring-boot-data-mongodb依赖

org.springframework.boot

spring-boot-starter-data-mongodb

然后yml配置文件中配置好mongodb的连接信息就可以了

spring:

data:

mongodb:

uri: mongodb://账号:密码@ip:port/库名

logging:

level:

org.springframework.data.mongodb.core: debug

 monggodb集合对应的实体类:

package com.dcboot.module.visit.mongodb.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import org.springframework.data.annotation.Id;

import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/7/16 15:47

* @Description

*/

@Document("visitObjectDeatil")

@ApiModel(value = "走访对象详情信息")

@Data

@JsonIgnoreProperties(ignoreUnknown = true)

public class VisitObjectDeatil {

private Long taskObjectId;

@Id

private Long id;

/**

* 走访状态(只有走访中和已走访两种状态了)

*/

private Integer visitStatus;

/**

* 属地镇街(顺德区的10个镇街)

* {@link com.dcboot.module.common.enums.task.ShunDeDistrictEnum}

*/

@ApiModelProperty(name = "townOrStreet",value = "属地镇街(顺德区的10个镇街)")

private String townOrStreet;

/**

* 走访主题

* {@link com.dcboot.module.common.enums.task.TaskSubjectEnum}

*/

@ApiModelProperty(name = "visitSubject",value = "走访主题")

private Integer visitSubject;

/**

* 走访对象

*/

@ApiModelProperty(name = "visitObject",value = "走访对象(6种类别)")

private VisitObject visitObject;

/**

* 走访详情说明

*/

@ApiModelProperty(name = "visitDeatilDescription",value = "走访详情说明")

private String visitDeatilDescription;

/**

* 走访附件信息列表

*/

@ApiModelProperty(name = "attachmentList",value = "走访附件")

private List attachmentList;

/**

* 走访现场图片

*/

@ApiModelProperty(name = "visitPictureList",value = "走访现场图片")

private List visitPictureList;

/**

* 企业联系人名片

*/

@ApiModelProperty(name = "enterpriseContactPersonBusinessCardList",value = "企业联系人名片")

private List enterpriseContactPersonBusinessCardList;

// private List visitThePersonOfEnterpriseList;

@ApiModelProperty(name = "visitEnterpriseBaseInfo",value = "走访企业基本信息")

private VisitEnterpriseBaseInfo visitEnterpriseBaseInfo;

/**

* 财务指标

*/

@ApiModelProperty(name = "financeIndexList",value = "财务指标")

private List financeIndexList;

/**

* 企业规模

*/

@ApiModelProperty(name = "enterpriseScale",value = "企业规模")

private EnterpriseScale enterpriseScale;

/**

* 企业营销情况

*/

@ApiModelProperty(name = "enterpriseMarktingCondition",value = "企业营销情况")

private EnterpriseMarktingCondition enterpriseMarktingCondition;

/**

* 经营主要指标

*/

@ApiModelProperty(name = "mainOperatingIndicators",value = "经营主要指标")

private MainOperatingIndicators mainOperatingIndicators;

/**

* 企业融资情况和意愿

*/

@ApiModelProperty(name = "enterpriseFinancingAndWilling",value = "企业融资意愿")

private EnterpriseFinancingAndWilling enterpriseFinancingAndWilling;

/**

* 企业专利著作情况

*/

@ApiModelProperty(name = "enterprisePatentAndBook",value = "企业专利著作情况")

private EnterprisePatentAndBook enterprisePatentAndBook;

/**

* 企业陪同走访人

*/

@ApiModelProperty(name = "companionPerson",value = "企业陪同走访人")

private String companionPerson;

/**

* 企业标签

*/

@ApiModelProperty(name = "enterpriseLabel",value = "企业标签")

private String enterpriseLabel;

/**

* 走访形式

*/

@ApiModelProperty(name = "visitMode",value = "走访形式")

private Integer visitMode;

/**

* 是否自主走访

*/

private Integer isSelfVisit;

/**

* 走访对象类型 分为:企业主体和非企业主体 1 企业主体 0 非企业主体

*/

@ApiModelProperty(name = "visitObjectType",value = "走访对象类型 分为:企业主体和非企业主体 1 企业主体 0 非企业主体")

private Integer visitObjectType;

/**

* 走访人

*/

@ApiModelProperty(name = "visitPersonId",value = "走访人id")

private Long visitPersonId;

private String visitPerson;

/**

* 走访人所在科室

*/

@ApiModelProperty(name = "visitDepartmentId",value = "走访人所在科室id")

private Long visitDepartmentId;

private String visitDepartment;

@ApiModelProperty(name = "visitTime",value = "走访日期")

private String visitTime;

private String createTime;

private String updateTime;

private String createBy;

private String updateBy;

}

内嵌对象

package com.dcboot.module.visit.mongodb.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/7/18 11:11

* @Description

*/

@Data

@ApiModel(value = "走访对象")

@JsonIgnoreProperties(ignoreUnknown = true)

public class VisitObject {

private String visitObjectId;

/**

* 走访对象类型

* {@link com.dcboot.module.common.enums.task.VisitObjectTypeEnum}

*/

@ApiModelProperty(name = "type",value = "走访对象类型")

private Integer type;

@ApiModelProperty(name = "typeName",value = "走访对象类型名称")

private String typeName;

@ApiModelProperty(name = "visitObjectName",value = "走访对象名称")

private String visitObjectName;

}

内部数组对象:

package com.dcboot.module.visit.mongodb.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.math.BigDecimal;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/7/16 18:47

* @Description 财务指标

*/

@Data

@ApiModel(value = "财务指标")

@JsonIgnoreProperties(ignoreUnknown = true)

public class FinanceIndex {

/**

* 年份

*/

private String year;

/**

* 营业总收入

*/

@ApiModelProperty(name = "income", value = "营业总收入")

private BigDecimal income;

/**

* 利润总额

*/

@ApiModelProperty(name = "profit", value = "利润总额")

private BigDecimal profit;

/**

* 纳税总额

*/

@ApiModelProperty(name = "amountOfTax", value = "纳税总额")

private BigDecimal amountOfTax;

/**

* 资产总额

*/

@ApiModelProperty(name = "totalAmountOfAssets", value = "资产总额")

private BigDecimal totalAmountOfAssets;

/**

* 负债总额

*/

@ApiModelProperty(name = "totalAmountOfLiabilities", value = "负债总额")

private BigDecimal totalAmountOfLiabilities;

/**

* 流动比率

*/

@ApiModelProperty(name = "currentRatio", value = "流动比率")

private BigDecimal currentRatio;

/**

* 速动比率

*/

@ApiModelProperty(name = "quickRatio", value = "速动比率")

private BigDecimal quickRatio;

/**

* 资产负债率

*/

@ApiModelProperty(name = "assetsLiabilityRatio", value = "资产负债率")

private Double assetsLiabilityRatio;

/**

* 净资产收益率

*/

@ApiModelProperty(name = "netAssetsYieldsRatio",value = "净资产收益率")

private BigDecimal netAssetsYieldsRatio;

/**

* 资产回报率

*/

@ApiModelProperty(name = "assetsReturnRatio",value = "资产回报率")

private BigDecimal assetsReturnRatio;

/**

* 毛利率

*/

@ApiModelProperty(name = "profitRatio",value = "毛利率")

private BigDecimal profitRatio;

/**

* 企业所得税实际纳税额

*/

@ApiModelProperty(name = "actualTaxAmountOfEnterpriseIncomeTax",value = "企业所得税实际纳税额")

private BigDecimal actualTaxAmountOfEnterpriseIncomeTax;

/**

* 经营活动产生的现金流量净额

*/

@ApiModelProperty(name = "netCashFlowFromOperatingActivities",value = "经营活动产生的现金流量净额")

private BigDecimal netCashFlowFromOperatingActivities;

/**

* 研发投入

*/

@ApiModelProperty(name = "researchInvestment",value = "研发投入")

private BigDecimal researchInvestment;

/**

* 文件列表

*/

// private List attachmentList;

}

查询例子1

package com.dcboot.module.visit.task.service.impl;

import com.dcboot.module.visit.mongodb.entity.VisitObjectDeatil;

import com.dcboot.module.visit.task.common.PageListResponse;

import com.dcboot.module.visit.task.service.PortalVisitService;

import com.dcboot.module.visit.task.vo.SimpleVisitRecordVO;

import lombok.Setter;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Sort;

import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.aggregation.Aggregation;

import org.springframework.data.mongodb.core.aggregation.AggregationResults;

import org.springframework.data.mongodb.core.aggregation.TypedAggregation;

import org.springframework.data.mongodb.core.query.Criteria;

import org.springframework.data.mongodb.core.query.Query;

import org.springframework.stereotype.Service;

import java.util.List;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/7/15 11:46

* @Description

*/

@Service

@Setter(onMethod_=@Autowired)

public class PortalVisitServiceImpl implements PortalVisitService {

private MongoTemplate mongoTemplate;

@Override

public PageListResponse getVisitRecords(Long page,Integer size,String enterpriseName) {

Criteria criteria = Criteria.where("visitObject.visitObjectName").is(enterpriseName);

Query query = Query.query(criteria);

TypedAggregation visitObjectDeatilTypedAggregation = Aggregation.newAggregation(

VisitObjectDeatil.class,

// 相当于select

Aggregation.project()

.and("visitPerson").as("userName")

.and("visitObject.visitObjectName").as("enterpriseName")

.and("visitTime").as("visitTime").andExclude("_id")

.and("createTime").as("createTime"),

// 查询条件,相当于where

Aggregation.match(Criteria.where("enterpriseName").is(enterpriseName)),

Aggregation.sort(Sort.Direction.DESC, "createTime"),

Aggregation.skip((page - 1) * size),

Aggregation.limit(size)

);

AggregationResults mapAggregationResults = mongoTemplate.aggregate(visitObjectDeatilTypedAggregation, SimpleVisitRecordVO.class);

List mappedResults = mapAggregationResults.getMappedResults();

mappedResults.forEach(System.out ::println);

long totalCount = mongoTemplate.count(query, VisitObjectDeatil.class);

PageListResponse pageListResponse = new PageListResponse<>();

pageListResponse.setRecordList(mappedResults);

pageListResponse.setTotalCount(totalCount);

return pageListResponse;

}

}

,这是一个分页查询,需要分页查询出结果,并且还要查询出总条数,提供给前端,所以只用一个聚合管道查询不够,聚合管道查询出分页的数据,然后还有一个查询查满足查询条件的总条数,然后封装返回给前端

mongodb聚合查询分页查出来的数据,对应的VO实体类:

package com.dcboot.module.visit.task.vo;

import lombok.Data;

import lombok.NoArgsConstructor;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/7/19 20:18

* @Description

*/

@NoArgsConstructor

@Data

public class SimpleVisitRecordVO {

private String visitTime;

private String userName;

private String enterpriseName;

}

还有一个mongodb集合collection是同事创建的,他创建的实体类是这样的:

package com.dcboot.module.manage.vo.req;

import com.baomidou.mybatisplus.annotation.TableField;

import com.fasterxml.jackson.annotation.JsonInclude;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import lombok.experimental.Accessors;

import org.springframework.data.annotation.CreatedDate;

import org.springframework.data.annotation.Id;

import org.springframework.data.annotation.LastModifiedDate;

import org.springframework.data.mongodb.core.index.Indexed;

import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;

import java.util.Date;

import java.util.List;

/**

* @description: ScienceProjectParams

* @date: 2022/4/25 16:32

* @author: HCF

* @version: 1.0

*/

@Document(collection = "scienceProjectParams")

@ApiModel(value = "科技项目参数")

@Data

@Accessors(chain = true)

@JsonInclude(JsonInclude.Include.NON_NULL)

public class ScienceProjectParams implements Serializable {

@ApiModelProperty(value = "_id")

private String _id;

@ApiModelProperty(value = "数据")

private Object data;

@ApiModelProperty(value = "类别")

private String category;

@ApiModelProperty(value = "备注")

private List remarks;

@ApiModelProperty(value = "创建时间")

private String createTime;

@ApiModelProperty(value = "修改时间")

private String updateTime;

}

那么在使用聚合管道查询时:

int thisYear = DateUtil.thisYear();

String thisYearStr = String.valueOf(thisYear);

Aggregation aggregation = Aggregation.newAggregation(

Aggregation.project().and("data.QYMC").as("enterpriseName")

.and("data.TYSHXYDM").as("uscc")

.andExclude("_id")

.and("createTime").as("createTimeStr")

.and("createTime").substring(5,2).as("month")

.and("createTime").substring(0,4).as("year")

.and("data.type").as("typeCode"),

Aggregation.match(Criteria.where("typeCode").is(typeCode).and("year").is(thisYearStr)),

Aggregation.sort(Sort.Direction.DESC, "createTimeStr")

);

AggregationResults aggregate = mongoTemplate.aggregate(aggregation,mongoTemplate.getCollectionName(ScienceProjectParams.class),EnterpriseDetailForTypeBO.class);

List mappedResults = aggregate.getMappedResults();

说明,由于 ScienceProjectParams类中有个属性data是Object类型,所以不能使用TypedAggregation,需要使用不指定泛型类型的Aggregation 然后在使用mongoTemplate.aggregate方法时,使用 mongoTemplate.getCollectionName(ScienceProjectParams.class),传入集合名;

如果使用TypedAggregation这种带泛型类型聚合api,会报异常,提示没有实体类可以转换成Object类(Couldn't find PersistentEntity for type java.lang.Object!)

比如之前的写法和异常出错

 因为属性data是Object类型 

出现异常提示

所以改成上边的写法就可以正常聚合查询了;

而上一个查询例子中,我自己创建的一个mongodb的collection对应的实体类可以使用TypedAggregation是因为,我创建的实体类VisitObjectDeatil的属性中没有Object类型

查询3

聚合中如果带有分组和求和,则查询出来的字段会只有分组的字段和求和的字段,其他字段是查询不出来的,而且查询出来的结构也是分组字段和求和字段是隔离开的

例如:

for (int i = 1; i <= 2 ; i++) {

String typeCode = EnterpriseTypeEnum.getTypeCode(i);

Aggregation aggregation = Aggregation.newAggregation(

Aggregation.project().and("data.QYMC").as("enterpriseName")

.and("data.TYSHXYDM").as("uscc")

.andExclude("_id")

.and("createTime").as("createTime")

.and("createTime").substring(5,2).as("month")

.and("createTime").substring(0,4).as("year")

.and("data.type").as("typeCode")

.and("enterpriseNum"),

Aggregation.match(Criteria.where("typeCode").is(typeCode)),

Aggregation.sort(Sort.Direction.DESC, "createTime"),

Aggregation.group("month","year","typeCode").count().as("enterpriseNum")

);

AggregationResults aggregate = mongoTemplate.aggregate(aggregation,mongoTemplate.getCollectionName(ScienceProjectParams.class),Map.class);

List mappedResults = aggregate.getMappedResults();

if (CollectionUtils.isNotEmpty(mappedResults)){

JSONArray jsonArray = JSONUtil.parseArray(mappedResults);

int size = jsonArray.size();

for (int j = 0; j < size; j++) {

JSONObject jsonObject = jsonArray.getJSONObject(j);

Integer enterpriseNum = jsonObject.getInt("enterpriseNum", NumberUtils.INTEGER_ZERO);

JSONObject groupInfo = jsonObject.getJSONObject("_id");

String month = groupInfo.getStr("month");

Integer monthNum=0;

if (NumberUtil.isInteger(month)){

monthNum = Integer.valueOf(month);

}

String year = groupInfo.getStr("year");

Integer yearNum=0;

if (NumberUtil.isInteger(year)) {

yearNum = Integer.valueOf(year);

}

groupInfo.getStr("typeCode");

EnterpriseTypeStatistics one = enterpriseTypeStatisticsService.getOne(Wrappers.lambdaQuery()

.eq(EnterpriseTypeStatistics::getIsValid, 1)

.eq(EnterpriseTypeStatistics::getMonth, monthNum)

.eq(EnterpriseTypeStatistics::getType, i)

.eq(EnterpriseTypeStatistics::getYear, yearNum));

EnterpriseTypeStatistics enterpriseTypeStatistics = new EnterpriseTypeStatistics();

enterpriseTypeStatistics.setEnterpriseNum(enterpriseNum);

enterpriseTypeStatistics.setYear(yearNum);

enterpriseTypeStatistics.setMonth(monthNum);

enterpriseTypeStatistics.setTypeCode(typeCode);

enterpriseTypeStatistics.setType(i);

String typeName = EnterpriseTypeEnum.getTypeName(i);

enterpriseTypeStatistics.setIsValid(1);

enterpriseTypeStatistics.setTypeName(typeName);

if (Objects.isNull(one)){

enterpriseTypeStatistics.insert();

}else {

Long id = one.getId();

enterpriseTypeStatistics.setId(id);

boolean update = enterpriseTypeStatistics.updateById();

}

}

}

}

 聚合查询出来的数据结构是这样的

[{

"_id": {

"month": "05",

"year": "2022",

"typeCode": "GXJSQYRD"

},

"enterpriseNum": 4905

}, {

"_id": {

"month": "06",

"year": "2022",

"typeCode": "GXJSQYRD"

},

"enterpriseNum": 2

}]

如果 Aggregation.group("month","year","typeCode").count().as("enterpriseNum")这个聚合操作,只是分组,没有计数count操作,则可以直接映射给一个实体类的形式

比如这个聚合查询中group操作之后,没有计数或者求和

/**

*

* @return

*/

@GetMapping("/web/thirteenthToTwentyEighthTypeDetailRecordTest")

@ApiOperation("添加第十三类至第二十八类企业数据明细测试")

public ApiResult thirteenthToTwentyEighthTypeDetailRecordTest(){

for (int i = 13; i <= 28; i++) {

List enterpriseTypeDetailRecordList = Lists.newArrayList();

String typeCode = EnterpriseTypeEnum.getTypeCode(i);

String typeName = EnterpriseTypeEnum.getTypeName(i);

List enterpriseTypeChildCodeList = enterpriseTypeDetailRecordService.getEnterpriseTypeChildCode(typeCode);

Aggregation aggregation = null;

if (i == 13 || Range.closed(18, 20).contains(i) || Range.closed(25, 28).contains(i)) {

aggregation = Aggregation.newAggregation(

Aggregation.project().and("data.list.SBDW").as("enterpriseName")

.and("data.list.TYSHXYDM").as("uscc")

.andExclude("_id")

.and("createTime").substring(5, 2).as("month")

.and("createTime").substring(0, 4).as("year")

.and("data.type").as("typeCode"),

Aggregation.match(Criteria.where("typeCode").in(enterpriseTypeChildCodeList)),

Aggregation.group("month","year","enterpriseName","uscc").last("month")

.as("month").last("year").as("year").last("enterpriseName").as("enterpriseName")

.last("uscc").as("uscc")

);

}

AggregationResults aggregate = mongoTemplate.aggregate(aggregation,mongoTemplate.getCollectionName(ScienceProjectParams.class),EnterpriseDetailForTypeBO.class);

List mappedResults = aggregate.getMappedResults();

log.info("第{}类表的数据:{}",i, JSONUtil.toJsonPrettyStr(mappedResults));

}

return ApiResult.success();

}

那么最终聚合查询出来的数据的格式就是这样的:

[

{

"enterpriseName": "广东伊之密精密机械股份有限公司",

"uscc": "91440606740846335Y",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区北航先进技术产业基地有限公司",

"uscc": "914406065778638477",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东顺德西安交通大学研究院",

"uscc": "124406065666480841",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东精进能源有限公司",

"uscc": "",

"year": 2022,

"month": 6

},

{

"enterpriseName": "美的集团股份有限公司",

"uscc": "91440606722473344C",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东宏石激光技术股份有限公司",

"uscc": "91440606698184197Y",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东尚研电子科技股份有限公司",

"uscc": "91440606577885966R",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东德宁水产科技有限公司",

"uscc": "91440606MA4UWEB632",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东万锦科技股份有限公司",

"uscc": "",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山隆深机器人有限公司",

"uscc": "91440606077869759B",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东光晟物联股份有限公司",

"uscc": "91440606059922085K",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东美的制冷设备有限公司",

"uscc": "9144060672547107X0",

"year": 2022,

"month": 6

},

{

"enterpriseName": "北京科技大学顺德研究生院",

"uscc": "12440606MB2C974763",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东顺德工业设计研究院(广东顺德创新设计研究院)",

"uscc": "124406063980588428",

"year": 2022,

"month": 6

},

{

"enterpriseName": "国药集团广东环球制药有限公司",

"uscc": "914406066176288779",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东博智林机器人有限公司",

"uscc": "91440606MA520YMC94",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东惠而浦家电制品有限公司",

"uscc": "91440606617655699P",

"year": 2022,

"month": 6

},

{

"enterpriseName": "美的智慧家居科技有限公司",

"uscc": "91440300342921205X",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东省皓智科技有限公司",

"uscc": "91440606MA533MMU0G",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东丁沃生医疗器械有限公司",

"uscc": "91440606MA51758K3X",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区美的电热电器制造有限公司",

"uscc": "91440606784896596B",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区德雅先进技术融合创新研究院",

"uscc": "52440606MJL6641547",

"year": 2022,

"month": 6

},

{

"enterpriseName": "华南智能机器人创新研究院",

"uscc": "12440606351932176Q",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区金泰德胜电机有限公司",

"uscc": "91440606737571924H",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东艾时代生物科技有限责任公司",

"uscc": "91440606323308667W",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东银珠医药科技有限公司",

"uscc": "91440400MA4W3Q8NXA",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东永爱医养产业有限公司",

"uscc": "914406060537673446",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东科龙模具有限公司",

"uscc": "914406066174753729",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区美的洗涤电器制造有限公司",

"uscc": "914406067211121414",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东锻压机床厂有限公司",

"uscc": "91440606193864031B",

"year": 2022,

"month": 6

},

{

"enterpriseName": "佛山市顺德区凯硕精密模具自动化科技有限公司",

"uscc": "91440606675234577Q",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东奥基德信机电有限公司",

"uscc": "91440606084537584M",

"year": 2022,

"month": 6

},

{

"enterpriseName": "广东顺德顺爷水产有限公司",

"uscc": "91440606MA4W7ELR4U",

"year": 2022,

"month": 6

}

]

就可以直接用定义好的实体类EnterpriseDetailForTypeBO来映射接收数据

package com.dcboot.module.manage.bo;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Data;

import java.io.Serializable;

import java.time.LocalDateTime;

/**

* @author xiaomifeng1010

* @version 1.0

* @date: 2022/8/4 14:43

* @Description

*/

@Data

public class EnterpriseDetailForTypeBO implements Serializable {

private static final long serialVersionUID = 4376512901512325623L;

private String enterpriseName;

/**

* 统一社会信用代码

*/

private String uscc;

private String createTimeStr;

private Integer year;

private Integer month;

private Integer enterpriseNum;

private String typeCode;

}

查询4

/**

* @param year

* @param month

* @description: 获取某年某月走访企业数量

* @author: xiaomifeng1010

* @date: 2022/9/3

* @return: Integer

**/

@Override

public Integer getVisitConditionStatistics(Integer year, Integer month) {

String yearStr = StringUtils.EMPTY;

String monthStr = StringUtils.EMPTY;

if (Objects.nonNull(year) && Objects.isNull(month)) {

yearStr = Convert.toStr(year);

} else if (ObjectUtils.allNotNull(year, month)) {

yearStr = Convert.toStr(year);

monthStr = String.format("%02d", month);

}

Criteria criteria = new Criteria();

MatchOperation match = null;

GroupOperation group = null;

if (StringUtils.isNotEmpty(yearStr) && StringUtils.isEmpty(monthStr)) {

match = Aggregation.match(Criteria.where("year").is(yearStr));

group = Aggregation.group("year", "enterpriseName").last("year").as("year")

.last("enterpriseName").as("enterpriseName");

} else if (!StringUtils.isAnyBlank(monthStr, yearStr)) {

match = Aggregation.match(Criteria.where("year").is(yearStr).and("month").is(monthStr));

group = Aggregation.group("year", "month", "enterpriseName").last("year").as("year")

.last("month").as("month")

.last("enterpriseName").as("enterpriseName");

} else {

match = Aggregation.match(criteria);

group = Aggregation.group("enterpriseName")

.last("enterpriseName").as("enterpriseName");

}

TypedAggregation visitObjectDeatilTypedAggregation = Aggregation.newAggregation(

VisitObjectDeatil.class,

Aggregation.project().and("visitTime").substring(0, 4).as("year")

.and("visitTime").substring(5, 2).as("month")

.and("visitObject.visitObjectName").as("enterpriseName")

.andExclude("_id"),

// 只获取指定年份的数据

match,

// 使用group去重

group,

// 统计今年已走访的不重复的企业数量(未走访的不统计,即visitTime为空的记录)

Aggregation.count().as("count")

);

AggregationResults aggregateResults = mongoTemplate.aggregate(visitObjectDeatilTypedAggregation, Map.class);

List mappedResults = aggregateResults.getMappedResults();

Integer yearOrMonthCount = 0;

if (CollectionUtils.isNotEmpty(mappedResults)) {

yearOrMonthCount = MapUtils.getInteger(mappedResults.get(0), "count");

}

return yearOrMonthCount;

}

各类的Aggregation对象是可以抽取出来的,因为有时候,查询条件是变化的,所以需要根据查询条件是否有值,来进行判断是否需要进行某种类型的聚合操作,因为mongodb的聚合管道查询类似于linux中的管道符操作,前一步的操作结果会直接影响下一步的聚合操作,所以聚合查询时,project,match,goup,count等的顺序很重要,比如你在Aggregation.newAggregation()中最先传入了project类型的聚合并给对应的字段取了别名也就是as后的名称,那么你在第二步match的时候,字段就需要使用project操作后的别名,如果还是使用mongodb中collection中的原始的字段名就会报错,就会提示无法找到reference_name,所以这就是管道操作需要注意的地方,第一步操作的结果是下一步操作时的入参参数值,所以一般情况下,需要根据你的查询需求,project,match,goup,sort,sikp,count,unwind,bucket等操作,放在Aggregation.newAggregation()中的顺序都会不一样的,对于mongodb中内置的一些字符串函数和日期函数,主要都是在project类型的聚合操作中进行的,比如上边的例子,为了取出日期中的年份,使用substring函数操作提取日期字符串的前四位,当然如果你保存日期的时候,不是使用String类型,而且DateTime类型,则可以直接使用year,month这类的函数提取,更方便

下边罗列一下,mongodb中常用的一些函数操作

字符串类型的操作:

$substr,$indexOfBytes,$switch,$strLenBytes,$toLower,$toUpper,$concat,$split,

对数字型字段可以做加减乘除,取余等操作$add,$subtract,$multiply,$divide,$mod

时间类型字段:$year,$month,$week,$hour,$minute,$second,$millsecond,$dayOfYear,$dayOfMonth,$dayOfWeek

如果你创建了多个collection,并且之间有关联关系,你想实现类似mysql的联表查询,你需啊哟使用聚合中的lookup

$lookup操作符可以查找出集合中与另一个集合天剑匹配的文档,此功能类似于关系型数据库中的join查询,此操作符需要以下参数:

from:想要关联的另一个集合

localField: 集合中需要关联的键

foreignField: 与另一个集合关联的键

as: 关联后将另外一个结合的数据嵌入到此字段下

有时候,你需要做一些报表统计,为了查询速度加快,那么你可能需要将聚合的操作放在另一个collection中,平时用定时任务去聚合查询,查询结构汇出到另一个新的collection中,这样以后每次查询只需要从新的collection中普通查询就可以了,那么你需要使用到聚合操作中的out操作符

用$out可以将聚合出来的结果写入到一个指定的结合中,如果指定的集合不存在,则会创建一个新集合,如果指定的集合已存在,则会覆写到此集合中。但如果原有的集合中存在索引,而使用$out写入文档违反此索引,则会写入失败

查看原文

发表评论

返回顶部暗黑模式