我想用Java来更简洁地编写MongoDB聚合

可以使用MongoDB中的聚合来实现

虽然比MapReduce快很多,但用Java写确实有点麻烦。如果Java驱动程序有这样的实用程序就好了。我想可能有些开源的东西在某个地方,但我暂时没有找到合适的。所以我简单写了一下。

举例来说,比如使用CLI

db.students.aggregate([
         {$unwind:"$scores"},
         {$group: {_id:"$name", average:{$avg:"$scores.score"}}},
         {$skip:5},
         {$limit:3} ])

用Java编写类似的处理

DBObject groupFields = new BasicDBObject("_id", "$name");
groupFields.put("average", new BasicDBObject("$avg", "$scores.score"));
AggregationOutput output = collection.aggregate(
        new BasicDBObject("$unwind", "$scores"),
        new BasicDBObject("$group", groupFields),
        new BasicDBObject("$skip", 5),
        new BasicDBObject("$limit", 3));

变成了这样。希望就是

    • \$groupとか\$avgとか、Stringで渡すのではなく、開発環境でgrとか入力したらgroup?と言って欲しい

 

    new BasicDBObject を減らしたい

因此,

我创建了两个名为AggBuilder和AggUtils的类。

为了使其更紧凑,省略 import 和 JavaDoc。

public class AggBuilder {

    private final DBCollection collection;

    private final List<DBObject> ops = new ArrayList<DBObject>();

    public AggBuilder(DBCollection collection) {
        this.collection = collection;
    }

    public AggregationOutput execute() {
        if (ops.size() > 1) {
            return collection.aggregate(ops.remove(0), ops.toArray(new DBObject[ops.size()]));
        }
        return collection.aggregate(ops.get(0));
    }
    public AggBuilder limit(int param) {
        ops.add(new BasicDBObject("$limit", param));
        return this;
    }

    public AggBuilder skip(int param) {
        ops.add(new BasicDBObject("$skip", param));
        return this;
    }

    public AggBuilder unwind(String param) {
        ops.add(new BasicDBObject("$unwind", param));
        return this;
    }

    public AggBuilder group(String id, DBObject param) {
        DBObject params = new BasicDBObject("_id", id);
        params.putAll(param);
        ops.add(new BasicDBObject("$group", params));
        return this;
    }
}

只是将”$什么什么”简单地改为了”什么什么方法”而已。然后,在Utils中加入使得可以在group等情况下使用的操作符进行静态导入。首先只加入了avg。

public class AggUtils {
    /**
     * @param fieldName
     *            name of the average field aggregated
     * @param fieldToAverage
     *            field to calculate average for
     */
    public static DBObject avg(String fieldName, String fieldToAverage) {
        return new BasicDBObject(fieldName, new BasicDBObject("$avg", fieldToAverage));
    }
}

使用以上准备好的代码进行聚合,与CLI接近。与之前的JS代码相比,感觉是这样的。唯一需要注意的是,平均值聚合后的名称和平均值操作符的顺序互换了,可能会有点混淆。

db.students.aggregate([
         {$unwind:"$scores"},
         {$group: {_id:"$name", average:{$avg:"$scores.score"}}},
         {$skip:5},
         {$limit:3} ])
AggregationOutput output = new AggBuilder(collection)
        .unwind("$scores")
        .group("$name", avg("average", "$scores.score"))
        .skip(5)
        .limit(3).execute();

今回はAggregationフレームワークでサポートされている機能の一部を実装したけど、あんまり時間もかからないしgroupをタイプミスしてもコード実行するまで気づかないなんてことがなくなるのは嬉しい。

最后一个示例代码

package advent;

import static advent.AggUtils.avg;

import java.net.UnknownHostException;

import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;

public class AggSample {
    public static void main(String[] args) throws UnknownHostException {
        MongoClient client = new MongoClient("localhost", 27017);
        DB database = client.getDB("school");
        DBCollection collection = database.getCollection("students");

        /*
         * db.students.aggregate([
         * {$unwind:"$scores"},
         * {$group: {_id:"$name", average:{$avg:"$scores.score"}}},
         * {$skip:5},
         * {$limit:3} ])
         */
        // Normal Java code
        DBObject groupFields = new BasicDBObject("_id", "$name");
        groupFields.put("average", new BasicDBObject("$avg", "$scores.score"));
        printResults(collection.aggregate(
                new BasicDBObject("$unwind", "$scores"),
                new BasicDBObject("$group", groupFields),
                new BasicDBObject("$skip", 5),
                new BasicDBObject("$limit", 3)));

        // Use AggBuilder
        printResults(new AggBuilder(collection)
        .unwind("$scores")
        .group("$name", avg("average", "$scores.score"))
        .skip(5)
        .limit(3).execute());
    }

    private static void printResults(AggregationOutput output) {
        for (DBObject obj : output.results()) {
            System.out.println(obj);
        }
    }
}

总结

我想有些人可能已经意识到了,关于\$project方面我没做任何事情。因为这个操作有很高的自由度,所以我没有很容易地想到一个写得漂亮的方法。也许如果仔细搜索的话,会找到有人已经制作了一个涵盖了聚合操作符的工具,但总之,只要自己迅速地写一下,用MongoDB真的很容易,这就是我的想法。

广告
将在 10 秒后关闭
bannerAds