Spring Data MongoDB聚合-按计算值匹配 [英] Spring Data MongoDB aggregation - match by calculated value

查看:89
本文介绍了Spring Data MongoDB聚合-按计算值匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有2个Number字段的文档集合,我需要根据计算出的条件过滤所有文档:

Number1/Number2 >= CONST%

如何使用Spring Data Mongo和Aggregation使其成为可能?

-更新-

我尝试了以下代码:

return new RedactAggregationOperation(
            new BasicDBObject(
                    "$redact",
                    new BasicDBObject(
                            "$cond", new BasicDBObject()
                            .append("if", new BasicDBObject(
                                    "$gte", Arrays.asList(new BasicDBObject(
                                    "$divide",
                                    Arrays.asList(
                                            "$Number1",
                                            new BasicDBObject(
                                                    "$cond",
                                                    new BasicDBObject()
                                                            .append("$or", Arrays.asList(
                                                                    new BasicDBObject(
                                                                            "$eq", new BasicDBObject(
                                                                            "$Number2", 0)),
                                                                    new BasicDBObject(
                                                                            "$eq", new BasicDBObject(
                                                                            new BasicDBObject(
                                                                                    "$ifNull",
                                                                                    new BasicDBObject(
                                                                                            "$Number2", 0)).toString(), 0))))
                                                            .append("then", 1000000)
                                                            .append("else", "$Number2")), CONSTANT_VAR))
                            )))
                            .append("then", "$$KEEP")
                            .append("else", "$$PRUNE")
                    )
            )
    );

但是出现错误"$ cond无法识别的参数:$ or" 您能协助解决这个问题吗?

解决方案

您需要的是 $$PRUNE 在条件为假的情况下删除"文档.

此操作类似于使用 $project 管道,该管道选择集合中的字段并创建一个新字段,该字段保存逻辑条件查询以及随后的


因为没有尚不支持 $redact 运算符(在撰写本文时),一种解决方法是实现AggregationOperation接口,该接口使用一个要包含DBObject的类包装聚合操作:

