2016-07-25 114 views
0

我有一個集合,其中有一些重複的文檔。在例如:合併重複並刪除最舊的

頭文件:

{ 
    "_id" : ObjectId("56f3d7cc1de31cb20c08ae6b"), 
    "AddedDate" : ISODate("2016-05-01T00:00:00.000Z"), 
    "Place": "THISPLACE", 
    "PresentInDB" : [ 
     { 
      "InDB" : ISODate("2016-05-01T00:00:00.000Z") 
     } 
    ], 
    "Checked" : [], 
    "Link": "http://www.mylink.com/first/84358" 
} 

第二份文件:

{ 
    "_id" : ObjectId("577740526c1e542904725238"), 
    "AddedDate" : ISODate("2016-05-02T00:00:00.000Z"), 
    "Place": "THISPLACE", 
    "PresentInDB" : [ 
     { 
      "InDB" : ISODate("2016-05-02T00:00:00.000Z") 
     }, 
     { 
      "InDB" : ISODate("2016-05-03T00:00:00.000Z") 
     } 
    ], 
    "Checked" : [ 
     { 
      "Done" : ISODate("2016-05-02T00:00:00.000Z") 
     }, 
    ], 
    "Link": "http://www.mylink.com/second/84358" 
} 

Link字段包含在這兩個文件的數字相同sequense,84358

所以我想實現這些步驟:

  1. 遍歷集合中的每個文件。
  2. 匹配數序列中的每個文件在Link字段(即84358以上),並且如果有在 收集幾個文檔具有在Link字段序列。並且如果Place字段匹配在兩個文件:
  3. 合併PresentInDBChecked字段 - >由(在AddedDate 場按日期)從最新的文檔添加數組值到最舊的文件合併PresentInDBChecked字段。
  4. 刪除最新的文件。

我該如何實現這樣的查詢?

回答

1

在MongoDB中3.3.6發佈新系列推出$split運算符用於處理彙總框架中的字符串(Jira)。在此版本之前,您只能使用map/reduce解決方案來解決此問題。

之後MongoDB 3.3。6版本:聚合框架解決方案

db.duplicatedCollection.aggregate(
    [ 
    { 
     $project: { 
     _id : 1, 
     AddedDate : 1, 
     Place : 1, 
     PresentInDB : 1, 
     Checked : 1, 
     Link : 1, 
     sequenceNumber: { $arrayElemAt: [ {$split: ["$Link", "/"]}, -1 ]}, 
     } 
    }, 
    { 
     $sort: { AddedDate: 1 } 
    }, 
    { 
     $group: { 
     _id : { 
      sequenceNumber : "$sequenceNumber", 
      Place : "$Place" 
     }, 
     id : { $first: "$_id"}, 
     AddedDate: { $first: "$AddedDate" }, 
     Place : { $first: "$Place" }, 
     PresentInDB: { 
      $push: '$PresentInDB' 
     }, 
     Checked: { 
      $push: '$Checked' 
     }, 
     Link: { $first: "$Link"} 
     } 
    }, 
    { 
     $unwind: "$PresentInDB" 
    }, 
    { 
     $unwind: { 
     path : "$PresentInDB", 
     preserveNullAndEmptyArrays: true 
     }  
    }, 
    { 
     $unwind: "$Checked" 
    }, 
    { 
     $unwind: { 
     path : "$Checked", 
     preserveNullAndEmptyArrays: true 
     } 
    },  
    { 
     $group: { 
     _id : "$id", 
     AddedDate: { $first: "$AddedDate" },   
     Place : { $first: "$Place" }, 
     PresentInDB : { 
      $addToSet: '$PresentInDB' 
     }, 
     Checked : { 
      $addToSet: '$Checked' 
     },   
     Link: { $first: "$Link"} 
     } 
    }, 
    { 
     $out: "duplicatedCollection" 
    } 
    ] 
); 

的MongoDB 3.3.6之前的版本:的Map/Reduce的解決方案

地圖功能:

var mapFunction = function() { 
    var linkArray = this.Link.split("/"); 
    var sequenceNumber = linkArray[linkArray.length - 1]; 

    var keyDoc = { 
     place : this.Place, 
     sequenceNumber: sequenceNumber, 
    }; 

    emit(keyDoc, this); 
}; 

Reduce函數:

var reduceFunction = function(key, values) { 
    var reducedDoc = {}; 
    reducedDoc._id = values[0]._id; 
    reducedDoc.AddedDate = values[0].AddedDate; 
    reducedDoc.Link = values[0].Link; 
    reducedDoc.PresentInDB = []; 
    reducedDoc.Checked = []; 

    var presentInDbMillisArray = []; 
    var checkedMillisArray = [];   

    values.forEach(function(doc) { 
     if (reducedDoc.AddedDate < doc.AddedDate) { 
      reducedDoc._id = doc._id; 
      reducedDoc.AddedDate = doc.AddedDate; 
      reducedDoc.Link = doc.Link; 
     } 

     // PresentInDB field merge 
     doc.PresentInDB.forEach(function(presentInDBElem) { 
      var millis = presentInDBElem.InDB.getTime(); 
      if (!Array.contains(presentInDbMillisArray, millis)) { 
       reducedDoc.PresentInDB.push(presentInDBElem); 
       presentInDbMillisArray.push(millis); 
      } 
     }); 

     // same here with Checked field 
     doc.Checked.forEach(function(checkedElem) { 
      var millis = checkedElem.Done.getTime(); 
      if (!Array.contains(checkedMillisArray, millis)) { 
       reducedDoc.Checked.push(checkedElem); 
       checkedMillisArray.push(millis); 
      } 
     }); 
    }); 
    return reducedDoc; 
}; 

地圖/減少:

db.duplicatedCollection.mapReduce(
    mapFunction, 
    reduceFunction, 
    { 
     "out": "duplicatedCollection" 
    } 
); 

展開地圖中的值/減少返回的文檔:

db.duplicatedCollection.find(
    { 
     value : { 
      $exists: true 
     } 
    } 
    ).forEach(function(doc) { 
     db.duplicatedCollection.insert(doc.value); 
     db.duplicatedCollection.remove({_id : doc._id}); 
    }); 
+0

太好了,非常感謝! – user1665355

0

您可以使用一個aggregation查詢做到這一點:

db.device.aggregate([{ 
    "$unwind": "$PresentInDB" 
}, { 
    "$match": { 
     "Link": /84358/ 
    } 
}, { 
    "$sort": { 
     "AddedDate": 1 
    } 
}, { 
    "$group": { 
     _id: 0, 
     PresentInDB: { 
      $addToSet: '$PresentInDB' 
     }, 
     AddedDate: { 
      $first: "$AddedDate" 
     }, 
     id: { 
      $first: "$_id" 
     }, 
     Link: { 
      $first: "$Link" 
     } 
    } 
}, { 
    $out: "documents" 
}]) 
  • $unwind你的陣列上
  • $match您的ID(這裏含84358)
  • $sort工作按升序日期
  • $group附:
    • a $addToSet將您所有的PresentInDB合併爲一個單一陣列,不需要重複
    • a $first爲每個字段保留。保持第一意味着你只需要前輩之一,因爲我們以前上升日期排序
  • $out將結果保存到一個名爲documents這裏
+0

謝謝,但我沒有更新的問題,好像更新之前沒有現在發生......我有兩個字段合併'PresentInDB'和'Checked'字段。我怎麼能用聚合來做到這一點? – user1665355

+0

我還需要從這個集合中'DROP'最新的複製文件,而不是添加到新的集合! – user1665355

+0

此外,有幾個重複,不僅''鏈接「:/ 84358 /' – user1665355