mongodb $unwind 聚合框架中的对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11189243/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 12:42:59  来源:igfitidea点击:

$unwind an object in aggregation framework

mongodbaggregation-framework

提问by Brett Thomas

In the MongoDB aggregation framework, I was hoping to use the $unwind operator on an object (ie. a JSON collection). Doesn't look like this is possible, is there a workaround? Are there plans to implement this?

在 MongoDB 聚合框架中,我希望在对象(即 JSON 集合)上使用 $unwind 运算符。看起来这不可能,有解决方法吗?有计划实施吗?

For example, take the article collection from the aggregation documentation. Suppose there is an additional field "ratings" that is a map from user -> rating. Could you calculate the average rating for each user?

例如,从聚合文档中获取文章集合。假设有一个附加字段“ratings”,它是来自用户 -> rating 的地图。你能计算出每个用户的平均评分吗?

Other than this, I'm quite pleased with the aggregation framework.

除此之外,我对聚合框架非常满意。

Update: here's a simplified version of my JSON collection per request. I'm storing genomic data. I can't really make genotypes an array, because the most common lookup is to get the genotype for a random person.

更新:这是我每个请求的 JSON 集合的简化版本。我正在存储基因组数据。我不能真正将基因型作为数组,因为最常见的查找是获取随机人的基因型。

variants: [

    {
        name: 'variant1', 
        genotypes: {

            person1: 2,
            person2: 5,
            person3: 7,

        }
    }, 

    {
        name: 'variant2', 
        genotypes: {

            person1: 3,
            person2: 3,
            person3: 2,

        }
    }

]

回答by Asya Kamsky

It is not possible to do the type of computation you are describing with the aggregation framework - and it's notbecause there is no $unwindmethod for non-arrays. Even if the person:value objects were documents in an array, $unwindwould not help.

不可能使用聚合框架进行您所描述的计算类型 - 这不是因为没有$unwind非数组的方法。即使 person:value 对象是数组中的文档,$unwind也无济于事。

The "group by" functionality (whether in MongoDB or in any relational database) is done on the value of a field or column. We group by value of field and sum/average/etc based on the value of another field.

“分组依据”功能(无论是在 MongoDB 中还是在任何关系数据库中)是在字段或列的值上完成的。我们根据另一个字段的值按字段值和总和/平均值/等分组。

Simple example is a variant of what you suggest, ratings field added to the example article collection, but not as a map from user to rating but as an array like this:

简单示例是您建议的变体,将 ratings 字段添加到示例文章集合中,但不是作为从用户到 rating 的映射,而是作为这样的数组:

{ title : title of article", ...
  ratings: [
         { voter: "user1", score: 5 },
         { voter: "user2", score: 8 },
         { voter: "user3", score: 7 }
  ]
}

Now you can aggregate this with:

现在您可以将其汇总为:

[ {$unwind: "$ratings"},
  {$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } } 
]

But this example structured as you describe it would look like this:

但是这个例子如你所描述的那样结构化,它看起来像这样:

{ title : title of article", ...
  ratings: {
         user1: 5,
         user2: 8,
         user3: 7
  }
}

or even this:

甚至这个:

{ title : title of article", ...
  ratings: [
         { user1: 5 },
         { user2: 8 },
         { user3: 7 }
  ]
}

Even if you could $unwindthis, there is nothing to aggregate on here. Unless you know the complete list of all possible keys (users) you cannot do much with this. [*]

即使你能做到$unwind这一点,这里也没有什么可聚合的。除非您知道所有可能的密钥(用户)的完整列表,否则您对此无能为力。[*]

An analogous relational DB schema to what you have would be:

与您所拥有的类似的关系数据库架构是:

CREATE TABLE T (
   user1: integer,
   user2: integer,
   user3: integer
   ...
);

That's not what would be done, instead we would do this:

这不是我们要做的,而是我们会这样做:

CREATE TABLE T (
   username: varchar(32),
   score: integer
);

and now we aggregate using SQL:

现在我们使用 SQL 进行聚合:

select username, avg(score) from T group by username;

select username, avg(score) from T group by username;

