如何匹配 MongoDB 中嵌入式数组或文档中的字符串?

Posted

技术标签:

【中文标题】如何匹配 MongoDB 中嵌入式数组或文档中的字符串?【英文标题】:How to match string within embedded array or doc in MongoDB? 【发布时间】:2022-01-18 10:12:04 【问题描述】:

搜索了一整天后,我怀疑MongoDB是否可以满足以下要求:

问:如何过滤掉满足以下条件的文档?

students_replies最后一个数组元素中,有一个学生的回复,其姓名包含字符串'ason'。
id_1: first_school, students_replies: [
    Date:20210101, replies: [
        name: jack, reply: 'I do not like this idea',
        name: jason, reply: 'I would rather stay at home',
        name: charles, reply: 'I have an plan to improve',
        ], 
    Date:20210401, replies: [
        ...], 
    Date:20210801, replies: [
        ...],
]

id_2: second_shool, students_replies: [..]
id_3: third_shool, students_replies: [...]

Mongoplayground

【问题讨论】:

您能否提供有效的json(使用mongoplayground.net)和预期结果?你试过$elemMatch吗? MongoDB 聚合? 感谢您的回复。我在queryaggregation 中都尝试过$in$elemMatch$indexOfByte,但没有一个对我有用。它们中的大多数将匹配整个值而不是其中的一部分(字符串匹配是特定的)。例如key: 'This is apple',我的匹配条件将在值中包含ple,而不是值等于'This is apple'。下面是有效的 json,预期结果将仅输出带有 key:1key:3 的文档。 mongoplayground.net/p/_-MFlpzF6eY 期望的输出是什么? 喜欢这个MongoPlayground ? 您是否需要对输出文档本身也进行过滤,以便回复数组仅包含匹配的回复? 【参考方案1】:

使用$slice$regex

对于您的示例,这变为:

db.collection.aggregate([
  // project only the last reply
  
    "$project": 
      key: 1,
      last_reply: 
        "$slice": [
          "$students_replies",
          -1
        ]
      
    
  ,
  // filter the documents
  
    "$match": 
      "last_reply.replies.name": 
        "$regex": "ason"
      
    
  
])

https://mongoplayground.net/p/a9piw2WQ8n6

【讨论】:

我好像也会输出key: 2文档,没想到。 我听从了您的建议并制定了一个更简单的解决方案,mongoplayground.net/p/cEaiYQXq8cN,非常感谢您的帮助。 @YanTian 好的,它不输出key: 2(不是在我运行它时),但它确实输出了除最后一个之外的其他回复,我没有抓住那部分。但是您发现为此使用$slice。我将使用您的组合解决方案更新答案。 @YanTian 请注意,即使您在students_replies.replies.name 上有索引,由于投影的原因,它也可能不会在此处使用。出于性能原因,您可以在students_replies.replies.name 上放置一个索引,然后在students_replies.replies.name 上的$project 之前添加一个额外的$match。这样,MongoDB 可以在不获取文档的情况下扫描正则表达式的索引,并且它还需要执行 $project 和第二个 $match 以获得更少的文档。最好尽早减少管道中的文档数量。 感谢您的见解。我完全同意It's always best to reduce the number of documents in the pipeline as early as possible、@YuTing 也指出了这一点。由于这种最佳实践,我猜,他给出了一个非常复杂的答案,尽管他已经知道那些简单的答案。【参考方案2】:

由于您需要students_replies 的最后一个数组元素,请使用$arrayElemAt

db.collection.aggregate([
  
    "$match": 
      $expr: 
        $regexMatch: 
          input: 
            $reduce: 
              input: 
                $arrayElemAt: [
                  "$students_replies.replies",
                  -1
                ]
              ,
              initialValue: "",
              in: 
                $concat: [
                  "$$value",
                  "$$this.name",
                  ","
                ]
              
            
          ,
          regex: "ason"
        
      
    
  ,
  
    "$project": 
      "students_replies": 0
    
  
])

mongoplayground


另一个答案

db.collection.aggregate([
  
    $match: 
      $expr: 
        $ne: [
          
            $filter: 
              input: 
                $map: 
                  input: 
                    $arrayElemAt: [
                      "$students_replies.replies",
                      -1
                    ]
                  ,
                  as: "r",
                  in: "$$r.name"
                
              ,
              as: "s",
              cond: 
                $regexMatch: 
                  input: "$$s",
                  regex: "ason"
                
              
            
          ,
          []
        ]
      
    
  ,
  
    "$project": 
      "students_replies": 0
    
  
])

mongoplayground

【讨论】:

感谢您的回复,虽然看起来有点复杂,但我确实需要时间来消化您的回答。 对不起,玉婷,虽然你的答案输出了预期的结果,但我不会将其标记为答案,因为它们太复杂了,我根据@herman 的输入找到了一个更简单的答案。我在他的回答的评论中粘贴了我的解决方案。感谢您为提供帮助所付出的时间和努力。 @YanTian $match 应该始终处于聚合的顶层以实现最佳查询速度。当您拥有数千或数百万个数据时。 感谢您的指出。会记住这一点。在通过任何进一步的聚合阶段之前清除所有不需要的数据是合理的。尽管到目前为止你的答案对我来说很复杂,但我仍然从中学到了很多东西,我相信它会让我更好地为未来更高级的聚合做好准备。 @YuTing 尽管您很早就进行了过滤,但我认为这些解决方案中的正则表达式过滤器实际上不会使用索引(假设有一个)。此外,当我运行这些时,它们实际上并没有包含最后的回复。

以上是关于如何匹配 MongoDB 中嵌入式数组或文档中的字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何匹配 MongoDB 中的子文档数组?

如何匹配MongoDB中的子文档数组?

如何在 mongoDB 中存储地理空间信息

如何使用 mongodb 中的聚合在嵌入文档的数组中执行操作?

使用 Spring Data MongodB 更新嵌入式 mongodb 文档中的数组字段?

使用 MongoDB 中的文档属性过滤器获取嵌入数组中的扁平文档数组