MongoDB 如何对嵌套子文档分组去重计算
MongoDB的数据存储使用嵌套结构非常普遍,它通过嵌套子文档,实现一对多的关联关系。但嵌套结构分组进行子文档去重数据计算时,常需要将嵌套结构提升为扁平结构,再通过 $group 聚集运算来实现。
下面以集合 fund 为例说明,按 id 分组获取 shares、trade 子文档下的不重复 fundcode 数量。具体数据如下:
{ "id" : 1, "shares" : [ { "fundcode" : "000001", "lastshares" : 1230.2, "agencyno" : "260", "netno" : "260" }, { "fundcode" : "000002", "lastshares" : 213124, "agencyno" : "469", "netno" : "001" }, { "fundcode" : "000001", "lastshares" : 10000.8, "agencyno" : "469", "netno" : "002" } ], "trade" : [ { "fundcode" : "000001", "c_date" : "20180412", |
"agencyno" : "260", "netno" : "260", "bk_tradetype" : "122", "confirmbalance" : 1230.2, }, { "fundcode" : "000002", "c_date" : "20180506", "agencyno" : "469", "netno" : "001", "bk_tradetype" : "122", "confirmbalance" : 213124, }, { "fundcode" : "000003", "c_date" : "20190502", "agencyno" : "469", "netno" : "002", "bk_tradetype" : "122", "confirmbalance" : 10000.8, "netvalue" : 1, } ] } ……. |
用 MongoDB 脚本实现思路,用 $group 按 id 分组,提取文档下的 fundcode,再用 $unwind 分别按 shares、trade 拆解成对象,将数据扁平化,再分组时用 $addToSet 去重处理,然后对 shares、trade 下的 fundcode 计数,实现过程比较麻烦。
使用集算器, 可将 trade 子文档整理成序表后,再分组去重计数,实现比较容易。集算器安装包可去 润乾网站 下载,运行时需要一个授权,免费版本就够用。
我们将上述事例实现步骤:
1. 在集算器中编写脚本 fund.dfx:
A | B | |
1 | =mongo_open("mongodb://localhost:27017/local") | / 连接 MongDB 数据库 |
2 | =mongo_shell(A1,"fund.find(, {_id: 0})").fetch() | / 条件过滤集合 fund 数据 |
3 | =A2.trade.conj(if (#==1, t=~.fname(), t=t^~.fname())).id() | / 取 trade 子文档下的字段交集 |
4 | =A2.run( trade=trade.new( ${A3.(A3(#)).concat@c()})) | / 将共有字段数据转换成序表 |
5 | =A2.group(id; ~.conj(shares.(fundcode)).id().count(): shares,~.conj(trade.(fundcode)).id().count():trade) | / 按 id 分组,对 shares, trade 下的 fundcode 去重计数 |
6 | >A1.close() | / 关闭连接 |
A3 | Member |
agencyno | |
bk_tradetype | |
…… |
A5 | id | shares | trade |
1.0 | 4 | 6 | |
2.0 | 3 | 3 | |
… | … | … |
由于子文档 trade 下面的字段结构不一致,需要转换成结构化的序表结构,有利于后面的数据处理。
集算器提供了 JDBC 接口,脚本 fund.dfx 很容易集成到 Java 中:
public static void doWork() {
Connection con = null;
java.sql.Statement st;
try{
Class.forName("com.esproc.jdbc.InternalDriver");
con = DriverManager.getConnection("jdbc:esproc:local://");
// 调用脚本 fund.dfx
st=con.createStatement();
ResultSet rst = st.executeQuery("call fund");
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 脚本》。
英文版