ค้นหากลุ่ม mongo วิธีการเก็บฟิลด์


97

ทุกคน. ในการสืบค้นกลุ่ม Mongo ผลลัพธ์จะแสดงเฉพาะคีย์ในอาร์กิวเมนต์ วิธีเก็บเอกสารแรกในแต่ละกลุ่มเช่น mysql query group ตัวอย่างเช่น:

-------------------------------------------------------------------------
|  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
จะเกิดอะไรขึ้นถ้าแทนที่จะนับการรวมนี้ทำ $ max หรือ $ min สำหรับอายุ? $ แรกไม่จำเป็นต้องตรงกับอายุต่ำสุดหรือสูงสุดที่พบในช่องอื่น ๆ แล้วจะจัดการยังไงดี?
Juliomac

2
ไม่ได้ผลโดยจะจัดกลุ่มตามฟิลด์อื่น ๆ ที่ไม่ต้องการ
Jack Cole

1
@Juliomac ฉันเชื่อว่าหากผลลัพธ์ที่คุณต้องการคือ $ max / $ min และเก็บฟิลด์ที่ไม่ได้อยู่ใน$group_id ไว้$sortก่อนหน้านี้ด้วยฟิลด์ที่ต้องการจากนั้นจัดกลุ่มและใช้$firstหรือ$lastตัวดำเนินการในฟิลด์ใดก็ได้ เมื่อสะสมความคิดที่จะรวมช่องอื่น ๆ (ซึ่งสะสม / funneled / ลด) ไม่สมเหตุสมผลมากนักแม้ในทางทฤษฎี อย่างไรก็ตามการเรียงลำดับก่อนมือนั้นไม่มีประสิทธิภาพเมื่อเทียบกับการเรียงลำดับแต่ละกลุ่มภายในตัวเองเนื่องจากอัลกอริทึมการเรียงลำดับซับซ้อนกว่า 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 น

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

สิ่งนี้มีประโยชน์มาก! ขอบคุณ!! ฉันค้นหามาระยะหนึ่งแล้ว แต่ไม่พบสิ่งที่ต้องการ สมบูรณ์แบบ!
The Student Soul

คำตอบนี้ 'ตรงประเด็น' และสมบูรณ์แบบ ขอบคุณ!
ม. นูนิสา

1

ฉันไม่รู้เกี่ยวกับตัว.groupช่วย แต่ถ้าคุณต้องการใช้Aggregation Frameworkคุณจะต้องระบุฟิลด์ที่จะส่งคืน แก้ไขฉันถ้าฉันผิด แต่ใน SQL คุณจะต้องทำเช่นนั้นต่อไป

นี่คือวิธีที่คุณจะทำกับ Aggregation Framework ที่กล่าวถึงก่อนหน้านี้:

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

10
ขอบคุณสำหรับความช่วยเหลือ ในแบบสอบถามนี้เป็นเขตข้อมูลที่ระบุเป็นกลุ่มฉันแค่ต้องการจัดกลุ่มตามเขตข้อมูลเดียวแล้วส่งผลให้คนอื่นระบุเขตข้อมูล ความคิดที่ดี?
บวก

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

โปรดตั้งตารางป้อนข้อมูลของคุณก่อน
Pankaj Cheema
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.