MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Obliczone pola grupowania w MongoDB

Możesz zrobić coś takiego najpierw za pomocą "projektu", ale dla mnie wymaganie $project jest trochę sprzeczne z intuicją etap przed rozdaniem:

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

Jak powiedziałem, jest to sprzeczne z intuicją, ponieważ powinieneś być w stanie zadeklarować to wszystko pod $group scena, ale pomocnicy nie wydają się działać w ten sposób. Serializacja wychodzi nieco śmiesznie (opakowuje argumenty operatora daty tablicami), ale wydaje się, że działa. Ale nadal są to dwa etapy potoku, a nie jeden.

Jaki jest z tym problem? Cóż, oddzielając etapy od etapów, część „projekt” wymusza przetwarzanie wszystkich dokumentów w potoku w celu uzyskania pól obliczeniowych, co oznacza, że ​​przechodzi przez wszystko przed przejściem do etapu grupowego.

Różnicę w czasie przetwarzania można wyraźnie zobaczyć, uruchamiając zapytania w obu formach. Z osobnym etapem projektu, na moim sprzęcie wykonanie trwa trzy razy dłużej niż zapytanie, w którym wszystkie pola są obliczane podczas operacji "grupowania".

Wygląda więc na to, że jedynym obecnym sposobem poprawnego skonstruowania tego jest samodzielne zbudowanie obiektu potoku:

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

Lub jeśli wszystko to wydaje ci się zwięzłe, zawsze możesz pracować ze źródłem JSON i przeanalizować to. Ale oczywiście musi to być prawidłowy JSON:

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


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Otrzymuję błąd:Route() w Route nie może być zastosowane do String

  2. MongoDB InsertBatch JObject — błąd serializacji

  3. nowa Date() w zagregowanym projekcie $project

  4. MongoDB $toLower

  5. Pierwsze kroki z nierelacyjnymi bazami danych przy użyciu Mongodb