200115-SpringBoot 系列教程 Solr 之查詢使用姿式小結
接下來進入 solr CURD 的第四篇,查詢的使用姿式介紹,本文將主要包括如下知識點java
<!-- more -->git
在介紹 demo 以前,須要先安裝 solr 環境,搭建 SpringBoot 項目工程,具體的環境搭建過程不細說,推薦參考文檔github
在application.yml
配置文件中紅,指定 solr 的域名spring
spring: data: solr: host: http://127.0.0.1:8983/solr
而後在 solr 中,寫入一些數據,供咱們查詢使用,能夠經過控制檯的方式寫入,也能夠經過190526-SpringBoot 高級篇搜索 Solr 之文檔新增與修改使用姿式 這篇文檔的 case 添加mongodb
初始化 solr 文檔內容以下json
{ "id":"1", "content_id":1, "title":"一灰灰blog", "content":"這是一灰灰blog的內容", "type":1, "create_at":1578912072, "publish_at":1578912072, "_version_":1655609540674060288}, { "id":"2", "content_id":2, "title":"一灰灰", "content":"這是一灰灰的內容", "type":1, "create_at":1578912072, "publish_at":1578912072, "_version_":1655609550229733376}, { "id":"3", "content_id":3, "title":"solrTemplate 修改以後!!!", "create_at":1578993153, "publish_at":1578993153, "type":0, "_version_":1655694325261008896}, { "id":"4", "content_id":4, "type":1, "create_at":0, "publish_at":0, "_version_":1655694325422489600}, { "id":"5", "content_id":5, "title":"addBatchByBean - 1", "content":"新增一個測試文檔", "type":1, "create_at":1578993153, "publish_at":1578993153, "_version_":1655694325129936896}, { "id":"6", "content_id":6, "title":"addBatchByBean - 2", "content":"新增又一個測試文檔", "type":1, "create_at":1578993153, "publish_at":1578993153, "_version_":1655694325136228352 }
solr 文檔對應的 POJO 以下,(注意 solr 中的主鍵 id 爲 string 類型,下面定義中用的是 Integer,推薦與 solr 的數據類型保持一致)app
@Data public class DocDO implements Serializable { private static final long serialVersionUID = 7245059137561820707L; @Id @Field("id") private Integer id; @Field("content_id") private Integer contentId; @Field("title") private String title; @Field("content") private String content; @Field("type") private Integer type; @Field("create_at") private Long createAt; @Field("publish_at") private Long publishAt; }
支持單個查詢和批量查詢,三個參數,第一個爲須要查詢的 Collection, 第二個爲 id/id 集合,第三個爲返回的數據類型less
private void queryById() { DocDO ans = solrTemplate.getById("yhh", 1, DocDO.class).get(); System.out.println("queryById: " + ans); Collection<DocDO> list = solrTemplate.getByIds("yhh", Arrays.asList(1, 2), DocDO.class); System.out.println("queryByIds: " + list); }
輸出結果以下spring-boot
queryById: DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072) queryByIds: [DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072)]
好比最簡單的根據某個字段進行查詢學習
Query query = new SimpleQuery("title:一灰灰"); Page<DocDO> ans = solrTemplate.query("yhh", query, DocDO.class); System.out.println("simpleQuery : " + ans.getContent());
直接在 SimpleQuery 中指定查詢條件,上面的 case 表示查詢 title 爲一灰灰
的文檔
輸出結果以下:
simpleQuery : [DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072)]
簡單的查詢使用上面的姿式 ok,固然就是閱讀起來不太優雅;推薦另一種基於Criteria
的查詢條件構建方式
query = new SimpleQuery(); // 查詢內容中包含一灰灰的文檔 query.addCriteria(new Criteria("content").contains("一灰灰")); ans = solrTemplate.query("yhh", query, DocDO.class); System.out.println("simpleQuery : " + ans.getContent());
輸出結果以下
simpleQuery : [DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072)]
Criteria
能夠構建複雜的且閱讀友好的查詢條件,後面會有具體的演示,這裏給出一個多條件查詢的 case
// 多個查詢條件 query = new SimpleQuery(); query.addCriteria(Criteria.where("title").contains("一灰灰").and("content_id").lessThan(2)); ans = solrTemplate.query("yhh", query, DocDO.class); System.out.println("multiQuery: " + ans.getContent());
輸出結果以下,在上面的基礎上,撈出了 contentId 小於 2 的記錄
multiQuery: [DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072)]
fq 主要用來快速過濾,配合 query 進行操做,主要是藉助org.springframework.data.solr.core.query.Query#addFilterQuery
來添加 fq 條件
// fq查詢 query = new SimpleQuery("content: *一灰灰*"); query.addFilterQuery(FilterQuery.filter(Criteria.where("title").contains("blog"))); ans = solrTemplate.query("yhh", query, DocDO.class); System.out.println("simpleQueryAndFilter: " + ans.getContent());
輸出結果如:
simpleQueryAndFilter: [DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072)]
當咱們只關注 solr 文檔中的部分字段時,能夠考慮指定 fl,只獲取所需的字段;經過org.springframework.data.solr.core.query.SimpleQuery#addProjectionOnFields(java.lang.String...)
來指定須要返回的字段名
/** * 查詢指定的字段 */ private void querySpecialFiled() { SimpleQuery query = new SimpleQuery(); query.addCriteria(Criteria.where("content_id").lessThanEqual(2)); // fl 查詢 query.addProjectionOnFields("id", "title", "content"); List<DocDO> ans = solrTemplate.query("yhh", query, DocDO.class).getContent(); System.out.println("querySpecialField: " + ans); }
輸出結果以下
querySpecialField: [DocDO(id=1, contentId=null, title=一灰灰blog, content=這是一灰灰blog的內容, type=null, createAt=null, publishAt=null), DocDO(id=2, contentId=null, title=一灰灰, content=這是一灰灰的內容, type=null, createAt=null, publishAt=null)]
請注意,咱們指定了只須要返回id
, title
, content
,因此返回的 DO 中其餘的成員爲 null
針對數字類型,支持範圍查詢,好比上面給出Criteria.where("content_id").lessThanEqual(2)
,表示查詢content_id
小於 2 的記錄,下面給出一個 between 的查詢
/** * 範圍查詢 */ private void queryRange() { Query query = new SimpleQuery(); query.addCriteria(Criteria.where("content_id").between(1, 3)); query.addSort(Sort.by("content_id").ascending()); List<DocDO> ans = solrTemplate.query("yhh", query, DocDO.class).getContent(); System.out.println("queryRange: " + ans); }
輸出結果以下,請注意 between 查詢,左右都是閉區間
queryRange: [DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=3, contentId=3, title=solrTemplate 修改以後!!!, content=null, type=0, createAt=1578997659, publishAt=1578997659)]
若是不想要閉區間,能夠用between
的重載方法
query = new SimpleQuery(); // 兩個false,分表表示不包含下界 上界 query.addCriteria(Criteria.where("content_id").between(1, 3, false, false)); query.addSort(Sort.by("content_id").ascending()); ans = solrTemplate.query("yhh", query, DocDO.class).getContent(); System.out.println("queryRange: " + ans);
輸出結果如
queryRange: [DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072)]
上面的 case 中,已經用到了排序,主要是Sort
來指定排序字段以及排序的方式;由於 id 在 solr 中其實是字符串格式,因此若是用 id 進行排序時,其實是根據字符串的排序規則來的(雖然咱們的 POJO 中 id 爲 int 類型)
/** * 查詢並排序 */ private void queryAndSort() { // 排序 Query query = new SimpleQuery(); query.addCriteria(new Criteria("content").contains("一灰灰")); // 倒排 query.addSort(Sort.by("content_id").descending()); Page<DocDO> ans = solrTemplate.query("yhh", query, DocDO.class); System.out.println("queryAndSort: " + ans.getContent()); }
輸出結果以下
queryAndSort: [DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072)]
分頁查詢比較常見,特別是當數據量比較大時,請必定記得,添加分頁條件
一個查詢 case 以下,查詢全部的數據,並制定了分頁條件,查詢第二條和第三條數據(計數從 0 開始)
/** * 分頁 */ private void queryPageSize() { Query query = new SimpleQuery("*:*"); query.addSort(Sort.by("content_id").ascending()); // 指定偏移量,從0開始 query.setOffset(2L); // 查詢的size數量 query.setRows(2); Page<DocDO> ans = solrTemplate.queryForPage("yhh", query, DocDO.class); // 文檔數量 long totalDocNum = ans.getTotalElements(); List<DocDO> docList = ans.getContent(); System.out.println("queryPageSize: totalDocNum=" + totalDocNum + " docList=" + docList); }
在返回結果中,查了返回查詢的文檔以外,還會給出知足條件的文檔數量,能夠經過Page#getTotalElements
獲取,
上面 case 輸出結果以下
queryPageSize: totalDocNum=6 docList=[DocDO(id=3, contentId=3, title=solrTemplate 修改以後!!!, content=null, type=0, createAt=1578997946, publishAt=1578997946), DocDO(id=4, contentId=4, title=null, content=null, type=1, createAt=0, publishAt=0)]
分組和前面的查詢有一點區別,主要在於結果的處理,以及分組參數必須指定分頁信息
/** * 分組查詢 */ private void queryGroup() { Query query = new SimpleQuery("*:*"); // 請注意,分組查詢,必須指定 offset/limit, 不然會拋異常,Pageable must not be null! GroupOptions groupOptions = new GroupOptions().addGroupByField("type").setOffset(0).setLimit(10); query.setGroupOptions(groupOptions); GroupPage<DocDO> ans = solrTemplate.queryForGroupPage("yhh", query, DocDO.class); GroupResult<DocDO> groupResult = ans.getGroupResult("type"); Page<GroupEntry<DocDO>> entries = groupResult.getGroupEntries(); System.out.println("============ query for group ============ "); for (GroupEntry<DocDO> sub : entries) { // type 的具體值 String groupValue = sub.getGroupValue(); Page<DocDO> contentList = sub.getResult(); System.out.println("queryGroup v=" + groupValue + " content=" + contentList.getContent()); } System.out.println("============ query for group ============ "); }
上面的 case 雖然比較簡單,可是有幾點須要注意, 特別是返回結果的獲取,包裝層級有點深
GroupOptions:
結果處理
GroupPage#getGroupResult(field)
獲取分組內容,其中 field 爲指定分組的成員GroupResult#getGroupEntries
,獲取每一個分組對應的文檔列表輸出結果以下
============ query for group ============ queryGroup v=1 content=[DocDO(id=1, contentId=1, title=一灰灰blog, content=這是一灰灰blog的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=2, contentId=2, title=一灰灰, content=這是一灰灰的內容, type=1, createAt=1578912072, publishAt=1578912072), DocDO(id=5, contentId=5, title=addBatchByBean - 1, content=新增一個測試文檔, type=1, createAt=1578997946, publishAt=1578997946), DocDO(id=6, contentId=6, title=addBatchByBean - 2, content=新增又一個測試文檔, type=1, createAt=1578997946, publishAt=1578997946), DocDO(id=4, contentId=4, title=null, content=null, type=1, createAt=0, publishAt=0)] queryGroup v=0 content=[DocDO(id=3, contentId=3, title=solrTemplate 修改以後!!!, content=null, type=0, createAt=1578997946, publishAt=1578997946)] ============ query for group ============
系列博文
工程源碼
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