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
}
]

답변:


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 }
  }
}

3
{$first: '$age'}등 이 필요 합니까? 그냥 가질 수 age: $age있습니까?
lightalchemist

7
@lightalchemist 불가능합니다. '그룹'에게 무엇을 선택해야할지 알려주는 일종의 속임수입니다.
TechWisdom

4
count 대신이 집계가 연령에 대해 $ max 또는 $ min을 수행하면 어떨까요? $ first는 다른 필드에서 찾은 최소 또는 최대 연령과 반드시 ​​일치하지는 않습니다. 그럼 어떻게 처리할까요?
Juliomac

2
이것은 작동하지 않으며 원하지 않는 다른 필드별로 그룹화됩니다.
Jack Cole

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
감사! 더 나아졌습니다 (세트로 주문을 엉망으로 만들지 마십시오) : data : {$ addToSet : {name : '$ name', _id : '$ _id', age : '$ age'}}
Benoit

14

[댓글 제안을 포함하도록 수정 됨]

나는 답을 찾기 위해 여기에 왔지만 선택한 답변에 만족하지 않았습니다 (특히 나이를 감안할 때). 더 나은 솔루션 (적응) 이 답변 을 찾았습니다 .

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

3
그러나 당신은 count필드 를 잃습니다 . 당신은 $mergeObjects그것을 유지하기 위해 사용해야 합니다.
0zkr PM

1
$ mergeObjects 사용에 대한 0zkr의 의견에 대해 자세히 설명하고 구문에 대해 다른 사람들을 돕기 위해 마지막 파이프 라인 구문은{"$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" }}
])

이것은 매우 도움이되었습니다! 감사합니다!! 한동안 찾고 있었지만 정확히 필요한 것을 찾을 수 없었습니다. 완벽 했어요!
The Student Soul

이 대답은 '정말'이며 완벽합니다. 감사합니다!
M. Nunisa

1

.group헬퍼에 대해서는 몰랐지만 Aggregation Framework 를 선호하는 경우 반환 할 필드를 지정해야합니다. 내가 틀렸다면 저를 정정하십시오. 그러나 SQL에서는 어쨌든 그렇게해야합니다.

음, 이것은 앞에서 언급 한 집계 프레임 워크를 사용하여 수행하는 방법입니다.

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

10
도와 주셔서 감사합니다. 이 쿼리에서 그룹 지정 필드는 한 필드로 그룹화하고 다른 필드를 지정하는 결과를 원합니다. 좋은 생각 없어?
plusor '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

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();

사용자의 입력 테이블이 먼저하시기 바랍니다 설정
판 카즈 Cheema에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.