之前的文章说到MongoDB聚合查询实现多表联查。其实是还是有一些遗留问题的,这个文章就是在之前的文章上做个补存。咱们通过MySql和MongoDB做个对比。
单条件分组求和
MySql数据库
java代码此处就忽略了
类似MySql的失去了语句如下
select goodsName,sum(net)from weightingDataDetail group by goodsName
sql执行成功后返回的数据应该是
goodsName | net |
---|---|
物料1 | 300 |
物料2 | 500 |
是可以直接映射到对象返回给前端直接渲染的。
MongoDB数据库
Java代码
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName")//设置分组字段
.sum("net").as("net"),
project("goodsName","net")
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
MongoDB这时候的sql为:
db.weightingDataDetail.aggregate( [{ "$group": { "_id":"$goodsName", "net": { "$sum": "$net" } } }, { "$project": { "goodsName": "$_id.goodsName", "net": 1, } }] )
sql执行成功后返回的数据是
_id | net |
---|---|
物料1 | 300 |
物料2 | 500 |
这时候映射到对象上物料在id上,这样前端是不能直接渲染,而且也不符合现实所需要的,但多条件分组时,就不会映射在id上了,很坑。
需要把mongDB的sql改为:
db.weightingDataDetail.aggregate( [{ "$group": { "_id": { "goodsName": "$goodsName", }, "net": { "$sum": "$net" } } }, { "$project": { "goodsName": "$_id.goodsName", "net": 1, } }] )
这时候运行结果就对了:
_id | net | goodsName |
---|---|---|
Document | 300 | 物料1 |
Document | 500 | 物料2 |
这样才能完全映射到对象上。
怎么才能用java构建出
“_id”: {
“goodsName”: “$goodsName”,
}
这种条件,就成了关键。
于是通过“曲线救国”的方式找到了如下办法:
方式一
将"_id"起个别名,换成要返回的字段。
sql如下:
db.weightingDataDetail.aggregate( [{ "$group": { "_id": "$goodsName", "net": { "$sum": "$net" } } }, { "$project": { "goodsName": "$_id", "net": "$net" } }] )
运行结果如下
_id | net | goodsName |
---|---|---|
物料1 | 300 | 物料1 |
物料2 | 500 | 物料2 |
Java代码如下
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName")//设置分组字段
.sum("net").as("net"),
project("goodsName","net")
.andExpression("_id").as("goodsName") //将id起别名
.andExpression("net").as("net")
//或者这样起别名
.and("_id").as("goodsName")
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
起别名还可以使用previousOperation,一般配合and使用,这里就不展示sql了;官方解释:
选择n字段并为ID字段创建一个别名,该别名是由前一个组操作(因此调用previousOperation())生成的,其名称为标记。
java代码如下
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName")//设置分组字段
.sum("net").as("net"),
project("goodsName","net").and("goodsName").previousOperation()
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
方式二
将分组条件变为多条件,分组一个压根不存在的条件。
sql如下:
db.weightingDataDetail.aggregate( [ { "$group": { "_id": { "goodsName": "$goodsName", "1": "$1" }, "net": { "$sum": "$net" } } }, { "$project": { "goodsName": "$_id.goodsName", "net": 1, "1": "$_id.1" } }] )
这样也是满足的,但是在MySql中这么做肯定是会报错的,不知道这个"1"是个啥。
_id | net | goodsName |
---|---|---|
Document | 300 | 物料1 |
Document | 500 | 物料2 |
java代码如下:
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName","1")//设置分组字段
.sum("net").as("net"),
project("goodsName","net")
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
方式三
可以使用first和last,前提是不适用sort的前提下,不适用排序,first和$last,会默认取出所有的,也会指定到返回的字段上。
$frist
返回将表达式应用到按键共享同一组文档的一组文档中的第一个文档所得到的值。仅在文档按定义的顺序排列时才有意义。
$last
返回将表达式应用于在一组按字段共享同一组文档的最后一个文档中得出的值。仅在文档按定义的 Sequences 排列时才有意义。
sql如下:
db.weightingDataDetail.aggregate( [{ "$group": { "_id": "$goodsName", "net": { "$sum": "$net" }, "goodsName": { "$first": "$goodsName" // "$last": "$goodsName" } } }, { "$project": { "goodsName": 1, "net": 1 } }] )
_id | net | goodsName |
---|---|---|
Document | 300 | 物料1 |
Document | 500 | 物料2 |
java代码如下:
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName")//设置分组字段
.sum("net").as("net")
.first("goodsName").as("goodsName"),
project("goodsName","net")
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
其实这三种方式我倾向第一种起别名。
返回指定字段特殊情况
多返回一个字段,前提是这个字段在分组条件里是唯一的,比如goodsName对应的orderType只有一种,不会出现一种goodsName有俩个orderType。
MySql sql如下
select goodsName,orderType,sum(net)from weightingDataDetail group by goodsName
值得注意的是mongo要像MySql这样返回分组以外的字段,这个字段必须在group条件下,才能作为返回条件。
可以使用返回指定字段方式三,不排序的情况下多加一种,也是可以的。
mongo sql如下,
db.weightingDataDetail.aggregate( [{ "$group": { "_id": "$goodsName", "net": { "$sum": "$net" }, "orderType": { "$first": "$orderType" // "$last": "$goodsName" } } }, { "$project": { "goodsName": 1, "net": 1, "orderType":1 } }] )
_id | net | orderType |
---|---|---|
物料1 | 300 | 1 |
物料2 | 500 | 2 |
结合一二种方式就可以返回指定的字段。
java代码如下
@Test
public void sum() {
Aggregation agg = null;
agg = Aggregation.newAggregation(
group("goodsName")//设置分组字段
.sum("net").as("net")
.first("orderType").as("orderType"),
project("goodsName","net","orderType")
.and("_id").as("goodsName")
.and("orderType").as("orderType")
);
AggregationResults<WeightingDataDetail> results = mongoTemplate.aggregate(agg,
"weightingDataDetail", WeightingDataDetail.class);
WeightingDataDetail weightingDataDetail = results.getMappedResults().get(0);
System.err.println(JSON.toJSONString(weightingDataDetail));
}
MongoDB是很强大的,我只是用了其中一种方式去构建聚合查询,它还有别的构建方式,后续再一点点学习。