学习一个新的数据库,一般怎么下手呢?基本的CURD没跑了,当可以熟练的增、删、改、查一个数据库时,可以说对这个数据库算是入门了,如果需要更进一步的话,就需要了解下数据库的特性,比如索引、事物、锁、分布式支持等

本篇博文为mongodb的入门篇,将介绍一下基本的查询操作,在Spring中可以怎么玩

原文可参看: 190113-SpringBoot高级篇MongoDB之查询基本使用姿势

I. 基本使用

0. 环境准备

在正式开始之前,先准备好环境,搭建好工程,对于这一步的详细信息,可以参考博文: 181213-SpringBoot高级篇MongoDB之基本环境搭建与使用

接下来,在一个集合中,准备一下数据如下,我们的基本查询范围就是这些数据

1. 根据字段进行查询

最常见的查询场景,比如我们根据查询user=一灰灰blog的数据,这里主要会使用Query + Criteria 来完成

@Component
public class MongoReadWrapper {
private static final String COLLECTION_NAME = "demo"; @Autowired
private MongoTemplate mongoTemplate; /**
* 指定field查询
*/
public void specialFieldQuery() {
Query query = new Query(Criteria.where("user").is("一灰灰blog"));
// 查询一条满足条件的数据
Map result = mongoTemplate.findOne(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | specialFieldQueryOne: " + result); // 满足所有条件的数据
List<Map> ans = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | specialFieldQueryAll: " + ans);
}
}

上面是一个实际的case,从中可以知道一般的查询方式为:

  • Criteria.where(xxx).is(xxx)来指定具体的查询条件
  • 封装Query对象 new Query(criteria)
  • 借助mongoTemplate执行查询 mongoTemplate.findOne(query, resultType, collectionName)

其中findOne表示只获取一条满足条件的数据;find则会将所有满足条件的返回;上面执行之后,删除结果如

query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { } | specialFieldQueryOne: {_id=5c2368b258f984a4fda63cee, user=一灰灰blog, desc=帅气逼人的码农界老秀}
query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { } | specialFieldQueryAll: [{_id=5c2368b258f984a4fda63cee, user=一灰灰blog, desc=帅气逼人的码农界老秀}, {_id=5c3afaf4e3ac8e8d2d39238a, user=一灰灰blog, desc=帅气逼人的码农界老秀3}, {_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}, {_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}]

2. and多条件查询

前面是只有一个条件满足,现在如果是要求同时满足多个条件,则利用org.springframework.data.mongodb.core.query.Criteria#and来斜街多个查询条件

/**
* 多个查询条件同时满足
*/
public void andQuery() {
Query query = new Query(Criteria.where("user").is("一灰灰blog").and("age").is(18));
Map result = mongoTemplate.findOne(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | andQuery: " + result);
}

删除结果如下

query: Query: { "user" : "一灰灰blog", "age" : 18 }, Fields: { }, Sort: { } | andQuery: {_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}

3. or或查询

and对应的就是or,多个条件中只要一个满足即可,这个与and的使用有些区别, 借助org.springframework.data.mongodb.core.query.Criteria#orOperator来实现,传参为多个Criteria对象,其中每一个表示一种查询条件

/**
* 或查询
*/
public void orQuery() {
// 等同于 db.getCollection('demo').find({"user": "一灰灰blog", $or: [{ "age": 18}, { "sign": {$exists: true}}]})
Query query = new Query(Criteria.where("user").is("一灰灰blog")
.orOperator(Criteria.where("age").is(18), Criteria.where("sign").exists(true)));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | orQuery: " + result); // 单独的or查询
// 等同于Query: { "$or" : [{ "age" : 18 }, { "sign" : { "$exists" : true } }] }, Fields: { }, Sort: { }
query = new Query(new Criteria().orOperator(Criteria.where("age").is(18), Criteria.where("sign").exists(true)));
result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | orQuery: " + result);
}

执行后输出结果为

query: Query: { "user" : "一灰灰blog", "$or" : [{ "age" : 18 }, { "sign" : { "$exists" : true } }] }, Fields: { }, Sort: { } | orQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}]
query: Query: { "$or" : [{ "age" : 18 }, { "sign" : { "$exists" : true } }] }, Fields: { }, Sort: { } | orQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}, {_id=5c3b0538e3ac8e8d2d392390, user=二灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}]

