接下来进入 solr CURD 的第四篇,查询的使用姿式介绍,本文将主要包括如下知识点git
<!-- more -->github
在介绍 demo 以前,须要先安装 solr 环境,搭建 SpringBoot 项目工程,具体的环境搭建过程不细说,推荐参考文档spring
在application.yml
配置文件中红,指定 solr 的域名mongodb
spring: data: solr: host: http://127.0.0.1:8983/solr
而后在 solr 中,写入一些数据,供咱们查询使用,能够经过控制台的方式写入,也能够经过190526-SpringBoot 高级篇搜索 Solr 之文档新增与修改使用姿式 这篇文档的 case 添加json
初始化 solr 文档内容以下app
{ "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 的数据类型保持一致)less
@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 集合,第三个为返回的数据类型spring-boot
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); }
输出结果以下学习
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 虽然比较简单,可是有几点须要注意, 特别是返回结果的获取,包装层级有点深
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 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