如何在 Spring 数据中进行 Mongo 聚合查询?
How to do a Mongo aggregation query in Spring Data?
这是我第一次在 Java 中使用 Mongo,我在使用此聚合查询时遇到了一些问题。我可以在 Mongo 中为 Spring 做一些简单的查询,在我的 Repository 接口中使用 @Query
注释扩展 MongoRepository<T, ID>
。当您在 Spring-Data.
中进行长聚合时,了解采用哪种方法会很有帮助
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
localField: "postedBy",
foreignField: "_id",
as: "user"
}
},
{
$group: {
_id: {
username: "$user.name",
title: "$title",
description: "$description",
upvotes: { $size: "$upvotesBy" },
upvotesBy: "$upvotesBy",
isUpvoted: { $in: [req.query.userId, "$upvotesBy"] },
isPinned: {
$cond: {
if: { $gte: [{ $size: "$upvotesBy" }, 3] },
then: true,
else: false
}
},
file: "$file",
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
},
id: "$_id"
}
}
},
{ $sort: { "_id.isPinned": -1, "_id.createdAt": -1 } }
])
您可以实现 AggregationOperation 并编写自定义聚合操作查询,然后使用 MongoTemplate
执行您在 mongo shell 如下:
自定义聚合操作
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
public class CustomAggregationOperation implements AggregationOperation {
private String jsonOperation;
public CustomAggregationOperation(String jsonOperation) {
this.jsonOperation = jsonOperation;
}
@Override
public org.bson.Document toDocument(AggregationOperationContext aggregationOperationContext) {
return aggregationOperationContext.getMappedObject(org.bson.Document.parse(jsonOperation));
}
}
任意MongoShell聚合查询执行器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.stereotype.Service;
import sample.data.mongo.models.Course;
@Service
public class LookupAggregation {
@Autowired
MongoTemplate mongoTemplate;
public void LookupAggregationExample() {
AggregationOperation unwind = Aggregation.unwind("studentIds");
String query1 = "{$lookup: {from: 'student', let: { stuId: { $toObjectId: '$studentIds' } },"
+ "pipeline: [{$match: {$expr: { $eq: [ '$_id', '$$stuId' ] },},}, "
+ "{$project: {isSendTemplate: 1,openId: 1,stu_name: '$name',stu_id: '$_id',},},], "
+ "as: 'student',}, }";
TypedAggregation<Course> aggregation = Aggregation.newAggregation(
Course.class,
unwind,
new CustomAggregationOperation(query1)
);
AggregationResults<Course> results =
mongoTemplate.aggregate(aggregation, Course.class);
System.out.println(results.getMappedResults());
}
}
有关详细信息,请查看 Github repository classes:CustomAggregationOperation & LookupAggregation
其他方法也使用Mongo模板:
#1. 为模型定义自定义代码的接口 Post:
interface CustomPostRepository {
List<Post> yourCustomMethod();
}
#2. 为此 class 添加实现并遵循命名约定以确保我们可以找到 class.
class CustomPostRepositoryImpl implements CustomPostRepository {
@Autowired
private MongoOperations mongoOperations;
public List<Post> yourCustomMethod() {
// custom match queries here
MatchOperation match = null;
// Group by , Lookup others stuff goes here
// For details: https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/aggregation/Aggregation.html
Aggregation aggregate = Aggregation.newAggregation(match);
AggregationResults<Post> orderAggregate = mongoOperations.aggregate(aggregate,
Post.class, Post.class);
return orderAggregate.getMappedResults();
}
}
#3. 现在让您的基本存储库接口扩展自定义接口,基础架构将自动使用您的自定义实现:
interface PostRepository extends CrudRepository<Post, Long>, CustomPostRepository {
}
虽然这是旧线程,但我希望找到此线程的人现在可以安全地在 MongoRepository 中进行多 stage/pipeline 聚合(不太确定它叫什么)。
因为我也在努力寻找 mongo 存储库 中没有 mongo 模板 .
的线索和聚合示例
但是现在,我可以按照 spring 文档在 here
中所说的进行聚合管道
我的聚合在 mongoshell 中看起来像这样:
db.getCollection('SalesPo').aggregate([
{$project: {
month: {$month: '$poDate'},
year: {$year: '$poDate'},
amount: 1,
poDate: 1
}},
{$match: {$and : [{year:2020} , {month:7}]
}}
,
{$group: {
'_id': {
month: {$month: '$poDate'},
year: {$year: '$poDate'}
},
totalPrice: {$sum: {$toDecimal:'$amount'}},
}
},
{$project: {
_id: 0,
totalPrice: {$toString: '$totalPrice'}
}}
])
虽然我将其转换为 MongoRepository 中的 @Aggregation 注释,但如下所示(我正在删除撇号并替换为方法参数):
@Repository
public interface SalesPoRepository extends MongoRepository<SalesPo, String> {
@Aggregation(pipeline = {"{$project: {\n" +
" month: {$month: $poDate},\n" +
" year: {$year: $poDate},\n" +
" amount: 1,\n" +
" poDate: 1\n" +
" }}"
,"{$match: {$and : [{year:?0} , {month:?1}] \n" +
" }}"
,"{$group: { \n" +
" '_id': {\n" +
" month: {$month: $poDate},\n" +
" year: {$year: $poDate} \n" +
" },\n" +
" totalPrice: {$sum: {$toDecimal:$amount}},\n" +
" }\n" +
" }"
,"{$project: {\n" +
" _id: 0,\n" +
" totalPrice: {$toString: $totalPrice}\n" +
" }}"})
AggregationResults<SumPrice> sumPriceThisYearMonth(Integer year, Integer month);
我的文档如下所示:
@Document(collection = "SalesPo")
@Data
public class SalesPo {
@Id
private String id;
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate poDate;
private BigDecimal amount;
}
以及持有预测的 SumPrice class:
@Data
public class SumPrice {
private BigDecimal totalPrice;
}
我希望这个答案可以帮助那些尝试在不使用mongo模板[=34=的情况下在mongo存储库中进行聚合的人].
这是我第一次在 Java 中使用 Mongo,我在使用此聚合查询时遇到了一些问题。我可以在 Mongo 中为 Spring 做一些简单的查询,在我的 Repository 接口中使用 @Query
注释扩展 MongoRepository<T, ID>
。当您在 Spring-Data.
db.post.aggregate([
{
$match: {}
},
{
$lookup: {
from: "users",
localField: "postedBy",
foreignField: "_id",
as: "user"
}
},
{
$group: {
_id: {
username: "$user.name",
title: "$title",
description: "$description",
upvotes: { $size: "$upvotesBy" },
upvotesBy: "$upvotesBy",
isUpvoted: { $in: [req.query.userId, "$upvotesBy"] },
isPinned: {
$cond: {
if: { $gte: [{ $size: "$upvotesBy" }, 3] },
then: true,
else: false
}
},
file: "$file",
createdAt: {
$dateToString: {
format: "%H:%M %d-%m-%Y",
timezone: "+01",
date: "$createdAt"
}
},
id: "$_id"
}
}
},
{ $sort: { "_id.isPinned": -1, "_id.createdAt": -1 } }
])
您可以实现 AggregationOperation 并编写自定义聚合操作查询,然后使用 MongoTemplate
执行您在 mongo shell 如下:
自定义聚合操作
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
public class CustomAggregationOperation implements AggregationOperation {
private String jsonOperation;
public CustomAggregationOperation(String jsonOperation) {
this.jsonOperation = jsonOperation;
}
@Override
public org.bson.Document toDocument(AggregationOperationContext aggregationOperationContext) {
return aggregationOperationContext.getMappedObject(org.bson.Document.parse(jsonOperation));
}
}
任意MongoShell聚合查询执行器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.stereotype.Service;
import sample.data.mongo.models.Course;
@Service
public class LookupAggregation {
@Autowired
MongoTemplate mongoTemplate;
public void LookupAggregationExample() {
AggregationOperation unwind = Aggregation.unwind("studentIds");
String query1 = "{$lookup: {from: 'student', let: { stuId: { $toObjectId: '$studentIds' } },"
+ "pipeline: [{$match: {$expr: { $eq: [ '$_id', '$$stuId' ] },},}, "
+ "{$project: {isSendTemplate: 1,openId: 1,stu_name: '$name',stu_id: '$_id',},},], "
+ "as: 'student',}, }";
TypedAggregation<Course> aggregation = Aggregation.newAggregation(
Course.class,
unwind,
new CustomAggregationOperation(query1)
);
AggregationResults<Course> results =
mongoTemplate.aggregate(aggregation, Course.class);
System.out.println(results.getMappedResults());
}
}
有关详细信息,请查看 Github repository classes:CustomAggregationOperation & LookupAggregation
其他方法也使用Mongo模板:
#1. 为模型定义自定义代码的接口 Post:
interface CustomPostRepository {
List<Post> yourCustomMethod();
}
#2. 为此 class 添加实现并遵循命名约定以确保我们可以找到 class.
class CustomPostRepositoryImpl implements CustomPostRepository {
@Autowired
private MongoOperations mongoOperations;
public List<Post> yourCustomMethod() {
// custom match queries here
MatchOperation match = null;
// Group by , Lookup others stuff goes here
// For details: https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/aggregation/Aggregation.html
Aggregation aggregate = Aggregation.newAggregation(match);
AggregationResults<Post> orderAggregate = mongoOperations.aggregate(aggregate,
Post.class, Post.class);
return orderAggregate.getMappedResults();
}
}
#3. 现在让您的基本存储库接口扩展自定义接口,基础架构将自动使用您的自定义实现:
interface PostRepository extends CrudRepository<Post, Long>, CustomPostRepository {
}
虽然这是旧线程,但我希望找到此线程的人现在可以安全地在 MongoRepository 中进行多 stage/pipeline 聚合(不太确定它叫什么)。 因为我也在努力寻找 mongo 存储库 中没有 mongo 模板 .
的线索和聚合示例但是现在,我可以按照 spring 文档在 here
中所说的进行聚合管道我的聚合在 mongoshell 中看起来像这样:
db.getCollection('SalesPo').aggregate([
{$project: {
month: {$month: '$poDate'},
year: {$year: '$poDate'},
amount: 1,
poDate: 1
}},
{$match: {$and : [{year:2020} , {month:7}]
}}
,
{$group: {
'_id': {
month: {$month: '$poDate'},
year: {$year: '$poDate'}
},
totalPrice: {$sum: {$toDecimal:'$amount'}},
}
},
{$project: {
_id: 0,
totalPrice: {$toString: '$totalPrice'}
}}
])
虽然我将其转换为 MongoRepository 中的 @Aggregation 注释,但如下所示(我正在删除撇号并替换为方法参数):
@Repository
public interface SalesPoRepository extends MongoRepository<SalesPo, String> {
@Aggregation(pipeline = {"{$project: {\n" +
" month: {$month: $poDate},\n" +
" year: {$year: $poDate},\n" +
" amount: 1,\n" +
" poDate: 1\n" +
" }}"
,"{$match: {$and : [{year:?0} , {month:?1}] \n" +
" }}"
,"{$group: { \n" +
" '_id': {\n" +
" month: {$month: $poDate},\n" +
" year: {$year: $poDate} \n" +
" },\n" +
" totalPrice: {$sum: {$toDecimal:$amount}},\n" +
" }\n" +
" }"
,"{$project: {\n" +
" _id: 0,\n" +
" totalPrice: {$toString: $totalPrice}\n" +
" }}"})
AggregationResults<SumPrice> sumPriceThisYearMonth(Integer year, Integer month);
我的文档如下所示:
@Document(collection = "SalesPo")
@Data
public class SalesPo {
@Id
private String id;
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate poDate;
private BigDecimal amount;
}
以及持有预测的 SumPrice class:
@Data
public class SumPrice {
private BigDecimal totalPrice;
}
我希望这个答案可以帮助那些尝试在不使用mongo模板[=34=的情况下在mongo存储库中进行聚合的人].