簡化MongoDB關(guān)聯(lián)運算-創(chuàng)新互聯(lián)

MongoDB屬于 NoSql 中的基于分布式文件存儲的文檔型數(shù)據(jù)庫,這種bson格式的文檔結(jié)構(gòu),更加貼近我們對物體各方面的屬性描述。而在使用 MongoDB 存儲數(shù)據(jù)的過程中,有時候難免需要進行關(guān)聯(lián)表查詢。自從 MongoDB 3.2 版本后,它提供了 $lookup 進行關(guān)聯(lián)表查詢,讓查詢功能改進了不少。但在實現(xiàn)應用場景中,所遇到的環(huán)境錯綜復雜,問題解決也非易事,腳本書寫起來也并不簡單。好在有了集算器 SPL 語言的協(xié)助,處理起來就相對容易多了。
        本文我們將針對 MongoDB 在關(guān)聯(lián)運算方面的問題進行討論分析,并通過集算器 SPL 語言加以改進,方便用戶使用 MongoDB。討論將分為以下幾個部分:
1. 關(guān)聯(lián)嵌套結(jié)構(gòu)情況 1…………………………………………….. 1
2. 關(guān)聯(lián)嵌套結(jié)構(gòu)情況 2…………………………………………….. 3
3. 關(guān)聯(lián)嵌套結(jié)構(gòu)情況 3…………………………………………….. 4
4. 兩表關(guān)聯(lián)查詢………………………………………………………. 6
5. 多表關(guān)聯(lián)查詢………………………………………………………. 8
6. 關(guān)聯(lián)表中的數(shù)組查找…………………………………………… 10
Java 應用程序調(diào)用 DFX 腳本…………………………………… 12

十多年的平樂網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應快,48小時及時工作處理。成都全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整平樂建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)公司從事“平樂網(wǎng)站設(shè)計”,“平樂網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。
1.關(guān)聯(lián)嵌套結(jié)構(gòu)情況1

兩個關(guān)聯(lián)表,表 A 與表 B 中的內(nèi)嵌文檔信息關(guān)聯(lián), 且返回的信息在內(nèi)嵌文檔中。表 childsgroup 字段 childs 是嵌套數(shù)組結(jié)構(gòu),需要合并的信息 name 在其下。

測試數(shù)據(jù):

history:

_ididHistorychild_id
1001today workedch001
2002Workingch004
3003now workingch009

childsgroup:

_idgidnamechilds
1g001group1{"id":"ch001","info":{"name":"a",mobile:1111}},{"id":"ch002","info":{"name":"b",mobile:2222}}
2g002group1{"id":"ch004","info":{"name":"c",mobile:3333}},{"id":"ch009","info":{"name":"d",mobile:4444}}

表History中的child_id與表childsgroup中的childs.id關(guān)聯(lián),希望得到下面結(jié)果:

{
   "_id" : ObjectId("5bab2ae8ab2f1bdb4f434bc3"),
   "id" : "001",
   "history" : "today worked",
   "child_id" : "ch001",
   "childInfo" :  
   {
        "name" : "a",
       " mobile" :  1111
   }
   ………………
}

Mongo 腳本

db.history.aggregate([
   {$lookup: {
      from:  "childsgroup",
      let: {child_id:  "$child_id"},
      pipeline: [
          {$match: {  $expr: { $in: [ "$$child_id", "$childs.id"] } } },
          {$unwind:  "$childs"},
          {$match: {  $expr: { $eq: [ "$childs.id", "$$child_id"] } } },
          {$replaceRoot: {  newRoot: "$childs.info"} }
          ],
          as:  "childInfo"
      }},
 {"$unwind": "$childInfo"}
])

       這個腳本用了幾個函數(shù)lookup、pipeline、match、unwind、replaceRoot處理,一般 mongodb 用戶不容易寫出這樣復雜腳本;那么我們再看看 spl 腳本是如何實現(xiàn)的:

SPL腳本 ( 文件名:childsgroup.dfx)


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"history.find()").fetch()
3=mongo_shell(A1,"childsgroup.find()").fetch()
4=A3.conj(childs)
5=A2.join(child_id,A4:id,info)
6>A1.close()

