且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在MongoDB集合中查找匹配给定条件的文档和单个子文档

更新时间:2023-02-14 09:55:06

您正在寻找的是位置$ 运算符和投影".对于单个字段,您需要使用点符号"匹配所需的数组元素,对于多个字段,请使用 $elemMatch:

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

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

Or the $elemMatch for more than one matching field:

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

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

These work for a single array element only though and only one will be returned. If you want more than one array element to be returned from your conditions then you need more advanced handling with the aggregation framework.

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 以来更短/更快的形式,其中您的项目数组包含唯一条目:

Or possibly in shorter/faster form since MongoDB 2.6 where your array of items contains unique entries:

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,但有点做作:

Or possibly with $redact, but a little contrived:

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

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

More modern, you would use $filter:

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

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

And with multiple conditions, the $elemMatch and $and within the $filter:

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() 方法通常会更快,因为它没有其他操作的开销,而在那些 last to 表单中,它根本不会落后太多.

So it just depends on whether you always expect a single element to match or multiple elements, and then which approach is better. But where possible the .find() method will generally be faster since it lacks the overhead of the other operations, which in those last to forms does not lag that far behind at all.

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

As a side note, your "dates" are represented as strings which is not a very good idea going forward. Consider changing these to proper Date object types, which will greatly help you in the future.