2017-01-03 74 views
1

我在MongoDB中保存以下JSON:的MongoDB的Java更新值

{ 
    "type": "FeatureCollection", 
    "features": [ 
    { 
     "type": "Feature", 
     "properties": { 
     "ID": "1753242", 
     "TYPE": "8003" 
     } 
    }, 
    { 
     "type": "Feature", 
     "properties": { 
     "ID": "4823034", 
     "TYPE": "7005" 
     } 
    }, 
    { 
     "type": "Feature", 
     "properties": { 
     "ID": "4823034", 
     "TYPE": "8003" 
     } 
    } 
    ] 
} 

而且我想改变这一切"Type" : "8003""Type" : "8"与Java。 我不喜欢这样写道:

BasicDBObject updateQuery = new BasicDBObject(); 
    updateQuery.append("$set", 
      new BasicDBObject().append("features.$.properties.TYPE", "8")); 

    BasicDBObject searchQuery = new BasicDBObject(); 
    searchQuery.append("features.properties.TYPE", "8003"); 

    collection.updateMulti(searchQuery, updateQuery); 

的问题是,"features.$.properties.TYPE"只选择和更新的第一个元素的值,而不是其他的(一个或多个)。 是否有人知道如何将所有"Type" : "8003"更改为"Type" : "8"而不仅仅是第一个?

回答

0

你基本上需要获得数组的一个子集,只需要更新元素,迭代子集和每个元素使用$ positional operator更新集合。

要获取子集,您需要使用聚合框架,其中运算符可用于生成此子集。例如,下面的蒙戈外壳操作演示如何使用上述更新您的收藏算法 :

