consulta del grupo mongo cómo mantener los campos


97

Todos. En la consulta del grupo mongo, el resultado muestra solo la (s) clave (s) en los argumentos. Cómo mantener el primer documento en cada grupo como grupo de consulta mysql. por ejemplo:

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

resultado:

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

Cómo conseguir lo siguiente:

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

Respuestas:


215

Si desea conservar la información sobre las primeras entradas coincidentes para cada grupo, puede intentar agregar como:

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
¿Por qué necesitas {$first: '$age'}etc.? ¿Es posible simplemente tener age: $age?
lightalchemist

7
@lightalchemist No es posible. Es una especie de truco para que el "grupo" sepa qué elegir.
TechWisdom

4
¿Qué pasa si en lugar de contar esta agregación estuviera haciendo un $ max o $ min por edad? El $ first no coincidiría necesariamente con la edad mínima o máxima encontrada para los otros campos. ¿Cómo lidiar con eso entonces?
Juliomac

2
Esto no funciona, agrupa por los otros campos lo cual no es deseado.
Jack Cole

1
@Juliomac, creo que si su salida deseada es $ max / $ min y conserva los campos que no están en el $group_id, puede $sortantes con el campo deseado y luego agrupar y usar operadores $firstu $lasten cualquier campo. Al acumular, la idea de incluir otros campos (que se acumulan / canalizan / reducen) no tiene tanto sentido ni siquiera teóricamente. Sin embargo, ordenar de antemano es en realidad ineficaz en comparación con ordenar cada grupo dentro de sí mismos, ya que los algoritmos de clasificación son más complejos que O (n). Ojalá hubiera mejores formas en MongoDB.
Vemulo

16

Por cierto, si desea conservar no solo el primer documento, puede usar $ addToSet Por ejemplo:

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

1
¡Gracias! Entendido mejor (evite alterar el pedido con un conjunto): datos: {$ addToSet: {nombre: '$ nombre', _id: '$ _id', edad: '$ edad'}}
Benoit

14

[editado para incluir sugerencias de comentarios]

Vine aquí buscando una respuesta, pero no estaba contento con la respuesta seleccionada (especialmente dada su edad). Encontré esta respuesta que es una mejor solución (adaptada):

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

3
PERO, pierdes el countcampo. Necesitas usar $mergeObjectspara mantenerlo.
0zkr PM

1
Para ampliar el comentario de 0zkr sobre el uso de $ mergeObjects y ayudar a otros con la sintaxis, la última sintaxis de canalización sería{"$replaceRoot": {"newRoot": {"$mergeObjects": ["$person", {count: "$count"}]}}}
Jerren Saunders

7

Puedes probar esto

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

}

4

Esto es lo que hice, funciona bien.

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

Solo una actualización rápida si uno enfrenta el mismo problema con documentos con numerosos campos. Se puede utilizar el poder de combinar la $replaceRootetapa de la $mergeObjectstubería y el operador de la tubería.

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

4

Úselo $firstcon el $$ROOTdocumento y luego use $replaceRootcon el primer campo.

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

¡Esto fue muy útil! ¡¡Gracias!! Había estado buscando por un tiempo, pero no pude encontrar exactamente lo que necesitaba. ¡Esto fue perfecto!
The Student Soul

Esta respuesta es "al grano" y perfecta. ¡Gracias!
M. Nunisa

1

No sabía nada sobre .grouphelper, pero si prefiere utilizar Aggregation Framework , tendrá que especificar qué campos devolver. Corrígeme si me equivoco, pero en SQL tendrías que hacerlo de todos modos.

Bueno, así es como lo haría con el Marco de agregación mencionado anteriormente:

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

10
Gracias por tu ayuda. en esta consulta, se agrupan los campos especificados, solo quiero agrupar por un campo y, a continuación, obtener otros campos especificados. alguna buena idea?
plusor

1

Creé esta función para generalizar la inversión de una etapa de desenrollado ... avíseme si encuentran algún error con ella, ¡pero está funcionando bien para mí!

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
}

Mejor cuando los documentos de la misma colección tienen varios nombres de campo.
user7364588

0

Si desea proyectar todos los campos, utilice la siguiente consulta.

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

-1

Aquí está la respuesta >>>>

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

primero configure su mesa de entrada
Pankaj Cheema
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.