高效查找和替换文档中的字符串
Find and Replace Strings in Documents Efficiently
我有以下查询,要在名称字段中找到
标签并将它们替换为空的 space - 以摆脱它们。
名称字符串可以有 1 到多个
标签,例如
AA aa
AA aa
AA aa
AA aa
AA AA aaaaaaaa
...就像那样。
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
除了重复相同的查询模式之外,是否有更好的解决方案来处理这种情况,减少重复并提高性能?
当然,如果您只想从文本中删除
实体,那么您只需进行全局匹配并替换:
db.tests.find({ "name": /\ /g }).forEach(function(doc) {
doc.name = doc.name.replace(/ /g,"");
db.tests.update({ "_id": doc._id },{ "$set": { "name": doc.name } });
});
所以不需要写出每个组合,正则表达式会用 /g
选项替换非常匹配。可能还使用 /m
表示多行是您的 "name" 字符串包含换行符。看一个基本的regexer example.
还建议使用 $set
以便只修改您真正想要修改的字段,而不是 .save()
整个文档。自文档被读取以来,流量和覆盖可能由另一个进程所做的更改的机会更少。
理想情况下,您可以将批量操作 API 与 MongoDB 2.6 及更高版本一起使用。这允许更新到 "batch",因此客户端和服务器之间的流量再次减少:
var bulk = db.tests.initializeOrderedBulkOp();
var count = 0;
db.tests.find({ "name": /\ /g }).forEach(function(doc) {
doc.name = doc.name.replace(/ /g,"");
bulk.find({ "_id": doc._id })
.updateOne({ "$set": { "name": doc.name } });
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.tests.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
这些是您改进它的主要方法。不幸的是,MongoDB 更新语句无法以这种方式使用现有值作为其更新表达式的一部分,因此唯一的方法是循环,但您可以做很多事情来减少操作,如图所示。
如今,
- 开始
Mongo 4.2
、db.collection.updateMany
(db.collection.update
的别名)可以接受聚合管道,最终允许根据自己的值更新字段。
- 从
Mongo 4.4
开始,新的聚合运算符 $replaceAll
使得替换部分字符串变得非常容易。
// { "name" : "AA aa" }
// { "name" : "AA aa" }
// { "name" : "AA AA aaaaaaaa" }
db.collection.updateMany(
{ name: { $regex: /\ \;/ } },
[{
$set: { name: {
$replaceAll: { input: "$name", find: " ", replacement: "" }
}}
}]
)
// { "name" : "AAaa" }
// { "name" : "AAaa" }
// { "name" : "AAAAaaaaaaaa" }
- 第一部分 (
{ name: { $regex: /\ \;/ } }
) 是匹配查询,筛选要更新的文档(包含 " "
的文档)
- 第二部分(
$set: { name: {...
)是更新聚合管道(注意方括号表示使用聚合管道):
$set
是一个新的聚合运算符 (Mongo 4.2
),在本例中它替换了一个字段的值。
- 新值是使用新的
$replaceAll
运算符计算的。注意 name
是如何直接根据其自身的值 ($name
) 进行修改的。
由于
在 MongoDB 搜索中没有作为字符串出现,因此我使用了它的 UNICODE u00a0
而不是字符串,如下所示:
db.tests.find({}).forEach(function (x) {
x.name = x.name.replace(/\u00a0/g, ' ');
db.tests.save(x);
});
在这里,我将名称数据字段中的
替换为 white space
我有以下查询,要在名称字段中找到
标签并将它们替换为空的 space - 以摆脱它们。
名称字符串可以有 1 到多个
标签,例如
AA aa
AA aa
AA aa
AA aa
AA AA aaaaaaaa
...就像那样。
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
db.tests.find({'name':/.* .*/}).forEach(function(test){
test.name = test.name.replace(" ","");
db.tests.save(test);
});
除了重复相同的查询模式之外,是否有更好的解决方案来处理这种情况,减少重复并提高性能?
当然,如果您只想从文本中删除
实体,那么您只需进行全局匹配并替换:
db.tests.find({ "name": /\ /g }).forEach(function(doc) {
doc.name = doc.name.replace(/ /g,"");
db.tests.update({ "_id": doc._id },{ "$set": { "name": doc.name } });
});
所以不需要写出每个组合,正则表达式会用 /g
选项替换非常匹配。可能还使用 /m
表示多行是您的 "name" 字符串包含换行符。看一个基本的regexer example.
还建议使用 $set
以便只修改您真正想要修改的字段,而不是 .save()
整个文档。自文档被读取以来,流量和覆盖可能由另一个进程所做的更改的机会更少。
理想情况下,您可以将批量操作 API 与 MongoDB 2.6 及更高版本一起使用。这允许更新到 "batch",因此客户端和服务器之间的流量再次减少:
var bulk = db.tests.initializeOrderedBulkOp();
var count = 0;
db.tests.find({ "name": /\ /g }).forEach(function(doc) {
doc.name = doc.name.replace(/ /g,"");
bulk.find({ "_id": doc._id })
.updateOne({ "$set": { "name": doc.name } });
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.tests.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
这些是您改进它的主要方法。不幸的是,MongoDB 更新语句无法以这种方式使用现有值作为其更新表达式的一部分,因此唯一的方法是循环,但您可以做很多事情来减少操作,如图所示。
如今,
- 开始
Mongo 4.2
、db.collection.updateMany
(db.collection.update
的别名)可以接受聚合管道,最终允许根据自己的值更新字段。 - 从
Mongo 4.4
开始,新的聚合运算符$replaceAll
使得替换部分字符串变得非常容易。
// { "name" : "AA aa" }
// { "name" : "AA aa" }
// { "name" : "AA AA aaaaaaaa" }
db.collection.updateMany(
{ name: { $regex: /\ \;/ } },
[{
$set: { name: {
$replaceAll: { input: "$name", find: " ", replacement: "" }
}}
}]
)
// { "name" : "AAaa" }
// { "name" : "AAaa" }
// { "name" : "AAAAaaaaaaaa" }
- 第一部分 (
{ name: { $regex: /\ \;/ } }
) 是匹配查询,筛选要更新的文档(包含" "
的文档) - 第二部分(
$set: { name: {...
)是更新聚合管道(注意方括号表示使用聚合管道):$set
是一个新的聚合运算符 (Mongo 4.2
),在本例中它替换了一个字段的值。- 新值是使用新的
$replaceAll
运算符计算的。注意name
是如何直接根据其自身的值 ($name
) 进行修改的。
由于
在 MongoDB 搜索中没有作为字符串出现,因此我使用了它的 UNICODE u00a0
而不是字符串,如下所示:
db.tests.find({}).forEach(function (x) {
x.name = x.name.replace(/\u00a0/g, ' ');
db.tests.save(x);
});
在这里,我将名称数据字段中的
替换为 white space