Spring 数据 mongodb 模拟 ifNull 行为

Spring data mongodb simulate ifNull behavior

我有一组表格:

{ "_id" : { "$oid" : "57050f22d5f79d2e6866c6f8" }, "form_name" : "Form 1", "ver" : 0, "createdAt" : { "$date" : 1459950030037 } }
{ "_id" : { "$oid" : "57050fa6d5f79d2e6866c6f9" }, "form_name" : "Form 1", "ver" : 1, "vid" : { "$oid" : "57050f22d5f79d2e6866c6f8" }, "createdAt" : { "$date" : 1459950030037 } }
{ "_id" : { "$oid" : "57050ff2d5f79d2e6866c6fa" }, "form_name" : "Form 1", "ver" : 2, "vid" : { "$oid" : "57050f22d5f79d2e6866c6f8" }, "createdAt" : { "$date" : 1459950030037 } }
{ "_id" : { "$oid" : "570511ced5f79d2e6866c6fb" }, "form_name" : "Form 2", "ver" : 0, "createdAt" : { "$date" : 1459950030037 } }

我得到的版本化表格如下:

db.forms.aggregate([
    {"$match":{"deletedAt":{"$exists":false}}},
    {"$sort":{"createdAt":1}},
    {"$group":{
            "_id": {"$ifNull":["$vid", "$_id"]},
            "vid":{"$push":"$$ROOT"}
        }
    }
])

我得到这样的 JSON 答案:

[
    {
        "_id" : ObjectId("57050f22d5f79d2e6866c6f8"),
        "vid" : [
            {
                "_id" : ObjectId("57050f22d5f79d2e6866c6f8"),
                "form_name" : "Form 1",
                "ver" : 0,
                "createdAt" : ISODate("2016-04-06T13:29:06.079Z")
            },
            {
                "_id" : ObjectId("57050fa6d5f79d2e6866c6f9"),
                "form_name" : "Form 1",
                "ver" : 1,
                "createdAt" : ISODate("2016-04-06T13:31:18.742Z"),
                "vid" : ObjectId("57050f22d5f79d2e6866c6f8")
            },
            {
                "_id" : ObjectId("57050ff2d5f79d2e6866c6fa"),
                "form_name" : "Form 1",
                "ver" : 2,
                "createdAt" : ISODate("2016-04-06T13:32:34.986Z"),
                "vid" : ObjectId("57050f22d5f79d2e6866c6f8")
            }
        ]
    },
    {
        "_id" : ObjectId("570511ced5f79d2e6866c6fb"),
        "vid" : [
            {
                "_id" : ObjectId("570511ced5f79d2e6866c6fb"),
                "form_name" : "Form 2",
                "ver" : 0,
                "createdAt" : ISODate("2016-04-06T13:40:30.037Z")
            }
        ]
    }
]

在我的 Java 后端中,我的操作如下:

List<DBObject> tmpAggr = new ArrayList<>();
tmpAggr.add(new BasicDBObject("$match", new BasicDBObject("deletedAt", new BasicDBObject("$exists", false))));
tmpAggr.add(new BasicDBObject("$sort",  new BasicDBObject("createdAt", 1)));
tmpAggr.add(new BasicDBObject("$group", BasicDBObjectBuilder.start("_id", new BasicDBObject("$ifNull", new String[]{"$vid", "$_id"}))
                                                        .append("vid", new BasicDBObject("$push", "$$ROOT")).get()));
tmpAggr.add(new BasicDBObject("$sort",  new BasicDBObject("_id", 1)));
        list_with_versions = Collections.unmodifiableList(tmpAggr);  
org.getDB().getCollection("forms").aggregate(list_with_versions).results()

如何使用 Spring 数据 MongoDB 框架重现此行为? 问题是没有 ifNull 构造。如何处理这个问题?

您可以创建一个实现 AggregationOperation 接口的解决方法,以接收 DBObject 表示聚合管道中的单个操作 $ifNull 运算符:

public class GroupAggregationOperation implements AggregationOperation {
    private DBObject operation;

    public GroupAggregationOperation (DBObject operation) {
        this.operation = operation;
    }

    @Override
    public DBObject toDBObject(AggregationOperationContext context) {
        return context.getMappedObject(operation);
    }
}

然后将 $group 操作作为聚合管道中的 DBObject 实现,与您拥有的相同:

DBObject operation = (DBObject) new BasicDBObject(
    "$group", BasicDBObjectBuilder.start(
        "_id", new BasicDBObject(
            "$ifNull", new String[]{"$vid", "$_id"}
        )
    )
    .append("vid", new BasicDBObject("$push", "$$ROOT"))
    .get()              
);

然后您可以将其用作:

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

GroupAggregationOperation groupOp = new GroupAggregationOperation(operation);
Aggregation agg = newAggregation(
    match(Criteria.where("deletedAt").exists(false)),
    sort(ASC, "deletedAt"),
    groupOp 
);
AggregationResults<Forms> results = mongoTemplate.aggregate(agg, Forms.class); 
List<Forms> forms = results.getMappedResults();