12

考虑以下 MongoDB“食谱”集合:

{
  "title" : "Macaroni and Cheese",
  "ingredients" : [
    { "name" : "noodles", "qty" : "2 c" },
    { "name" : "butter", "qty" : "2 tbl" },
    { "name" : "cheese", "qty" : "1 c" },
  ]
},
{
  "title" : "Pound Cake",
  "ingredients" : [
    { "name" : "sugar", "qty" : "1 lb" },
    { "name" : "butter", "qty" : "1 lb" },
    { "name" : "flour", "qty" : "1 lb" },
  ]
},
{
  "title" : "Dough",
  "ingredients" : [
    { "name" : "water", "qty" : "2 c" },
    { "name" : "butter", "qty" : "8 tbl" },
    { "name" : "flour", "qty" : "1 lb" },
  ]
}

我希望编写一个查询来生成要购买的物品的“购物清单”以制作所有食谱。所以我基本上想退回配料“面条”、“黄油”、“奶酪”、“糖”、“黄油”、“面粉”、“水”。我不想重复。(例如,糖和黄油出现在不止一个食谱中,但我只想退回一次,即没有重复。)

是否可以在 MongoDB 中创建这样的查询,如果可以,该查询会是什么?还是需要我为“成分”创建一个单独的集合?

4

4 回答 4

35

用于distinct查找不同值的数组ingredients.name

db.recipes.distinct('ingredients.name')

产量[ "butter", "cheese", "noodles", "flour", "sugar", "water" ]

于 2013-11-12T08:07:58.387 回答
13

以下查询将为您提供减去任何重复项的裸列表:

db.recipes.aggregate([{$unwind:"$ingredients"},{$group:{_id:"$ingredients.name"}}])

需要更多的工作来增加数量。如果单独指定数量的单位会更容易。

于 2013-11-12T08:01:02.000 回答
4

这是您正在寻找的完整聚合管道:

db.recipes.aggregate([
       {$unwind:"$ingredients"},
       {$group:{_id:"$ingredients.name",
                quantity:{$first:"$ingredients.qty"},
                recipes:{$push:"$title"},
                total:{$sum:1}
               }
       },
       {$project:{_id:0,ingredient:"$_id", total:1, quantity:1, recipes:1}
])

这将展开数组,按成分分组,将它们相加以查看有多少食谱需要它们,保持用例中相同的数量字段,并添加它使用的食谱列表。最后一步重命名按字段分组为“成分”。

结果:

{ "quantity" : "1 lb", "recipes" : [  "Pound Cake",  "Dough" ], "total" : 2, "ingredient" : "flour" }
{ "quantity" : "2 c", "recipes" : [  "Dough" ], "total" : 1, "ingredient" : "water" }
{ "quantity" : "1 lb", "recipes" : [  "Pound Cake" ], "total" : 1, "ingredient" : "sugar" }
{ "quantity" : "1 c", "recipies" : [  "Macaroni and Cheese" ], "total" : 1, "ingredient" : "cheese" }
{ "quantity" : "2 tbl", "recipes" : [  "Macaroni and Cheese",  "Pound Cake",  "Dough" ], "total" : 3, "ingredient" : "butter" }
{ "quantity" : "2 c", "recipes" : [  "Macaroni and Cheese" ], "total" : 1, "ingredient" : "noodles" }
于 2013-11-12T17:19:32.520 回答
2

foreach使用运算符在 mongoshell 中执行此操作的一种有趣方式

var list = [];
db.recepies.find(
  {},
  {'ingredients.name' : 1, _id : 0}
).forEach(function(doc){
  var ingredients = doc.ingredients;
  for (var i=0; i< ingredients.length; i++){
    var ingredient = ingredients[i].name;
    if (list.indexOf(ingredient) == -1){
       list.push(ingredient)
    }
  }
});

之后list将包含所有元素。PS我相信这也可以通过聚合框架实现。

于 2013-11-12T08:00:07.737 回答