public class RedactAggregationOperation implements AggregationOperation {
    private DBObject operation;

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

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

然后可以在TypeAggregation中使用:

Aggregation agg = newAggregation(
    new RedactAggregationOperation(
        new BasicDBObject(
            "$redact",
            new BasicDBObject(
                "$cond", new BasicDBObject()
                    .append("if", new BasicDBObject(
                        "$gte", Arrays.asList(
                            new BasicDBObject(
                                "$divide", Arrays.asList( "$Number1", "$Number2" )
                            ), 
                            CONSTANT_VAR
                        )
                    )
                )
                .append("then", "$$KEEP")
                .append("else", "$$PRUNE")
            )
        )
    )
);

AggregationResults<Example> results = mongoTemplate.aggregate(
    (TypedAggregation<Example>) agg, Example.class);


-更新-

对注释进行后续操作,以检查分区中Number2字段中的空值或零值,您必须嵌套

等效的Spring数据聚合(未试用)

Aggregation agg = newAggregation(
    new RedactAggregationOperation(
        new BasicDBObject(
            "$redact",
            new BasicDBObject(
                "$cond", Arrays.asList(
                    new BasicDBObject(
                        "$gte", Arrays.asList(
                            new BasicDBObject(
                                "$divide", Arrays.asList( 
                                    "$Number1", 
                                    new BasicDBObject(
                                        "$cond", Arrays.asList( 
                                            new BasicDBObject( "$or": Arrays.asList( 
                                                    new BasicDBObject("$eq", Arrays.asList("$Number2", 0)),
                                                    new BasicDBObject("$eq", Arrays.asList(
                                                            new BasicDBObject("$ifNull", Arrays.asList("$Number2", 0)), 0
                                                        )
                                                    )
                                                )
                                            ),
                                            1,  // placeholder value if Number2 is 0
                                            "$Number2"                                          
                                        )
                                    ) 
                                )
                            ), 
                            CONSTANT_VAR
                        )
                    ), "$$KEEP", "$$PRUNE"
                )
            )
        )
    )
);

I've got a collection of documents with 2 Number fields and I need to filter all the documents based on a calculated condition:

Number1/Number2 >= CONST%

How can I make this posible using Spring Data Mongo and Aggregation?

--UPDATE--

I've tried the following code:

return new RedactAggregationOperation(
            new BasicDBObject(
                    "$redact",
                    new BasicDBObject(
                            "$cond", new BasicDBObject()
                            .append("if", new BasicDBObject(
                                    "$gte", Arrays.asList(new BasicDBObject(
                                    "$divide",
                                    Arrays.asList(
                                            "$Number1",
                                            new BasicDBObject(
                                                    "$cond",
                                                    new BasicDBObject()
                                                            .append("$or", Arrays.asList(
                                                                    new BasicDBObject(
                                                                            "$eq", new BasicDBObject(
                                                                            "$Number2", 0)),
                                                                    new BasicDBObject(
                                                                            "$eq", new BasicDBObject(
                                                                            new BasicDBObject(
                                                                                    "$ifNull",
                                                                                    new BasicDBObject(
                                                                                            "$Number2", 0)).toString(), 0))))
                                                            .append("then", 1000000)
                                                            .append("else", "$Number2")), CONSTANT_VAR))
                            )))
                            .append("then", "$$KEEP")
                            .append("else", "$$PRUNE")
                    )
            )
    );

But there is an error "Unrecognized parameter to $cond: $or" Could you please assist with this issue?

解决方案

What you need is the $redact operator in the aggregation framework which allows you to proccess the above logical condition with the $cond operator and uses the special operations $$KEEP to "keep" the document where the logical condition is true or $$PRUNE to "remove" the document where the condition was false.

This operation is similar to having a $project pipeline that selects the fields in the collection and creates a new field that holds the result from the logical condition query and then a subsequent $match, except that $redact uses a single pipeline stage which is more efficient.

Consider the following example which demonstrate the above concept:

db.collection.aggregate([
    { 
        "$redact": {
            "$cond": [
                { 
                    "$gte": [
                        { "$divide": ["$Number1", "$Number2"] },
                        CONSTANT_VAR
                    ]
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])


Since there's no support for the $redact operator yet (at the time of writing), a workaround will be to implement an AggregationOperation interface that wraps the aggregation operation with a class to take in a DBObject:

public class RedactAggregationOperation implements AggregationOperation {
    private DBObject operation;

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

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

which you can then use in TypeAggregation:

Aggregation agg = newAggregation(
    new RedactAggregationOperation(
        new BasicDBObject(
            "$redact",
            new BasicDBObject(
                "$cond", new BasicDBObject()
                    .append("if", new BasicDBObject(
                        "$gte", Arrays.asList(
                            new BasicDBObject(
                                "$divide", Arrays.asList( "$Number1", "$Number2" )
                            ), 
                            CONSTANT_VAR
                        )
                    )
                )
                .append("then", "$$KEEP")
                .append("else", "$$PRUNE")
            )
        )
    )
);

AggregationResults<Example> results = mongoTemplate.aggregate(
    (TypedAggregation<Example>) agg, Example.class);


--UPDATE--

Follow-up from the comments, to check for nulls or zero values on the Number2 field in the division, you would have to nest a $cond expression with the logic instead.

The following example assumes you have a placeholder value of 1 if either Number2 does not exist/is null or have a value of zero:

db.collection.aggregate([
    { 
        "$redact": {
            "$cond": [
                { 
                    "$gte": [
                        { 
                            "$divide": [
                                "$Number1", {
                                    "$cond": [
                                        {
                                            "$or": [
                                                { "$eq": ["$Number2", 0] },
                                                { 
                                                    "$eq": [
                                                        { "$ifNull": ["$Number2", 0] }, 0
                                                    ]
                                                }
                                            ]
                                        },
                                        1,  // placeholder value if Number2 is 0
                                        "$Number2"                                          
                                    ]
                                }
                            ] 
                        },
                        CONSTANT_VAR
                    ]
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

Equivalent Spring Data Aggregation (untested)

Aggregation agg = newAggregation(
    new RedactAggregationOperation(
        new BasicDBObject(
            "$redact",
            new BasicDBObject(
                "$cond", Arrays.asList(
                    new BasicDBObject(
                        "$gte", Arrays.asList(
                            new BasicDBObject(
                                "$divide", Arrays.asList( 
                                    "$Number1", 
                                    new BasicDBObject(
                                        "$cond", Arrays.asList( 
                                            new BasicDBObject( "$or": Arrays.asList( 
                                                    new BasicDBObject("$eq", Arrays.asList("$Number2", 0)),
                                                    new BasicDBObject("$eq", Arrays.asList(
                                                            new BasicDBObject("$ifNull", Arrays.asList("$Number2", 0)), 0
                                                        )
                                                    )
                                                )
                                            ),
                                            1,  // placeholder value if Number2 is 0
                                            "$Number2"                                          
                                        )
                                    ) 
                                )
                            ), 
                            CONSTANT_VAR
                        )
                    ), "$$KEEP", "$$PRUNE"
                )
            )
        )
    )
);

这篇关于Spring Data MongoDB聚合-按计算值匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