truy vấn nhóm mongo cách giữ các trường


97

Mọi người. Trong truy vấn nhóm mongo, kết quả chỉ hiển thị (các) khóa trong các đối số. Cách giữ tài liệu đầu tiên trong mỗi nhóm như nhóm truy vấn mysql. ví dụ:

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

kết quả:

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

Cách lấy những thứ sau:

[
   {
   "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
}
]

Câu trả lời:


215

Nếu bạn muốn giữ thông tin về các mục nhập phù hợp đầu tiên cho mỗi nhóm, bạn có thể thử tổng hợp như:

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
Tại sao bạn cần {$first: '$age'}vv? Là nó có thể chỉ có age: $age?
lightalchemist

7
@lightalchemist Không thể. Đó là một mẹo để cho 'nhóm' biết phải chọn gì.
TechWisdom

4
Điều gì sẽ xảy ra nếu thay vì đếm, tập hợp này đang thực hiện $ max hoặc $ min cho độ tuổi? $ Đầu tiên không nhất thiết phải khớp với tuổi tối thiểu hoặc tối đa được tìm thấy cho các trường khác. Làm thế nào để đối phó với điều đó sau đó?
Juliomac

2
Điều này không hoạt động, nó nhóm theo các trường khác không mong muốn.
Jack Cole

1
@Juliomac, tôi tin rằng nếu đầu ra mong muốn của bạn là $ max / $ min và giữ các trường không có trong $group_id, $sorttrước đó bạn có thể với trường mong muốn, sau đó nhóm và sử dụng $firsthoặc các $lasttoán tử trên bất kỳ trường nào. Khi tích lũy, ý tưởng bao gồm các trường khác (được tích lũy / tạo thành / giảm bớt) không có ý nghĩa nhiều về mặt lý thuyết. Tuy nhiên, sắp xếp trước khi thực hiện không hiệu quả so với sắp xếp từng nhóm trong chính chúng vì các thuật toán sắp xếp phức tạp hơn O (n). Tôi ước sẽ có nhiều cách tốt hơn trong MongoDB.
Vemulo

16

Nhân tiện, nếu bạn không chỉ muốn giữ lại tài liệu đầu tiên, bạn có thể sử dụng $ addToSet Ví dụ:

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

1
Cảm ơn! Tốt hơn (tránh làm lộn xộn thứ tự với một Tập hợp): data: {$ addToSet: {name: '$ name', _id: '$ _id', age: '$ age'}}
Benoit

14

[đã chỉnh sửa để bao gồm các đề xuất nhận xét]

Tôi đến đây để tìm kiếm câu trả lời nhưng không hài lòng với câu trả lời đã chọn (đặc biệt là vì nó có tuổi). Tôi thấy câu trả lời này là một giải pháp tốt hơn (đã điều chỉnh):

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

3
NHƯNG, bạn mất countlĩnh vực này. Bạn cần sử dụng $mergeObjectsđể giữ nó.
0zkr PM

1
Để giải thích thêm về nhận xét của 0zkr về việc sử dụng $ mergeObjects và giúp người khác về cú pháp, cú pháp đường dẫn cuối cùng sẽ là{"$replaceRoot": {"newRoot": {"$mergeObjects": ["$person", {count: "$count"}]}}}
Jerren Saunders

7

Bạn có thể thử cái này

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

}

4

Đây là những gì tôi đã làm, nó hoạt động tốt.

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

Chỉ cần cập nhật nhanh nếu một người gặp phải vấn đề tương tự với các tài liệu có nhiều trường. Người ta có thể sử dụng sức mạnh của việc kết hợp giữa $replaceRootgiai đoạn đường ống và người $mergeObjectsvận hành đường ống.

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

4

Sử dụng $firstvới $$ROOTtài liệu và sau đó sử dụng $replaceRootvới trường đầu tiên.

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

Điều này rất hữu ích! Cảm ơn bạn!! Tôi đã tìm kiếm một lúc, nhưng không thể tìm thấy chính xác những gì tôi cần. Điều này là hoàn hảo!
The Student Soul

Câu trả lời này là 'đến mức' và hoàn hảo. Cảm ơn bạn!
M. Nunisa

1

Tôi không biết về .grouphelper, nhưng nếu bạn thích sử dụng Khung tổng hợp , thì bạn sẽ phải chỉ định trường nào sẽ trả về. Hãy sửa cho tôi nếu tôi sai, nhưng trong SQL, bạn vẫn phải làm điều đó.

Đây là cách bạn làm với Khung tổng hợp đã đề cập trước đây:

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

10
cảm ơn vì sự giúp đỡ của bạn. trong truy vấn này là nhóm các trường được chỉ định, tôi chỉ muốn nhóm theo một trường, và kết quả là những trường khác chỉ định các trường. bất kỳ ý tưởng tốt?
plusor

1

Tôi đã tạo chức năng này để tổng quát hóa việc đảo ngược một giai đoạn thư giãn ... hãy cho tôi biết nếu các bạn gặp bất kỳ lỗi nào với nó, nhưng nó hoạt động tốt với tôi!

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
}

Tốt nhất khi các tài liệu trong cùng một bộ sưu tập có nhiều tên trường khác nhau.
user7364588

0

Nếu bạn muốn chiếu tất cả các trường, hãy sử dụng truy vấn này bên dưới.

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

-1

Đây là câu trả lời >>>>

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

dọn bàn ăn đầu vào của bạn hài lòng đầu tiên
Pankaj Cheema
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.