sql >> Base de Datos >  >> NoSQL >> MongoDB

Campos de agrupación calculados en MongoDB

De hecho, puedes hacer algo como esto con "proyecto" primero, pero para mí es un poco contrario a la intuición requerir un $project escenario antes de la mano:

    Aggregation agg = newAggregation(
        project("quantity")
            .andExpression("dayOfMonth(date)").as("day")
            .andExpression("month(date)").as("month")
            .andExpression("year(date)").as("year")
            .andExpression("price * quantity").as("totalAmount"),
        group(fields().and("day").and("month").and("year"))
            .avg("quantity").as("averavgeQuantity")
            .sum("totalAmount").as("totalAmount")
            .count().as("count")
    );

Como dije, contrario a la intuición, ya que debería poder declarar todo esto en $group etapa, pero los ayudantes no parecen funcionar de esta manera. La serialización resulta un poco divertida (envuelve los argumentos del operador de fecha con matrices) pero parece funcionar. Pero aún así, se trata de dos etapas de canalización en lugar de una.

Cual es el problema con esto? Bueno, al separar las etapas, la parte del "proyecto" fuerza el procesamiento de todos los documentos en proceso para obtener los campos calculados, lo que significa que pasa por todo antes de pasar a la etapa de grupos.

La diferencia en el tiempo de procesamiento se puede ver claramente ejecutando las consultas en ambos formularios. Con una etapa de proyecto separada, mi hardware tarda tres veces más en ejecutarse que la consulta en la que todos los campos se calculan durante la operación de "grupo".

Por lo tanto, parece que la única forma actual de construir esto correctamente es construyendo el objeto de canalización usted mismo:

    ApplicationContext ctx =
            new AnnotationConfigApplicationContext(SpringMongoConfig.class);
    MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");

    BasicDBList pipeline = new BasicDBList();
    String[] multiplier = { "$price", "$quantity" };

    pipeline.add(
        new BasicDBObject("$group",
            new BasicDBObject("_id",
                new BasicDBObject("month", new BasicDBObject("$month", "$date"))
                    .append("day", new BasicDBObject("$dayOfMonth", "$date"))
                    .append("year", new BasicDBObject("$year", "$date"))
            )
            .append("totalPrice", new BasicDBObject(
                "$sum", new BasicDBObject(
                    "$multiply", multiplier
                )
            ))
            .append("averageQuantity", new BasicDBObject("$avg", "$quantity"))
            .append("count",new BasicDBObject("$sum",1))
        )
    );

    BasicDBObject aggregation = new BasicDBObject("aggregate","collection")
        .append("pipeline",pipeline);

    System.out.println(aggregation);

    CommandResult commandResult = mongoOperation.executeCommand(aggregation);

O si todo eso le parece conciso, entonces siempre puede trabajar con la fuente JSON y analizarlo. Pero claro, tiene que ser JSON válido:

    String json = "[" +
        "{ \"$group\": { "+
            "\"_id\": { " +
                "\"month\": { \"$month\": \"$date\" }, " +
                "\"day\": { \"$dayOfMonth\":\"$date\" }, " +
                "\"year\": { \"$year\": \"$date\" } " +
            "}, " +
            "\"totalPrice\": { \"$sum\": { \"$multiply\": [ \"$price\", \"$quantity\" ] } }, " +
            "\"averageQuantity\": { \"$avg\": \"$quantity\" }, " +
            "\"count\": { \"$sum\": 1 } " +
        "}}" +
    "]";

    BasicDBList pipeline = (BasicDBList)com.mongodb.util.JSON.parse(json);