【问题标题】:How to find document and single subdocument matching given criterias in MongoDB collection如何在 MongoDB 集合中查找与给定条件匹配的文档和单个子文档
【发布时间】:2014-10-24 13:33:23
【问题描述】:

我收集了一些产品。每个产品都包含一系列项目。

> db.products.find().pretty()
{
    "_id" : ObjectId("54023e8bcef998273f36041d"),
    "shop" : "shop1",
    "name" : "product1",
    "items" : [
            {
                    "date" : "01.02.2100",
                    "purchasePrice" : 1,
                    "sellingPrice" : 10,
                    "count" : 15
            },
            {
                    "date" : "31.08.2014",
                    "purchasePrice" : 10,
                    "sellingPrice" : 1,
                    "count" : 5
            }
    ]
}

那么,您能否给我一个建议,我如何查询 MongoDB 以检索所有产品,其中只有一个项目,其日期等于我作为参数传递给查询的日期。

“31.08.2014”的结果必须是:

    {
    "_id" : ObjectId("54023e8bcef998273f36041d"),
    "shop" : "shop1",
    "name" : "product1",
    "items" : [
            {
                    "date" : "31.08.2014",
                    "purchasePrice" : 10,
                    "sellingPrice" : 1,
                    "count" : 5
            }
    ]
}

【问题讨论】:

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

您正在寻找的是the positional $ 运算符和“投影”。对于单个字段,您需要使用“点表示法”匹配所需的数组元素,对于多个字段,请使用 $elemMatch:

db.products.find(
    { "items.date": "31.08.2014" },
    { "shop": 1, "name":1, "items.$": 1 }
)

或$elemMatch 用于多个匹配字段:

db.products.find(
    { "items":  { 
        "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
    }},
    { "shop": 1, "name":1, "items.$": 1 }
)

这些仅适用于单个数组元素,并且只会返回一个。如果您希望从条件中返回多个数组元素,那么您需要使用聚合框架进行更高级的处理。

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$unwind": "$items" },
    { "$match": { "items.date": "31.08.2014" } },
    { "$group": {
        "_id": "$_id",
        "shop": { "$first": "$shop" },
        "name": { "$first": "$name" },
        "items": { "$push": "$items" }
    }}
])

或者可能是自 MongoDB 2.6 以来更短/更快的形式,其中您的项目数组包含唯一条目:

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$project": {
        "shop": 1,
        "name": 1,
        "items": {
            "$setDifference": [
                { "$map": {
                    "input": "$items",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.date", "31.08.2014" ] },
                            "$$el",
                            false 
                        ]
                    }
                }},
                [false]
            ]
        }
    }}
])

或者可能是$redact,但有点做作:

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$redact": {
        "$cond": [
             { "$eq": [ { "$ifNull": [ "$date", "31.08.2014" ] }, "31.08.2014" ] },
             "$$DESCEND",
             "$$PRUNE"
         ]
    }}
])

更现代的,你会使用$filter:

db.products.aggregate([
  { "$match": { "items.date": "31.08.2014" } },
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { "$eq": [ "$$this.date", "31.08.2014" ] }
    }
  }}
])

在多个条件下,$filter 中的 $elemMatch 和 $and:

db.products.aggregate([
  { "$match": { 
    "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
  }},
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { 
        "$and": [
          { "$eq": [ "$$this.date", "31.08.2014" ] },
          { "$eq": [ "$$this.purchasePrice", 1 ] }
        ]
      }
    }
  }}
])

所以这取决于您是否总是期望单个元素匹配或多个元素,然后哪种方法更好。但在可能的情况下,.find() 方法通常会更快,因为它没有其他操作的开销,而在最后的表单中,这些操作根本不会落后那么远。

作为旁注,您的“日期”表示为字符串,这不是一个很好的主意。考虑将这些更改为正确的 Date 对象类型,这将对您将来有很大帮助。

【讨论】:

    【解决方案2】:

    Mongo 支持子查询的点表示法。

    见:http://docs.mongodb.org/manual/reference/glossary/#term-dot-notation

    根据您的驱动程序,您需要以下内容:

    db.products.find({"items.date":"31.08.2014"});
    

    请注意,该属性在点符号的引号中,即使您的驱动程序通常不需要这个。

    【讨论】:

    • 不幸的是,这个答案是错误的,因为它同时找到了items,但是OP要求我们只找到item和date === "31.08.2014"。
    【解决方案3】:

    基于我使用此解决方案的Neil Lunn's 代码,它自动包含所有第一级键(但您也可以根据需要排除键):

    db.products.find(
        { "items.date": "31.08.2014" },
        { "shop": 1, "name":1, "items.$": 1 }
        { items: { $elemMatch: { date: "31.08.2014" } } },
    )
    

    有多个要求:

    db.products.find(
        { "items":  { 
            "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
        }},
        { items: { $elemMatch: { "date": "31.08.2014",  "purchasePrice": 1 } } },
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-08
      • 1970-01-01
      • 2020-07-25
      • 2018-08-20
      • 1970-01-01
      • 2019-11-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多