当前位置: 首页 > news >正文

java 操作Mongodb

CRUD基础操作

Springboot 操作 MongoDB 有两种方式。

  • 第一种方式是采用 Springboot 官方推荐的 JPA 方式,这种操作方式,使用简单但是灵活性比较差。
  • 第二种方式是采用 Spring Data MongoDB 封装的 MongoDB 官方 Java 驱动 MongoTemplate 对 MongoDB 进行操作,这种方式非常灵活,能满足绝大部分需求。

本文将采用第二种方式进行介绍!

插入文档

MongoTemplate提供了insert()方法,用于插入文档,示例代码如下:

  • 用于插入文档

没指定集合名称时,会取@Document注解中的集合名称

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 插入文档* @throws Exception*/@Testpublic void insert() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());mongoTemplate.insert(person);}
}
  • 自定义集合名称,插入文档
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 自定义集合,插入文档* @throws Exception*/@Testpublic void insertCustomCollection() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());mongoTemplate.insert(person, "custom_person");}
}
  • 自定义集合,批量插入文档

如果采用批量插入文档,必须指定集合名称

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 自定义集合,批量插入文档* @throws Exception*/@Testpublic void insertBatch() throws Exception {List<Person> personList = new ArrayList<>();Person person1 =new Person();person1.setId(10l);person1.setUserName("张三");person1.setPassWord("123456");person1.setCreateTime(new Date());personList.add(person1);Person person2 =new Person();person2.setId(11l);person2.setUserName("李四");person2.setPassWord("123456");person2.setCreateTime(new Date());personList.add(person2);mongoTemplate.insert(personList, "custom_person");}
}

存储文档

MongoTemplate提供了save()方法,用于存储文档。

在存储文档的时候会通过主键ID进行判断,如果存在就更新,否则就插入,示例代码如下:

  • 存储文档,如果没有插入,否则通过主键ID更新
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 存储文档,如果没有插入,否则更新* @throws Exception*/@Testpublic void save() throws Exception {Person person =new Person();person.setId(13l);person.setUserName("八八");person.setPassWord("123456");person.setAge(40);person.setCreateTime(new Date());mongoTemplate.save(person);}
}
  • 自定义集合,存储文档
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 自定义集合,存储文档* @throws Exception*/@Testpublic void saveCustomCollection() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());mongoTemplate.save(person, "custom_person");}
}

更新文档

MongoTemplate提供了updateFirst()和updateMulti()方法,用于更新文档,示例代码如下:

  • 更新文档,匹配查询到的文档数据中的第一条数据
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 更新文档,匹配查询到的文档数据中的第一条数据* @throws Exception*/@Testpublic void updateFirst() throws Exception {//更新对象Person person =new Person();person.setId(1l);person.setUserName("张三123");person.setPassWord("123456");person.setCreateTime(new Date());//更新条件Query query= new Query(Criteria.where("id").is(person.getId()));//更新值Update update= new Update().set("userName", person.getUserName()).set("passWord", person.getPassWord());//更新查询满足条件的文档数据(第一条)UpdateResult result =mongoTemplate.updateFirst(query,update, Person.class);if(result!=null){System.out.println("更新条数:" + result.getMatchedCount());}}
}
  • 更新文档,匹配查询到的文档数据中的所有数据
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 更新文档,匹配查询到的文档数据中的所有数据* @throws Exception*/@Testpublic void updateMany() throws Exception {//更新对象Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());//更新条件Query query= new Query(Criteria.where("id").is(person.getId()));//更新值Update update= new Update().set("userName", person.getUserName()).set("passWord", person.getPassWord());//更新查询满足条件的文档数据(全部)UpdateResult result = mongoTemplate.updateMulti(query, update, Person.class);if(result!=null){System.out.println("更新条数:" + result.getMatchedCount());}}
}

删除文档

MongoTemplate提供了remove()、findAndRemove()和findAllAndRemove()方法,用于删除文档,示例代码如下:

  • 删除符合条件的所有文档
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 删除符合条件的所有文档* @throws Exception*/@Testpublic void remove() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());Query query = new Query(Criteria.where("userName").is(person.getUserName()));DeleteResult result = mongoTemplate.remove(query, Person.class);System.out.println("删除条数:" + result.getDeletedCount());}
}
  • 删除符合条件的单个文档,并返回删除的文档
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 删除符合条件的单个文档,并返回删除的文档* @throws Exception*/@Testpublic void findAndRemove() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());Query query = new Query(Criteria.where("id").is(person.getId()));Person result = mongoTemplate.findAndRemove(query, Person.class);System.out.println("删除的文档数据:" + result.toString());}
}
  • 删除符合条件的所有文档,并返回删除的文档
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 删除符合条件的所有文档,并返回删除的文档* @throws Exception*/@Testpublic void findAllAndRemove() throws Exception {Person person =new Person();person.setId(1l);person.setUserName("张三");person.setPassWord("123456");person.setCreateTime(new Date());Query query = new Query(Criteria.where("id").is(person.getId()));List<Person> result = mongoTemplate.findAllAndRemove(query, Person.class);System.out.println("删除的文档数据:" + result.toString());}
}

查询文档

MongoTemplate提供了非常多的文档查询方法,日常开发中用的最多的就是find()方法,示例代码如下:

  • 查询集合中的全部文档数据
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 查询集合中的全部文档数据* @throws Exception*/@Testpublic void findAll() throws Exception {List<Person> result = mongoTemplate.findAll(Person.class);System.out.println("查询结果:" + result.toString());}
}

