2017-01-16 219 views
1

我使用了MongoDB幾個月但基本用法,也許這是我第一次嘗試使用更復雜的函數。在MongoDB中合併兩個集合

我有兩個類別:

集 '交易':

{ "_id" : ObjectId("5878ec66ad251f4fb4d2aacc"), "clubCode" : "43W0K", "date" : ISODate("2017-01-13T15:03:52.410Z"), "symbol" : "ENGI.PA", "name" : "ENGIE SA", "buyOrSell" : "buy", "orderDone" : true, "quantity" : 12, "price" : 11.99, "fees" : 0.99, "total" : 144.87, "__v" : 0 } 
{ "_id" : ObjectId("5878f8c339b47f0ee4a3b80b"), "clubCode" : "43W0K", "date" : ISODate("2017-01-13T15:56:32.088Z"), "symbol" : "PRIO.PA", "name" : "Lyxor ETF PEA Brazil (Ibovespa) C-EUR", "buyOrSell" : "buy", "orderDone" : true, "quantity" : 56, "price" : 8.92, "fees" : 0.99, "total" : 500.51, "__v" : 0 } 
{ "_id" : ObjectId("5878fadf39b47f0ee4a3b80c"), "clubCode" : "43W0K", "date" : ISODate("2017-01-13T16:05:35.849Z"), "symbol" : "ALVIV.PA", "name" : "Visiativ SA", "buyOrSell" : "buy", "orderDone" : true, "quantity" : 10, "price" : 18.15, "fees" : 0.99, "total" : 182.49, "__v" : 0 } 
{ "_id" : ObjectId("587a03319e3fe23138119937"), "clubCode" : "43W0K", "date" : ISODate("2017-01-14T10:52:56.208Z"), "symbol" : "BIG.PA", "name" : "BigBen Interactive", "buyOrSell" : "buy", "orderDone" : false, "orderType" : "ACL", "quantity" : 83, "price" : 6.01, "fees" : 0.99, "total" : 499.82, "__v" : 0 } 

收集 '訂閱':

{ "_id" : ObjectId("587c946f3aa3f229a0922761"), "email" : "[email protected]", "clubCode" : "43W0K", "period" : ISODate("2016-09-01T22:00:00Z"), "amount" : 100, "type" : "recurrent", "__v" : 0 } 
{ "_id" : ObjectId("587c946f3aa3f229a0922762"), "email" : "[email protected]", "clubCode" : "43W0K", "period" : ISODate("2016-09-01T22:00:00Z"), "amount" : 100, "type" : "recurrent", "__v" : 0 } 
{ "_id" : ObjectId("587c946f3aa3f229a0922763"), "email" : "[email protected]", "clubCode" : "43W0K", "period" : ISODate("2016-09-01T22:00:00Z"), "amount" : 100, "type" : "recurrent", "__v" : 0 } 

每個都涉及到一個clubCode(這裏 '43W0K')。

我想有這樣的結果(在另一個集合,或只是一個彙總):

{ 
    clubCode: "43W0K", 
    treasury_moves: [ 
     { 
      date: ISODate("2017-01-13T15:03:52.410Z"), 
      wording: "Achat ENGIE SA", 
      amount: 144.87 
     }, { 
      date: ISODate("2017-01-13T15:56:32.088Z"), 
      wording: "Achat Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
      amount: 500.51 
     }, { 
      date: ISODate("2017-01-13T16:05:35.849Z"), 
      wording: "Achat Visiativ SA", 
      amount: 182.49 
     }, { 
      date: ISODate("2017-01-14T10:52:56.208Z"), 
      wording: "Achat BigBen Interactive", 
      amount: 499.82 
     }, { 
      date: ISODate("2016-09-01T22:00:00Z"), 
      wording: "Subscription [email protected]", 
      amount: 100 
     }, { 
      date: ISODate("2016-09-01T22:00:00Z"), 
      wording: "Subscription [email protected]", 
      amount: 100 
     }, { 
      date: ISODate("2016-09-01T22:00:00Z"), 
      wording: "Subscription [email protected]", 
      amount: 100 
     } 
    ] 
} 

所以之間的「交易」和「訂閱」集合合併,

  • date等於'交易'的$date或'訂閱'的$period
  • 其中wording等於Achat $name'交易'或Subscription $email的「訂閱」
  • amount結果等於$total,如果它在「交易」,或者$amount如果它在「訂閱」

首先,是有可能做到這一點?用mapreduce?聚合?

我期待着this topic這似乎相似,但我沒有成功,我想要的結果。

如果您可以幫助我或只是指示我遵循的方式。

回答

1

可能通過aggregate()函數在聚合框架中。您需要運行下面的管道,以獲得期望的結果:

