mongo组查询如何保留字段


97

大家 在mongo组查询中,结果仅显示参数中的键。如何在每个组(如mysql查询组)中保留第一个文档。例如:

-------------------------------------------------------------------------
|  name  | age  |  sex  | province |   city   |   area   |   address     |
-------------------------------------------------------------------------
| ddl1st | 22   | 纯爷们 |  BeiJing |  BeiJing | ChaoYang | QingNianLu    |
| ddl1st | 24   | 纯爷们 |  BeiJing |  BeiJing | XuHui    | ZhaoJiaBangLu |
|  24k   | 220  | ...   |  ....    |  ...     | ...      | ...           |
-------------------------------------------------------------------------



db.users.group({key: { name: 1},reduce: function ( curr, result ) { result.count ++ },initial: {count : 0 } })

结果:

[
{
    "name" : "ddl1st",
    "count" : 1
},
{
    "name" : "24k",
    "count" : 1
}
]

如何获得以下内容:

[
   {
   "name" : "ddl1st",
   "age" : 22,
   "sex" : "纯爷们",
   "province" : "BeiJing",
   "city" : "BeiJing",
   "area" : "ChaoYang",
   "address" : "QingNianLu",
   "count" : 1
   },
   {
   "name" : "24k",
   "age" : 220,
   "sex" : "...",
   "province" : "...",
   "city" : "...",
   "area" : "...",
   "address" : "...",
   "count" : 1
}
]

Answers:


215

如果要保留有关每个组的第一个匹配条目的信息,则可以尝试汇总:

db.test.aggregate({
  $group: {
   _id : '$name',
   name : { $first: '$name' },
   age : { $first: '$age' },
   sex : { $first: '$sex' },
   province : { $first: '$province' },
   city : { $first: '$city' },
   area : { $first: '$area' },
   address : { $first: '$address' },
   count : { $sum: 1 }
  }
}

4
为什么需要{$first: '$age'}等?有可能有age: $age吗?
lightalchemist

7
@lightalchemist这是不可能的。让“分组”知道选择什么是种技巧。
TechWisdom

4
如果不是将这个聚合计算成年龄的$ max或$ min怎么办?$ first不一定与其他字段的最小或最大年龄相匹配。那该如何处理呢?
朱利奥马克

2
这是行不通的,它会按不需要的其他字段进行分组。
杰克·科尔

1
@Juliomac,我相信如果您想要的输出是$ max / $ min并保留不在$group_id中的字段,则可以$sort在使用所需字段之前对任何字段进行分组和使用$first$last运算符。累积时,包含其他字段(累积/漏斗/归约)的想法即使从理论上讲也没有太大意义。但是,与排序自己内部的每个组相比,事前排序实际上效率低下,因为排序算法比O(n)更复杂。我希望MongoDB中会有更好的方法。
Vemulo

16

顺便说一句,如果您不仅要保留第一个文档,还可以使用$ addToSet 例如:

db.test.aggregate({
  $group: {
    _id: '$name',
    name : { $addToSet: '$name' }
    age : { $addToSet: '$age' },
    count: { $sum: 1 }
  }
}

1
谢谢!变得更好(避免使Set与顺序混乱):数据:{$ addToSet:{name:'$ name',_ id:'$ _ id',年龄:'$ age'}}}
Benoit

14

[编辑后加入评论建议]

我来到这里寻找答案,但对所选答案不满意(特别是考虑到年龄)。我发现这个答案是更好的解决方案(适应):

db.test.aggregate({
  $group: {
    _id: '$name',
   person: { "$first": "$$ROOT" },
   count: { $sum: 1 }
  },
  {
    "$replaceRoot": { "newRoot": { "$mergeObjects": ["$person", { count: "$count" }]} }
  }
}

3
但是,你输了count。您需要使用$mergeObjects它来保留它。
下午4点34分0zkr

1
为了详细说明0zkr有关使用$ mergeObjects的评论并帮助他人使用该语法,最后一个管道语法为{"$replaceRoot": {"newRoot": {"$mergeObjects": ["$person", {count: "$count"}]}}}
Jerren Saunders

7

你可以试试看

db.test.aggregate({
      { $group: 
            { _id: '$name',count: { $sum: 1 }, data: { $push: '$$ROOT' } } },
      {
        $project: {
          _id:0,
          data:1,
          count :1
        }
      }

}

4

这是我所做的,效果很好。

db.person.aggregate([
{
  $group: { _id: '$name'}, // pass the set of field to be grouped
   age : { $first: '$age' }, // retain remaining field
   count: { $sum: 1 } // count based on your group
},
{
  $project:{
       name:"$_id.name",
       age: "$age",
       count: "$count",
       _id:0 
  }
}])

4

如果涉及多个字段的文档面临相同的问题,请快速更新。可以使用结合$replaceRoot流水线阶段和$mergeObjects流水线操作员的力量。

db.users.aggregate([
  {
    $group: {
      _id: '$name',
      user: { $first: '$$ROOT' },
      count: { $sum: 1 }
    },
  },
  {
    $replaceRoot: {
      newRoot: { $mergeObjects: [{ count: '$count' }, '$user'] }
    }
  }
])

4

使用$first$$ROOT文档,然后用$replaceRoot与第一场。

db.test.aggregate([
  { "$group": {
    "_id": "$name",
    "doc": { "$first": "$$ROOT" }
  }},
  { "$replaceRoot": { "newRoot": "$doc" }}
])

这很有帮助!谢谢!!我已经搜寻了一段时间,但找不到确切的需求。这太完美了!
学生灵魂

这个答案是“切题”的,而且是完美的。谢谢!
M. Nunisa

1

我不知道.grouphelper,但是如果您喜欢使用Aggregation Framework,则必须指定要返回的字段。如果我错了,请纠正我,但是在SQL中,您还是必须这样做。

好吧,这就是您使用前面提到的聚合框架来执行的操作:

db.test.aggregate({
  $group: {
    _id: { name: "$name", city: "$city", fieldName: "$fieldName" },
    count: { $sum: 1 }
  }
})

10
谢谢你的帮助。在此查询中是组指定的字段,我只想按一个字段分组,然后导致其他人指定字段。有什么好主意吗?
2013年

1

我创建此函数的目的是为了概括放松阶段的步伐...让我知道你们是否遇到任何错误,但对我来说很好用!

const createReverseUnwindStages = unwoundField => {
  const stages = [
    //
    // Group by the unwound field, pushing each unwound value into an array,
    //
    // Store the data from the first unwound document
    // (which should all be the same apart from the unwound field)
    // on a field called data.
    // This is important, since otherwise we have to specify every field we want to keep individually.
    //
    {
      $group: {
        _id: '$_id',
        data: {$first: '$$ROOT'},
        [unwoundField]: {$push: `$${unwoundField}`},
      },
    },

    //
    // Copy the array of unwound fields resulting from the group into the data object,
    // overwriting the singular unwound value
    //
    {
      $addFields: {[`data.${unwoundField}`]: `$${unwoundField}`},
    },

    //
    // Replace the root with our data object
    //
    {
      $replaceRoot: {
        newRoot: '$data',
      },
    },
  ]

  return stages
}

当同一集合中的文档具有不同的字段名称时,这是最佳选择。
user7364588 '19

0

如果要投影所有字段,请在查询下面使用此文档。

db.persons.aggregate({
      { $group: { _id: '$name', data: { $push: '$$ROOT' }, total: { $sum: 1 }} },
      {
        $project: {
          _id:0,
          data:1,
          total :1
        }
      }
}

-1

这是答案>>>>

    $m = new \MongoDB\Driver\Manager();

    $command = new \MongoDB\Driver\Command([
        'aggregate' => 'mytestusers',
        'pipeline' => [
            ['$match' => ['name' => 'Pankaj Choudhary']],

            ['$unwind'=>'$skills'],
            ['$lookup' => array('from'=>'mytestskills','localField'=>'skills','foreignField'=>'_id','as'=>'sdfg')],
            ['$unwind'=>'$sdfg'],

            ['$group'=>array('_id'=>array('_id'=>'$_id','name'=>'$name','email'=>'$email'),'skills'=>array('$push'=>'$skills'),'sdfg'=>array('$push'=>'$sdfg'))],


        ],
        'cursor' => new \stdClass,
    ]);
    $cursor = $m->executeCommand('targetjob-plus', $command);
    $result = $cursor->toArray();

设置你的输入表请先
潘卡吉玛
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.