4. in查询

标准的in查询case

/**
* in查询
*/
public void inQuery() {
// 相当于:
Query query = new Query(Criteria.where("age").in(Arrays.asList(18, 20, 30)));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | inQuery: " + result);
}

输出

query: Query: { "age" : { "$in" : [18, 20, 30] } }, Fields: { }, Sort: { } | inQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]

5. 数值比较

数值的比较大小,主要使用的是 get, gt, lt, let

/**
* 数字类型,比较查询 >
*/
public void compareBigQuery() {
// age > 18
Query query = new Query(Criteria.where("age").gt(18));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | compareBigQuery: " + result); // age >= 18
query = new Query(Criteria.where("age").gte(18));
result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | compareBigQuery: " + result);
} /**
* 数字类型,比较查询 <
*/
public void compareSmallQuery() {
// age < 20
Query query = new Query(Criteria.where("age").lt(20));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | compareSmallQuery: " + result); // age <= 20
query = new Query(Criteria.where("age").lte(20));
result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | compareSmallQuery: " + result);
}

输出

query: Query: { "age" : { "$gt" : 18 } }, Fields: { }, Sort: { } | compareBigQuery: [{_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]
query: Query: { "age" : { "$gte" : 18 } }, Fields: { }, Sort: { } | compareBigQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]
query: Query: { "age" : { "$lt" : 20 } }, Fields: { }, Sort: { } | compareSmallQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}]
query: Query: { "age" : { "$lte" : 20 } }, Fields: { }, Sort: { } | compareSmallQuery: [{_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]

6. 正则查询

牛逼高大上的功能

/**
* 正则查询
*/
public void regexQuery() {
Query query = new Query(Criteria.where("user").regex("^一灰灰blog"));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | regexQuery: " + result);
}

输出

query: Query: { "user" : { "$regex" : "^一灰灰blog", "$options" : "" } }, Fields: { }, Sort: { } | regexQuery: [{_id=5c2368b258f984a4fda63cee, user=一灰灰blog, desc=帅气逼人的码农界老秀}, {_id=5c3afacde3ac8e8d2d392389, user=一灰灰blog2, desc=帅气逼人的码农界老秀2}, {_id=5c3afaf4e3ac8e8d2d39238a, user=一灰灰blog, desc=帅气逼人的码农界老秀3}, {_id=5c3afafbe3ac8e8d2d39238b, user=一灰灰blog4, desc=帅气逼人的码农界老秀4}, {_id=5c3afb0ae3ac8e8d2d39238c, user=一灰灰blog5, desc=帅气逼人的码农界老秀5}, {_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}, {_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}]

7. 查询总数

统计常用,这个主要利用的是mongoTemplate.count方法

/**
* 查询总数
*/
public void countQuery() {
Query query = new Query(Criteria.where("user").is("一灰灰blog"));
long cnt = mongoTemplate.count(query, COLLECTION_NAME);
System.out.println("query: " + query + " | cnt " + cnt);
}

输出

query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { } | cnt 5

8. 分组查询

这个对应的是mysql中的group查询,但是在mongodb中,更多的是通过聚合查询,可以完成很多类似的操作,下面借助聚合,来看一下分组计算总数怎么玩

/*
* 分组查询
*/
public void groupQuery() {
// 根据用户名进行分组统计,每个用户名对应的数量
// aggregate([ { "$group" : { "_id" : "user" , "userCount" : { "$sum" : 1}}}] )
Aggregation aggregation = Aggregation.newAggregation(Aggregation.group("user").count().as("userCount"));
AggregationResults<Map> ans = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);
System.out.println("query: " + aggregation + " | groupQuery " + ans.getMappedResults());
}

