報表示例——用Jasper report實現MongoDB join,jaspermongodb
多樣性資料來源是報表開發的常見問題,但用JasperReport等報表工具本身難以處理,比如展現兩個MongoDB collection串連的結果。雖然JasperReport有virtual data source或table join,但這些功能只在商業版或高端版本出現,在免費版中實現的難度很大。而且這些功能只支援兩個資料來源的串連,要實現多串連則麻煩得多。另外,這些功能只是圖形化介面,無法對串連後的資料進行類似SQL的結構化計算。
集算器具有結構化強計算引擎,支援多樣性資料來源,整合簡單,可以協助報表工具方便地實現此類需求。下面通過一個例子來說明MongoDB join的實現過程。
Sales和emp是MongoDB中的兩個collection,sales中的欄位SellerId邏輯上相當於外鍵,指向emp的EId欄位,現在需要按時間段查詢出sales中的訂單,並和emp進行左串連,最後在報表中展現。部分來源資料如下:
Collection sales
Collectionemp
集算器指令碼:
A1=MongoDB("mongo://localhost:27017/test?user=root&password=sa")
上述代碼用來建立MongoDB的資料庫連接,可用user和password來指定使用者名稱和密碼。
集算器也支援用JDBC方式串連MongoDB,用法和普通資料庫一樣,但由於第三方JDBC不僅收費,而且功能上不如官方庫函數,比如無法擷取多層資料,因此集算器直接封裝原生方法,MongoDB的功能和文法都被保留,比如可以在此基礎上使用find函數,
A2=A1.find("sales","{'$and':[{'OrderDate':{'$gte':'"+string(begin)+"'}},{'OrderDate':{'$lte':'"+string(end)+"'}}]}","{_id:0}").fetch()
上述代碼從MongoDB的salescollection中查詢出某時間段的記錄。函數find的第一個參數是collection名,第二個參數是查詢條件,遵循MongoDB規範,第三個參數限定返回的欄位。注意查詢條件中的begin和end是來自報表的外部參數,分別表示OrderDate的起始時間和終止時間。
函數find返回的是遊標,並不會把資料直接讀入記憶體,因此支援大資料量。可以用skip、sort、conj等函數繼續操作遊標,直到遇到函數fetch、groups,或語句for時才會真正取數。本例直接用函數fetch()將資料讀入記憶體,假如時間段是2009-01-01到2009-12-31,則A2的計算結果如下:
A3=A1.find("emp",,"{_id:0}").fetch()
上述代碼從emp collection取數,無條件,除了_id之外取出所有欄位,結果如下:
A4=A1.close()
上述代碼用來關閉A1中的資料庫連接。
A5=join@1(A2:sales,SellerId;A3:emp,EId)
上述代碼將A2和A3進行左串連,串連欄位是A2的SellerId和A3 的Eld,直觀起見,串連後的兩部分資料分別命名為sales和emp。函數join執行串連計算,選項@1表示左串連,計算結果如左側:
可以看到,由於是左串連,因此sales中部分SellerId無法在emp中找到對應的記錄。如果想進行全串連,可以使用選項@f,無選項則表示內串連。
A6=A5.new(sales.OrderID:OrderID,sales.Client:Client,sales.Amount:Amount,sales.OrderDate:OrderDate,emp.Name:Name,emp.Dept:Dept,emp.Gender:Gender)
A5執行串連操作,A6則從串連的結果中取出需要的欄位,並用函數new組成二維表。比如sales.OrderID:OrderID表示從A5取出sales.OrderID欄位,重新命名為OrderID(報表工具無法識別sales.OrderID這樣的欄位名)。計算結果如下:
到此為止,報表需要的資料就全部計算出來了。最後只需用result A6將A6中的二維表返回報表工具。集算器對外提供JDBC介面,報表工具會將集算器識別為普通資料庫,整合方案請參考相關文檔。
接下來以JasperReport為例設計報表,表樣如下:
需要定義兩個報表參數Pbegin、Pend,分別對應集算器中的兩個參數。預覽後可以看到報表結果:
報表調用集算器的方法和調用預存程序一樣,比如將本指令碼儲存為mongodbJoin.dfx,則在JasperReport的SQL設計器中可以用mongodbJoin $P{pbegin},$P{pend}來調用。