db.collection.aggregate([ 
    { "$match": { "features.properties.TYPE": "8003" } }, 
    { 
     "$project": { 
      "features": { 
       "$filter": [     
        "input": "$features", 
        "as": "feature", 
        "cond": { "$eq": [ "$$feature.properties.TYPE", "8003" ] } 
      }   
     } 
    } 
]).forEach(function(doc) { 
    doc.features.forEach(function(feature) { 
     db.collection.updateOne(
      { "_id": doc._id, "features.properties.TYPE": "8003" }, 
      { "$set": { "features.$.properties.TYPE": "8" } 
     }); 
    }); 
}); 

现在几点需要注意。首先,如果你的MongoDB的版本不支持3.2. X版本和新推出的$filter运营商,那么可以考虑使用$setDifference$map运营商组合作为后备过滤在$project管道数组元素。

$map操作在本质上创建保持值作为一个子表达式到数组的每个元素的逻辑评价的结果的新的数组字段。然后$setDifference将返回一个集合,其中元素出现在第一个集合中,但不在第二个集合中;即执行第二组相对于第一组的相对补偿。在这种情况下,它将返回最终features阵列具有不经由所述子文档TYPE属性相关的母文件的元素:

db.collection.aggregate([ 
    { "$match": { "features.properties.TYPE": "8003" } }, 
    { 
     "$project": { 
      "features": { 
       "$setDifference": [ 
        { 
         "$map": { 
          "input": "$features", 
          "as": "feature", 
          "in": { 
           "$cond": [ 
            { "$eq": [ "$$feature.properties.TYPE", "8003" ] }, 
            "$$feature", 
            false 
           ] 
          } 
         } 
        }, 
        [false] 
       ] 
      } 
     } 
    } 
]).forEach(function(doc) { 
    doc.features.forEach(function(feature) { 
     db.collection.updateOne(
      { "_id": doc._id, "features.properties.TYPE": "8003" }, 
      { "$set": { "features.$.properties.TYPE": "8" } 
     }); 
    }); 
}); 

其次,做在嵌套循环更新与O(N^2)的复杂性可遭受表演处罚。在这方面,您可以利用Bulk API优化您的代码,该代码允许您将精简版批次发送到 更新,即不必每次更新都将每个更新请求发送到服务器,而是可以将更新操作批量化为单一的请求更快,更高效。以下显示如何使用bulkWrite()方法来利用更新。

对于MongoDB的版本3.0及以下:

var bulk = db.collection.initializeOrderedBulkOp(), 
    counter = 0; 

db.collection.aggregate([ 
    { "$match": { "features.properties.TYPE": "8003" } }, 
    { 
     "$project": { 
      "features": { 
       "$setDifference": [ 
        { 
         "$map": { 
          "input": "$features", 
          "as": "feature", 
          "in": { 
           "$cond": [ 
            { "$eq": [ "$$feature.properties.TYPE", "8003" ] }, 
            "$$feature", 
            false 
           ] 
          } 
         } 
        }, 
        [false] 
       ] 
      } 
     } 
    } 
]).forEach(function(doc) { 
    doc.features.forEach(function(feature) { 
     bulk.find({ "_id": doc._id, "features.properties.TYPE": "8003" }) 
      .updateOne({ "$set": { "features.$.properties.TYPE": "8" }); 

     counter++; 
     if (counter % 500 === 0) { 
      bulk.execute(); 
      bulk = db.collection.initializeOrderedBulkOp(); 
     } 
    }); 
}); 

if (counter % 500 !== 0) 
    bulk.execute(); 

的MongoDB 3。2或更新

var ops = []; 

db.collection.aggregate([ 
    { "$match": { "features.properties.TYPE": "8003" } }, 
    { 
     "$project": { 
      "features": { 
       "$filter": [     
        "input": "$features", 
        "as": "feature", 
        "cond": { "$eq": [ "$$feature.properties.TYPE", "8003" ] } 
      }   
     } 
    } 
]).forEach(function(doc) { 
    doc.features.forEach(function(feature) { 
     ops.push({ 
      "updateOne": { 
       "filter": { "_id": doc._id, "features.properties.TYPE": "8003" }, 
       "update": { "$set": { "features.$.properties.TYPE": "8" } 
      } 
     }); 
     counter++;  
    }); 

    if (counter % 500 === 0) { 
     db.collection.bulkWrite(ops); 
     ops = []; 
    } 
}); 


if (counter % 500 !== 0) 
    db.collection.bulkWrite(ops); 

上面的计数器变量是有管理您的批量更新有效,如果您的收藏大。它允许您批量更新操作并以500批为单位将写入发送到服务器,因为您不会将每个请求发送到服务器,因此您可以获得更好的性能,每500次请求只发送一次。

对于批量操作,MongoDB每个批处理操作的默认内部限制为1000次,所以从某种意义上说,您可以控制批处理大小,而不是让MongoDB强加默认值,在> 1000文件量的操作。


适应上述用Java将产生以下(未经测试的代码):

前3.0

MongoClient mongo = new MongoClient(); 
DB db = mongo.getDB("yourDB"); 

DBCollection coll = db.getCollection("yourCollection"); 

// create the pipeline operations, first with the $match 
DBObject match = new BasicDBObject("$match", 
    new BasicDBObject("features.properties.TYPE", "8003") 
); 

// build the $project operations 
BasicDBList eq = new BasicDBList(); 
eq.add("$$feature.properties.TYPE"); 
eq.add("8") 
DBObject equalityClause = new BasicDBObject("$eq", eq); 

BasicDBList cond = new BasicDBList(); 
cond.add(equalityClause); 
cond.add("$$feature"); 
cond.add(false); 

DBObject conditionalOperator = new BasicDBObject("$cond", cond); 

DBObject map = new BasicDBObject("input", "$features"); 
map.put("as", 1); 
map.put("in", conditionalOperator); 

BasicDBList setList = new BasicDBList(); 
setList.add(map); 
setList.add(new BasicDBList().add(false)); 
DBObject setDifference = new BasicDBObject("$setDifference", setList); 

DBObject fields = new BasicDBObject("feature", setDifference); 
DBObject project = new BasicDBObject("$project", fields); 

AggregationOutput output = coll.aggregate(match, project); 

BulkWriteOperation bulk = coll.initializeUnorderedBulkOperation(); 
for (DBObject result: output.results()) { 
    System.out.println(result); 
    for (Object feature: result.features) { 
     bulk.find(new BasicDBObject("_id", result._id) 
      .append("features.properties.TYPE", "8003") 
     ).update(new BasicDBObject(
       "$set", new BasicDBObject(
        "features.$.properties.TYPE", "8" 
       ) 
      ) 
     ); 
    } 
}  
BulkWriteResult result = bulk.execute(); 
System.out.println(result.isAcknowledged()); 

如果您使用的是MongoDB的Java驱动程序版本3.0和更新,你可以尝试(也未经测试):

MongoCollection<Document> collection = database.getCollection("yourCollection");  
List<WriteModel<Document>> writes = new ArrayList<WriteModel<Document>>(); 

Block<Document> bulkBlock = new Block<Document>() { 
    @Override 
    public void apply(final Document document) { 
     for (Obj el: document.features) { 
      writes.add(new UpdateOneModel<Document>(
       new Document("_id", document._id) 
        .append("features.properties.TYPE", "8003"), 
       new Document("$set", new Document("features.$.properties.TYPE", "8")) 
      ));  
     } 
    } 
}; 

collection.aggregate(asList(
    match(eq("features.properties.TYPE", "8003")), 
    project(Document.parse("{ 
     'features': { 
      '$filter': [     
       'input': '$features', 
       'as': 'feature', 
       'cond': { '$eq': [ '$$feature.properties.TYPE', '8003' ] } 
     }   
    }")) 
).forEach(bulkBlock); 

collection.bulkWrite(writes);