mongoグループクエリフィールドの保持方法


100

みんな。mongo groupクエリでは、結果には引数のキーのみが表示されます。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
}
]

回答:


221

各グループの最初に一致するエントリに関する情報を保持する場合は、次のように集計してみてください。

    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 2017

7
@lightalchemistそれは不可能です。「グループ」に何を選択するかを知らせるのは一種のトリックです。
techWisdom 2018年

4
カウントする代わりに、この集計が年齢に対して$ maxまたは$ minを実行していた場合はどうなりますか?$ firstは、他のフィールドで見つかった最小および最大年齢と必ずしも一致しません。それではどう対処するのですか?
Juliomac 2018年

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で注文を台無しにしないでください):data:{$ addToSet:{name: '$ name'、_ id: '$ _id'、age: '$ age'}}
Benoit

16

[コメントの提案を含めるように編集]

私は答えを探してここに来ましたが、選択された答えに満足していませんでした(特に年齢を考えると)。私はより良い解決策(適応)であるこの答えを見つけました:

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

3
しかし、あなたはcountフィールドを失います。あなたは$mergeObjectsそれを維持するために使用する必要があります。
0zkrPM20年

1
$ mergeObjectsの使用に関する0zkrのコメントを詳しく説明し、他の人の構文を支援するには、最後のパイプライン構文は次のようになります{"$replaceRoot": {"newRoot": {"$mergeObjects": ["$person", {count: "$count"}]}}}
JerrenSaunders20年

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.Nunisa20年

1

.groupヘルパーについては知りませんでしたが、Aggregation Frameworkを使用する場合は、返すフィールドを指定する必要があります。私が間違っている場合は訂正してください。ただし、SQLではとにかくそれを行う必要があります。

さて、これはあなたが前述のアグリゲーションフレームワークでそれを行う方法です:

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

10
助けてくれてありがとう。このクエリでは、グループ指定のフィールドがあります。1つのフィールドでグループ化するだけで、他の人がフィールドを指定します。何か良いアイデアはありますか?
プラスまたは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
}

同じコレクション内のドキュメントにさまざまなフィールド名がある場合に最適です。
user73645 8819

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

最初に入力テーブルを設定してください
Pankaj Cheema 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.