基於 MongoTemplate 實現MongoDB的複雜查詢

王若伊_恩赐解脱發表於2024-12-03

MongoDB是典型的非關係型資料庫,但是它的功能越來越複雜,很多專案中,我們為了快速擴充,甚至直接使用Mongo 來替代傳統DB做資料持久化。
雖然MongoDB在支援具體業務時沒有問題,但是由於它是文件型資料庫,擁有一套獨立的語法,不再支援傳統的SQL。
開發人員發現在實際開發過程中,由於語法問題,在處理複雜的業務查詢時,不知該如何下手,使不上勁。
在這裡我總結了一下接觸到的使用場景:
如果是簡單的業務,那麼我們直接使用spring JPA來實現就可以,比如這些操作:
1、建立
2、刪除
3、修改
4、簡單的查詢
因為這些語句的邏輯往往不是很複雜,JPA完全可以勝任,而且還清晰直觀。
如果是複雜的場景,我們就使用MongoTemplate 來組織條件邏輯:
假設背景是有張Student 表,結構如下:
我們已經預先插入了下邊的資料:

(1)先來一個簡單的
單條件查詢:

1     private void simpleInQuery() {
2         Query query = new Query();
3         query.addCriteria(Criteria.where("classNo").ne(2));
4         List<Student> students = mongoTemplate.find(query, Student.class);
5         log.warn("the query result is: {}", students);
6     }

輸出如下:

14:39:19.805  WARN 83348 --- [           main] c.e.demo.learn.mongo.MongodbController   : the query result is: 
[Student(_id=674d8125bf8e9e35bbc08718, name=xiaoa, description=good1, classNo=1, age=15), 
Student(_id=674d8125bf8e9e35bbc08719, name=xiaob, description=good2, classNo=1, age=13), 
Student(_id=674d8125bf8e9e35bbc0871a, name=xiaoc, description=good3, classNo=1, age=15),
Student(_id=674d8125bf8e9e35bbc0871b, name=xiaod, description=good4, classNo=1, age=15), Student(_id=674d8153c993425aaa5c4fec, name=zhongd, description=perfect2, classNo=3, age=15), Student(_id=674d81dc8130705614f23311, name=biga, description=nice, classNo=3, age=15), Student(_id=674d81dd8130705614f23312, name=bigb, description=nice, classNo=3, age=13), Student(_id=674d81dd8130705614f23313, name=bigc, description=nice, classNo=3, age=15), Student(_id=674d81dd8130705614f23314, name=bigd, description=nice, classNo=3, age=15)]

