【问题标题】:Query to Match on nth Document of an Array查询以匹配数组的第 n 个文档
【发布时间】:2016-04-06 06:55:00
【问题描述】:

我是 MongoDB 的新手,我正在做一些练习。特别是我被困在this exercise 上,我在这里报告了这个问题:

给定文档“Restaurant”的以下结构:

{
        "_id" : ObjectId("5704adbc2eb7ebe23f582818"),
        "address" : {
                "building" : "1007",
                "coord" : [
                        -73.856077,
                        40.848447
                ],
                "street" : "Morris Park Ave",
                "zipcode" : "10462"
        },
        "borough" : "Bronx",
        "cuisine" : "Bakery",
        "grades" : [
                {
                        "date" : ISODate("2014-03-03T00:00:00Z"),
                        "grade" : "A",
                        "score" : 2
                },
                {
                        "date" : ISODate("2013-09-11T00:00:00Z"),
                        "grade" : "A",
                        "score" : 6
                },
                {
                        "date" : ISODate("2013-01-24T00:00:00Z"),
                        "grade" : "A",
                        "score" : 10
                },
                {
                        "date" : ISODate("2011-11-23T00:00:00Z"),
                        "grade" : "A",
                        "score" : 9
                },
                {
                        "date" : ISODate("2011-03-10T00:00:00Z"),
                        "grade" : "B",
                        "score" : 14
                }
        ],
        "name" : "Morris Park Bake Shop",
        "restaurant_id" : "30075445"
}

编写一个 MongoDB 查询来查找餐厅 ID、名称和等级,这些餐厅的等级数组的第二个元素包含等级“A”并且在 ISODate“2014-08-11T00:00:00Z”上得分为 9。

我写了这个查询:

db.restaurants.find(
{
    'grades.1': {
        'score': 'A',
        'grade': 9,
        'date' : ISODate("2014-08-11T00:00:00Z")
    }
},
{
    restaurant_id: 1,
    name: 1,
    grades: 1
});

这不起作用。 提供的解决方案如下:

db.restaurants.find( 
    { "grades.1.date": ISODate("2014-08-11T00:00:00Z"), 
      "grades.1.grade":"A" , 
      "grades.1.score" : 9
    }, 
    {"restaurant_id" : 1,"name":1,"grades":1}
);

我的问题是:

  1. 有没有办法编写查询避免重复grades.1 部分?
  2. 既然grades.1 是一个文档对象,为什么我的查询有误?

如果它可以帮助回答我的问题,我正在使用MongoDB shell version: 3.2.4

编辑

感谢this question,我找到了问题 2 的答案。

我特别发现顺序很重要。事实上,如果我执行以下查询,我会得到一个有效的结果:

db.restaurants.find({'grades.1': {'date': ISODate("2014-08-11T00:00:00Z"), 'grade':'A', score:9}}, {restaurant_id:1, name:1, grades:1})

请注意,此查询之所以有效,是因为指定了所有子文档的“字段”,并且它们以相同的顺序指定。

【问题讨论】:

  • 这不是一个有效的文档,因为字段名不能以$开头
  • 没错,他们提供了错误的结构。我用真实的纠正了它
  • 您也应该使用有效的date,因为它出现在您的查询条件中。
  • @user3100115 我现在粘贴了db.restaurants.find().limit(1) 的结果,所以你有一个有效且格式正确的文档

标签: javascript mongodb mongodb-query aggregation-framework


【解决方案1】:

不是真的。但也许是对您“可以”做什么的解释:

  db.junk.find({
    "grades": { 
      "$elemMatch": {
        "date" : ISODate("2014-03-03T00:00:00Z"),
        "grade" : "A",
        "score" : 2
      }
    },
    "$where": function() {
      var grade = this.grades[0];
      return (
        grade.date.valueOf() == ISODate("2014-03-03T00:00:00Z").valueOf() &&
        grade.grade === "A" &&
        grade.score ==== 2
      )
    }
  })

$elemMatch 允许您缩短一点,但它不是数组的“第 n 个”元素。为了进一步缩小范围,您需要使用 $where 子句检查“第 n 个”数组元素以查看是否所有值都匹配。

  db.junk.aggregate([
    { "$match": {
      "grades": { 
        "$elemMatch": {
          "date" : ISODate("2014-03-03T00:00:00Z"),
          "grade" : "A",
          "score" : 2
        }
      }
    }},
    { "$redact": {
      "$cond": {
        "if": {
          "$let": {
            "vars": { "grade": { "$arrayElemAt": [ "$grades", 0 ] } },
            "in": {
              "$and": [
                { "$eq": [ "$grade.date", ISODate("2014-03-03T00:00:00Z") ] },
                { "$eq": [ "$grade.grade", "A" ] },
                { "$eq": [ "$grade.score", 2 ] }
              ]
            }
          }
        },
        "then": "$$KEEP",
        "else": "$$PRUNE"
      }
    }}
  ])

您可以使用$redact 执行相同的逻辑,也可以使用.aggregate()。它运行得快了一点,但现在基本的真相应该很清楚了。

因此,使用"dot notation" 为数组中的每个元素指定“第n 个”位置,就像您已经完成的那样最有效和“简短”的编写方式。你不能让它更短或更好。

您的另一个尝试是在"grades.1" 中查找与您提供的文档条件完全匹配的“文档”。如果出于某种原因这些不是 only 字段,或者如果它们在存储的文档中确实处于“不同的顺序”,那么这样的查询条件 将不会匹配。

【讨论】:

  • 谢谢!我正在寻找一种方法来避免重复“不必要的”重复(或者,至少,我认为这是不必要的),节省一些字符。不幸的是,似乎为了保存字符,我需要重复所有条件:) 但是,在这种特殊情况下,我可以使用附加到我的问题的查询来保存一些字符。编辑:我刚刚看到你对答案的编辑!
  • @user2340612 所以“点表示法”是最简洁和高效的方法。如果你能以某种方式做到"grades.0": { "$elemMatch": { ... } },那将是“很好”的,但当然你不能,因为此时该元素已经被“尊重”为“对象”。 $elemMatch 和“点符号”都存在以解决“精确匹配”问题。
  • 是的,像"grades.0": {"$elemMatch": {...} } 这样的解决方案,具有“点表示法”(w.r.t.“完全匹配”问题)的优点和“完全匹配”表示法的简洁性在这方面会很棒案例
猜你喜欢
  • 2020-11-17
  • 2018-08-20
  • 2013-06-24
  • 1970-01-01
  • 2019-06-22
  • 2013-11-22
  • 2021-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多