【问题标题】:mongodb check regex on fields from one collection to all fields in other collectionmongodb检查一个集合中的字段到另一个集合中的所有字段的正则表达式
【发布时间】:2017-10-10 03:40:52
【问题描述】:

在挖掘谷歌和 SO 一周后,我最终在这里提出了这个问题。假设有两个集合,

用户集合:

[
{...
    name:"James"
    userregex: "a|regex|str|here"
},
{...
    name:"James"
    userregex: "another|regex|string|there"
},
...
]

PostCollection:

[
{...
    title:"a string here ..."
},
{...
    title: "another string here ..."
},
...
]

我需要获取所有userregex 将匹配任何post.title 的用户(需要 user_id、post_id 组或类似的东西)。

到目前为止我已经尝试过:
1.收集所有用户,在所有产品上运行正则表达式,工作但太脏!它必须为每个用户执行一个查询 2.同上,但在Mongo查询中使用foreach,同上,只是数据库层而不是应用层

我搜索了很多可用的方法,例如聚合、逆风等,但没有运气。
那么在Mongo中可以做到这一点吗?我应该更改我的数据库类型吗?如果是的话,什么类型会好?性能是我的首要任务。谢谢

【问题讨论】:

  • 由于这似乎是您不需要为特定用户执行的操作,因此可能需要进行昂贵的计算(无论如何您都无法绕过)并将结果缓存在内存中。听起来这可能是一个 AB 问题。
  • @IngoBürk 我无法缓存结果,新数据来自 PostCollection 和 UsersCollection 也得到了更新。也许使用一些标志来处理新的\更改的文档,但它太脏了
  • 添加帖子不会改变缓存结果。只有删除它们才会,并且您可以通过记住与用户匹配的帖子来解释(如果这是已删除的帖子,请为受影响的用户重新计算)。而添加用户时,您只需计算单个用户的信息并将其添加到缓存中。
  • 你做过类似stackoverflow.com/a/22739813/4110233的事情对吧?
  • 您的正则表达式是否都只是像您的示例中那样通过管道传输“或”单个关键字?如果是这样,您也许可以将它们存储在一个数组中并使用聚合框架进行 $lookup。

标签: mongodb mongoose


【解决方案1】:

无法在匹配表达式中的正则表达式运算符中引用存储在文档中的正则表达式字段。

所以它不能在当前结构的 mongo 端完成。

$lookup 适用于相等条件。因此,一种替代方法(类似于 Nic 建议的)是更新您的帖子集,为每个标题添加一个名为 keywords 的额外字段(可以搜索的关键字值数组)。

db.users.aggregate([
   {$lookup: {
          from: "posts",
          localField: "userregex",
          foreignField: "keywords",
          as: "posts"
        }
    }
])

上面的查询会做这样的事情(从 3.4 开始工作)。

keywords: { $in: [ userregex.elem1, userregex.elem2, ... ] }.

来自文档

如果该字段包含一个数组,则 $in 运算符选择 其字段包含一个数组的文档,该数组至少包含一个 与指定数组中的值匹配的元素(例如, 等)

看起来早期版本(在 3.2 上测试)只有在数组具有相同顺序、值和数组长度相同时才会匹配。

示例输入:

用户

db.users.insertMany([
  {
    "name": "James",
    "userregex": [
      "another",
      "here"
    ]
  },
  {
    "name": "John",
    "userregex": [
      "another",
      "string"
    ]
  }
])

帖子

db.posts.insertMany([
  {
    "title": "a string here",
    "keyword": [
      "here"
    ]
  },
  {
    "title": "another string here",
    "keywords": [
      "another",
      "here"
    ]
  },
  {
    "title": "one string here",
    "keywords": [
      "string"
    ]
  }
])

样本输出:

[
  {
    "name": "James",
    "userregex": [
      "another",
      "here"
    ],
    "posts": [
      {
        "title": "another string here",
        "keywords": [
          "another",
          "here"
        ]
      },
      {
        "title": "a string here",
        "keywords": [
          "here"
        ]
      }
    ]
  },
  {
    "name": "John",
    "userregex": [
      "another",
      "string"
    ],
    "posts": [
      {
        "title": "another string here",
        "keywords": [
          "another",
          "here"
        ]
      },
      {
        "title": "one string here",
        "keywords": [
          "string"
        ]
      }
    ]
  }
]

【讨论】:

    【解决方案2】:

    MongoDB 适合您的用例,但您需要使用与当前不同的方法。由于您只关心与任何帖子匹配的任何标题,因此您可以存储此类匹配的最后结果。下面是示例代码

    db.users.find({last_post_id: {$exists: 0}}).forEach(
       function(row) {
           var regex = new RegExp(row['userregex']);
           var found = db.post_collection.findOne({title: regex});
           if (found) {
               post_id = found["post_id"];
               db.users.updateOne({
                     user_id: row["user_id"]
                   }, {
                        $set :{ last_post_id:  post_id}
                       }); 
           }
       }
    )
    

    它的作用是只过滤没有设置last_post_id的用户,搜索帖子记录并在找到记录时设置last_post_id。所以运行这个之后,你可以返回类似的结果

    db.users.find({last_post_id: {$exists: 1}}, {user_id:1, last_post_id:1, _id:0})
    

    您唯一需要关心的是编辑/删除现有帖子。因此,在每次编辑/删除之后,您应该只在下面运行,以便再次运行该帖子 ID 的所有匹配项。

    post_id_changed = 1
    db.users.updateMany({last_post_id: post_id_changed}, {$unset: {last_post_id: 1}})
    

    这将确保在您下次运行更新时再次处理这些用户。该方法确实有一个缺点,即对于没有匹配标题的每个用户,对此类用户的查询将一次又一次地运行。虽然您可以通过使用一些时间戳或帖子计数检查来解决这个问题

    另外你应该确保将索引放在post_collection.title

    【讨论】:

      【解决方案3】:

      我在想,如果你像这样预先标记你的帖子标题:

      {
        "_id": ...
        "title": "Another string there",
        "keywords": [
          "another",
          "string",
          "there"
        ]
      }
      

      但不幸的是,$lookup 要求 foreignField 是一个单一元素,所以我对这样的想法不会起作用:(但也许它会给你另一个想法?

      db.Post.aggregate([
         {$lookup: {
                from: "Users",
                localField: "keywords",
                foreignField: "keywords",
                as: "users"
              }
          },
      ]))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-10
        • 1970-01-01
        • 2018-12-03
        相关资源
        最近更新 更多