There is an enhancement request for MongoDB that may allow you to do this in the aggregation framework in the future - the ability to project values to keys to vice versa. Meanwhile, there is always map/reduce.

对 MongoDB 的增强请求可能允许您将来在聚合框架中执行此操作 - 将值投影到键的能力,反之亦然。同时,总是有map/reduce。

[*] There is a complicated way to do this if you know all unique keys (you can find all unique keys with a method similar to this) but if you know all the keys you may as well just run a sequence of queries of the form db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1})for each userX which will return all their ratings and you can sum and average them simply enough rather than do a very complex projection the aggregation framework would require.

[*] 如果您知道所有唯一键(您可以使用与此类似的方法找到所有唯一键),则有一种复杂的方法可以做到这一点,但如果您知道所有键,您也可以运行一系列查询db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1})每个 userX 的表单将返回他们的所有评分,您可以简单地对它们进行求和和平均,而不是进行聚合框架所需的非常复杂的投影。

回答by Adrian

Since 3.4.4, you can transform object to array using $objectToArray

从 3.4.4 开始,您可以使用 $objectToArray 将对象转换为数组

See: https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/

请参阅:https: //docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/

回答by saarp

This is an old question, but I've run across a tidbit of information through trial and error that people may find useful.

这是一个老问题,但我通过反复试验发现了一些人们可能会觉得有用的信息。

It's actually possible to unwind on a dummy value by fooling the parser this way:

实际上可以通过以这种方式欺骗解析器来解除虚拟值:

db.Opportunity.aggregate(
  { $project: {
        Field1: 1, Field2: 1, Field3: 1,
        DummyUnwindField: { $ifNull: [null, [1.0]] }
    }
  },
  { $unwind: "$DummyUnwindField" }
);

This will produce 1 row per document, regardless of whether or not the value exists. You may be able tinker with this to generate the results you want. I had hoped to combine this with multiple $unwinds to (sort of like emit() in map/reduce), but alas, the last $unwind wins or they combine as an intersection rather than union which makes it impossible to achieve the results I was looking for. I am sadly disappointed with the aggregate framework functionality as it doesn't fit the one use case I was hoping to use it for (and seems strangely like a lot of the questions on StackOverflow in this area are asking) - ordering results based on match rate. Improving the poor map reduce performance would have made this entire feature unnecessary.

无论该值是否存在,这都会为每个文档生成 1 行。您可以修改它以生成您想要的结果。我曾希望将它与多个 $unwinds 结合起来(有点像 map/reduce 中的 emit()),但是唉,最后一个 $unwind 获胜,或者它们组合为一个交集而不是联合,这使得我无法实现我的结果正在寻找。我对聚合框架功能感到遗憾,因为它不适合我希望使用它的一个用例(并且似乎很奇怪,这方面的 StackOverflow 上的很多问题都在问)-根据匹配对结果进行排序速度。改善糟糕的地图减少性能会使整个功能变得不必要。

回答by vijay

This is what I found & extended.

这是我发现和扩展的。

Lets create experimental database in mongo

让我们在 mongo 中创建实验数据库

db.copyDatabase('livedb' , 'experimentdb')

Now Use experimentdb & convert Array to object in your experimentcollection

现在使用 Experimentdb 并将数组转换为您的实验集合中的对象

db.getCollection('experimentcollection').find({}).forEach(function(e){
    if(e.store){
        e.ratings = [e.ratings]; //Objects name to be converted to array eg:ratings
        db.experimentcollection.save(e);
    }
})

Some nerdy js code to convert json to flat object

一些书呆子js代码将json转换为平面对象

var flatArray = [];

var data = db.experimentcollection.find().toArray();

for (var index = 0; index < data.length; index++) {

  var flatObject = {};

  for (var prop in data[index]) {

    var value = data[index][prop];

    if (Array.isArray(value) && prop === 'ratings') {
      for (var i = 0; i < value.length; i++) {
        for (var inProp in value[i]) {
          flatObject[inProp] = value[i][inProp];
        }
      }
    }else{
        flatObject[prop] = value;
    }
  }
  flatArray.push(flatObject);
}

printjson(flatArray);