我有一个包含2个数字字段的文档集合,我需要根据计算的条件过滤所有文档:
Number1/Number2 >= CONST%如何使用Spring Data Mongo和Aggregation创建这个?
--UPDATE--
我试过以下代码:
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:$或”你能否协助解决这个问题?
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?
最满意答案
您需要的是聚合框架中的$redact运算符,它允许您使用$cond运算符处理上述逻辑条件,并使用特殊操作$$KEEP来“保留”逻辑条件为真的文档或$$PRUNE “删除”条件为假的文档。
此操作类似于具有$project管道,该管道选择集合中的字段并创建一个新字段,该字段保存逻辑条件查询的结果,然后是后续的$match ,但$redact使用单个管道阶段更多高效。
考虑以下示例演示上述概念:
db.collection.aggregate([ { "$redact": { "$cond": [ { "$gte": [ { "$divide": ["$Number1", "$Number2"] }, CONSTANT_VAR ] }, "$$KEEP", "$$PRUNE" ] } } ])由于目前还没有对$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使用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--
从注释中跟进,要检查除法中Number2字段的空值或零值,您必须使用逻辑嵌套$cond表达式。
以下示例假定如果Number2不存在/为null或值为零,则占位符值为1:
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" ] } } ])等效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" ) ) ) ) );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" ) ) ) ) );更多推荐
发布评论