9

我正在尝试学习 MongoDB,到目前为止它非常棒。但是我遇到了一种情况,我不太确定如何解决它。希望有人可以帮助我,并提前感谢。

我想获取(整个)数组值在查询中的记录。例如:

记录 1:

{"name" : "Mango Shake", 
 "ingredients" : [{"type" : "fruit", "name" : "mango"},
                  {"type" : "milk", "name" : "soy milk"}]}

记录 2:

{"name" : "Mango Banana Shake", 
 "ingredients" : [{"type" : "fruit", "name" : "mango"},
                  {"type" : "milk", "name" : "soy milk"},
                  {"type" : "fruit", "name" : "banana"}]}

记录 3:

{"name" : "Milk Shake", 
 "ingredients" : [{"type" : "milk", "name" : "soy milk"}]}

然后我会有一个类似的查询

{"ingredients" : {$all : [{"type" : "fruit", "name" : "mango"},
                          {"type" : "milk", "name" : "soy milk"},
                          {"type" : "fruit", "name" : "strawberry"}]}}

因为我有“芒果”“豆浆”“草莓”。所以我想知道我可以做哪些奶昔。显然这不会返回任何东西,因为查询不能有额外的东西。如果我使用$in,那么一切都会回来,但我不能做芒果香蕉奶昔,因为我没有香蕉..

所以我只需要第一个和最后一个。任何想法?欣赏它:)

4

8 回答 8

0

如果您愿意,Map reduce 将起作用,或者您可以简单地使用 $and 并为每种成分使用一个条件:

http://docs.mongodb.org/manual/reference/operator/and/

我之前使用过 $and 来满足类似的查询需求。不幸的是,在这两种情况下,每次查询时都必须检查每个配方文档,如果您的集合变大,这将对性能产生严重影响。

使用关系数据库可能会更好地处理此问题。至少您应该考虑保留一个单独的可查询成分索引,该索引具有到使用它们的配方 ID 的映射。然后,您可以仅检索您拥有的成分并在代码中获取配方的交集。同样,由于这实际上是关系数据库可以为您做的事情,它可能只是这里更好的选择。

于 2013-01-19T18:05:02.970 回答
0

您可以使用点符号来访问数组的特定元素并检查每个元素是否是$in您的可用成分列表,并且您可以构造一个$and查询来为正在查询的文档中的每个元素执行此操作,但当然这不起作用,因为您不这样做'不知道每个食谱的成分列表中有多少元素。

该问题的一种解决方案是向每个文档添加一个固定大小(可能是 3 个元素)的数组,将配方中的 n 个最不常见的成分放入其中,填充(必要时重复以填充数组)。现在您可以使用$and$in针对该数组的每个元素进行查询。这将找到所有“可能”的食谱,并且由于您存储了最不常见的成分,它应该可以很好地过滤掉您无法制作的食谱。然后,您可以在客户端执行最终过滤器。

有时添加不同的数据表示形式可能是解决此类棘手查询问题的好方法,有时结合使用服务器和客户端过滤可能是合适的。

于 2013-01-19T20:18:49.607 回答
0

我想不出用 find 来做这件事的方法。但是您可以使用mapReduce来做到这一点。

您的 map 函数将迭代当前检查的文档的成分,并且仅在所有字段都是您拥有的成分时才发出它。您不需要为此使用 reduce 函数,因此发出值的键应该是饮料文档的 _id。

当键是唯一的时,绝不应该调用 reduce 函数(reduce 函数用于确定当为同一个键发出多个值时会发生什么)。我不认为你可以省略 reduce 函数,所以你应该编写一个只返回 values 数组的第一个元素的函数。

于 2012-10-15T09:11:36.650 回答
0

为了带来一些新的想法,如果你有一套已知的所有成分。例如,可能的成分只有 5 种:

["mango","soy milk", "strawberry", "banana", "pineapple"]

你既没有香蕉也没有菠萝

一种选择是查询您没有的成分:

{"ingredients" : {$nin : [{"type" : "fruit", "name" : "banana"},
                          {"type" : "fruit", "name" : "pineapple"}]}}

这将返回所有没有您没有的成分的文件,因此,包含您实际拥有的成分的收据。

于 2014-11-05T17:58:34.910 回答
0

您可以使用 javascript 函数作为查找参数。但是,我认为 MapReduce 总体上会是一个更优雅的解决方案,因为它具有可伸缩性,以及将用户输入插入到执行的代码中所带来的普遍不安。

这是将要构建的示例查询。基本上,它过滤项目的成分并检查过滤列表的长度是否与原始成分列表相同:

db.shakes.find(function() {

  // Implant your query JSON object here
  var query_obj = [{"type" : "fruit", "name" : "mango"},
                   {"type" : "milk", "name" : "soy milk"},
                   {"type" : "fruit", "name" : "strawberry"}];  
  return this.ingredients.filter(function(ingredient) {
    var has_ingredients = false;
    query_obj.forEach(function(query) {
      has_ingredients |= (query.type == ingredient.type && query.name == ingredient.name);
    });
    return has_ingredients;
  }).length === this.ingredients.length;
})
于 2013-03-12T19:53:45.647 回答
0

没有直接的方法可以做到这一点。您可以构建一个简单的 javascript 函数来解决您的查询,方法是查看每个文档并检查数组成分是否包含所有三种成分。

db.shakes.find().forEach(

function (shakes) {
    for (i = 0; i < shakes.ingredients.length; i++) {
        switch (shakes.ingredients[i].name) {
        case "banana":
            var count = 1;
        case "soy milk":
            count++;
        case "mango":
            count++;
        }
        if (count == 3) {
            print(shakes.name);
        }
    }
}
);
于 2014-05-02T06:07:22.300 回答
0

假设您对使用$whereoperator的注意事项没有意见,您可以将它与Array.prototype.someArray.prototype.every结合使用以获得所需的结果。

这看起来像是一个特定于 javascript 的解决方案,但事实并非如此。javascript 被直接注入到 Mongo (V8) 中。所有语言驱动程序都应该支持它。我至少知道PHP 可以

您应该能够将此代码直接粘贴到Mongo REPL中并获得您期望的结果(敲木头)。

如果您的奶昔数据库具有超出$where您所能提供的扩展的潜力,那么聚合框架就是您的朋友。

var query = {
    "$where": function() {
        var myIngredients = [
            {"type" : "fruit", "name" : "mango"},
            {"type" : "milk", "name" : "soy milk"},
            {"type" : "fruit", "name" : "strawberry"}
        ];
        return obj.ingredients.every(function(milkShakeIngredient){
            return myIngredients.some(function(myIngredient){
                return ( milkShakeIngredient.name === myIngredient.name &&  milkShakeIngredient.type === myIngredient.type );
            });
        });
    }
};

db.milkShakes.find(query);
于 2014-12-12T21:15:06.647 回答
0

您可以为此使用聚合管道。

使用$addFields 为当前的奶昔创建missing包含缺失成分的元素($setDifference将帮助您获取缺失的成分)。然后用$match过滤掉所有没有缺失成分的奶昔。

查询示例:

db.getCollection('shakes').aggregate([
    {
        $addFields: {
            'missing': {
                $setDifference: [
                    "$ingredients",
                    [
                        {"type" : "fruit", "name" : "mango"},
                        {"type" : "milk", "name" : "soy milk"},
                        {"type" : "fruit", "name" : "strawberry"}
                    ]
                ]
            }
        }
    },
    { $match: {'missing': []} }
])
于 2019-12-10T15:03:59.437 回答