關(guān)聯(lián)查詢結(jié)果:

_ididhistorychild_idinfo
1001today workedch001[a,1111]
2002workingch004[c,3333]
3003now workingch009[d,4444]

腳本說明:
      A1:連接 mongodb 數(shù)據(jù)庫。
      A2:獲取 history 表中的數(shù)據(jù)。
      A3:獲取 childsgroup 表中的數(shù)據(jù)。
      A4:將 childsgroup 中的 childs 數(shù)據(jù)提取出來合并成序表。
      A5:表 history 中的 child_id 與表 childs 中的 id 關(guān)聯(lián)查詢,追加 info 字段, 返回序表。
      A6:關(guān)閉數(shù)據(jù)庫連接。

      相對 mongodb 腳本寫法,SPL 腳本的難度降低了不少,思路也更加清晰,也不需要再去熟悉有關(guān) mongo 函數(shù)的用法,以及如何去組合處理數(shù)據(jù)等,節(jié)約了不少時間。

2.關(guān)聯(lián)嵌套結(jié)構(gòu)情況 2

兩個關(guān)聯(lián)表,表 A 與表 B 中的內(nèi)嵌文檔信息關(guān)聯(lián), 將信息合并到內(nèi)嵌文檔中。表 txtPost 字段 comment 是嵌套數(shù)組結(jié)構(gòu),需要把 comment_content 合并到其下。

txtComment:

_IDcomment_nocomment_content
1143test test
2140math

txtPost

_IDpost_noComment
148[{"comment_no"  : 143, "comment_group" : 1} ]
247[{"comment_no"  : 140, "comment_group" : 2},
 {"comment_no" : 143, "comment_group" : 3} ]

期望結(jié)果:

_IDpost_noComment
148[{"comment_no"  : 143, "comment_group" : 1,"comment_content" : "test test"} ]
247[{"comment_no"  : 140, "comment_group" : 2,"comment_content" : "math"},
 {"comment_no" : 143, "comment_group" : 3,"comment_content" :  "test test"} ]

Mongo 腳本

db.getCollection("txtPost").aggregate([
 { "$unwind": "$comment"},
 {  "$lookup": {

    "from": "txtComment",
    "localField": "comment.comment_no",
    "foreignField": "comment_no",
    "as": "comment.comment_content"
 }},
 { "$unwind": "$comment.comment_content"},
 {  "$addFields": { "comment.comment_content":
 "$comment.comment_content.comment_content" }},
 {  "$group": {
    "_id": "$_id",
    'post_no':{"$first": "$post_no"},
    "comment": {"$push": "$comment"}
    }},
 ]).pretty()

表txtPost 按 comment 拆解成記錄,然后與表 txtComment 關(guān)聯(lián)查詢,將其結(jié)果放到數(shù)組中,再將數(shù)組拆解成記錄,將comment_content 值移到 comment 下,最后分組合并。

SPL 腳本:


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"txtPost.find()").fetch()
3=mongo_shell(A1,"txtComment.find()").fetch()
4=A2.conj(comment.derive(A2.post_no:pno))
5=A4.join(comment_no,A3:comment_no,comment_content:Content)
6=A5.group(pno;~:comment)
7>A1.close()

關(guān)聯(lián)查詢結(jié)果:

pnoComment
47[[ 140, 2,47, …],[143, 3,47, …] ]
48[[143, 1,48, …]]

腳本說明:
      A1:連接 mongodb 數(shù)據(jù)庫。
      A2:獲取 txtPost 表中的數(shù)據(jù)。
      A3:獲取 txtComment 表中的數(shù)據(jù)。
      A4:將序表 A2 下的 comment 與 post_no 組合成序表,其中 post_no 改名為 pno。
      A5:序表 A4 通過 comment_no 與序表 A3 關(guān)聯(lián),追加字段 comment_content,將其改名為 Content。
      A6:按 pno 分組返回序表,~ 表示當前記錄。
      A7:關(guān)閉數(shù)據(jù)庫連接。

      Mongo、SPL 腳本實現(xiàn)方式類似,都是把嵌套結(jié)構(gòu)的數(shù)據(jù)轉(zhuǎn)換成行列結(jié)構(gòu)的數(shù)據(jù),再分組合并。但 SPL 腳本的實現(xiàn)更簡單明了。

