MongoDB 记录的字段不同时如何合并求和
MongoDB数据库采用bson的格式存储,存储的内容是文档型的。每一条记录就是一个文档,每个文档中的键不一定相同,键的个数也不一定相等,对这样嵌套的文档结构关系,进行分组统计就比较麻烦。比如有以下数据集
{"name": "test","num": {"text": 1,"face": 2}},
{"name": "test","num": {"image": 3,"face": 4}},
{"name": "test","num": {"text":5,"image": 6, "book": 12}},
{"name": "demo","num": {"text": 3,"face": 4}},
{"name": "demo","num": {"image": 7,"face": 8}},
{"name": "demo","num": {"text":5,"image": 5}}
希望获得各产品数量,查询结果为:
{"_id" : "test", "count" : { "image" : 9, "book" : 12, "face" : 6, "text" : 6} }
{"_id" : "demo", "count" : { "image" : 12, "text" : 8, "face" : 12} }
用 MongoDB 脚本实现思路,先将 num 作为数组拆开,转换成文档分别统计,完成后重新分组过滤显示,实现比较复杂。
使用集算器,将数据整理成序表,通过序表分组求和, 再行列转换就可以,这样就方便得多了。
集算器安装包可去润乾网站下载,运行时需要一个授权,免费版本就够用。
我们将上面描述实现步骤:
1. 在集算器中编写脚本 test.dfx:
A | B | |
1 | =mongo_open("mongodb://localhost:27017/cookie ") | / 连接数据库 |
2 | =mongo_shell(A1,"test.find( {}, {_id: 0})").fetch() | / 查询集合 test |
3 | =A2.(~.(( d=num.array(), num.fname().( name|~| d(#)) )).conj()) | / 将 num 下的字段与其及对应 |
4 | =create(NAME, SNAME, NUM).record(A3.conj()) | /A3 记录存储新序表 |
5 | =A4.groups(NAME, SNAME; sum(NUM): COUNT) | / 按 NAME,SNAME 分组对 num 求和 |
6 | =A5.pivot(NAME;SNAME,COUNT) | / 进行行列转换, 将 SNAME,COUNT 行变成列 |
7 | >mongo_close(A1) | / 关闭连接 |
2. 调试执行一下,可以看到 A3 的格值为
A3 | Member |
[test,text,1.0,…] | |
[test,image,3.0,…] | |
… |
> 其它格值就不再一一列出。
3. 执行脚本返回结果:
A6 | NAME | book | face | image |
demo | (null) | 12.0 | 12.0 | |
test | 12.0 | 6.0 | 9.0 |
A3中把 NUM 下的列名与其值对应,再与 NAME 组成一条条记录, A4 将记录存入序表;对序表分组求和再进行行列转换。
集算器提供了 JDBC 接口,上述脚本 test.dfx 很容易集成到 Java 中:
public static void doTest() {
Connection con = null;
java.sql.Statement st;
try{
Class.forName("com.esproc.jdbc.InternalDriver");
con = DriverManager.getConnection("jdbc:esproc:local://");
// 调用 test.dfx 脚本
st=con.createStatement();
ResultSet rst = st.executeQuery("call test");
System.out.println(rst);
}catch(Exception e){
System.out.println(e);
}finally{
// 关闭连接
if (con!=null) {
try {
con.close();
}catch(Exception e) {
System.out.println(e);
}
}
}
}
集算器与 JAVA 集成的进一步信息可参考:《Java如何调用 SPL脚本》。
英文版