如何使用 mongo 搜索集合和 return 子文档列表 (Spring-data-mongo)
How to search a collection and return a list of sub document with mongo (Sping-data-mongo)
鉴于此文档集(工作流):
[
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
},
{
id: 3,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
}
]
我已经有一个搜索功能,return 我有一个工作流列表(页面),该列表(页面)匹配使用查询和 mongoTemplate.find();
的一系列条件
我需要做的是将这个结果转换成这样:
(假设查询 return 所有元素
[
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
]
},
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task2', value:'started'}
]
},
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task3', value:'completed'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task2', value:'started'}
]
},
.... etc
]
换句话说,我想 return 我的工作流的扁平化版本,每个工作流只有 1 个任务。尽可能分页!!
我可以使用的另一个版本是 return 带有聚合工作流对象(父级)的任务列表到添加的字段中,例如:
[
{taskId: 'task1', value:'new', workflow: {the workflow object}},
{taskId: 'task2', value:'started', workflow: {the workflow object}},
]
我玩了一点聚合和展开等,但我是 mongodb 的新手,我找不到对我有帮助的例子。
提前致谢!
更新:
基于此处和其他人的回答。我想出了这个有效的查询,并且完全按照我的意愿行事。 :
db.Workflow.aggregate([
{
$match: {}
},
{
$unwind: "$tasks"
},
{
$facet: {
data: [
{
$skip: 0
},
{
$limit: 30
},
],
count: [
{
$group: {
_id: null,
count: {
$sum: 1
}
}
},
],
}
}
])
因此,如果有人可以帮助我翻译 spring-数据聚合请求中的内容...我很难处理群组部分。谢谢
所以我会尝试使用示例代码来回答。我正在使用 SpringTemplates 而不是 SpringRepositories。虽然存储库可以进行聚合,但对于模板具有更多控制权的大多数企业应用程序而言,它们从根本上来说过于基础。在我看来,我只会使用模板而不会使用存储库 - 但这只是我的意见。
请记住 - SpringData 想要将 POJO 映射到 MongoDB 集合中的数据。查询的响应很容易,因为两者相互同步——POJO 与数据库中找到的预期结构相匹配。执行聚合时,结果通常会因各种原因而改变形状。
在您的用例中,您似乎想要展开字段“任务”并且每个更高级别的父对象只有一个任务。这意味着父字段将重复 - 很像原始 post 中显示的预期输出。执行展开时,数组不再存在,但有一个文档取而代之。由于这个原因,输出的形状略有不同。对于 Spring,这意味着不同的 class(继承在这里可以提供帮助)。出于这个原因,在我的示例代码中,我有两个 POJO - 一个名为 Workflow
代表原始保存的文档形状,包括字段 tasks
的数组,另一个名为 Workflow2
的 POJO 代表重塑聚合结果。唯一的区别是字段 tasks
。一个有一个 List<Task>
而另一个有一个 Task
子对象。
所以,实际上我有 3 个 POJO:
- 工作流程
- 工作流 2
- 任务
任务是 class 定义字段 task
中的子文档。无论它是否是一个数组 - 它仍然需要一个 class 来保存两个子文档字段 taskId
和 value
.
我正在使用 maven 进行依赖管理。为了更加清楚起见,我完全限定了没有导入语句的每个对象。
所以,不用多说了,这里是代码。
文件pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>test.barry</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<start-class>test.barry.Main</start-class>
<mongodb.version>4.3.4</mongodb.version> <!-- BARRY NOTE: FORCE SPRING-BOOT TO USE THE MONGODB DRIVER VERSION 4.4.0 INSTEAD OF 4.0.5 -->
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
</project>
文件src/main/resources/application.properties
spring.data.mongodb.uri=mongodb://testuser:mysecret@localhost:50011,localhost:50012,localhost:50013/?replicaSet=replSet&w=majority&readConcernLevel=majority&readPreference=primary&authSource=admin&retryWrites=true&maxPoolSize=10&waitQueueTimeoutMS=1000
spring.data.mongodb.database=javaspringtestX
spring.data.mongodb.socketconnecttimeout=60
文件 src/main/java/test.barry/Main.java
package test.barry;
@org.springframework.boot.autoconfigure.SpringBootApplication
public class Main {
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(Main.class, args);
}
}
文件 src/main/java/test.barry/MySpringBootApplication.java
package test.barry;
@org.springframework.boot.autoconfigure.SpringBootApplication
public class MySpringBootApplication implements org.springframework.boot.CommandLineRunner {
@org.springframework.beans.factory.annotation.Autowired
org.springframework.data.mongodb.core.MongoTemplate mongoTemplate;
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(org.springframework.boot.autoconfigure.SpringBootApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println("Drop collections for automatic cleanup during test:");
System.out.println("-------------------------------");
this.mongoTemplate.dropCollection(test.barry.models.Workflow.class);
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.set(2021, 2, 10);
test.barry.models.Workflow workflow1 = new test.barry.models.Workflow();
workflow1.id = 1;
workflow1.name = "workflow";
workflow1.status = "started";
workflow1.createdDate = calendar.getTime();
workflow1.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow1.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow1.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow1);
test.barry.models.Workflow workflow2 = new test.barry.models.Workflow();
workflow2.id = 2;
workflow2.name = "workflow";
workflow2.status = "started";
workflow2.createdDate = calendar.getTime();
workflow2.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow2.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow2.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow2);
test.barry.models.Workflow workflow3 = new test.barry.models.Workflow();
workflow3.id = 3;
workflow3.name = "workflow";
workflow3.status = "started";
workflow3.createdDate = calendar.getTime();
workflow3.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow3.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow3.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow3);
org.springframework.data.mongodb.core.aggregation.Aggregation pipeline = org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation (
org.springframework.data.mongodb.core.aggregation.Aggregation.unwind("tasks")
);
org.springframework.data.mongodb.core.aggregation.AggregationResults<test.barry.models.Workflow2> aggregationResults = this.mongoTemplate.aggregate(pipeline, test.barry.models.Workflow.class, test.barry.models.Workflow2.class);
java.util.List<test.barry.models.Workflow2> listResults = aggregationResults.getMappedResults();
System.out.println(listResults.size());
}
}
文件 src/main/java/test.barry/SpringConfiguration.java
package test.barry;
@org.springframework.context.annotation.Configuration
@org.springframework.context.annotation.PropertySource("classpath:/application.properties")
public class SpringConfiguration {
@org.springframework.beans.factory.annotation.Autowired
org.springframework.core.env.Environment env;
@org.springframework.context.annotation.Bean
public com.mongodb.client.MongoClient mongoClient() {
String uri = env.getProperty("spring.data.mongodb.uri");
return com.mongodb.client.MongoClients.create(uri);
}
@org.springframework.context.annotation.Bean
public org.springframework.data.mongodb.MongoDatabaseFactory mongoDatabaseFactory() {
String uri = env.getProperty("spring.data.mongodb.uri");
String database = env.getProperty("spring.data.mongodb.database");
return new org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory(com.mongodb.client.MongoClients.create(uri), database);
}
@org.springframework.context.annotation.Bean
public org.springframework.data.mongodb.core.MongoTemplate mongoTemplate() throws Exception {
return new org.springframework.data.mongodb.core.MongoTemplate(mongoClient(), env.getProperty("spring.data.mongodb.database"));
}
}
文件 src/main/java/test.barry/models/Workflow.java
package test.barry.models;
@org.springframework.data.mongodb.core.mapping.Document(collection = "Workflow")
public class Workflow
{
@org.springframework.data.annotation.Id
public int id;
public String name;
public String status;
public java.util.Date createdDate;
public java.util.List<Task> tasks;
public Workflow() {
this.tasks = new java.util.ArrayList<Task>();
}
public Workflow(String name, String status, java.util.Date createdDate) {
this();
this.name = name;
this.status = status;
this.createdDate = createdDate;
}
@Override
public String toString() {
return String.format("Workflow[id=%s, name='%s', status='%s', createdDate='%s']", id, name, status, createdDate);
}
}
文件 src/main/java/test.barry/models/Workflow2.java
package test.barry.models;
@org.springframework.data.mongodb.core.mapping.Document(collection = "Workflow")
public class Workflow2
{
@org.springframework.data.annotation.Id
public int id;
public String name;
public String status;
public java.util.Date createdDate;
public Task tasks;
public Workflow2() {
this.tasks = new Task();
}
public Workflow2(String name, String status, java.util.Date createdDate) {
this();
this.name = name;
this.status = status;
this.createdDate = createdDate;
}
@Override
public String toString() {
return String.format("Workflow[id=%s, name='%s', status='%s', createdDate='%s']", id, name, status, createdDate);
}
}
文件 src/main/java/test.barry/models/Task.java
package test.barry.models;
public class Task
{
public Task() {}
public Task(String taskId, String value) {
this.taskId = taskId;
this.value = value;
}
public String taskId;
public String value;
}
结论
使用 MongoShell 时,我们看到创建了以下记录:
Enterprise replSet [primary] javaspringtestX> db.Workflow.find()
[
{
_id: 1,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
},
{
_id: 2,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
},
{
_id: 3,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
}
]
要查看聚合结果,我们必须使用调试器。我正在使用 IntelliJ IDEA 进行调试,并将结果显示在 Workflow2
类型的列表中。不确定如何在此处显示它们。我的测试表明这在我理解的情况下是有效的。请评估并让我知道是否需要调整...
顺便说一下,分页的概念最适合由您的应用程序而不是数据库来管理。在实践中,您可能会发现 skip() 和 limit() 的用法,但对于具有许多页面的大型数据集,您可能会发现对下一页的重新查询会导致性能问题,因为每次它们都必须识别所有文档,然后识别要跳过的文档。最好跟踪上一页上显示的范围,然后重新查询下一页上的记录。即,限制结果集以获得更好的性能。
编辑 - 2021-12-09
在查看保存的数据时,它显示了奇怪的日期。显然,不推荐使用 java.util.Date myDate = java.util.Date(2021, 2, 10);
会创建无效日期。为此,我添加了 java.util.Calendar calendar = java.util.Calendar.getInstance();
MongoDB聚合就是你所需要的:
db.Workflow.aggregate([
{
$match: {} // put here your search criteria
},
{
$unwind: "$tasks"
},
{
$addFields: {
tasks: [
"$tasks"
]
}
},
//pageable
{
$skip: 0
},
{
$limit: 100
}
])
SpringBoot方式:
@Autowired
private MongoTemplate mongoTemplate;
...
List<AggregationOperation> pipeline = new ArrayList<>();
//$match (put here your filter)
pipeline.add(Aggregation.match(Criteria.where("status").is("started")));
//$unwind
pipeline.add(Aggregation.unwind("tasks"));
//$addFields
pipeline.add(Aggregation.addFields().addFieldWithValue("tasks", Arrays.asList("$tasks")).build());
//$skip
pipeline.add(Aggregation.skip(0L));
//$limit
pipeline.add(Aggregation.limit(100L));
Aggregation agg = Aggregation.newAggregation(pipeline)
.withOptions(Aggregation
.newAggregationOptions().allowDiskUse(Boolean.TRUE).build());
return mongoTemplate.aggregate(agg, Workflow.class, Workflow.class).getMappedResults();
鉴于此文档集(工作流):
[
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
},
{
id: 3,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
{taskId: 'task2', value:'started'}
{taskId: 'task3', value:'completed'}
]
}
]
我已经有一个搜索功能,return 我有一个工作流列表(页面),该列表(页面)匹配使用查询和 mongoTemplate.find();
的一系列条件我需要做的是将这个结果转换成这样: (假设查询 return 所有元素
[
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
]
},
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task2', value:'started'}
]
},
{
id: 1,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task3', value:'completed'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task1', value:'new'}
]
},
{
id: 2,
name: 'workflow',
status: 'started',
createdDate: '2021-02-10'
tasks: [
{taskId: 'task2', value:'started'}
]
},
.... etc
]
换句话说,我想 return 我的工作流的扁平化版本,每个工作流只有 1 个任务。尽可能分页!!
我可以使用的另一个版本是 return 带有聚合工作流对象(父级)的任务列表到添加的字段中,例如:
[
{taskId: 'task1', value:'new', workflow: {the workflow object}},
{taskId: 'task2', value:'started', workflow: {the workflow object}},
]
我玩了一点聚合和展开等,但我是 mongodb 的新手,我找不到对我有帮助的例子。
提前致谢!
更新:
基于此处和其他人的回答。我想出了这个有效的查询,并且完全按照我的意愿行事。 :
db.Workflow.aggregate([
{
$match: {}
},
{
$unwind: "$tasks"
},
{
$facet: {
data: [
{
$skip: 0
},
{
$limit: 30
},
],
count: [
{
$group: {
_id: null,
count: {
$sum: 1
}
}
},
],
}
}
])
因此,如果有人可以帮助我翻译 spring-数据聚合请求中的内容...我很难处理群组部分。谢谢
所以我会尝试使用示例代码来回答。我正在使用 SpringTemplates 而不是 SpringRepositories。虽然存储库可以进行聚合,但对于模板具有更多控制权的大多数企业应用程序而言,它们从根本上来说过于基础。在我看来,我只会使用模板而不会使用存储库 - 但这只是我的意见。
请记住 - SpringData 想要将 POJO 映射到 MongoDB 集合中的数据。查询的响应很容易,因为两者相互同步——POJO 与数据库中找到的预期结构相匹配。执行聚合时,结果通常会因各种原因而改变形状。
在您的用例中,您似乎想要展开字段“任务”并且每个更高级别的父对象只有一个任务。这意味着父字段将重复 - 很像原始 post 中显示的预期输出。执行展开时,数组不再存在,但有一个文档取而代之。由于这个原因,输出的形状略有不同。对于 Spring,这意味着不同的 class(继承在这里可以提供帮助)。出于这个原因,在我的示例代码中,我有两个 POJO - 一个名为 Workflow
代表原始保存的文档形状,包括字段 tasks
的数组,另一个名为 Workflow2
的 POJO 代表重塑聚合结果。唯一的区别是字段 tasks
。一个有一个 List<Task>
而另一个有一个 Task
子对象。
所以,实际上我有 3 个 POJO:
- 工作流程
- 工作流 2
- 任务
任务是 class 定义字段 task
中的子文档。无论它是否是一个数组 - 它仍然需要一个 class 来保存两个子文档字段 taskId
和 value
.
我正在使用 maven 进行依赖管理。为了更加清楚起见,我完全限定了没有导入语句的每个对象。
所以,不用多说了,这里是代码。
文件pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>test.barry</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<start-class>test.barry.Main</start-class>
<mongodb.version>4.3.4</mongodb.version> <!-- BARRY NOTE: FORCE SPRING-BOOT TO USE THE MONGODB DRIVER VERSION 4.4.0 INSTEAD OF 4.0.5 -->
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
</project>
文件src/main/resources/application.properties
spring.data.mongodb.uri=mongodb://testuser:mysecret@localhost:50011,localhost:50012,localhost:50013/?replicaSet=replSet&w=majority&readConcernLevel=majority&readPreference=primary&authSource=admin&retryWrites=true&maxPoolSize=10&waitQueueTimeoutMS=1000
spring.data.mongodb.database=javaspringtestX
spring.data.mongodb.socketconnecttimeout=60
文件 src/main/java/test.barry/Main.java
package test.barry;
@org.springframework.boot.autoconfigure.SpringBootApplication
public class Main {
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(Main.class, args);
}
}
文件 src/main/java/test.barry/MySpringBootApplication.java
package test.barry;
@org.springframework.boot.autoconfigure.SpringBootApplication
public class MySpringBootApplication implements org.springframework.boot.CommandLineRunner {
@org.springframework.beans.factory.annotation.Autowired
org.springframework.data.mongodb.core.MongoTemplate mongoTemplate;
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(org.springframework.boot.autoconfigure.SpringBootApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println("Drop collections for automatic cleanup during test:");
System.out.println("-------------------------------");
this.mongoTemplate.dropCollection(test.barry.models.Workflow.class);
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.set(2021, 2, 10);
test.barry.models.Workflow workflow1 = new test.barry.models.Workflow();
workflow1.id = 1;
workflow1.name = "workflow";
workflow1.status = "started";
workflow1.createdDate = calendar.getTime();
workflow1.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow1.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow1.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow1);
test.barry.models.Workflow workflow2 = new test.barry.models.Workflow();
workflow2.id = 2;
workflow2.name = "workflow";
workflow2.status = "started";
workflow2.createdDate = calendar.getTime();
workflow2.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow2.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow2.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow2);
test.barry.models.Workflow workflow3 = new test.barry.models.Workflow();
workflow3.id = 3;
workflow3.name = "workflow";
workflow3.status = "started";
workflow3.createdDate = calendar.getTime();
workflow3.tasks.add(new test.barry.models.Task ("task1", "new"));
workflow3.tasks.add(new test.barry.models.Task ("task2", "started"));
workflow3.tasks.add(new test.barry.models.Task ("task3", "completed"));
this.mongoTemplate.save(workflow3);
org.springframework.data.mongodb.core.aggregation.Aggregation pipeline = org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation (
org.springframework.data.mongodb.core.aggregation.Aggregation.unwind("tasks")
);
org.springframework.data.mongodb.core.aggregation.AggregationResults<test.barry.models.Workflow2> aggregationResults = this.mongoTemplate.aggregate(pipeline, test.barry.models.Workflow.class, test.barry.models.Workflow2.class);
java.util.List<test.barry.models.Workflow2> listResults = aggregationResults.getMappedResults();
System.out.println(listResults.size());
}
}
文件 src/main/java/test.barry/SpringConfiguration.java
package test.barry;
@org.springframework.context.annotation.Configuration
@org.springframework.context.annotation.PropertySource("classpath:/application.properties")
public class SpringConfiguration {
@org.springframework.beans.factory.annotation.Autowired
org.springframework.core.env.Environment env;
@org.springframework.context.annotation.Bean
public com.mongodb.client.MongoClient mongoClient() {
String uri = env.getProperty("spring.data.mongodb.uri");
return com.mongodb.client.MongoClients.create(uri);
}
@org.springframework.context.annotation.Bean
public org.springframework.data.mongodb.MongoDatabaseFactory mongoDatabaseFactory() {
String uri = env.getProperty("spring.data.mongodb.uri");
String database = env.getProperty("spring.data.mongodb.database");
return new org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory(com.mongodb.client.MongoClients.create(uri), database);
}
@org.springframework.context.annotation.Bean
public org.springframework.data.mongodb.core.MongoTemplate mongoTemplate() throws Exception {
return new org.springframework.data.mongodb.core.MongoTemplate(mongoClient(), env.getProperty("spring.data.mongodb.database"));
}
}
文件 src/main/java/test.barry/models/Workflow.java
package test.barry.models;
@org.springframework.data.mongodb.core.mapping.Document(collection = "Workflow")
public class Workflow
{
@org.springframework.data.annotation.Id
public int id;
public String name;
public String status;
public java.util.Date createdDate;
public java.util.List<Task> tasks;
public Workflow() {
this.tasks = new java.util.ArrayList<Task>();
}
public Workflow(String name, String status, java.util.Date createdDate) {
this();
this.name = name;
this.status = status;
this.createdDate = createdDate;
}
@Override
public String toString() {
return String.format("Workflow[id=%s, name='%s', status='%s', createdDate='%s']", id, name, status, createdDate);
}
}
文件 src/main/java/test.barry/models/Workflow2.java
package test.barry.models;
@org.springframework.data.mongodb.core.mapping.Document(collection = "Workflow")
public class Workflow2
{
@org.springframework.data.annotation.Id
public int id;
public String name;
public String status;
public java.util.Date createdDate;
public Task tasks;
public Workflow2() {
this.tasks = new Task();
}
public Workflow2(String name, String status, java.util.Date createdDate) {
this();
this.name = name;
this.status = status;
this.createdDate = createdDate;
}
@Override
public String toString() {
return String.format("Workflow[id=%s, name='%s', status='%s', createdDate='%s']", id, name, status, createdDate);
}
}
文件 src/main/java/test.barry/models/Task.java
package test.barry.models;
public class Task
{
public Task() {}
public Task(String taskId, String value) {
this.taskId = taskId;
this.value = value;
}
public String taskId;
public String value;
}
结论
使用 MongoShell 时,我们看到创建了以下记录:
Enterprise replSet [primary] javaspringtestX> db.Workflow.find()
[
{
_id: 1,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
},
{
_id: 2,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
},
{
_id: 3,
name: 'workflow',
status: 'started',
createdDate: ISODate("2021-03-10T23:49:46.704Z"),
tasks: [
{ taskId: 'task1', value: 'new' },
{ taskId: 'task2', value: 'started' },
{ taskId: 'task3', value: 'completed' }
],
_class: 'test.barry.models.Workflow'
}
]
要查看聚合结果,我们必须使用调试器。我正在使用 IntelliJ IDEA 进行调试,并将结果显示在 Workflow2
类型的列表中。不确定如何在此处显示它们。我的测试表明这在我理解的情况下是有效的。请评估并让我知道是否需要调整...
顺便说一下,分页的概念最适合由您的应用程序而不是数据库来管理。在实践中,您可能会发现 skip() 和 limit() 的用法,但对于具有许多页面的大型数据集,您可能会发现对下一页的重新查询会导致性能问题,因为每次它们都必须识别所有文档,然后识别要跳过的文档。最好跟踪上一页上显示的范围,然后重新查询下一页上的记录。即,限制结果集以获得更好的性能。
编辑 - 2021-12-09
在查看保存的数据时,它显示了奇怪的日期。显然,不推荐使用 java.util.Date myDate = java.util.Date(2021, 2, 10);
会创建无效日期。为此,我添加了 java.util.Calendar calendar = java.util.Calendar.getInstance();
MongoDB聚合就是你所需要的:
db.Workflow.aggregate([
{
$match: {} // put here your search criteria
},
{
$unwind: "$tasks"
},
{
$addFields: {
tasks: [
"$tasks"
]
}
},
//pageable
{
$skip: 0
},
{
$limit: 100
}
])
SpringBoot方式:
@Autowired
private MongoTemplate mongoTemplate;
...
List<AggregationOperation> pipeline = new ArrayList<>();
//$match (put here your filter)
pipeline.add(Aggregation.match(Criteria.where("status").is("started")));
//$unwind
pipeline.add(Aggregation.unwind("tasks"));
//$addFields
pipeline.add(Aggregation.addFields().addFieldWithValue("tasks", Arrays.asList("$tasks")).build());
//$skip
pipeline.add(Aggregation.skip(0L));
//$limit
pipeline.add(Aggregation.limit(100L));
Aggregation agg = Aggregation.newAggregation(pipeline)
.withOptions(Aggregation
.newAggregationOptions().allowDiskUse(Boolean.TRUE).build());
return mongoTemplate.aggregate(agg, Workflow.class, Workflow.class).getMappedResults();