觀察程式碼,我們發現需要首先建立一個Query 例項,表示是一個查詢動作。
query 物件,繼續補充一個Criteria 例項。Criteria 英[kraɪ'tɪəriə] 譯為比標準、準則、尺度。我們可以直接理解為查詢條件。
注意Criteria 例項是由 Criteria.where 方法建立出來的。(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )這是一個簡單工廠,引數為要查詢的表的列名(文件的欄位)。再跟一個in() ,表示列的in 操作,in() 中跟的是in操作的值。
最後直接用mongoTemplate 例項執行find 操作就好,條件為查詢邏輯和表對應的類檔案。
我們這裡使用的是in 操作,除此之外,常用的還有

方法 作用 類比sql
gt 表示 大於 >
gte 表示 大於等於 >=
lt 表示 小於 <
lte 表示 小於等於 <=
ne 表示 不等於 !=
nin 表示 不屬於 not in
is 表示等於 =

regex

表示 like (注意後面跟正規表示式,如 "^.*" + queryKeyWord + ".*$") like ‘%關鍵字%’

這些都是基本操作,有sql經驗的同學肯定明白具體怎麼使用。
我們再補充一個模糊查詢的例子:

1     private void simpleRegexQuery() {
2         Query query = new Query();
3         String queryKeyWord = "ong";
4         query.addCriteria(Criteria.where("name").regex("^.*" + queryKeyWord + ".*$"));
5         List<Student> students = mongoTemplate.find(query, Student.class);
6         log.warn("the query result is: {}", students);
7     }

輸出如下:

2024-12-03 14:46:23.781  WARN 81708 --- [           main] c.e.demo.learn.mongo.MongodbController   : the query result is: 
[Student(_id=674d8152c993425aaa5c4fe9, name=zhonga, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fea, name=zhongb, description=perfect2, classNo=2, age=13),
Student(_id=674d8153c993425aaa5c4feb, name=zhongc, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fec, name=zhongd, description=perfect2, classNo=3, age=15)]

(2)接著來看一個相對複雜點的組合條件:
兩個或條件,類似於SQL中的: A表示式 OR B表示式,程式碼如下

 1     private void simpleOrQuery() {
 2         Query query = new Query();
 3         String queryKeyWord = "ong";
 4         Criteria neCri = Criteria.where("age").ne(15);
 5         Criteria regexCri = Criteria.where("name").regex("^.*" + queryKeyWord + ".*$");
 6         Criteria orCri = new Criteria().orOperator(neCri, regexCri);
 7         query.addCriteria(orCri);
 8         List<Student> students = mongoTemplate.find(query, Student.class);
 9         log.warn("the query result is: {}", students);
10     }

執行效果如下:

2024-12-03 14:48:28.787  WARN 83804 --- [           main] c.e.demo.learn.mongo.MongodbController   : the query result is: 
[Student(_id=674d8125bf8e9e35bbc08719, name=xiaob, description=good2, classNo=1, age=13),
Student(_id=674d8152c993425aaa5c4fe9, name=zhonga, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fea, name=zhongb, description=perfect2, classNo=2, age=13),
Student(_id=674d8153c993425aaa5c4feb, name=zhongc, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fec, name=zhongd, description=perfect2, classNo=3, age=15),
Student(_id=674d81dd8130705614f23312, name=bigb, description=nice, classNo=3, age=13)]

我們建立好兩個Criteria的簡單條件之後,再建立一個新的Criteria 例項,用一個or操作將二者關聯起來.
query 接收最新的Criteria 例項,然後執行查詢即可。
這裡的寫法類似於sql中的
where name like "%ong%" or age != 15

如果是兩個AND 條件,類似於SQL中的: (防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )A表示式 AND B表示式,用法和or的使用方法是一樣的 。這裡就不舉例了,
我們這裡寫一個複雜的用法:

 1     private void complexQuery() {
 2         Query query = new Query();
 3 
 4         String queryKeyWord = "ong";
 5         Criteria ageCri = Criteria.where("age").ne(15);
 6         Criteria nameCri = Criteria.where("name").regex("^.*" + queryKeyWord + ".*$");
 7         Criteria cri1 = new Criteria().orOperator(ageCri, nameCri);
 8 
 9         Criteria descCri = Criteria.where("description").is("nice");
10         Criteria classNoCri = Criteria.where("classNo").in(1, 2, 3);
11         Criteria cri2 = new Criteria().andOperator(descCri, classNoCri);
12 
13         query.addCriteria(new Criteria().orOperator(cri1, cri2));
14         List<Student> students = mongoTemplate.find(query, Student.class);
15         log.warn("the query result is: {}", students);
16     }

輸出如下:

2024-12-03 14:51:46.908  WARN 92840 --- [           main] c.e.demo.learn.mongo.MongodbController   : the query result is: 
[Student(_id=674d8125bf8e9e35bbc08719, name=xiaob, description=good2, classNo=1, age=13),
Student(_id=674d8152c993425aaa5c4fe9, name=zhonga, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fea, name=zhongb, description=perfect2, classNo=2, age=13),
Student(_id=674d8153c993425aaa5c4feb, name=zhongc, description=perfect2, classNo=2, age=15),
Student(_id=674d8153c993425aaa5c4fec, name=zhongd, description=perfect2, classNo=3, age=15),
Student(_id=674d81dc8130705614f23311, name=biga, description=nice, classNo=3, age=15),
Student(_id=674d81dd8130705614f23312, name=bigb, description=nice, classNo=3, age=13),
Student(_id=674d81dd8130705614f23313, name=bigc, description=nice, classNo=3, age=15),
Student(_id=674d81dd8130705614f23314, name=bigd, description=nice, classNo=3, age=15)](防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )

這裡的寫法類似於sql中的

where ( description = "nice" and classNo in (1 ,2 ,3) ) or ("name like %ong%" or age != 15)

總體來看:一個Criteria 例項,就是一個查詢條件。

我們可以透過 or、and 操作來不斷的組合生成一個新的Criteria例項,也就是一個新的查詢條件 ,並且可以以此查詢條件繼續組合生成更高階的Criteria,以此不斷的類推。

這個過程就像壘積木一樣:

(3) 接著我們整合下分頁功能,並且以班級排序

PageRequest pageable = PageRequest.of(pageIndex - 1, pageSize);
Query pageQuery=query.with(pageable).with(Sort.by(Sort.Direction.DESC,"classNo"));

注意分頁時,頁碼數是從0開始,所以要-1。同時排序使用Sort生成sort的物件,包含排序方式和欄位,並且這裡支援多級排序。

整體程式碼如下:

 1     private void complexPageQuery() {
 2         int pageIndex=2;
 3         int pageSize=3;
 4         Query query = new Query();
 5         String queryKeyWord = "ong";
 6         Criteria ageCri = Criteria.where("age").ne(15);
 7         Criteria nameCri = Criteria.where("name").regex("^.*" + queryKeyWord + ".*$");
 8         Criteria cri1 = new Criteria().orOperator(ageCri, nameCri);
 9 
10         Criteria descCri = Criteria.where("description").is("nice");
11         Criteria classNoCri = Criteria.where("classNo").in(1, 2, 3);
12         Criteria cri2 = new Criteria().andOperator(descCri, classNoCri);
13 
14         query.addCriteria(new Criteria().orOperator(cri1, cri2));
15         long allDataSize = mongoTemplate.count(query, Student.class);
16         PageRequest pageable = PageRequest.of(pageIndex - 1, pageSize);
17         Query pageQuery=query.with(pageable).with(Sort.by(Sort.Direction.DESC,"classNo"));
18         List<Student> students = mongoTemplate.find(pageQuery, Student.class);
19         log.warn("the query result is: {}", students);
20     }

輸出如下:

2024-12-03 14:56:46.059  WARN 18516 --- [           main] c.e.demo.learn.mongo.MongodbController   : the query result is: 
[Student(_id=674d81dd8130705614f23314, name=bigd, description=nice, classNo=3, age=15),
Student(_id=674d81dc8130705614f23311, name=biga, description=nice, classNo=3, age=15),
Student(_id=674d8152c993425aaa5c4fe9, name=zhonga, description=perfect2, classNo=2, age=15)]

相關文章