db.trades.aggregate([ 
    { 
     "$lookup": { 
      "from": "subscriptions", 
      "localField": "clubCode", 
      "foreignField": "clubCode", 
      "as": "subs" 
     } 
    }, 
    { "$unwind": "$subs" }, 
    { 
     "$group": { 
      "_id": "$clubCode", 
      "trades": { 
       "$push": { 
        "date": "$date", 
        "wording": { "$concat": ["Achat ", "$name"] }, 
        "amount": "$total" 
       } 
      }, 
      "subs": { 
       "$push": { 
        "date": "$subs.period", 
        "wording": { "$concat": ["Subscription ", "$subs.email"] }, 
        "amount": "$subs.amount" 
       } 
      } 
     } 
    }, 
    { 
     "$project": { 
      "clubCode": "$_id", 
      "_id": 0, 
      "treasury_moves": { "$setUnion": ["$subs", "$trades"] } 
     } 
    } 
]) 

樣本輸出

/* 1 */ 
{ 
    "clubCode" : "43W0K", 
    "treasury_moves" : [ 
     { 
      "date" : ISODate("2017-01-13T15:03:52.410Z"), 
      "wording" : "Achat ENGIE SA", 
      "amount" : 144.87 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2017-01-14T10:52:56.208Z"), 
      "wording" : "Achat BigBen Interactive", 
      "amount" : 499.82 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:56:32.088Z"), 
      "wording" : "Achat Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
      "amount" : 500.51 
     }, 
     { 
      "date" : ISODate("2017-01-13T16:05:35.849Z"), 
      "wording" : "Achat Visiativ SA", 
      "amount" : 182.49 
     } 
    ] 
} 

在上面的管道,第一階段包括$lookup運營商。這允許您對同一數據庫中的其他集合執行「左外部聯接」,以便從「已加入」集合中對文檔進行篩選以進行處理。當您運行對trades收集管道與 只是這一步:

db.trades.aggregate([ 
    { 
     "$lookup": { 
      "from": "subscriptions", 
      "localField": "clubCode", 
      "foreignField": "clubCode", 
      "as": "subs" 
     } 
    } 
]) 

,你會得到以下結果:

/* 1 */ 
{ 
    "_id" : ObjectId("5878ec66ad251f4fb4d2aacc"), 
    "clubCode" : "43W0K", 
    "date" : ISODate("2017-01-13T15:03:52.410Z"), 
    "symbol" : "ENGI.PA", 
    "name" : "ENGIE SA", 
    "buyOrSell" : "buy", 
    "orderDone" : true, 
    "quantity" : 12, 
    "price" : 11.99, 
    "fees" : 0.99, 
    "total" : 144.87, 
    "__v" : 0, 
    "subs" : [ 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922761"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922762"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922763"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     } 
    ] 
} 

/* 2 */ 
{ 
    "_id" : ObjectId("5878f8c339b47f0ee4a3b80b"), 
    "clubCode" : "43W0K", 
    "date" : ISODate("2017-01-13T15:56:32.088Z"), 
    "symbol" : "PRIO.PA", 
    "name" : "Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
    "buyOrSell" : "buy", 
    "orderDone" : true, 
    "quantity" : 56, 
    "price" : 8.92, 
    "fees" : 0.99, 
    "total" : 500.51, 
    "__v" : 0, 
    "subs" : [ 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922761"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922762"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922763"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     } 
    ] 
} 

/* 3 */ 
{ 
    "_id" : ObjectId("5878fadf39b47f0ee4a3b80c"), 
    "clubCode" : "43W0K", 
    "date" : ISODate("2017-01-13T16:05:35.849Z"), 
    "symbol" : "ALVIV.PA", 
    "name" : "Visiativ SA", 
    "buyOrSell" : "buy", 
    "orderDone" : true, 
    "quantity" : 10, 
    "price" : 18.15, 
    "fees" : 0.99, 
    "total" : 182.49, 
    "__v" : 0, 
    "subs" : [ 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922761"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922762"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922763"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     } 
    ] 
} 

/* 4 */ 
{ 
    "_id" : ObjectId("587a03319e3fe23138119937"), 
    "clubCode" : "43W0K", 
    "date" : ISODate("2017-01-14T10:52:56.208Z"), 
    "symbol" : "BIG.PA", 
    "name" : "BigBen Interactive", 
    "buyOrSell" : "buy", 
    "orderDone" : false, 
    "orderType" : "ACL", 
    "quantity" : 83, 
    "price" : 6.01, 
    "fees" : 0.99, 
    "total" : 499.82, 
    "__v" : 0, 
    "subs" : [ 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922761"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922762"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     }, 
     { 
      "_id" : ObjectId("587c946f3aa3f229a0922763"), 
      "email" : "[email protected]", 
      "clubCode" : "43W0K", 
      "period" : ISODate("2016-09-01T22:00:00.000Z"), 
      "amount" : 100, 
      "type" : "recurrent", 
      "__v" : 0 
     } 
    ] 
} 

