2017-01-03 66 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);