java 操作Mongodb

CRUD基础操作

Springboot 操作 MongoDB 有两种方式。

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

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

插入文档

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

  • 用于插入文档

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 插入文档
     * @throws Exception
     */
    @Test
    public void insert() throws Exception {
        Person person =new Person();
        person.setId(1l);
        person.setUserName("张三");
        person.setPassWord("123456");
        person.setCreateTime(new Date());
        mongoTemplate.insert(person);
    }
}
  • 自定义集合名称,插入文档
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 自定义集合,插入文档
     * @throws Exception
     */
    @Test
    public 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");
    }
}
  • 自定义集合,批量插入文档

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 自定义集合,批量插入文档
     * @throws Exception
     */
    @Test
    public 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更新
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 存储文档,如果没有插入,否则更新
     * @throws Exception
     */
    @Test
    public 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);
    }
}
  • 自定义集合,存储文档
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 自定义集合,存储文档
     * @throws Exception
     */
    @Test
    public 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()方法,用于更新文档,示例代码如下:

  • 更新文档,匹配查询到的文档数据中的第一条数据
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 更新文档,匹配查询到的文档数据中的第一条数据
     * @throws Exception
     */
    @Test
    public 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());
        }
    }
}
  • 更新文档,匹配查询到的文档数据中的所有数据
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 更新文档,匹配查询到的文档数据中的所有数据
     * @throws Exception
     */
    @Test
    public 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()方法,用于删除文档,示例代码如下:

  • 删除符合条件的所有文档
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 删除符合条件的所有文档
     * @throws Exception
     */
    @Test
    public 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());
    }
}
  • 删除符合条件的单个文档,并返回删除的文档
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 删除符合条件的单个文档,并返回删除的文档
     * @throws Exception
     */
    @Test
    public 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());
    }
}
  • 删除符合条件的所有文档,并返回删除的文档
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 删除符合条件的所有文档,并返回删除的文档
     * @throws Exception
     */
    @Test
    public 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()方法,示例代码如下:

  • 查询集合中的全部文档数据
java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

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

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

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

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据条件查询集合中符合条件的文档,返回第一条数据
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据条件查询集合中符合条件的文档
     */
    @Test
    public 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】关联多个查询条件,查询集合中的文档数据

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据【AND】关联多个查询条件,查询集合中的文档数据
     */
    @Test
    public 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】关联多个查询条件,查询集合中的文档数据

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据【OR】关联多个查询条件,查询集合中的文档数据
     */
    @Test
    public 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】关联多个查询条件,查询集合中的文档数据

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据【IN】关联多个查询条件,查询集合中的文档数据
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据【逻辑运算符】查询集合中的文档数据
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据【正则表达式】查询集合中的文档数据
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 根据条件查询集合中符合条件的文档,获取其文档列表并排序
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

   /**
     * 根据单个条件查询集合中的文档数据,并按指定字段进行排序与限制指定数目
     */
    @Test
    public 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());
    }
}

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

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

   /**
     * 统计集合中符合【查询条件】的文档【数量】
     */
    @Test
    public 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);
    }
}

查询指定字段返回

java 复制代码
    @Test
    public 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的解析。

java 复制代码
@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;
    }
 
    @Override
    public String toString() {
        return "MyLong{" +
                "pid=" + pid +
                '}';
    }
}

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

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

java 复制代码
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);

排序

java 复制代码
@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);

}

分页

java 复制代码
@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 聚合操作

聚合表达式

java 复制代码
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";

    @Resource
    private 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();
    }

}

聚合管道操作符

java 复制代码
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";

    @Resource
    private 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

json 复制代码
{
  "_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

json 复制代码
{
    "_id": "222",
    "sActiveId": "333",
    "sItemId": "111",
    "sUserId": "444",
    "iCount": NumberInt("4")
}

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

java 复制代码
// 联表
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"),
    // select
    projectionOperation
);

AggregationResults<JSONObject> aggregationResults = mongoTemplate.aggregate(aggregation, "ItemUser", JSONObject.class);
List<JSONObject> result = aggregationResults.getMappedResults();

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

实际的联查写法

java 复制代码
        //多表关联的条件声明
        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返回,例如一个用户有多个收货地址,则会返回

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

分组,两表三表联查

java 复制代码
####分组,两表三表联查###########################################################
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 {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public 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);

    }

    @Test
    public 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);

    }

    @Test
    public void groupBy() {// group
        List<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);
        }
    }

    @Test
    public 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);
        }
    }

    @Test
    public 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);
        }
    }

    @Test
    public 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);
        }
    }

    @Test
    public 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);

    }

    @Test
    public 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);
        }
    }

    // 数据模拟
    @Test
    public 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);
        }
    }

    // 数据模拟
    @Test
    public 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操作

plain 复制代码
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管道过滤:

plain 复制代码
Copymongodb: 
db.getCollection('mro_fn_subscribes').aggregate(
  {$match:{userId:"a"}}
)

java:
Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(new Criteria().and("userId").is("a")
);

project筛选字段:

plain 复制代码
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拆分数组

plain 复制代码
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处理数据:

plain 复制代码
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多表关联查询:

plain 复制代码
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

plain 复制代码
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运算符。具体使用方法如下:

javascript 复制代码
{
$project: {
filteredArray: {
$filter: {
input: "$array",
as: "element",
cond: { $gt: ["$$element", 5] }
}
}
}
}

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

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

javascript 复制代码
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操作符来实现。具体操作方法可参考以下代码:

javascript 复制代码
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 介绍

相关推荐
follycat1 分钟前
ISCTF2024
java·网络·数据库·学习·网络安全·python3.11
baozhengw28 分钟前
IntelliJ+SpringBoot项目实战(七)--在SpringBoot中整合Redis
java·spring boot·redis
羊小猪~~37 分钟前
前端入门一之ES6--递归、浅拷贝与深拷贝、正则表达式、es6、解构赋值、箭头函数、剩余参数、String、Set
开发语言·前端·javascript·css·正则表达式·html·es6
code_shenbing1 小时前
跨平台WPF框架Avalonia教程 十四
开发语言·ui·c#·wpf
服务端相声演员2 小时前
IOException: Broken pipe与IOException: 远程主机强迫关闭了一个现有的连接
java·服务器·网络
灰阳阳2 小时前
2022年蓝桥杯JavaB组 省赛 题目解析(含AC_Code)
java·职场和发展·蓝桥杯·省赛·超详解
932我2 小时前
C++中的组合模式
开发语言·c++·组合模式
皎味小行家2 小时前
第三十二天|动态规划| 理论基础,509. 斐波那契数,70. 爬楼梯 ,746. 使用最小花费爬楼梯
java·数据结构·算法·leetcode·动态规划
up向上up2 小时前
基于51单片机的电子钟+秒表LCD1602仿真设计
嵌入式硬件·mongodb·51单片机
WuMingf_2 小时前
Mongodb
数据库·mongodb