查询集合中指定的ID文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 查询集合中指定的ID文档数据* @throws Exception*/@Testpublic void findById() {long id = 1l;Person result = mongoTemplate.findById(id, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据条件查询集合中符合条件的文档,返回第一条数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据条件查询集合中符合条件的文档,返回第一条数据*/@Testpublic void findOne() {String userName = "张三";Query query = new Query(Criteria.where("userName").is(userName));Person result = mongoTemplate.findOne(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据条件查询集合中符合条件的文档

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据条件查询集合中符合条件的文档*/@Testpublic void findByCondition() {String userName = "张三";Query query = new Query(Criteria.where("userName").is(userName));List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据【AND】关联多个查询条件,查询集合中的文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据【AND】关联多个查询条件,查询集合中的文档数据*/@Testpublic void findByAndCondition() {// 创建条件Criteria criteriaUserName = Criteria.where("userName").is("张三");Criteria criteriaPassWord = Criteria.where("passWord").is("123456");// 创建条件对象,将上面条件进行 AND 关联Criteria criteria = new Criteria().andOperator(criteriaUserName, criteriaPassWord);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据【OR】关联多个查询条件,查询集合中的文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据【OR】关联多个查询条件,查询集合中的文档数据*/@Testpublic void findByOrCondition() {// 创建条件Criteria criteriaUserName = Criteria.where("userName").is("张三");Criteria criteriaPassWord = Criteria.where("passWord").is("123456");// 创建条件对象,将上面条件进行 OR 关联Criteria criteria = new Criteria().orOperator(criteriaUserName, criteriaPassWord);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据【IN】关联多个查询条件,查询集合中的文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据【IN】关联多个查询条件,查询集合中的文档数据*/@Testpublic void findByInCondition() {// 设置查询条件参数List<Long> ids = Arrays.asList(1l, 10l, 11l);// 创建条件Criteria criteria = Criteria.where("id").in(ids);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据【逻辑运算符】查询集合中的文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据【逻辑运算符】查询集合中的文档数据*/@Testpublic void findByOperator() {// 设置查询条件参数int min = 20;int max = 35;Criteria criteria = Criteria.where("age").gt(min).lte(max);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据【正则表达式】查询集合中的文档数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据【正则表达式】查询集合中的文档数据*/@Testpublic void findByRegex() {// 设置查询条件参数String regex = "^张*";Criteria criteria = Criteria.where("userName").regex(regex);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据条件查询集合中符合条件的文档,获取其文档列表并排序

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据条件查询集合中符合条件的文档,获取其文档列表并排序*/@Testpublic void findByConditionAndSort() {String userName = "张三";Query query = new Query(Criteria.where("userName").is(userName)).with(Sort.by("age"));List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

根据单个条件查询集合中的文档数据,并按指定字段进行排序与限制指定数目

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 根据单个条件查询集合中的文档数据,并按指定字段进行排序与限制指定数目*/@Testpublic void findByConditionAndSortLimit() {String userName = "张三";//从第一行开始,查询2条数据返回Query query = new Query(Criteria.where("userName").is(userName)).with(Sort.by("createTime")).limit(2).skip(1);List<Person> result = mongoTemplate.find(query, Person.class);System.out.println("查询结果:" + result.toString());}
}

统计集合中符合【查询条件】的文档【数量】

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 统计集合中符合【查询条件】的文档【数量】*/@Testpublic void countNumber() {// 设置查询条件参数String regex = "^张*";Criteria criteria = Criteria.where("userName").regex(regex);// 创建查询对象,然后将条件对象添加到其中Query query = new Query(criteria);long count = mongoTemplate.count(query, Person.class);System.out.println("统计结果:" + count);}
}

查询指定字段返回

    @Testpublic void test1() {Query query = Query.query(Criteria.where("userId").is(1L));query.fields().include("pid").exclude("_id");/*Expected to read Document Document{{pid=2031}} into type class java.lang.Integer but didn't find a PersistentEntity for the latter!* {_id=5fae53927e52992e78a3aecd, pid=2031}*{_id=5fae53927e52992e78a3aed9, pid=2032}*{_id=5fae53927e52992e78a3aee5, pid=2033}* {pid=2033}* {pid=2034}* {pid=2035}*/List<Object> result = mongoTemplate.find(query, Object.class, "quanzi_publish");result.forEach(System.out::println);}

通过query点fields 可以通过 include 指定需要返回的字段。可以链式编程。

exclude排除需要的字段。 因为如果指定需要的字段。不排除_id 的话查询的数据会默认包含_id

{_id=5fae53927e52992e78a3aecd, pid=2031} 所以我这里做了排除。

最后查询的结果是 {pid=2033} 此时我的pid 是一个Long 类型的数据。但是不能用

Long.class 去解析。只能用Object.clss 因为 {pid=2033} 虽然只有一个字段。但是从mongodb查询出来的应该是一个json对象。对象的属性只有一个。没有去看mongodb底层。但应该是做了json的解析。

@Test
public void test2() {Query query = Query.query(Criteria.where("userId").is(1L));List<MyLong> result = mongoTemplate.find(query, MyLong.class, "quanzi_publish");List<Long> pids = result.stream().map(MyLong::getPid).collect(Collectors.toList());pids.forEach(System.out::println);
}class MyLong {private Long pid;public Long getPid() {return pid;}public void setPid(Long pid) {this.pid = pid;}@Overridepublic String toString() {return "MyLong{" +"pid=" + pid +'}';}
}

既然是解析json 。我自己定义了一个类。同样的属性去接。然后用stream 流转为了long类型的list集合。这样做感觉也挺麻烦。但是如果集合字段特别多的话。这样应该能提升一些性能。毕竟如果直接读取整个集合,也是需要遍历。然后提取出id 到一个新集合。步骤差不多。这样只是从mongodb只读取了需要的数据。

查询某字段在MongoDB中是否存在,并返回一条查询记录

String field = "F1_0909";
Query query = new Query();
query.fields().include(field);
query.addCriteria(new Criteria(filed).exists(true));
Map map = mongoTemplate.findOne(query, Map.class, collectionName);

排序

@Test
public void findListStudentSort() {// 排序Query query = new Query();query.with(Sort.by(Sort.Direction.DESC, "age"));List<Student> students = mongoTemplate.find(query, Student.class);// students】】】" + students);}

分页

@Test
public void findFenYeList() {// 分页// 设置分页参数Query query = new Query();int currentPage = 2;// 0,1相同int pageSize = 2;// 设置分页信息query.limit(pageSize);query.skip(pageSize * (currentPage - 1));// query.addCriteria(Criteria.where("clazzName").regex("天"));List<Clazz> clazzes = mongoTemplate.find(query, Clazz.class);// clazzs】】】" + clazzes);
}
@Test
public void findZongHe() {// 分页+范围+模糊查询+排序// 拼装查询信息Query query = new Query();query.addCriteria(Criteria.where("age").gte(6).lte(18));query.with(Sort.by(Sort.Direction.ASC, "age"));query.addCriteria(Criteria.where("name").regex("小"));// 模糊查询名字Long count = mongoTemplate.count(query, Student.class);// 查询总记录数List<Student> list = mongoTemplate.find(query, Student.class);}/ 将筛选条件放入管道中
Aggregation aggregation = Aggregation.newAggregation(lookup,Aggregation.match(criteria),Aggregation.group("startSolitaireId") // 分组的字段.first("startSolitaireId").as("startSolitaireId")  // 映射的字段 并取别名.first("userId").as("userId").first("interact").as("interact").first("createTime").as("createTime").first("startVO").as("startVO"),Aggregation.sort(Sort.Direction.DESC,"createTime"), // 排序Aggregation.skip((long) page.getCurrent() * page.getSize()),Aggregation.limit(page.getSize())); // 分页

MongoDB 聚合操作

聚合表达式

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
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.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;/*** 聚合表达式 $group** @author mydlq*/
@Slf4j
@Service
public class AggregateGroupService {/*** 设置集合名称*/private static final String COLLECTION_NAME = "users";@Resourceprivate MongoTemplate mongoTemplate;/*** 使用管道操作符 $group 结合 $count 方法进行聚合统计** @return 聚合结果*/public Object aggregationGroupCount() {// 使用管道操作符 $group 进行分组,然后统计各个组的文档数量AggregationOperation group = Aggregation.group("age").count().as("numCount");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $max 进行聚合统计** @return 聚合结果*/public Object aggregationGroupMax() {// 使用管道操作符 $group 进行分组,然后统计各个组文档某字段最大值AggregationOperation group = Aggregation.group("sex").max("salary").as("salaryMax");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $min 进行聚合统计** @return 聚合结果*/public Object aggregationGroupMin() {// 使用管道操作符 $group 进行分组,然后统计各个组文档某字段最小值AggregationOperation group = Aggregation.group("sex").min("salary").as("salaryMin");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $sum 进行聚合统计** @return 聚合结果*/public Object aggregationGroupSum() {// 使用管道操作符 $group 进行分组,然后统计各个组文档某字段值合计AggregationOperation group = Aggregation.group("sex").sum("salary").as("salarySum");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $avg 进行聚合统计** @return 聚合结果*/public Object aggregationGroupAvg() {// 使用管道操作符 $group 进行分组,然后统计各个组文档某字段值平均值AggregationOperation group = Aggregation.group("sex").avg("salary").as("salaryAvg");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $first 获取每个组的包含某字段的文档的第一条数据** @return 聚合结果*/public Object aggregationGroupFirst() {// 先对数据进行排序,然后使用管道操作符 $group 进行分组,最后统计各个组文档某字段值第一个值AggregationOperation sort = Aggregation.sort(Sort.by("salary").ascending());AggregationOperation group = Aggregation.group("sex").first("salary").as("salaryFirst");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(sort, group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $last 获取每个组的包含某字段的文档的最后一条数据** @return 聚合结果*/public Object aggregationGroupLast() {// 先对数据进行排序,然后使用管道操作符 $group 进行分组,最后统计各个组文档某字段值第最后一个值AggregationOperation sort = Aggregation.sort(Sort.by("salary").ascending());AggregationOperation group = Aggregation.group("sex").last("salary").as("salaryLast");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(sort, group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用管道操作符 $group 结合表达式操作符 $push 获取某字段列表** @return 聚合结果*/public Object aggregationGroupPush() {// 先对数据进行排序,然后使用管道操作符 $group 进行分组,然后以数组形式列出某字段的全部值AggregationOperation push = Aggregation.group("sex").push("salary").as("salaryFirst");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(push);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}}

聚合管道操作符

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
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.query.Criteria;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;@Slf4j
@Service
public class AggregatePipelineService {/*** 设置集合名称*/private static final String COLLECTION_NAME = "users";@Resourceprivate MongoTemplate mongoTemplate;/*** 使用 $group 和 $match 聚合,先使用 $match 过滤文档,然后再使用 $group 进行分组** @return 聚合结果*/public Object aggregateGroupMatch() {// 设置聚合条件,先使用 $match 过滤岁数大于 25 的用户,然后按性别分组,统计每组用户工资最高值AggregationOperation match = Aggregation.match(Criteria.where("age").lt(25));AggregationOperation group = Aggregation.group("sex").max("salary").as("sexSalary");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(match, group);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用 $group 和 $sort 聚合,先使用 $group 进行分组,然后再使用 $sort 排序** @return 聚合结果*/public Object aggregateGroupSort() {// 设置聚合条件,按岁数分组,然后统计每组用户工资最大值和用户数,按每组用户工资最大值升序排序AggregationOperation group = Aggregation.group("age").max("salary").as("ageSalary").count().as("ageCount");AggregationOperation sort = Aggregation.sort(Sort.by("ageSalary").ascending());// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group, sort);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用 $group 和 $limit 聚合,先使用 $group 进行分组,然后再使用 $limit 限制一定数目文档** @return 聚合结果*/public Object aggregateGroupLimit() {// 设置聚合条件,先按岁数分组,然后求每组用户的工资总数、最大值、最小值、平均值,限制只能显示五条AggregationOperation group = Aggregation.group("age").sum("salary").as("sumSalary").max("salary").as("maxSalary").min("salary").as("minSalary").avg("salary").as("avgSalary");AggregationOperation limit = Aggregation.limit(5L);// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group, limit);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用 $group 和 $skip 聚合,先使用 $group 进行分组,然后再使用 $skip 跳过一定数目文档** @return 聚合结果*/public Object aggregateGroupSkip() {// 设置聚合条件,先按岁数分组,然后求每组用户的工资总数、最大值、最小值、平均值,跳过前 2 条AggregationOperation group = Aggregation.group("age").sum("salary").as("sumSalary").max("salary").as("maxSalary").min("salary").as("minSalary").avg("salary").as("avgSalary");AggregationOperation limit = Aggregation.skip(2L);// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group, limit);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用 $group 和 $project 聚合,先使用 $group 进行分组,然后再使用 $project 限制显示的字段** @return 聚合结果*/public Object aggregateGroupProject() {// 设置聚合条件,按岁数分组,然后求每组用户工资最大值、最小值,然后使用 $project 限制值显示 salaryMax 字段AggregationOperation group = Aggregation.group("age").max("salary").as("maxSalary").min("salary").as("minSalary");AggregationOperation project = Aggregation.project("maxSalary");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(group, project);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}/*** 使用 $group 和 $unwind 聚合,先使用 $project 进行分组,然后再使用 $unwind 拆分文档中的数组为一条新文档记录** @return 聚合结果*/public Object aggregateProjectUnwind() {// 设置聚合条件,设置显示`name`、`age`、`title`字段,然后将结果中的多条文档按 title 字段进行拆分AggregationOperation project = Aggregation.project("name", "age", "title");AggregationOperation unwind = Aggregation.unwind("title");// 将操作加入到聚合对象中Aggregation aggregation = Aggregation.newAggregation(project, unwind);// 执行聚合查询AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);for (Map result : results.getMappedResults()) {log.info("{}", result);}return results.getMappedResults();}}

聚合管道操作符

  • $project: 可以从文档中选择想要的字段,和不想要的字段(指定的字段可以是来自输入文档或新计算字段的现有字段 ,也可以通过管道表达式进行一些复杂的操作,例如数学操作,日期操作,字符串操作,逻辑操作。
  • ** m a t c h : ∗ ∗ 用于过滤数据,只输出符合条件的文档。 match:** 用于过滤数据,只输出符合条件的文档。 match用于过滤数据,只输出符合条件的文档。match使用MongoDB的标准查询操作。
  • $limit: 用来限制MongoDB聚合管道返回的文档数。
  • $skip: 在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind: 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group: 将集合中的文档分组,可用于统计结果。
  • $sort: 将输入文档排序后输出。

关联查询

LookupOperation这个类就是用来进行联表操作的类,具体方法:

  • newLookup ,用来创建一个LookupOperation.Builder;
  • from, 要连接哪张表,类似Mysql的JOIN;
  • localField,主表哪个字段去连接,指明出来;from表参考主表的哪个字段
  • foreignField ,form连接的那个表哪个字段关联;
  • as, 从表结果集名,最后会在主表多出这个自定义列,默认List;理解为as一个别名,会把从表的数据以数组的形式在as字段内

表结构

Item

{"_id": "111","sActiveId": "222","sName": "333","sIcon": "444","sDes": "555","iItemType": NumberInt("1"),"sValue": "666","iProperty": ["0"],"iDuration": NumberInt("77"),"createTime": ISODate("2022-03-23T08:13:19.694Z"),"updateTime": ISODate("2022-03-23T08:13:19.694Z"),"iStatus": NumberInt("0")
}

ItemUser

{"_id": "222","sActiveId": "333","sItemId": "111","sUserId": "444","iCount": NumberInt("4")
}

生成管道,以下是ItemUser 是主表****Item是从表

// 联表
LookupOperation lookupOperation = LookupOperation.newLookup()// 连接哪张表.from("Item")// 主表哪个字段连接.localField("sItemId")// 从表哪个字段关联.foreignField("_id")// 从表结果集名 最后会在主表多出这个自定义列 默认List.as("item");
// 查询(只能是主表的字段,取别名字段不可以,例如:item.sName 是不可以的)
Criteria criteria = Criteria.where("sUserId").is("444");
// 类似mysql的select列 如果要用从表的列 就as自定义列名点属性
ProjectionOperation  projectionOperation = Aggregation.project("id", "sItemId", "iCount", "item.iDuration", "item.sName", "item.sIcon", "item.sDes", "item.iItemType");
// 建立管道
Aggregation aggregation = Aggregation.newAggregation(// 联表lookupOperation,// 查询Aggregation.match(criteria),// 将某个集合列拆成字段添加主表Aggregation.unwind("item"),// 排序Aggregation.sort(Sort.Direction.DESC, "createTime"),// selectprojectionOperation
);AggregationResults<JSONObject> aggregationResults = mongoTemplate.aggregate(aggregation, "ItemUser", JSONObject.class);
List<JSONObject> result = aggregationResults.getMappedResults();

前面提到,as会在主表新增个列,列里内容是数组,Aggregation.unwind(“item”)的作用就是把as列里数组拆掉,通过ProjectionOperation 加在主表自定义字段中

实际的联查写法

        //多表关联的条件声明LookupOperation cusAndInfoLookup = LookupOperation.newLookup().from("adrs").//1.副表表名字localField("_id").//2.主表的关联字段foreignField("adrsId").//3.副表的关联字段as("adrs");//4.建议和1一致,结果的别名//关联多张表就写多个
//        LookupOperation cusAndInfoLookup1 = LookupOperation.newLookup().
//                from("adrs").
//                localField("_id").
//                foreignField("adrsId").
//                as("adrs");//多表的关联条件,查询条件均传入到此Aggregation aggregation = Aggregation.newAggregation(cusAndInfoLookup,
//                cusAndInfoLookup1,
//                Aggregation.match(Criteria.where("_id").lte(2)),//5.此作用处下文解释
//                Aggregation.unwind("adrs"),//筛选条件,筛选主表的字段直接写,副表则是'别名.字段名'Aggregation.match(Criteria.where("adrs.adrsId").is(1)));//5.此处填写主表名称AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, "tab_map", JSONObject.class);List<JSONObject> objectList = results.getMappedResults();for (JSONObject json : objectList) {System.out.println(json);}

unwind的作用,默认关联查询出现多个关联值时,结果会以array返回,例如一个用户有多个收货地址,则会返回

{"id":"1","adrs":[{adrs1},{adrs2}]}
unwind("adrs")返回数据成为常见的sql返回多条
{"id":"1","adrs":{adrs1}}
{"id":"1","adrs":{adrs2}}

分组,两表三表联查

####分组,两表三表联查###########################################################
import com.csw.mongodbspringbootdemo.entity.Chair;
import com.csw.mongodbspringbootdemo.entity.Desk;
import com.csw.mongodbspringbootdemo.entity.Room;
import com.mongodb.BasicDBObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
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.LookupOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;@SpringBootTest
@RunWith(SpringRunner.class)
public class RoomDeskRecommend {@Autowiredprivate MongoTemplate mongoTemplate;@Testpublic void saveRoom() {// 添加房间Room room = new Room();room.setName("空房间2");room.setUnitCode(UUID.randomUUID().toString());mongoTemplate.save(room);Room room2 = new Room();room2.setName("空房间1");room2.setUnitCode(UUID.randomUUID().toString());mongoTemplate.save(room2);}@Testpublic void saveDesk() {// 添加桌子String roomName = "光明房间";String deskName = "5号桌子";Query query = new Query(Criteria.where("name").is(roomName));Room room = mongoTemplate.findOne(query, Room.class);Desk desk = new Desk();desk.setName(deskName);assert room != null;desk.setUnitCode(room.getUnitCode());mongoTemplate.save(desk);System.out.println(room);Query query2 = new Query(Criteria.where("name").is(deskName));Desk desk2 = mongoTemplate.findOne(query2, Desk.class);System.out.println(desk2);}@Testpublic void groupBy() {// groupList<AggregationOperation> aggs = new ArrayList<>();// aggs.add(Aggregation.match(Criteria.where("name").is("log")));aggs.add(Aggregation.group("name").count().as("count"));aggs.add(Aggregation.project().and("_id").as("name").and("count").as("count"));Aggregation agg = Aggregation.newAggregation(aggs);AggregationResults<Map> results = mongoTemplate.aggregate(agg, Desk.class, Map.class);for (Map result : results) {System.out.println(result);}}@Testpublic void findMoreTable() {// 两表联查LookupOperation lookupOperation = LookupOperation.newLookup().from("room"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("rooms");// 查询结果集合名Criteria ordercri = Criteria.where("rooms").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);Aggregation aggregation = Aggregation.newAggregation(lookupOperation, matchZi);// 排序List<Map> results = mongoTemplate.aggregate(aggregation, "desk", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}@Testpublic void findMoreTable2() {// 两表联查LookupOperation lookupOperation = LookupOperation.newLookup().from("desk"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("desks");// 查询结果集合名Criteria ordercri = Criteria.where("desks").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);Aggregation aggregation = Aggregation.newAggregation(lookupOperation, matchZi);// 排序List<Map> results = mongoTemplate.aggregate(aggregation, "room", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}@Testpublic void findMoreTableZongHe() {// 两表联查int pageNumber = 2;// 0,1相同int pageSize = 2;// 拼装关联信息LookupOperation lookupOperation = LookupOperation.newLookup().from("room"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("ClazzStudents");// 查询结果集合名// 拼装具体查询信息// 次表Criteria ordercri = Criteria.where("ClazzStudents").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);// 主表Criteria qqq = Criteria.where("name").regex("号");// 只查询名字中带有文的人AggregationOperation matchFu = Aggregation.match(qqq);// 分页查询Aggregation aggregation = Aggregation.newAggregation(matchFu, lookupOperation, matchZi,Aggregation.sort(Sort.Direction.DESC, "name"),Aggregation.skip(pageSize > 1 ? (pageNumber - 1) * pageSize : 0), Aggregation.limit(pageSize));// 排序// Aggregation.skip(pageable.getPageNumber()>1?(pageable.getPageNumber()-1)*pageable.getPageSize():0),//pagenumber// 分页/** Aggregation.skip(pageSize>1?(pageNumber-1)*pageSize:0);* Aggregation.limit(pageSize);*/// Aggregation.group("name");// 总数查询Aggregation counts = Aggregation.newAggregation(matchFu, lookupOperation, matchZi);int count = mongoTemplate.aggregate(counts, Desk.class, BasicDBObject.class).getMappedResults().size();System.out.println("【count】" + count);List<Map> results = mongoTemplate.aggregate(aggregation, "desk", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}@Testpublic void saveChair() {// 添加椅子String roomName = "光明房间";String chairName = "1号椅子";Query query = new Query(Criteria.where("name").is(roomName));Room room = mongoTemplate.findOne(query, Room.class);Chair chair = new Chair();chair.setName(chairName);assert room != null;chair.setUnitCode(room.getUnitCode());mongoTemplate.save(chair);}@Testpublic void findMoreTable3_0() {// 三表联查测试,第一个表关联第二个人表(关联字段1),第一个表关联第三个表(关联字段1)LookupOperation lookupOperation = LookupOperation.newLookup().from("desk"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("desks");// 查询结果集合名Criteria ordercri = Criteria.where("desks").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);LookupOperation lookupOperation2 = LookupOperation.newLookup().from("chair"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("chairs");// 查询结果集合名Criteria ordercri2 = Criteria.where("chairs").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi2 = Aggregation.match(ordercri2);Aggregation aggregation = Aggregation.newAggregation(lookupOperation, matchZi, lookupOperation2, matchZi2);// 排序List<Map> results = mongoTemplate.aggregate(aggregation, "room", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}// 数据模拟@Testpublic void findMoreTable3_1() {// 三表联查测试,第一个表关联第二个人表(关联字段1),第一个表关联第三个表(关联字段2)LookupOperation lookupOperation = LookupOperation.newLookup().from("desk"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("desks");// 查询结果集合名Criteria ordercri = Criteria.where("desks").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);LookupOperation lookupOperation2 = LookupOperation.newLookup().from("chair"). // 关联表名localField("lastCode"). // 主关联字段foreignField("lastCode").// 从表关联字段对应的次表字段as("chairs");// 查询结果集合名Criteria ordercri2 = Criteria.where("chairs").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi2 = Aggregation.match(ordercri2);Aggregation aggregation = Aggregation.newAggregation(lookupOperation, matchZi, lookupOperation2, matchZi2);// 排序List<Map> results = mongoTemplate.aggregate(aggregation, "room", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}// 数据模拟@Testpublic void findMoreTable3_3() {// 三表联查测试,第一个表关联第二个人表(关联字段1),第二个表关联第三个表(关联字段2)LookupOperation lookupOperation = LookupOperation.newLookup().from("room"). // 关联表名localField("unitCode"). // 主关联字段foreignField("unitCode").// 从表关联字段对应的次表字段as("rooms");// 查询结果集合名Criteria ordercri = Criteria.where("rooms").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi = Aggregation.match(ordercri);LookupOperation lookupOperation2 = LookupOperation.newLookup().from("chair"). // 关联表名localField("rooms.lastCode"). // 主关联字段foreignField("lastCode").// 从表关联字段对应的次表字段as("chairs");// 查询结果集合名Criteria ordercri2 = Criteria.where("chairs").not().size(0);// 只查询有宠物的人// ordercri.and("age").gte(1).lte(5);//只查询1岁到5岁的宠物AggregationOperation matchZi2 = Aggregation.match(ordercri2);Aggregation aggregation = Aggregation.newAggregation(lookupOperation, matchZi, lookupOperation2, matchZi2);// 排序List<Map> results = mongoTemplate.aggregate(aggregation, "desk", Map.class).getMappedResults(); // 查询出的结果集为BasicDBObject类型for (Map result : results) {System.out.println(result);}}

常用函数

使用前我们先来了解一下常用的函数

  1. Aggregation.group() : 聚合函数,将某个字段或者某个数组作为分组统计的依据,在group的基础上又扩展出以下函数:
    • sum() : 求和
    • max() : 获取最大值
    • min() : 获取最小值
    • avg() : 获取平均值
    • count() : 统计条目数
    • first () : 获取group by 后的某个字段的首个值
    • last() : 获取 group by 后的某个字段的最后一个值
    • push() : 在结果文档中插入值到一个数组中
    • addToSet() : 在结果文档中插入值到一个数组中,但不创建副本(作为集合)。
  2. Aggregation.match() : 过滤函数,主要存储过滤数据的条件,输出符合条件的记录
  3. Aggregation.project(): 修改数据结构函数,将前面管道中的获取的字段进行重名,增加,修改字段等操作。
  4. Aggregation.unwind():将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。当preserveNullAndEmptyArrays为true时,将包括字段为null,空,或者缺失的数据;
  5. Aggregation.sort(): 排序函数,将上级管道的内容按照某个字段进行排序并且输出。值为1升、-1降。sort一般放在group后,也就是说得到结果后再排序,如果先排序再分组没什么意义;
  6. Aggregation.limit(): 限制输出函数,将聚合返回的内容限定在某个条目之内。通常作为页面大小
  7. Aggregation.skip(): 跳过指定数量的条目再开始返回数据的函数,通常和sort(),limit()配合,实现数据翻页查询等操作。
  8. Aggregation.lookup(): 连表查询,将被关联集合添加到执行操作的集合中。

实例

用accountId和status做group操作

Copymongodb: 
db.getCollection('mro_fn_subscribes').aggregate([{$group:{"_id":{"_id":"$accountId", "status": "$status" },"count":{"$sum": NumberInt(1)},"statusSum":{"$sum": "$status"},"codeAvg":{"$avg":"$fnCode"},"codeMax":{"$max":"$fnCode"},"codeMin":{"$min":"$fnCode"},"codeFirst":{"$first":"$fnCode"},"codeLast":{"$last":"$fnCode"},}}
])java:
Aggregation aggregation = Aggregation.newAggregation(Aggregation.group("accountId", "status").count().as("count").sum("status").as("statusSum").avg("fnCode").as("codeAvg").max("fnCode").as("codeMax").min("fnCode").as("codeMin").first("fnCode").as("codeFirst").last("fnCode").as("codeLast")
);

match管道过滤:

Copymongodb: 
db.getCollection('mro_fn_subscribes').aggregate({$match:{userId:"a"}}
)java:
Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(new Criteria().and("userId").is("a")
);

project筛选字段:

Copymongo:
db.getCollection('mro_fn_subscribes').aggregate([{"$group" : {"_id" : "$_id", "num" : {"$sum" : "$num"}, "firstName" : {"$first" : "$name"}, "lastName" : {"$last" : "$name"}}}, {"$project" : {"_id" : 1, "num" : 1, "firstName" : 1, "name" : "$lastName"}}
])java:
// 初始化聚合
Aggregation aggregation = Aggregation.newAggregation(Aggregation.group(new String[] {"_id"}).sum("num").as("num").first("name").as("firstName").last("name").as("lastName"),Aggregation.project("_id", "num", "firstName").and("lastName").as("name") // 重新命名字段
);

unwind拆分数组

Copymongo:
db.col.aggregate({$match:{userid:"a"}}, {$unwind:{path:"$items", includeArrayIndex: "arrayIndex"}}
)java:
Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(new Criteria().and("userId").is("a"),Aggregation.unwind("items",true)
);

sort skip limit处理数据:

CopyMongo:
db.getCollection('mro_fn_subscribes').aggregate([{"$group" : {_id:{"_id":"$accountId", "status": "$status" } }}, {"$sort" : {"num" : -1}}, {"$skip" : NumberInt(10)}, {"$limit" : NumberInt(2)}
]
)
Java:
Aggregation aggregation = Aggregation.newAggregation(Aggregation.group("accountId", "status")Aggregation.sort(Direction.DESC, "num"),		//将num降序Aggregation.skip(10),					//从第10条记录开始Aggregation.limit(2)					//取两条数据
);

lookup多表关联查询:

Copy# Mongo:
db.getCollection('mro_accounts').aggregate([{$lookup: {from:"mro_profiles",   # 被关联表名localField:"userName", # 主表(mro_accounts)中用于关联的字段foreignField:"mobile", # 被关联表(mro_profiles)中用于关联的字段as:"profileDoc"        # 被关联的表的别名}}
])
# Java
Aggregation aggregation = Aggregation.newAggregation(Aggregation.lookup("mro_profiles", "userName", "mobile", "profileDoc") //分别对应from, localField, foreignField, as
);

获取查询结果

在创建好Aggregation对象之后,再用 mongotemplate.aggregate(aggregation, “mro_fn_subscribes”, Fnsubscribe.class).getMappedResults() 获取查询的对象列表

日期字符串转ISO日期格式mogodb

public static void main(String[] args) {ZoneId zone = ZoneId.systemDefault();DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");LocalDateTime birthDate = LocalDateTime.parse("2022-03-11" + " 00:00:00", df);Instant instant = birthDate.atZone(zone).toInstant();Date date = Date.from(instant);System.out.println("birthDate: " + birthDate);System.out.println("instant: " + instant);System.out.println("date: " + date);}

支持 $filter 数组聚合运算符

Java Spring Data MongoDB是用于与MongoDB数据库进行交互的框架,提供了许多功能强大的 API 和管理工具,其中包含了大量数据操作函数,包括数组聚合运算符,$filter也是其中一种。

在MongoDB中,当需要使用聚合函数过滤数组中的元素时,可以使用$filter运算符。具体使用方法如下:

{
$project: {
filteredArray: {
$filter: {
input: "$array",
as: "element",
cond: { $gt: ["$$element", 5] }
}
}
}
}

上述代码将会筛选数组中所有大于5的元素并返回一个新的数组。

在Java Spring Data MongoDB中,$filter运算符同样被支持。可以使用AggregationOperation来拼接查询条件。具体实现代码如下:

AggregationOperation filter = Aggregation.project()
.andFilter(ArrayOperators.Filter
.filter("array")
.as("element")
.by(ComparisonOperators.Gt.valueOf("$$element").greaterThan(5)))
.as("filteredArray");

在上述代码中,使用了ArrayOperators.Filter来表示$filter运算符,并使用ComparisonOperators.Gt来表示’>'符号,从而实现了对数组中元素进行筛选。

需要注意的是, f i l t e r 操作符只适用于聚合操作中,即使用聚合函数进行筛选。如果想要在数据操作中对数组中的元素进行筛选,则需要使用 filter操作符只适用于聚合操作中,即使用聚合函数进行筛选。如果想要在数据操作中对数组中的元素进行筛选,则需要使用 filter操作符只适用于聚合操作中,即使用聚合函数进行筛选。如果想要在数据操作中对数组中的元素进行筛选,则需要使用elemMatch操作符来实现。具体操作方法可参考以下代码:

Criteria criteria = Criteria.where("array").elemMatch(
Criteria.where("$gt").gt(5));
Query query = new Query(criteria);
List<DBObject> results = mongoTemplate.find(query, DBObject.class,"collectionName");

上述代码使用$elemMatch操作符来筛选数组中所有大于5的元素,并使用mongoTemplate.find方法来获取符合条件的数据项。

扩展的相关知识点也非常广泛,如MongoDB聚合函数的使用、Java Spring Data MongoDB的API使用、MongoDB数据库的操作等。在实际应用过程中,需要注意的是,在使用聚合函数进行操作时,应尽量避免数据过多而导致的性能,可以采用分批次操作的方法来进行优化。同时,需要根据实际情况来选择 e l e m M a t c h 或者 elemMatch或者 elemMatch或者filter操作符,以及其他更合适的操作函数来实现查询需求。

参考

1、菜鸟教程 - mongodb

2、超级小豆丁 - MongoDB 介绍

3、MongoDB 文档查询 api 介绍

相关文章:

java 操作Mongodb

CRUD基础操作 Springboot 操作 MongoDB 有两种方式。 第一种方式是采用 Springboot 官方推荐的 JPA 方式&#xff0c;这种操作方式&#xff0c;使用简单但是灵活性比较差。第二种方式是采用 Spring Data MongoDB 封装的 MongoDB 官方 Java 驱动 MongoTemplate 对 MongoDB 进行…...

以Java为例,实现一个简单的命令行图书管理系统,包括添加图书、删除图书、查找图书等功能。

江河湖海中的代码之旅&#xff1a;打造你的命令行图书管理系统 一、系统简介 1. Java简介 Java&#xff0c;这个编程语言界的“瑞士军刀”&#xff0c;自1995年诞生以来就以其跨平台的特性和强大的生态系统征服了无数开发者的心。想象一下&#xff0c;Java就像是一条蜿蜒曲折…...

[JavaWeb]微头条项目

完整笔记和项目代码&#xff1a; https://pan.baidu.com/s/1PZBO0mfpwDPic4Ezsk8orA?pwdwwp5 提取码: wwp5 JavaWeb-微头条项目开发 1 项目简介 1.1 业务介绍 微头条新闻发布和浏览平台,主要包含业务如下 用户功能 注册功能登录功能 头条新闻 新闻的分页浏览通过标题关键字搜…...

Linux(CentOS)安装达梦数据库 dm8

CentOS版本&#xff1a;CentOS 7&#xff0c;查看操作系统版本信息&#xff0c;请查阅 查看Linux内核版本信息 达梦数据库版本&#xff1a;dm8 一、获取 dm8 安装文件 1、下载安装文件 打开达梦官网&#xff1a;https://www.dameng.com/ 下载的文件 解压后的文件 2、上传安…...

【专题】中国企业出海洞察报告暨解码全球制胜之道报告汇总PDF洞察(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p38314 在当今全球化的浪潮中&#xff0c;中国企业的出海行动正以前所未有的规模和速度展开&#xff0c;成为全球经济舞台上的重要力量。本报告旨在对 2024 年中国企业出海情况进行深度洞察&#xff0c;涵盖多个领域和视角。 从对外投…...

[ 跨域问题 ] 前后端以及服务端 解决跨域的各种方法

这篇文章主要介绍了跨域问题&#xff0c;包括其定义、产生原因及各种解决方法。原因是浏览器安全策略限制&#xff0c;方法有 JSONP、CORS、Domain、 postMessage、Nginx配置、.NetCore配置。 前言 什么是跨域问题? 在Web应用中&#xff0c;当一个网页的脚本试图去请求另一个域…...

网络安全之信息收集-实战-2

请注意&#xff0c;本文仅供合法和授权的渗透测试使用&#xff0c;任何未经授权的活动都是违法的。 目录 7、网络空间引擎搜索 8、github源码泄露 9、端口信息 10、框架指纹识别 11、WAF识别 12、后台查找 7、网络空间引擎搜索 FOFA&#xff1a;https://fofa.info/ 360 …...

利用飞书多维表格自动发布版本

文章目录 背景尝试1&#xff0c;轮询尝试2&#xff0c;长连接 背景 博主所在的部门比较奇特&#xff0c;每个车型每周都需要发版&#xff0c;所以实际上一周会发布好几个版本。经过之前使用流水线自动发版改造之后&#xff0c;发版的成本已经大大降低了&#xff0c;具体参考&a…...

深入内核讲明白Android Binder【一】

深入内核讲明白Android Binder【一】 前言一、Android Binder应用编写概述二、基于C语言编写Android Binder跨进程通信Demo0. Demo简介1. 服务的管理者server_manager.c2. Binder服务端代码实现 test_service.c2.1 实现思路2.2 完整实现代码 3. Binder客户端代码实现 test_clie…...

Photoshop(PS)——人像磨皮

1.新建一个文件&#xff0c;背景为白色&#xff0c;将图片素材放入文件中 2.利用CtrlJ 复制两个图层出来&#xff0c;选择第一个拷贝图层&#xff0c;选择滤镜---杂色---蒙尘与划痕 3.调整一下数值&#xff0c;大概能够模糊痘印痘坑&#xff0c;点击确定。 4.然后选择拷贝2图层…...

如何用Excel批量提取文件夹内所有文件名?两种简单方法推荐

在日常办公中&#xff0c;我们有时需要将文件夹中的所有文件名整理在Excel表格中&#xff0c;方便管理和查阅。手动复制文件名既费时又易出错&#xff0c;因此本文将介绍两种利用Excel自动提取文件夹中所有文件名的方法&#xff0c;帮助你快速整理文件信息。 方法一&#xff1…...

YOLOv8改进,YOLOv8通过RFAConv卷积创新空间注意力和标准卷积,包括RFCAConv, RFCBAMConv,二次创新C2f结构,助力涨点

摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…...

【实验11】卷积神经网络(2)-基于LeNet实现手写体数字识别

&#x1f449;&#x1f3fc;目录&#x1f448;&#x1f3fc; &#x1f352;1. 数据 1.1 准备数据 1.2 数据预处理 &#x1f352;2. 模型构建 2.1 模型测试 2.2 测试网络运算速度 2.3 输出模型参数量 2.4 输出模型计算量 &#x1f352;3. 模型训练 &#x1f352;4.模…...

chatgpt训练需要什么样的gpu硬件

训练像ChatGPT这样的大型语言模型对GPU硬件提出了极高的要求&#xff0c;因为这类模型的训练过程涉及大量的计算和数据处理。以下是训练ChatGPT所需的GPU硬件的关键要素&#xff1a; ### 1. **高性能计算能力** - **Tensor Cores**: 现代深度学习训练依赖于Tensor Cores&#…...

Kubernetes常用命令

Kubernetes常用命令 一、集群管理 kubectl cluster-info&#xff1a;显示集群信息&#xff0c;包括控制平面地址和服务的 URL。 kubectl get nodes&#xff1a;查看集群中的节点列表&#xff0c;包括节点状态、IP 地址等信息。 kubectl describe node <node-name>&…...

Flutter:key的作用原理(LocalKey ,GlobalKey)

第一段代码实现的内容&#xff1a;创建了3个块&#xff0c;随机3个颜色&#xff0c;每次点击按钮时&#xff0c;把第一个块删除 import dart:math; import package:flutter/material.dart; import package:flutter_one/demo.dart;void main() {runApp(const App()); }class App…...

R语言基础入门详解

文章目录 R语言基础入门详解一、引言二、R语言环境搭建1、安装R和RStudio1.1、步骤1.2、获取工作目录 三、R语言基础2、语法基础2.1、赋值操作2.2、注释 3、数据类型与结构3.1、向量3.2、矩阵 4、基本操作4.1、数据读取4.2、数据可视化 四、R语言使用示例4.1、统计分析示例4.2、…...

django启动项目报错解决办法

在启动此项目报错&#xff1a; 类似于&#xff1a; django.core.exceptions.ImproperlyConfigured: Requested setting EMOJI_IMG_TAG, but settings are not c启动方式选择django方式启动&#xff0c;以普通python方式启动会报错 2. 这句话提供了对遇到的错误的一个重要线索…...

详细描述一下Elasticsearch搜索的过程?

大家好&#xff0c;我是锋哥。今天分享关于【详细描述一下Elasticsearch搜索的过程&#xff1f;】面试题。希望对大家有帮助&#xff1b; 详细描述一下Elasticsearch搜索的过程&#xff1f; Elasticsearch 的搜索过程是其核心功能之一&#xff0c;允许用户对存储在 Elasticsea…...

Spring、SpringMVC、SpringBoot、Mybatis小结

Spring Spring是一个轻量级的控制反转&#xff08;IoC&#xff09;和面向切面&#xff08;AOP&#xff09;的容器&#xff08;框架&#xff09; Spring框架的核心特性包括依赖注入&#xff08;Dependency Injection &#xff0c;DI&#xff09;、面向切面编程&#xff08;Aspe…...

.NET 9 运行时中的新增功能

本文介绍了适用于 .NET 9 的 .NET 运行时中的新功能和性能改进。 文章目录 一、支持修剪的功能开关的属性模型二、UnsafeAccessorAttribute 支持泛型参数三、垃圾回收四、控制流实施技术.NET 安装搜索行为性能改进循环优化感应变量加宽Arm64 上的索引后寻址强度降低循环计数器可…...

Linux下安装mysql8.0版本

先确定我的下载安装的目录,安装文件是下载在 /opt/install 目录下面 (安装地址不同的话注意修改地址) 1.在线下载 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz2.解压 tar -xvf mysql-8.0.20-linux-glibc2.12-x86_64.t…...

kvm-dmesg:从宿主机窥探虚拟机内核dmesg日志

在虚拟化环境中&#xff0c;实时获取虚拟机内核日志对于系统管理员和开发者来说至关重要。传统的 dmesg 工具可以方便地查看本地系统的内核日志&#xff0c;但在KVM&#xff08;基于内核的虚拟机&#xff09;环境下&#xff0c;获取虚拟机内部的内核日志则复杂得多。为了简化这…...

植物明星大乱斗15

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 player.hplayer.cppparticle.hparticle.cpp player.h #pragma once #include <graphics.h> #include "vector2.h" #include "animation.h" #include "playerID.h" #include &…...

go-zero(三) 数据库操作

go-zero 数据库操作 在本篇文章中&#xff0c;我们将实现一个用户注册和登录的服务。我们将为此构建一个简单而高效的 API&#xff0c;包括请求参数和响应参数的定义。 一、Mysql连接 1. 创建数据库和表 在 MySQL 中创建名为 test_zero的数据库&#xff0c;并创建user 表 …...

SQL面试题——间隔连续问题

间隔连续问题 某游戏公司记录的用户每日登录数据如下 +----+----------+ | id| date| +----+----------+ |1001|2021-12-12| |1001|2021-12-13| |1001|2021-12-14| |1001|2021-12-16| |1001|2021-12-19| |1001|2021-12-20| |1002|2021-12-12| |1002|2021-12-16| |1002|…...

vim配置 --> 在创建的普通用户下

在目录/etc/ 下面&#xff0c;有个名为vimrc 的文件&#xff0c;这是系统中公共的vim配置文件对所有用户都有效 我们现在创建一个普通用户 dm 创建好以后&#xff0c;我们退出重新链接 再切换到普通用户下 再输入密码&#xff08;是不显示的&#xff0c;输入完后&#xff0c;…...

(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现

博主可接毕设设计&#xff01;&#xff01;&#xff01; 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互…...

【含开题报告+文档+PPT+源码】基于SpringBoot的医院药房管理系统

开题报告 在科技迅速发展的今天&#xff0c;各行各业都在积极寻求与现代技术的融合&#xff0c;以提升自身的运营效率和竞争力。医疗行业作为关乎国计民生的关键领域&#xff0c;其信息化建设的步伐尤为迅速。医院药房作为医疗体系中的核心环节&#xff0c;其管理效率和服务质…...

基于SpringBoot的“数码论坛系统设计与实现”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“数码论坛系统设计与实现”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体结构图 系统首页界面图 数码板…...

网站404页面编写/seo关键词有话要多少钱

姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名姓名烦烦烦烦烦烦烦烦烦烦烦烦烦123456456健康健康就好看…...

微信公众号影视网站怎么做/关键词怎么选择技巧

租车骑绿道 题目 部门组织绿岛骑行团建活动。租用公共双人自行车骑行,每辆自行车最多坐两人、做大载重 M。 给出部门每个人的体重,请问最多需要租用多少双人自行车。 输入 第一行两个数字 m、n,自行车限重 m,代表部门总人数 n。 第二行,n 个数字,代表每个人的体重。体…...

做外贸出口的网站/网站策划是干什么的

%86时出现报错 Error in invoking target agent nmhs of makefile 解决方案在makefile中添加链接libnnz11库的参数修改$ORACLE_HOME/sysman/lib/ins_emagent.mk&#xff0c;将$(MK_EMAGENT_NMECTL)修改为&#xff1a;$(MK_EMAGENT_NMECTL) -lnnz11建议修改前备份原始文件[ora…...

视差滚动效果网站/一个具体网站的seo优化方案

第一种&#xff0c;for循环&#xff0c;此情况是list1属于list2 list1 [1, 2, 3] list2 [1, 2, 3, 4, 5] for i in list2:if i not in list1:print(i) 第二种&#xff0c;得出列表中相同的元素&#xff0c;和不同元素&#xff08;两个列表都存在&#xff09; list1 [1, 2,…...

在手机上怎么注册公司/一个网站可以优化多少关键词

智能家居网络随着集成技术、通信技术、互操作能力和布线标准的实现而不断改进。它涉及对家庭网络内所有的智能家具、设备和系统的操作、管理以及集成技术的应用 。其技术特点表现如下&#xff1a; 1&#xff0e;通过家庭网关及其系统软件建立智能家居平台系统 家庭网关是智能…...

无锡专业网站制作的公司/房产网站模板

数据结构与算法总览 文章目录数据结构与算法总览1、职业训练&#xff1a;拆分知识点、刻意练习、反馈&#xff08;1&#xff09;Chunk it up 切碎知识点数据结构算法&#xff08;2&#xff09;Deliberate Practicing 刻意练习&#xff08;3&#xff09;Feedback反馈2、五步刷题…...