2014-08-30 57 views
11

我有产品集合。每个产品都包含一系列项目。如何在MongoDB集合中找到与给定标准匹配的文档和单个子文档

> 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的查询检索与该日期是等于我通过查询作为参数的日期只有单个项目的所有产品。

为 “2014年8月31日” 的结果必然是:

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

从3.2开始使用'$ filter'。更多在这里https://stackoverflow.com/questions/3985214/retrieve-only-the-queried-element-in-an-object-array-in-mongodb-collection – Veeram 2017-12-13 19:47:49

回答

26

你所寻找的是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" 
     ] 
    }} 
]) 

所以它只取决于你是否总希望单个元素匹配或多个元素,然后哪种方法更好。但是,如果可能的话,.find()方法通常会更快,因为它缺少其他操作的开销,而这些操作在最后一种形式中根本不落后。

作为一个便笺,你的“日期”被表示为字符串,这不是一个好主意。考虑将这些改为适当的Date对象类型,这对你将来会有很大的帮助。

+0

谢谢!很好的答案!这绝对是我正在寻找的,甚至更多。 – evgeniy44 2014-08-31 11:39:59

+0

谢谢。你已经详细解释了它 – 2017-12-30 08:38:15

相关问题