bokire / bamboo Goto Github PK
View Code? Open in Web Editor NEW虽有竹节,却不止步
Home Page: https://github.com/bokire/bamboo
虽有竹节,却不止步
Home Page: https://github.com/bokire/bamboo
截止至发布该文章前,spring-data-mongodb稳定版本还是1.9.x,本文中所提到的坑主要针对1.9.x或之前的版本,2.0.x版本的可以忽略本篇文章或直接浏览本文最后说明内容。
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和,分组等),并返回计算后的数据结果。有点类似sql语句中的 count(*),group by。MongoDB中通过在aggregate方法中添加一系列管道(pipeline)来达到数据处理的目的。(关于MongoDB aggregate的详细介绍、语法、使用说明等这里不做具体说明,大家自己查阅其他资料进行了解。)
Aggregate 中有一种操作方式是Conditional Aggregation Operators,先看看官方文档中的说明$cond (aggregation):
$cond (aggregation)
Evaluates a boolean expression to return one of the two specified return expressions.
The $cond expression has one of two syntaxes:
New in version 2.6.
{ $cond: { if: <boolean-expression>, then: <true-case>, else: <false-case-> } }
Or:
{ $cond: [ <boolean-expression>, <true-case>, <false-case> ] }
If the <boolean-expression> evaluates to true, then $cond evaluates and returns the value of the <true-case> expression. Otherwise, $cond evaluates and returns the value of the <false-case> expression.
The arguments can be any valid expression. For more information on expressions, see Expressions.
翻译成java语法就是一个简单的三目运算符:
<boolean-expression>?<true-case>:<false-case->
再来看看官网上的例子还是上面那地址:
Example
The following example use a inventory collection with the following documents:
{ "_id" : 1, "item" : "abc1", qty: 300 }
{ "_id" : 2, "item" : "abc2", qty: 200 }
{ "_id" : 3, "item" : "xyz1", qty: 250 }
The following aggregation operation uses the $cond expression to set the discount value to 30 if qty value is greater than or equal to 250 and to 20 if qty value is less than 250:
db.inventory.aggregate(
[
{
$project:
{
item: 1,
discount:
{
$cond: { if: { $gte: [ "$qty", 250 ] }, then: 30, else: 20 }
}
}
}
]
)
The operation returns the following results:
{ "_id" : 1, "item" : "abc1", "discount" : 30 }
{ "_id" : 2, "item" : "abc2", "discount" : 20 }
{ "_id" : 3, "item" : "xyz1", "discount" : 30 }
The following operation uses the array syntax of the $cond expression and returns the same results:
db.inventory.aggregate(
[
{
$project:
{
item: 1,
discount:
{
$cond: [ { $gte: [ "$qty", 250 ] }, 30, 20 ]
}
}
}
]
)
例子很简单,通过$cond表达式,将qty值大于等于250的记录的discount值设为30,qty值小于250的记录的discount值设为20。
要实现上面的效果,在spring-data-mongodb中该怎么操作呢?
很遗憾,在spring-data-mongodb 1.9.x 的Reference 文档中并没有找到Conditional Aggregation Operators相关的例子,那就只能通过API中内容来找到想要的内容了。
在org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder
类中发现了这个一个方法:
public ProjectionOperation.ProjectionOperationBuilder project(String operation, Object... values)
Adds a generic projection for the current field.
Parameters:
operation - the operation key, e.g. $add.
values - the values to be set for the projection operation.
Returns:
那就试试这个方法吧,还是采用上面这个例子(代码片段,无法执行):
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = newAggregation(
project("item").and("qty").project("cond", 30, 20).as("discount")
//project("item").and("qty").project("cond", "qty >= 250", 30, 20).as("discount")
//project("item").and("qty >= 250").project("cond", 30, 20).as("discount")
//project("item").andExpression("qty >= 250").project("cond", 30, 20).as("discount")
);
mongoTemplate.aggregate(agg, "collectionName", OutputType.class);
经过尝试,发现上诉四种方式都无法达到我们想要的目的(结果不对,语法错误等)。
难道就spring-data-mongodb就无法使用$cond了吗?上个大招吧!!!
首先,自己定义一个新的AggregationOperation实现类来实现spring-data-mongodb的AggregationOperation:
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import com.mongodb.DBObject;
public class DBObjectAggregationOperation implements AggregationOperation {
private DBObject operation;
public DBObjectAggregationOperation (DBObject operation) {
this.operation = operation;
}
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
然后,我们就可以在代码中这样实现我们的功能:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
/**
* 通过DBObject 来创建mongodb相同的project语句
* $project:
* {
* item: 1,
* iscount:
* {
* $cond: [ { $gte: [ "$qty", 250 ] }, 30, *20 ]
* }
* }
*/
DBObject operation = (DBObject) new BasicDBObject("$project", new BasicDBObject("discount",
new BasicDBObject("$cond", new Object[] {
new BasicDBObject("$gte", new Object[] {
"$qty",250
}), 30, 20 }).append("item", 1)
));
Aggregation agg = newAggregation(
new DBObjectAggregationOperation(operation)
);
mongoTemplate.aggregate(agg, "collectionName", OutputType.class);
经过测试,上诉代码能得到我们想要的结果。
mongodb的操作命令语法都是采用json(bson)格式,在处理复杂的命令时都可以通过类似上诉方法通过DBObject组装命令,然后通过spring-data-mongodb来执行得到相应的结果。
说明1:
在spring-data-mongodb 2.0.x 的Reference的文档中已经可以找到ConditionalOperator相关例子了Aggregation Framework Example 7:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
project("item").and("discount")
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
.then(30)
.otherwise(20))
.and(ifNull("description", "Unspecified")).as("description")
);
AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, "inventory", InventoryItemProjection.class);
List<InventoryItemProjection> stateStatsList = result.getMappedResults();
github的代码中也找了相关实现类ConditionalOperator,
org.springframework.data.mongodb.core.aggregation.ConditionalOperator
后续可以通过上述例子中的代码来实现MongoDB官网的例子。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.