删除名称为“XX”的所有字段开头

时间:2017-08-02 23:35:54

标签: javascript mongodb mongodb-query

集合中的示例文档:

{ "teamAlpha": { }, "teamBeta": { }, "leader_name": "leader" }

对于此类文档,我想删除所有以"team"开头的字段。所以预期的结果是

{leader_name: "leader"}

我目前正在使用一个功能:

db.teamList.find().forEach(
    function(document) {
        for(var k in document) {
            if (k.startsWith('team')) {
                delete document[k];
            }
        }
        db.teamList.save(document);
    }
);

我想知道是否有更好的方法解决这个问题。

1 个答案:

答案 0 :(得分:0)

它会更好"改为事先确定所有可能的密钥,然后发出单个"multi"更新以删除所有密钥。根据可用的MongoDB版本,会有不同的方法。

MongoDB 3.4:$ objectToArray

let fields = db.teamList.aggregate([
  { "$project": {
    "_id": 0,
    "fields": {
      "$map": {
        "input": {
          "$filter": {
            "input": { "$objectToArray": "$$ROOT" },
            "as": "d",
            "cond": { "$eq": [{ "$substrCP": [ "$$d.k", 0, 4 ] }, "team" ] }
          }
        },
        "as": "f",
        "in": "$$f.k"
      }
    }
  }},
  { "$unwind": "$fields" },
  { "$group": { "_id": "$fields" } }
])
.map( d => ({ [d._id]: "" }))
.reduce((acc,curr) => Object.assign(acc,curr),{})

db.teamList.updateMany({},{ "$unset": fields });

.aggregate()语句通过$objectToArray将文档中的字段转换为数组,然后应用$filter仅返回"键&#前四个字母的字段34;匹配字符串"team"。然后使用$unwind$group对其进行处理,以制作一个"唯一列表"匹配字段。

后续指令仅将光标中返回的列表处理为单个对象,如:

{
    "teamBeta" : "",
    "teamAlpha" : ""
}

然后将其传递给$unset以从所有文档中删除这些字段。

早期版本:mapReduce

var fields = db.teamList.mapReduce(
  function() {
    Object.keys(this).filter( k => /^team/.test(k) )
      .forEach( k => emit(k,1) );
  },
  function() {},
  { "out": { "inline": 1 } }
)
.results.map( d => ({ [d._id]: "" }))
.reduce((acc,curr) => Object.assign(acc,curr),{})  

db.teamList.update({},{ "$unset": fields },{ "multi": true });

同样基本的事情,唯一的区别是,.updateMany()作为一种方法不存在.update(),我们只需使用"multi" parameter调用{{3}}来应用全部匹配的文件。这实际上是新的API调用。

超越这些选项

为了删除字段而迭代所有文档当然是不明智的,因此上述任何一个都是"首选"做法。唯一可能的失败是构建"不同列表"密钥实际上超过了16MB BSON限制。这是非常极端的,但根据实际数据,这是可能的。

因此基本上有两个扩展"这自然适用于这些技术:

  1. 使用"光标"使用.aggregate()

    var fields = [];
    
    db.teamList.aggregate([
      { "$project": {
        "_id": 0,
        "fields": {
          "$map": {
            "input": {
              "$filter": {
                "input": { "$objectToArray": "$$ROOT" },
                "as": "d",
                "cond": { "$eq": [{ "$substrCP": [ "$$d.k", 0, 4 ] }, "team" ] }
              }
            },
            "as": "f",
            "in": "$$f.k"
          }
        }
      }},
      { "$unwind": "$fields" },
      { "$group": { "_id": "$fields" } }
    ]).forEach( d => {
      fields.push(d._id);
    
      if ( fields.length >= 2000 ) {
        db.teamList.updateMany({},
          { "$unset": 
            fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
          }
        );
      }
    });
    
    if ( fields.length > 0 ) {
      db.teamList.updateMany({},
        { "$unset": 
          fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
        }
      );
    }
    

    这基本上是"批次"在"光标"上处理的字段数很多2000年,#34;应该"保留在16MB BSON限制之下作为请求。

  2. 使用mapReduce()

    的临时收藏集
    db.teamList.mapReduce(
      function() {
        Object.keys(this).filter( k => /^team/.test(k) )
          .forEach( k => emit(k,1) );
      },
      function() {},
      { "out": { "replace": "tempoutput" } }
    );
    
    db.tempoutput.find({},{ "_id": 1 }).forEach(d => {
      fields.push(d._id);
    
      if ( fields.length >= 2000 ) {
        db.teamList.update({},
          { "$unset": 
            fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
          },
          { "multi": true }
        );
      }
    });
    
    if ( fields.length > 0 ) {
      db.teamList.update({},
        { "$unset": 
          fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
        },
        { "multi": true }
      );
    }
    

    除非mapReduce无法输出到"光标",它再次基本上是相同的过程,您需要输出到仅包含" distinct字段的临时集合名称"然后从该集合中迭代光标,以便在同一批处理#34;方式。

  3. 正如类似的初始方法一样,这些是比迭代整个集合并单独调整每个文档更高效的选项。它通常不应该是必要的,因为任何"不同列表的可能性"实际上导致单个更新请求超过16MB确实是极端的。但这将再次成为"首选"处理这种极端情况的方法。

    常规

    当然,如果您只是知道所有字段名称并且不需要通过检查集合来解决它们,那么只需使用已知名称编写语句:

    db.teamList.update({},{ "$unset": { "teamBeta": "", "teamAlpha": "" } },{ "multi": true })
    

    这是完全有效的,因为所有其他陈述正在做的是确定这些名称应该适合你。