你基本上需要获得数组的一个子集,只需要更新元素,迭代子集和每个元素使用$
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);