注意下,这里用Aggregation而不是前面的QueryCriteria,输出如下

query: { "aggregate" : "__collection__", "pipeline" : [{ "$group" : { "_id" : "$user", "userCount" : { "$sum" : 1 } } }] } | groupQuery [{_id=一灰灰blog, userCount=5}, {_id=一灰灰blog2, userCount=1}, {_id=一灰灰blog4, userCount=1}, {_id=二灰灰blog, userCount=1}, {_id=一灰灰blog5, userCount=1}]

9. 排序

sort,比较常见的了,在mongodb中有个有意思的地方在于某个字段,document中并不一定存在,这是会怎样呢?

/**
* 排序查询
*/
public void sortQuery() {
// sort查询条件,需要用with来衔接
Query query = Query.query(Criteria.where("user").is("一灰灰blog")).with(Sort.by("age"));
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | sortQuery " + result);
}

输出结果如下,对于没有这个字段的document也被查出来了

query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { "age" : 1 } | sortQuery [{_id=5c2368b258f984a4fda63cee, user=一灰灰blog, desc=帅气逼人的码农界老秀}, {_id=5c3afaf4e3ac8e8d2d39238a, user=一灰灰blog, desc=帅气逼人的码农界老秀3}, {_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}, {_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]

10. 分页

数据量多的时候,分页查询比较常见,用得多就是limit和skip了

/**
* 分页查询
*/
public void pageQuery() {
// limit限定查询2条
Query query = Query.query(Criteria.where("user").is("一灰灰blog")).with(Sort.by("age")).limit(2);
List<Map> result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | limitPageQuery " + result); // skip()方法来跳过指定数量的数据
query = Query.query(Criteria.where("user").is("一灰灰blog")).with(Sort.by("age")).skip(2);
result = mongoTemplate.find(query, Map.class, COLLECTION_NAME);
System.out.println("query: " + query + " | skipPageQuery " + result);
}

输出结果表明,limit用来限制查询多少条数据,skip则表示跳过前面多少条数据

query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { "age" : 1 } | limitPageQuery [{_id=5c2368b258f984a4fda63cee, user=一灰灰blog, desc=帅气逼人的码农界老秀}, {_id=5c3afaf4e3ac8e8d2d39238a, user=一灰灰blog, desc=帅气逼人的码农界老秀3}]
query: Query: { "user" : "一灰灰blog" }, Fields: { }, Sort: { "age" : 1 } | skipPageQuery [{_id=5c3b003ee3ac8e8d2d39238f, user=一灰灰blog, desc=帅气逼人的码农界老秀6, sign=hello world}, {_id=5c3afb1ce3ac8e8d2d39238d, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=18.0}, {_id=5c3b0031e3ac8e8d2d39238e, user=一灰灰blog, desc=帅气逼人的码农界老秀6, age=20.0}]

11. 小结

上面给出的一些常见的查询姿势,当然并不全面,比如我们如果需要查询document中的部分字段怎么办?比如document内部结果比较复杂,有内嵌的对象或者数组时,嵌套查询可以怎么玩?索引什么的又可以怎么利用起来,从而优化查询效率?如何通过传说中自动生成的_id来获取文档创建的时间戳?

先留着这些疑问,后面再补上

II. 其他

0. 项目

1. 一灰灰Blog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

知识星球

SpringBoot之MongoTemplate的查询可以怎么耍的更多相关文章

  1. 【第十二章】 springboot + mongodb(复杂查询)

    简单查询:使用自定义的XxxRepository接口即可.(见 第十一章 springboot + mongodb(简单查询)) 复杂查询:使用MongoTemplate以及一些查询条件构建类(Bas ...

  2. 第十二章 springboot + mongodb(复杂查询)

    简单查询:使用自定义的XxxRepository接口即可.(见 第十一章 springboot + mongodb(简单查询)) 复杂查询:使用MongoTemplate以及一些查询条件构建类(Bas ...

  3. SpringBoot+Mybatis实现关联查询

    SpringBoot+Mybatis实现关联查询 今天学习了下Mybatis的动态查询,然后接着上次的Demo改造了下实现表的关联查询. 话不多说,开始今天的小Demo 首先接着上次的项目 https ...

  4. 第11章—使用对象关系映射持久化数据—SpringBoot+SpringData+Jpa进行查询修改数据库

    SpringBoot+SpringData+Jpa进行查询修改数据库 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分.但它又不限于EJB 3.0,你可以在Web应用.甚至桌面应用 ...

  5. Nebula Graph介绍和SpringBoot环境连接和查询

    Nebula Graph介绍和SpringBoot环境连接和查询 转载请注明来源 https://www.cnblogs.com/milton/p/16784098.html 说明 当前Nebula ...

  6. SpringBoot之mongoTemplate的使用

    springboot的版本1.5.17.RELEASE. 1.mongo的IP和端口 在resources下的application.properties中加入如下内容 spring.data.mon ...

  7. mongoTemplate 条件查询

    构建条件方法 @Override public Query getQuery(ReportParam param){ //check MeenoAssert.hasLength(param.getUu ...

  8. java~springboot~ibatis数组in查询的实现

    在ibatis的xml文件里,我们去写sql语句,对应mapper类的方法,这些sql语句与控制台上没什么两样,但在有些功能上需要注意,如where in这种从数组里查询符合条件的集合里,需要在xml ...

  9. Mybatis的应用2 使用mybits+SpringBoot完成第一个查询的demo(随后加增加,更新,删除)

    首先在mapper下面新建一个mysql.xml mysql.xml <?xml version="1.0" encoding="UTF-8" ?> ...

随机推荐

  1. 如何从Microsoft web platform installer取得离线安装包

    有一架visual studio 2012的开发环境A由于某种原因无法链接internet, 于是乎安装officetoolsforvisual2012就有问题了. 从微软的官网上只可以下载 offi ...

  2. September 29th 2017 Week 39th Friday

    Human life is ephemera, which makes it precious. 生命短暂,所以珍贵. Don't waste time on praying to the God. ...

  3. DevExpress01:Bar Manager,bar 、Toolbars

    Bar Manager : 不可见的控件 如果想在窗体或用户控件添加工具条或弹出菜单,我们需要把一个不可见的控件 BarManager拖放到这个窗体或用户控件上. 这个BarManager控件维护工具 ...

  4. 【收集】Python 微优化

    1. 第二种方式可以节省寻找result的append属性的时间, 但会降低代码可读性和可维护性 # The way we're used to seeing it: result.append(&q ...

  5. jQuery事件处理

    浏览器的事件模型 DOM第0级事件模型 Event实例 他的属性提供了关于当前正被处理的已触发事件的大量信息.这包括一些细节,比如在哪个元素上触发的事件.鼠标事件的坐标以及键盘事件中单击了哪个键. 事 ...

  6. P3694 邦邦的大合唱站队

    题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶像. 现在要求重新安排队列,使来自同一 ...

  7. [USACO09JAN]Earthquake Damage

    嘟嘟嘟 刚开始因为没看到只能走没有损坏的农场,磨叽了20多分钟……不管了,写题解吧. 首先如果一个点不能到达原点,那么和他相邻的点也不能到达原点,所以刚开始我们把不能走的点和他相邻的点都打上标记,然后 ...

  8. 【node.js】GET/POST请求、Web 模块

    获取GET请求内容 node.js 中 url 模块中的 parse 函数提供了这个功能. var http = require('http'); var url = require('url'); ...

  9. centos中安装、升级git

    yum install git 若是从老版本升级,则按下面方法.(centos中) 先更新系统sudo yum update     安装依赖的包yum install curl-devel expa ...

  10. TCP建立连接与释放连接

    1.建立连接:(三次握手) (1)客户端发送一个SYN包给服务器,然后等待应答. (2)服务器端回应给客户端一个ACK=1.SYN=1的TCP数据段. (3)客户必须再次回应服务器端一个ACK确认数据 ...