3.關(guān)聯(lián)嵌套結(jié)構(gòu)情況 3

兩個關(guān)聯(lián)表,表 A 與表 B 中的內(nèi)嵌文檔信息關(guān)聯(lián), 且返回的信息在記錄上。表 collection2 字段 product 是嵌套數(shù)組結(jié)構(gòu),返回的信息是 isCompleted 等字段 。

測試數(shù)據(jù):
collection1:
{
  _id: '5bc2e44a106342152cd83e97',
  description
   {
     status: 'Good',
     machine: 'X'
    },
  order: 'A',
  lot: '1'
  };
  
collection2:
{
  _id: '5bc2e44a106342152cd83e80',
  isCompleted: false,
  serialNo: '1',
  batchNo: '2',
  product: [ // note the subdocuments here
       {order: 'A', lot: '1'},
       {order: 'A', lot: '2'}
   ]
}

期待結(jié)果
{
  _id: 5bc2e44a106342152cd83e97,
  description:
      {
        status: 'Good',
        machine: 'X',
      },
  order: 'A',
  lot: '1' ,
  isCompleted: false,
  serialNo: '1',
  batchNo: '2'
}

Mongo 腳本

db.collection1.aggregate([{
      $lookup:  {

             from:  "collection2",
             let:  {order: "$order", lot: "$lot"},
             pipeline:  [{
                    $match:  {
                    $expr:{ $in: [ { order: "$$order", lot: "$$lot"},  "$product"] }
                    }  
                    }],  
                    as:  "isCompleted"
                    }  
             },  {
                    $addFields:  {
                    "isCompleted":  {$arrayElemAt: [ "$isCompleted", 0] }
                    }  
             },  {
                    $addFields:  { // add the required fields to the top level structure
                    "isCompleted":  "$isCompleted.isCompleted",
                    "serialNo":  "$isCompleted.serialNo",
                    "batchNo":  "$isCompleted.batchNo"
             }  
}])

lookup 兩表關(guān)聯(lián)查詢,首個 addFields獲取isCompleted數(shù)組的第一個記錄,后一個addFields 轉(zhuǎn)換成所需要的幾個字段信息

SPL腳本:


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"collection1.find()").fetch()
3=mongo_shell(A1,"collection2.find()").fetch()
4=A3.conj(A2.select(order:A3.product.order,lot:A3.product.lot).derive(A3.serialNo:sno,A3.batchNo:bno))
5>A1.close()

腳本說明:
      A1:連接 mongodb 數(shù)據(jù)庫。
      A2:獲取 collection1 表中的數(shù)據(jù)。
      A3:獲取 collection2 表中的數(shù)據(jù)。
      A4:根據(jù)條件 order, lot 從序表 A2 中查詢記錄,然后追加序表 A3 中的字段 serialNo, batchNo,返回合并后的序表。
      A5:關(guān)閉數(shù)據(jù)庫連接。

      Mongo、SPL 腳本都實現(xiàn)了預期的結(jié)果。SPL 很清晰地實現(xiàn)了從數(shù)據(jù)記錄中的內(nèi)嵌結(jié)構(gòu)中篩選,將符合條件的數(shù)據(jù)合并成新序表。


4.兩表關(guān)聯(lián)查詢

從關(guān)聯(lián)表中選擇所需要的字段組合成新表。

Collection1:

user1user2income
120.56
130.26

 collection2:

user1user2output
120.3
130.4
230.5

期望結(jié)果:

user1user2incomeoutput
120.560.3
130.260.4

Mongo 腳本

db.c1.aggregate([
   {  "$lookup": {
    "from": "c2",
        "localField": "user1",
        "foreignField": "user1",
        "as": "collection2_doc"
    }},
   { "$unwind": "$collection2_doc"},
   {  "$redact": {
        "$cond": [
            {"$eq": [ "$user2",  "$collection2_doc.user2"] },
            "$$KEEP",
            "$$PRUNE"
        ]
    }},
   {  "$project": {
        "user1": 1,
        "user2": 1,
        "income": "$income",
        "output":  "$collection2_doc. output"
    }}
    ]).pretty()

