MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Champs groupés calculés dans MongoDB

Vous pouvez en fait faire quelque chose comme ça avec "project" en premier, mais pour moi, il est un peu contre-intuitif d'exiger un $project étape préalable :

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

Comme je l'ai dit, contre-intuitif car vous devriez pouvoir déclarer tout cela sous $group étape, mais les aides ne semblent pas fonctionner de cette façon. La sérialisation est un peu drôle (enveloppe les arguments de l'opérateur de date avec des tableaux) mais cela semble fonctionner. Néanmoins, il s'agit de deux étapes de pipeline plutôt que d'une.

Quel est le problème avec cela? Eh bien en séparant les étapes les étapes la partie "projet" force le traitement de tous les documents dans le pipeline afin d'obtenir les champs calculés, cela signifie qu'il passe par tout avant de passer à l'étape de groupe.

La différence de temps de traitement est clairement visible en exécutant les requêtes dans les deux formulaires. Avec une étape de projet distincte, sur mon matériel, l'exécution prend trois fois plus de temps que la requête où tous les champs sont calculés lors de l'opération "grouper".

Il semble donc que la seule façon actuelle de construire cela correctement soit de construire vous-même l'objet pipeline :

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

Ou si tout cela vous semble concis, vous pouvez toujours travailler avec la source JSON et l'analyser. Mais bien sûr, il doit s'agir d'un JSON valide :

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