高效查找和替换文档中的字符串

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.2db.collection.updateManydb.collection.update 的别名)可以接受聚合管道,最终允许根据自己的值更新字段。
  • Mongo 4.4 开始,新的聚合运算符 $replaceAll 使得替换部分字符串变得非常容易。
// { "name" : "AA aa" }
// { "name" : "AA  aa" }
// { "name" : "AA AA aaaaaaaa" }
db.collection.updateMany(
  { name: { $regex: /\&nbsp\;/ } },
  [{
    $set: { name: {
      $replaceAll: { input: "$name", find: " ", replacement: "" }
    }}
  }]
)
// { "name" : "AAaa" }
// { "name" : "AAaa" }
// { "name" : "AAAAaaaaaaaa" }
  • 第一部分 ({ name: { $regex: /\&nbsp\;/ } }) 是匹配查询,筛选要更新的文档(包含 " " 的文档)
  • 第二部分($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