lookup 兩表進行關(guān)聯(lián)查詢,redact 對記錄根據(jù)條件進行遍歷處理,project 選擇要顯示的字段。

SPL腳本:


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"c1.find()").fetch()
3=mongo_shell(A1,"c2.find()").fetch()
4=A2.join(user1:user2,A3:user1:user2,output)
5>A1.close()

腳本說明:
      A1:連接 mongodb 數(shù)據(jù)庫。
      A2:獲取c1表中的數(shù)據(jù)。
      A3:獲取c2表中的數(shù)據(jù)。
      A4:兩表按字段 user1,user2 關(guān)聯(lián),追加序表 A3 中的 output 字段,返回序表。
      A5:關(guān)閉數(shù)據(jù)庫連接。

      Mongo、SPL 腳本都實現(xiàn)了預期的結(jié)果。SPL 通過 join 把兩個關(guān)聯(lián)表不同的字段合并成新表,與關(guān)系數(shù)據(jù)庫用法類似。

5.多表關(guān)聯(lián)查詢

多于兩個表的關(guān)聯(lián)查詢,結(jié)合成一張大表。

Doc1:

_idfirstNamelastName
U001shubhamverma

 Doc2:

_iduserIdaddressmob
2U001Gurgaon9876543200

 Doc3:

_iduserIdfbURLstwitterURLs
3U001http://www.facebook.comhttp://www.twitter.com

合并后的結(jié)果:
{
   "_id" : ObjectId("5901a4c63541b7d5d3293766"),
   "firstName" : "shubham",
   "lastName" : "verma",
   "address" : {
       "address" : "Gurgaon"
   },
   "social" : {
       "fbURLs" : "http://www.facebook.com",
       "twitterURLs" : "http://www.twitter.com"
   }
}


Mongo 腳本

db.doc1.aggregate([
   {$match:  { _id: ObjectId("5901a4c63541b7d5d3293766") } },
   {
       $lookup:
       {
            from: "doc2",
            localField: "_id",
            foreignField: "userId",
            as: "address"
       }
   },
   {
        $unwind: "$address"
   },
   {
        $project: {
            "address._id": 0,
            "address.userId": 0,
            "address.mob": 0
       }
   },
   {
        $lookup:
       {
            from: "doc3",
            localField: "_id",
            foreignField: "userId",
            as: "social"
       }
   },
   {
        $unwind: "$social"
   },
 
 {  
   $project:  {     
           "social._id": 0,     
           "social.userId": 0
      }
 }
]).pretty();

      由于 Mongodb 數(shù)據(jù)結(jié)構(gòu)原因,寫法也多樣化,展示也各不相同。

SPL腳本:


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"doc1.find()").fetch()
3=mongo_shell(A1,"doc2.find()").fetch()
4=mongo_shell(A1,"doc3.find()").fetch()
5=A2.join(_id,A3:userId,address,mob)
6=A5.join(_id,A4:userId,fbURLs,twitterURLs)
7>A1.close()

      Mongo、SPL 腳本都實現(xiàn)了預期的結(jié)果。此 SPL 腳本與上面例子類似,只是多了一個關(guān)聯(lián)表,每次 join 就新增加字段,最后疊加構(gòu)成一張大表。

      SPL 腳本的簡潔性、統(tǒng)一性非常明顯。

6.關(guān)聯(lián)表中的數(shù)組查找

從關(guān)聯(lián)表記錄數(shù)據(jù)組中查找符合條件的記錄, 用給定的字段組合成新表。

測試數(shù)據(jù):

users:

_idNameworkouts
1000xxx[2,4,6]
1002yyy[1,3,5]

workouts:

_idDateBook
11/1/2001Othello
22/2/2001A Midsummer  Night's Dream
33/3/2001The Old  Man and the Sea
44/4/2001GULLIVER’S  TRAVELS
55/5/2001Pickwick  Papers
66/6/2001The Red  and the Black

期望結(jié)果:

