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);}}
常用函数
使用前我们先来了解一下常用的函数
- Aggregation.group() : 聚合函数,将某个字段或者某个数组作为分组统计的依据,在group的基础上又扩展出以下函数:
- sum() : 求和
- max() : 获取最大值
- min() : 获取最小值
- avg() : 获取平均值
- count() : 统计条目数
- first () : 获取group by 后的某个字段的首个值
- last() : 获取 group by 后的某个字段的最后一个值
- push() : 在结果文档中插入值到一个数组中
- addToSet() : 在结果文档中插入值到一个数组中,但不创建副本(作为集合)。
- Aggregation.match() : 过滤函数,主要存储过滤数据的条件,输出符合条件的记录
- Aggregation.project(): 修改数据结构函数,将前面管道中的获取的字段进行重名,增加,修改字段等操作。
- Aggregation.unwind():将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。当preserveNullAndEmptyArrays为true时,将包括字段为null,空,或者缺失的数据;
- Aggregation.sort(): 排序函数,将上级管道的内容按照某个字段进行排序并且输出。值为1升、-1降。sort一般放在group后,也就是说得到结果后再排序,如果先排序再分组没什么意义;
- Aggregation.limit(): 限制输出函数,将聚合返回的内容限定在某个条目之内。通常作为页面大小
- Aggregation.skip(): 跳过指定数量的条目再开始返回数据的函数,通常和sort(),limit()配合,实现数据翻页查询等操作。
- 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 方式,这种操作方式,使用简单但是灵活性比较差。第二种方式是采用 Spring Data MongoDB 封装的 MongoDB 官方 Java 驱动 MongoTemplate 对 MongoDB 进行…...
以Java为例,实现一个简单的命令行图书管理系统,包括添加图书、删除图书、查找图书等功能。
江河湖海中的代码之旅:打造你的命令行图书管理系统 一、系统简介 1. Java简介 Java,这个编程语言界的“瑞士军刀”,自1995年诞生以来就以其跨平台的特性和强大的生态系统征服了无数开发者的心。想象一下,Java就像是一条蜿蜒曲折…...

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

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

【专题】中国企业出海洞察报告暨解码全球制胜之道报告汇总PDF洞察(附原数据表)
原文链接:https://tecdat.cn/?p38314 在当今全球化的浪潮中,中国企业的出海行动正以前所未有的规模和速度展开,成为全球经济舞台上的重要力量。本报告旨在对 2024 年中国企业出海情况进行深度洞察,涵盖多个领域和视角。 从对外投…...
[ 跨域问题 ] 前后端以及服务端 解决跨域的各种方法
这篇文章主要介绍了跨域问题,包括其定义、产生原因及各种解决方法。原因是浏览器安全策略限制,方法有 JSONP、CORS、Domain、 postMessage、Nginx配置、.NetCore配置。 前言 什么是跨域问题? 在Web应用中,当一个网页的脚本试图去请求另一个域…...

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

利用飞书多维表格自动发布版本
文章目录 背景尝试1,轮询尝试2,长连接 背景 博主所在的部门比较奇特,每个车型每周都需要发版,所以实际上一周会发布好几个版本。经过之前使用流水线自动发版改造之后,发版的成本已经大大降低了,具体参考&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.新建一个文件,背景为白色,将图片素材放入文件中 2.利用CtrlJ 复制两个图层出来,选择第一个拷贝图层,选择滤镜---杂色---蒙尘与划痕 3.调整一下数值,大概能够模糊痘印痘坑,点击确定。 4.然后选择拷贝2图层…...

如何用Excel批量提取文件夹内所有文件名?两种简单方法推荐
在日常办公中,我们有时需要将文件夹中的所有文件名整理在Excel表格中,方便管理和查阅。手动复制文件名既费时又易出错,因此本文将介绍两种利用Excel自动提取文件夹中所有文件名的方法,帮助你快速整理文件信息。 方法一࿱…...

YOLOv8改进,YOLOv8通过RFAConv卷积创新空间注意力和标准卷积,包括RFCAConv, RFCBAMConv,二次创新C2f结构,助力涨点
摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…...

【实验11】卷积神经网络(2)-基于LeNet实现手写体数字识别
👉🏼目录👈🏼 🍒1. 数据 1.1 准备数据 1.2 数据预处理 🍒2. 模型构建 2.1 模型测试 2.2 测试网络运算速度 2.3 输出模型参数量 2.4 输出模型计算量 🍒3. 模型训练 🍒4.模…...
chatgpt训练需要什么样的gpu硬件
训练像ChatGPT这样的大型语言模型对GPU硬件提出了极高的要求,因为这类模型的训练过程涉及大量的计算和数据处理。以下是训练ChatGPT所需的GPU硬件的关键要素: ### 1. **高性能计算能力** - **Tensor Cores**: 现代深度学习训练依赖于Tensor Cores&#…...
Kubernetes常用命令
Kubernetes常用命令 一、集群管理 kubectl cluster-info:显示集群信息,包括控制平面地址和服务的 URL。 kubectl get nodes:查看集群中的节点列表,包括节点状态、IP 地址等信息。 kubectl describe node <node-name>&…...

Flutter:key的作用原理(LocalKey ,GlobalKey)
第一段代码实现的内容:创建了3个块,随机3个颜色,每次点击按钮时,把第一个块删除 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启动项目报错解决办法
在启动此项目报错: 类似于: django.core.exceptions.ImproperlyConfigured: Requested setting EMOJI_IMG_TAG, but settings are not c启动方式选择django方式启动,以普通python方式启动会报错 2. 这句话提供了对遇到的错误的一个重要线索…...

详细描述一下Elasticsearch搜索的过程?
大家好,我是锋哥。今天分享关于【详细描述一下Elasticsearch搜索的过程?】面试题。希望对大家有帮助; 详细描述一下Elasticsearch搜索的过程? Elasticsearch 的搜索过程是其核心功能之一,允许用户对存储在 Elasticsea…...
Spring、SpringMVC、SpringBoot、Mybatis小结
Spring Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架) Spring框架的核心特性包括依赖注入(Dependency Injection ,DI)、面向切面编程(Aspe…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...