【问题标题】:$projection vs $elemMatch$projection 与 $elemMatch
【发布时间】:2016-12-03 10:09:09
【问题描述】:

两者的功能区别是什么:

db.docs.find({ 'a.b': 'c' }, { 'a.$': 1 })

db.docs.find({ 'a.b': 'c' }, { 'a': { $elemMatch: { b: 'c' } } })

$elemMatch 是多余的吗?索引将如何变化?

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    投影使用的差异有些微妙。在您的示例用法中,这些应该是等效查询(就索引使用而言),但 $elemMatch 示例不必要地重复查询条件。对于此示例,$ 投影将是更明智的选择。

    文档中提到的一个本质区别是array field limitation 用于$ 投影:

    由于查询文档中只能出现一个数组字段,如果该数组包含文档,要对这些文档的多个字段指定条件,请使用 $elemMatch 运算符。

    关于以下投影运算符差异的一些进一步说明...

    positional ($) projection operator:

    • 将包含在查询结果中的数组字段的内容限制为包含与查询文档匹配的第一个元素。

    • 要求匹配的数组字段包含在查询条件中

    • 只能在查询条件中出现单个数组字段时使用

    • 在投影中只能使用一次

    $elemMatch projection operator

    • 将包含在查询结果中的数组字段的内容限制为仅包含第一个与 $elemMatch 条件匹配的数组元素

    • 不要求匹配的数组在查询条件中

    • 可用于为嵌入文档的数组元素匹配多个条件

    $elemMatch query operator

    请注意,还有一个 $elemMatch 查询运算符执行类似的匹配,但在查询中而不是在结果投影中。将此与 $ 投影结合使用的情况并不少见。

    借用example from the docs,您可以同时使用两者:

    db.students.find(
        // use $elemMatch query operator to match multiple criteria in the grades array
        { grades: {
            $elemMatch: {
                mean:  { $gt: 70 },
                grade: { $gt: 90 }
            }
        }},
    
        // use $ projection to get the first matching item in the "grades" array
        { "grades.$": 1 }
    )
    

    【讨论】:

      【解决方案2】:

      如果你单独使用 $elemMatch,在查找投影中,

      $elemMatch 不会过滤文档(条件不适用)

      假设,'students' 包含以下文档

      { "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] }
      { "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] }
      { "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] }
      { "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] }
      { "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] }
      { "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }
      {
          "_id" : 7,
          "semester" : 3,
          "grades" : [
              {
                  "grade" : 80,
                  "mean" : 75,
                  "std" : 8
              },
              {
                  "grade" : 85,
                  "mean" : 90,
                  "std" : 5
              },
              {
                  "grade" : 90,
                  "mean" : 85,
                  "std" : 3
              }
          ]
      }
      {
          "_id" : 8,
          "semester" : 3,
          "grades" : [
              {
                  "grade" : 92,
                  "mean" : 88,
                  "std" : 8
              },
              {
                  "grade" : 78,
                  "mean" : 90,
                  "std" : 5
              },
              {
                  "grade" : 88,
                  "mean" : 85,
                  "std" : 3
              }
          ]
      }
      

      下面的查询将返回集合中的所有文档,

      db.students.find({},{ grades: { $elemMatch: { mean: 90 } } }  ).pretty()
      

      其中,下面的查询只返回匹配的记录

      db.students.find({"grades.mean":90},{"grades.$":1}).pretty()
      

      我希望索引不会对这两个查询产生任何影响

      【讨论】:

      • 我认为您将投影与查询混淆了:)。为了公平比较,您需要在两个查找示例中都有一个查询。
      • 可能是:(,如果$elemMatch(projection)不过滤文档,只用于返回第一个匹配元素,那么为什么要在嵌套查询中使用$elemMatch,我们可以简单地使用$project
      • $elemMatch 投影“可用于匹配作为嵌入文档的数组元素的多个条件”,正如@Stennie 在他的回答中指出的那样。老实说,我希望投影文档能够提供 Stennie 概述的更直接的比较。
      • 我对差异进行了更详细的回答。通常您可能希望使用$ 投影,但在某些用例中$elemMatch 更合适(例如,匹配数组元素的多个条件)。另请注意,当返回结果时,投影是在 document 级别完成的。对于您在所有文档上使用$elemMatch 的第一个示例,在您获得的结果中,您将看到_id 加上第一个匹配的grades 数组元素,或者单独看到_id(因为没有匹配的元素可以投影)。
      • @paulkon:同意,文档应该包含这种直接比较。我会提交一个相同的建议:)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多