如何在Java中运行.group()函数

3
我在MongoDB中有一个查询,它产生了我想要的结果。但是现在我正在尝试在Java中使用它。
这是在MongoDB中的查询:
var red = function(doc, out) {
out.count_order++;
out.sum_qty += doc.quantity;
out.sum_base_price += doc.extendedprice;
out.sum_disc_price += doc.extendedprice * (1 - doc.discount);
out.sum_charge += doc.extendedprice * (1 - doc.discount) * (1 + doc.tax);
out.avg_disc += doc.discount;
};

var avg = function(out) {
out.avg_qty = out.sum_qty / out.count_order;
out.avg_price = out.sum_base_price / out.count_order;
out.avg_disc = out.avg_disc / out.count_order;
};

db.lineitems.group( {
key : { returnflag : true, linestatus : true},
cond : { "shipdate" : {$lte: 19980801}},
initial: { count_order : 0, sum_qty : 0, sum_base_price : 0, sum_disc_price : 0,
sum_charge : 0, avg_disc : 0},
reduce : red,
finalize : avg
});

现在我正在使用Java,但我不知道如何使用avg函数。
    String avg = "var avg = function(out) {"
            + "out.avg_qty = out.sum_qty / out.count_order;"
            + "out.avg_price = out.sum_base_price / out.count_order;"
            + "out.avg_disc = out.avg_disc / out.count_order;};";

    String reduce = "function(doc, out) {"
            + "out.count_order++;"
            + "out.sum_qty += doc.quantity;"
            + "out.sum_base_price += doc.extendedprice;"
            + "out.sum_disc_price += doc.extendedprice * (1 - doc.discount);"
            + "out.sum_charge += doc.extendedprice * (1 - doc.discount) * (1 + doc.tax);"
            + "out.avg_disc += doc.discount;};";

    String finalize = "function(out) {"
            + "out.avg_qty = out.sum_qty / out.count_order;"
            + "out.avg_price = out.sum_base_price / out.count_order;"
            + "out.avg_disc = out.avg_disc / out.count_order;};";

    MapReduceIterable<Document> iterable = collection.mapReduce(**????**, reduce).finalizeFunction(finalize);

我该如何使用那个功能?
1个回答

1
你最好使用聚合框架来处理这个问题,而不要调用.group()方法,因为它是在聚合框架实际存在之前的旧实现。
在shell中应用该逻辑的方式如下:
db.lineitems.aggregate([
    { "$match": { "shipdate": { "$lte": 19980801 } },
    { "$group": {
        "_id": {
            "returnflag": "$returnflag",
            "linestatus": "$linestatus"
        },
        "count": { "$sum": 1 },
        "sum_qty": { "$sum": "$quantity" },
        "avg_qty": { "$avg": "$quantity" },
        "sum_base_price": { "$sum": "$extendedprice" },
        "avg_base_price": { "$avg": "$extendedprice" },
        "sum_disc_price": {
            "$sum": {
                "$multiply": [
                    "$extendedprice",
                    { "$subtract": [ 1, "$discount" ] }
                ]
            }
        },
        "avg_disc_price": {
            "$avg": {
                "$multiply": [
                    "$extendedprice",
                    { "$subtract": [ 1, "$discount" ] }
                ]
            }
        },
        "sum_charge": {
            "$sum": {
                "$multiply": [
                    "$extendedprice",
                    { "$subtract": [ 1, "$discount" ] },
                    { "$add": [ 1, "$tax" ] }
                ]
            }
        },
        "avg_disc": { "$avg": "$discount" }
    }}
])

聚合框架作为一个非常高效的$group管道阶段操作符,可以通过本地编码操作完成所有相同的操作。内置了$sum$avg累加器以及其他数学运算。
简而言之,由于是“本地编码”而不是依赖“JavaScript解释”,因此执行速度比.group()提供的要快得多。此外,它应该非常容易理解。
将其翻译成Java大概是这样的:
List<Document> pipeline = Arrays.<Document>asList(
  new Document(
    "$match", new Document(
      "shipdate", new Document(
        "$lte", 19980801             
      )
    )
  ),
  new Document(
    "$group", new Document(
      "_id", new Document(
        "returnflag", "$returnflag"
      ).append( "linestatus", "$linestatus" )
    ).append(
      "count", new Document( "$sum", 1 )
    ).append(
      "sum_qty", new Document( "$sum", "$quantity" )
    ).append(
      "avg_qty", new Document( "$avg", "$quantity" )
    ).append(
      "sum_base_price", new Document( "$sum", "$extendedprice" )
    ).append(
      "avg_base_price", new Document( "$avg", "$extendedprice" )
    ).append(
      "sum_disc_price", new Document(
        "$sum", new Document(
          "$multiply", Arrays.asList(
            "$extendedprice",
            new Document(
              "$subtract", Arrays.asList(1, "$discount")
            )
          )
        )
      )
    ).append(
      "avg_disc_price", new Document(
        "$avg", new Document(
          "$multiply", Arrays.asList(
            "$extendedprice",
            new Document(
              "$subtract", Arrays.asList(1, "$discount")
            )
          )
        )
      )
    ).append(
      "sum_charge", new Document(
        "$sum", new Document(
          "$multiply", Arrays.asList(
            "$extendedprice",
            new Document(
              "$subtract", Arrays.asList(1, "$discount")
            ),
            new Document(
              "$add", Arrays.asList(1, "$tax")
            )
          )
        )
      )
    ).append(
      "avg_disc", new Document( "$avg", "$discount" )
    )
  )
);

AggregateIterable<Document> result = collection.aggregate(pipeline);

我知道,但我有一个使用聚合框架的解决方案,我需要使用MapReduce获得相同的结果进行比较...我需要使用Java调用这两种情况。 - duknust
1
@duknust 你为什么认为你需要mapReduce?这里的解释表明,使用.aggregate()不仅更“容易”,而且实际上性能远远优于mapReduce。mapReduce无法在性能或规模上与.aggregate()竞争。而且你还得到了Java代码。你的评论表明你没有完全阅读答案。我建议你阅读并学习它的教训。 - Blakes Seven
1
@duknust 当然,这里的另一件事是,你编写的函数以及在shell中尝试的内容都是针对.group()方法的。因此,即使JavaScript代码已经编写好了,也需要进行相当大的修改才能适应mapReduce。所以你要么在Java中调用.group(),要么改变所有的mapReduce代码。但总的来说,你不应该这样做。使用如上所示的.aggregate()更好。这就是你应该学习的内容。 - Blakes Seven
我知道这一点,但我需要有两个查询可以给我相同的结果。只是为了获取有关运行所花费的能量的信息。这就是为什么我需要不止一个选项,而我已经有了.aggregate()选项。 - duknust

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接