因?yàn)槟繕?biāo)站點(diǎn)返回的數(shù)據(jù)是json格式,一方面為了學(xué)習(xí)新知識(shí),另一方面是想偷個(gè)懶,所以直接使用Mongdb保存爬蟲結(jié)果。
最近有個(gè)新需求:使用Data-V對(duì)這些存量數(shù)據(jù)制作數(shù)據(jù)大屏,但目前Data-V支持的數(shù)據(jù)庫(kù)只有關(guān)系型數(shù)據(jù)庫(kù)(當(dāng)然也可以寫API來給Data-V投喂數(shù)據(jù),但是如此好像更麻煩些),因此需要將數(shù)據(jù)從Mongodb 遷移到 MySQL。
最樸素的想法,當(dāng)然是用Python寫一段腳本,從Mongodb讀取需求的字段,然后再逐條插入MySQL中。這個(gè)方法肯定可以獲得預(yù)期的效果,預(yù)計(jì)100行以內(nèi)的代碼都能完成。但是,MongoDB和MySQL既然已經(jīng)是很成熟的產(chǎn)品了,對(duì)數(shù)據(jù)遷移應(yīng)該有更好的解決方案,自己再造一遍輪子實(shí)在不是個(gè)好主意。
尋找新方法
之前查看MongoDB的根目錄mongodb安裝路徑\bin發(fā)現(xiàn)有一個(gè)叫做mongoexport.exe的文件,第一眼就覺得是想要找的功能。
> mongoexport.exe --help
> Usage:
mongoexport <options>
Export data from MongoDB in CSV or JSON format.
See http://docs.mongodb.org/manual/reference/program/mongoexport/ for more information.
利用這個(gè)工具可以將查詢結(jié)果導(dǎo)出為CSV或者JSON格式,MySQL的數(shù)據(jù)導(dǎo)入工具剛好可以直接導(dǎo)入以上兩種格式的數(shù)據(jù)??磥磉@應(yīng)該就是我們需要的路徑了。
mongoexport.exe的參數(shù)挺多,按照上一段代碼的方法可以仔細(xì)查看,我使用的幾個(gè)參數(shù)如下:
> mongoexport.exe /uri:mongodb://username:password@host:port/database /c collection /f field /o resultPATH /jsonArray
/uri 導(dǎo)出數(shù)據(jù)的mongodb的uri,username填用戶名,password密碼,host IP, port 端口,database 驗(yàn)證用戶名、密碼的數(shù)據(jù)庫(kù)。
/c 想要導(dǎo)出的Collections
/f 需要導(dǎo)出的字段
/o 導(dǎo)出文件的路徑及文件名
/jsonArray 導(dǎo)出json數(shù)組而非jsonline
如果需要查詢,可以使用 \q參數(shù)輸入查詢語(yǔ)句。接下來,使用MySQL的文件導(dǎo)入工具即可將數(shù)據(jù)導(dǎo)入到MySQL中,速度還是挺有保障的。
這種方法針對(duì)形如{ "_id" : 0, "nref_date" : "20190318-20190416", "nname" : "未知", "nvalue" : 1 }的數(shù)據(jù)是比較有效的,但涉及到擁有嵌套數(shù)組的數(shù)據(jù)就顯得有些難以處理了。
例如:

如何將上圖中l(wèi)ist[0]的item_list中的5對(duì)(key, value)添加ref_date存儲(chǔ)成MySQL中的5條記錄?
新的挑戰(zhàn)
之前看文檔的時(shí)候了解到,MongoDB有聚合操作(aggregate),也支持管道(pipeline)操作。將數(shù)組解包使用的是"$unwind"操作,嵌套的數(shù)組用點(diǎn)號(hào)分隔即可,還是先回顧下常用的一些聚合操作吧:
$project:修改輸入文檔結(jié)構(gòu),可用來新增、刪除域,或者構(gòu)建新結(jié)果。
$match:輸出符合條件的文檔,可以用來過濾樹。
$limit:用來限制管道返回的文檔數(shù)。
$skip:在聚合管道中跳過指定數(shù)量的文檔。
$unwind:將文檔中的某一個(gè)數(shù)組類型字段拆分成多條,每條包含數(shù)組中的一個(gè)值。
$group:將集合中的文檔分組。
$sort:排序。
$out:將管道聚合操作返回的結(jié)果另存成一個(gè)collections,必須位于管道的最后一個(gè)操作。
本例中可以使用將list.item_list解包,注意因?yàn)閘ist也是數(shù)組,所以使用了兩次$unwind:
db.collections.aggregate([{"$unwind": "$list"}, {"$unwind": "$list.item_list"})
執(zhí)行過后,得到如下形狀的5個(gè)文檔:
{
"_id" : ObjectId("5cb595a2923d072acc8bdf8b"),
"ref_date" : "20181129",
"list" : {
"index" : "access_source_session_cnt",
"item_list" : {
"key" : 36,
"value" : 19
}
}
}
但輸出的形式,和我們想要的結(jié)果還不太一樣,如果直接使用MySQL的導(dǎo)入工具還是無(wú)法找到對(duì)應(yīng)字段的映射關(guān)系。
接下來使用$project修改返回文檔的結(jié)構(gòu),直接在上面的管道中新增
db.collections.aggregate([{"$unwind": "$list"}, {"$unwind": "$list.item_list"}, {"$project": {"newkey": "$list.item_list.key", "newvalue": "$list.item_list.value", "new_ref_date": "$ref_date"}}])
輸出的第一個(gè)元素變成以下結(jié)構(gòu):
{
"_id" : ObjectId("5cb595a2923d072acc8bdf8b"),
"newkey" : 29,
"newvalue" : 17,
"new_ref_date" : "20181129"
}
如果使用的MongoDB支持將查詢結(jié)果導(dǎo)出成JSON,那到這里已經(jīng)可以完成任務(wù)了!遺憾的是我用的Robot 3T貌似并不支持,不過也沒有關(guān)系,我們使用$out操作符將管道的聚合結(jié)果另存成一個(gè)Collections即可。
最終的管道聚合操作有四步操作:
db.collections.aggregate([
{"$unwind": "$list"},
{"$unwind": "$list.item_list"},
{"$project": {"newkey": "$list.item_list.key", "newvalue": "$list.item_list.value", "new_ref_date": "$ref_date"}},
{"$out": "outputjson"}
])
接下來使用mongoexport.exe工具將outputjson導(dǎo)出成JSON格式即可導(dǎo)入MySQL了!
MongoDB多層嵌套腦殼疼、查個(gè)文檔又盡是英文、阿里云Data-v居然都不支持,以后不會(huì)再用了?。。?br>
臥槽原來可以這樣!真香!