2016-07-25 112 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