在接下來的步驟中,您將需要拼合具有$unwind的子陣列用於處理作爲您的下一個流水線階段:

db.trades.aggregate([ 
    { 
     "$lookup": { 
      "from": "subscriptions", 
      "localField": "clubCode", 
      "foreignField": "clubCode", 
      "as": "subs" 
     } 
    }, 
    { "$unwind": "$subs" } 
]) 

這將產生i x j文檔,其中i是原始集合(4)中的文檔數量,而j是數組元素數量(3)。


下一步,$group,然後將組中的12份文件由clubCode字段,並創建兩個陣列與來自字段中指定的子文檔:在管道

db.trades.aggregate([ 
    { 
     "$lookup": { 
      "from": "subscriptions", 
      "localField": "clubCode", 
      "foreignField": "clubCode", 
      "as": "subs" 
     } 
    }, 
    { "$unwind": "$subs" }, 
    { 
     "$group": { 
      "_id": "$clubCode", 
      "trades": { 
       "$push": { 
        "date": "$date", 
        "wording": { "$concat": ["Achat ", "$name"] }, 
        "amount": "$total" 
       } 
      }, 
      "subs": { 
       "$push": { 
        "date": "$subs.period", 
        "wording": { "$concat": ["Subscription ", "$subs.email"] }, 
        "amount": "$subs.amount" 
       } 
      } 
     } 
    } 
]) 

輸出

/* 1 */ 
{ 
    "_id" : "43W0K", 
    "trades" : [ 
     { 
      "date" : ISODate("2017-01-13T15:03:52.410Z"), 
      "wording" : "Achat ENGIE SA", 
      "amount" : 144.87 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:03:52.410Z"), 
      "wording" : "Achat ENGIE SA", 
      "amount" : 144.87 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:03:52.410Z"), 
      "wording" : "Achat ENGIE SA", 
      "amount" : 144.87 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:56:32.088Z"), 
      "wording" : "Achat Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
      "amount" : 500.51 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:56:32.088Z"), 
      "wording" : "Achat Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
      "amount" : 500.51 
     }, 
     { 
      "date" : ISODate("2017-01-13T15:56:32.088Z"), 
      "wording" : "Achat Lyxor ETF PEA Brazil (Ibovespa) C-EUR", 
      "amount" : 500.51 
     }, 
     { 
      "date" : ISODate("2017-01-13T16:05:35.849Z"), 
      "wording" : "Achat Visiativ SA", 
      "amount" : 182.49 
     }, 
     { 
      "date" : ISODate("2017-01-13T16:05:35.849Z"), 
      "wording" : "Achat Visiativ SA", 
      "amount" : 182.49 
     }, 
     { 
      "date" : ISODate("2017-01-13T16:05:35.849Z"), 
      "wording" : "Achat Visiativ SA", 
      "amount" : 182.49 
     }, 
     { 
      "date" : ISODate("2017-01-14T10:52:56.208Z"), 
      "wording" : "Achat BigBen Interactive", 
      "amount" : 499.82 
     }, 
     { 
      "date" : ISODate("2017-01-14T10:52:56.208Z"), 
      "wording" : "Achat BigBen Interactive", 
      "amount" : 499.82 
     }, 
     { 
      "date" : ISODate("2017-01-14T10:52:56.208Z"), 
      "wording" : "Achat BigBen Interactive", 
      "amount" : 499.82 
     } 
    ], 
    "subs" : [ 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     }, 
     { 
      "date" : ISODate("2016-09-01T22:00:00.000Z"), 
      "wording" : "Subscription [email protected]", 
      "amount" : 100 
     } 
    ] 
} 

您需要與$setUnion運營商加入兩個數組,而忽略重複在您的最終聚合管道,$project。這也將重塑文檔,將_id密鑰替換爲clubCode字段。運行這些階段的最終管道將爲您提供理想的結果。

+1

哇,令人印象深刻!非常感謝,它工作得很好,我現在明白了(呃...我相信... :))一切的工作! 再次感謝! – Toma

+0

不用擔心,總是樂意幫助:)如果您需要對管道進行進一步的說明,請隨時在評論中給我留言。 – chridam

+0

現在,沒有關於你給我什麼的其他問題。 只是另一件事,我想按日期排序結果。我試圖在流水線中添加「排序」,但我沒有成功對它們進行排序。 我試圖把'{$ sort:{treasury_moves.date:1}}'放在'$ project'之後,但它不起作用。 如果我把它放在$ unwind之後,我可以對每個集合進行排序,但不能放在一起。 你有任何解決方案嗎? 謝謝! :) – Toma