Name_idDateBook
xxx22/2/2001A  Midsummer Night's Dream
xxx44/4/2001GULLIVER’S  TRAVELS
xxx66/6/2001The Red  and the Black
yyy11/1/2001Othello
yyy33/3/2001The Old  Man and the Sea
yyy55/5/2001Pickwick  Papers

Mongo 腳本

db.users.aggregate([
 { "$lookup": {

   "from" :  "workouts",
   "localField" :  "workouts",
   "foreignField" :  "_id",
   "as" :  "workoutDocumentsArray"
 }},
 {$project: {  _id:0,workouts:0} } ,
 {"$unwind":  "$workoutDocumentsArray"},
 {"$replaceRoot": {  "newRoot": { $mergeObjects:  [ "$$ROOT", "$workoutDocumentsArray"] } } },
 {$project: {  workoutDocumentsArray: 0} }
 ]).pretty()

把關(guān)聯(lián)表 users,workouts 查詢結(jié)果放到數(shù)組中,再將數(shù)組拆解,提升子記錄的位置,去掉不需要的字段。

SPL腳本 (users.dfx):


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"users.find()").fetch()
3=mongo_shell(A1,"workouts.find()").fetch()
4=A2.conj(A3.select(A2.workouts^~.array(_id)!=[]).derive(A2.name))
5>A1.close()

腳本說明:
      A1:連接 mongodb 數(shù)據(jù)庫。
      A2:獲取users表中的數(shù)據(jù)。
      A3:獲取workouts表中的數(shù)據(jù)。
      A4:查詢序表 A3 的 _id 值存在于序表A2中 workouts 數(shù)組的記錄, 并追加 name 字段。返回合并的序表。
      A5:關(guān)閉數(shù)據(jù)庫連接。
      由于需要獲取序列的交集不為空為條件,故將 _id 轉(zhuǎn)換成序列。
      Mongo、SPL 腳本都實現(xiàn)了預期的結(jié)果。從腳本實現(xiàn)過程來看,SPL 集成度高而又不失靈活性,讓程序簡化了不少。

7.Java 應用程序調(diào)用 DFX 腳本

      在通過 SPL 腳本對 MongoDB 數(shù)據(jù)進行了關(guān)聯(lián)計算后,其結(jié)果可以被 java 應用程序很容易地使用。集算器提供了 JDBC 驅(qū)動程序,用 JDBC 存儲過程方式訪問,與調(diào)用存儲過程相同。(JDBC 具體配置參考《集算器教程》中的“ JDBC 基本使用”章節(jié) )
   Java 調(diào)用主要過程如下:
   public void testUsers(){
      Connection con = null;
      com.esproc.jdbc.InternalCStatement st;
       try{
        // 建立連接
        Class.forName("com.esproc.jdbc.InternalDriver");
        con= DriverManager.getConnection("jdbc:esproc:local://");
        // 調(diào)用存儲過程,其中 users 是 dfx 的文件名
        st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call users>  ()");
        // 執(zhí)行存儲過程
        st.execute();
        // 獲取結(jié)果集
        ResultSet rs = st.getResultSet();
         。。。。。。。
   catch(Exception e){
        System.out.println(e);
   }
       可以看到,使用時按標準的 JDBC 方法操作,集算器很方便嵌入到 Java 應用程序中。同時,集算器也支持 ODBC 驅(qū)動,因此集成到其它支持 ODBC 的語言也非常容易。

       Mongo 存儲的數(shù)據(jù)結(jié)構(gòu)相對關(guān)系數(shù)據(jù)庫更復雜、更靈活,其提供的查詢語言也非常強、適應面廣,同時需要了解函數(shù)也不少,函數(shù)之間的結(jié)合更是變化無窮,因此要熟練掌握并應用也并非易事。集算器的離散性、易用性恰好能彌補 Mongo 這方面的不足,在降低 mongo 學習成本及使用復雜度、難度的同時,讓 mongo 的功能得到更充分的展現(xiàn)。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

網(wǎng)站題目:簡化MongoDB關(guān)聯(lián)運算-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://muchs.cn/article34/dsgese.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、ChatGPT、網(wǎng)站內(nèi)鏈、標簽優(yōu)化、做網(wǎng)站App設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁設(shè)計公司