带有条件的 mongotemplate 聚合 [英] mongotemplate aggregation with condition
问题描述
我有一个文档看起来像这样的集合:
I have a collection where the documents look like this:
{
_id: "545b9fa0dd5318a4285f7ce7",
owner: "admin",
messages: [
{
id: "100",
status: "sent",
note: ""
},
{
id: "100",
status: "pending",
note: ""
},
{
id: "101",
status: "sent",
note: ""
},
{
id: "102",
status: "sent",
note: ""
},
{
id: "101",
status: "done",
note: ""
}
]
}
(这只是一个简短的例子,在我的例子中子数组非常大)
(This is just a short example, in my case the sub array is very large)
我需要查询集合并获取特定文档的一些统计信息.所以在这个例子中,如果我查询具有 id 的文件:545b9fa0dd5318a4285f7ce7"我应该得到这个结果:
I need to query the collection and get some statistics for a specific document. So in this example if I query the doucment that has id: "545b9fa0dd5318a4285f7ce7" I should get this result:
{
sent: 3,
pending: 1,
done: 1
}
如何使用 spring mongotemplate 进行这种聚合?
How can I do such aggregation with spring mongotemplate ?
推荐答案
要做这种事情你需要 $cond
聚合框架中的运算符.Spring Data MongoDB 还没有做到这一点,还有很多东西是常见的$group
操作,甚至仅在 $project
.
To do this sort of thing you need the $cond
operator in the aggregation framework. Spring Data MongoDB does not do that yet, and there are many things lacking from common $group
operations that are even only implemented under $project
.
跟踪任何$cond的实现的问题
支持在这里:
The issue for tracking implementation of any $cond
support is here:
https://jira.spring.io/browse/DATAMONGO-861
对于世界其他地方,它看起来像这样:
For the rest of the world it looks like this:
db.collection.aggregate([
{ "$match": { "_id": ObjectId("545b9fa0dd5318a4285f7ce7") } },
{ "$unwind": "$messages" },
{ "$group": {
"_id": "$_id",
"sent": {
"$sum": {
"$cond": [
{ "$eq": [ "$mesages.status", "sent" ] },
1,
0
]
}
},
"pending": {
"$sum": {
"$cond": [
{ "$eq": [ "$messages.status", "pending" ] },
1,
0
]
}
},
"done": {
"$sum": {
"$cond": [
{ "$eq": [ "$messages.status", "done" ] },
1,
0
]
}
}
}}
])
为了让这种事情在 mongotemplate 聚合下工作,您需要一个扩展聚合操作的类,该类可以从 DBObject 构建:
To get that sort of thing to work under a mongotemplate aggregation you need a class extending aggregation operations that can be built from a DBObject:
public class CustomGroupOperation implements AggregationOperation {
private DBObject operation;
public CustomGroupOperation (DBObject operation) {
this.operation = operation;
}
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
然后你可以将$group"定义为一个DBObject
并在聚合管道中实现:
Then you can define the "$group" as a DBObject
and implement in the aggregation pipeline:
DBObject myGroup = (DBObject)new BasicDBObject(
"$group", new BasicDBObject(
"_id","$_id"
).append(
"sent", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "sent"}
),
1,
0
}
)
)
).append(
"pending", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "pending"}
),
1,
0
}
)
)
).append(
"done", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "done"}
),
1,
0
}
)
)
)
);
ObjectId myId = new ObjectId("545b9fa0dd5318a4285f7ce7");
Aggregation aggregation = newAggregation(
match(Criteria.where("_id").is(myId)),
unwind("messges"),
new CustomGroupOperation(myGroup)
);
这允许你想出一个与上面的 shell 表示基本相同的管道.
That allows you to come up with a pipeline that is basically the same as the shell representation above.
所以现在看来,在不支持某些操作和序列的情况下,最好的情况是在 AggregationOperation 接口,可以提供 DBObject 或从您自己的自定义方法内部构造一个.
So it would seem that for now, where certain operations and sequences are not supported the best case is to implement a class on the AgggregationOperation interface that can be fed a DBObject or otherwise internally construct one from your own custom methods.
这篇关于带有条件的 mongotemplate 聚合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!