mongodb - array - $ desenrollar un objeto en el marco de agregación
mongoose aggregate (4)
Desde 3.4.4, puedes transformar el objeto en una matriz usando $ objectToArray
Ver: https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/
En el marco de agregación MongoDB, esperaba usar el operador $ unwind en un objeto (es decir, una colección JSON). No parece que esto es posible , ¿hay alguna solución? ¿Hay planes para implementar esto?
Por ejemplo, tome la colección de artículos de la documentación de agregación. Supongamos que hay un campo adicional "calificaciones" que es un mapa de usuario -> calificación. ¿Podría calcular la calificación promedio de cada usuario?
Aparte de esto, estoy bastante satisfecho con el marco de agregación.
Actualización: aquí hay una versión simplificada de mi colección JSON por solicitud. Estoy almacenando datos genómicos. Realmente no puedo hacer genotipos como una matriz, porque la búsqueda más común es obtener el genotipo de una persona al azar.
variants: [
{
name: ''variant1'',
genotypes: {
person1: 2,
person2: 5,
person3: 7,
}
},
{
name: ''variant2'',
genotypes: {
person1: 3,
person2: 3,
person3: 2,
}
}
]
Esta es una vieja pregunta, pero me encontré con una gran cantidad de información a través de prueba y error que las personas pueden encontrar útiles.
De hecho, es posible relajarse con un valor ficticio engañando al analizador de esta manera:
db.Opportunity.aggregate(
{ $project: {
Field1: 1, Field2: 1, Field3: 1,
DummyUnwindField: { $ifNull: [null, [1.0]] }
}
},
{ $unwind: "$DummyUnwindField" }
);
Esto producirá 1 fila por documento, independientemente de si el valor existe o no. Es posible que pueda jugar con esto para generar los resultados que desea. Tenía la esperanza de combinar esto con múltiples $ unwinds para (algo así como emitir () en map / reduce), pero desafortunadamente, los últimos $ unwind wins o combinan como una intersección en lugar de unión lo que hace que sea imposible lograr los resultados. estaba buscando. Estoy tristemente decepcionado con la funcionalidad del marco agregado, ya que no se ajusta al caso de uso en el que esperaba usarlo (y parece extrañamente como lo hacen muchas de las preguntas sobre en esta área) - ordenar los resultados en función del emparejamiento tarifa. Mejorar el pobre mapa reduce el rendimiento hubiera hecho innecesaria toda esta característica.
No es posible realizar el tipo de cálculo que está describiendo con el marco de agregación, y no es porque no hay $unwind
método $unwind
para los que no son matrices. Incluso si la persona: los objetos de valor fueran documentos en una matriz, $unwind
no ayudaría.
La funcionalidad "agrupar por" (ya sea en MongoDB o en cualquier base de datos relacional) se realiza sobre el valor de un campo o columna. Agrupamos por valor de campo y suma / promedio / etc. según el valor de otro campo.
El ejemplo simple es una variante de lo que sugiere, campo de calificaciones agregado a la colección de artículos de ejemplo, pero no como un mapa de usuario a calificación sino como una matriz como esta:
{ title : title of article", ...
ratings: [
{ voter: "user1", score: 5 },
{ voter: "user2", score: 8 },
{ voter: "user3", score: 7 }
]
}
Ahora puedes agregar esto con:
[ {$unwind: "$ratings"},
{$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } }
]
Pero este ejemplo estructurado como lo describes se vería así:
{ title : title of article", ...
ratings: {
user1: 5,
user2: 8,
user3: 7
}
}
o incluso esto:
{ title : title of article", ...
ratings: [
{ user1: 5 },
{ user2: 8 },
{ user3: 7 }
]
}
Incluso si pudieras $unwind
esto, no hay nada que agregar aquí. A menos que conozca la lista completa de todas las claves posibles (usuarios), no puede hacer mucho con esto. [*]
Un esquema de base de datos relacional análogo a lo que tiene sería:
CREATE TABLE T (
user1: integer,
user2: integer,
user3: integer
...
);
Eso no es lo que se haría, sino que haríamos esto:
CREATE TABLE T (
username: varchar(32),
score: integer
);
y ahora agregamos usando SQL:
select username, avg(score) from T group by username;
Existe una solicitud de mejora para MongoDB que puede permitirle hacer esto en el marco de agregación en el futuro: la capacidad de proyectar valores a claves, viceversa. Mientras tanto, siempre hay un mapa / reducir.
[*] Hay una manera complicada de hacer esto si conoces todas las claves únicas (puedes encontrar todas las claves únicas con un método similar a esto ) pero si conoces todas las claves también puedes ejecutar una secuencia de consultas de la forma db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1})
para cada usuarioX que devolverá todas sus calificaciones y puede sumar y prométalos simplemente lo suficiente en lugar de hacer una proyección muy compleja que requeriría el marco de agregación.
Esto es lo que encontré y extendí.
Permite crear una base de datos experimental en mongo
db.copyDatabase(''livedb'' , ''experimentdb'')
Ahora usa experimentdb y convert Array para objetar en tu colección de experimentos
db.getCollection(''experimentcollection'').find({}).forEach(function(e){
if(e.store){
e.ratings = [e.ratings]; //Objects name to be converted to array eg:ratings
db.experimentcollection.save(e);
}
})
Algunos nerds js codifican para convertir json en objeto plano
var flatArray = [];
var data = db.experimentcollection.find().toArray();
for (var index = 0; index < data.length; index++) {
var flatObject = {};
for (var prop in data[index]) {
var value = data[index][prop];
if (Array.isArray(value) && prop === ''ratings'') {
for (var i = 0; i < value.length; i++) {
for (var inProp in value[i]) {
flatObject[inProp] = value[i][inProp];
}
}
}else{
flatObject[prop] = value;
}
}
flatArray.push(flatObject);
}
printjson(flatArray);