本文前端传递的参数:
searchMap:{
'keywords':'', // 搜索关键字
'category':'', // 分类
'brand':'', // 品牌
'spec':{}, // 规格
'price':'', // 价格
'pageNo':1, // 当前页
'pageSize':40, // 每页展示多少条数据
'sort':'', // 排序
'sortField':'' // 排序的字段
},
本文Solr域的设置:
<!-- 普通域:商品id、标题、价格、图片、分类、卖家、品牌、修改时间 -->
<!-- 是否索引、是否存储 -->
<field name="item_goodsid" type="long" indexed="true" stored="true"/>
<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_price" type="double" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category" type="string" indexed="true" stored="true" />
<field name="item_seller" type="text_ik" indexed="true" stored="true" />
<field name="item_brand" type="string" indexed="true" stored="true" />
<field name="item_updatetime" type="date" indexed="true" stored="true" />
<!-- 可以根据哪些关键字来搜索 -->
<!-- 暴露在外面的keywords无需存储 -->
<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<!-- keywrods来源 -->
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_category" dest="item_keywords"/>
<copyField source="item_seller" dest="item_keywords"/>
<copyField source="item_brand" dest="item_keywords"/>
<!-- 商品规格是什么:选择版本、颜色等 不确定,实际运行才确定 -->
<dynamicField name="item_spec_*" type="string" indexed="true" stored="true" />
本文存入到Solr的Java对象:
/**
* 商品id,同时也是商品编号
*/
@Field
private Long id;
/**
* 商品标题
*/
@Field("item_title")
private String title;
// 商品图片
@Field("item_image")
private String image;
// 商品价格,单位为:元
@Field("item_price")
private BigDecimal price;
// 更新时间
@Field("item_updatetime")
private Date updateTime;
@Field("item_goodsid")
private Long goodsId;
@Field("item_category")
private String category;
@Field("item_brand")
private String brand;
@Field("item_seller")
private String seller;
@Dynamic
@Field("item_spec_*")
private Map<String,String> specMap;
普通分页查询
概述:使用Solr查询,首先需要注入solrTemplate对象。然后创建SimpleQuery对象,设置查询条件Criteria,把查询条件放到查询对象中。分页查询,还需在查询对象中设置查询数据开始的下标及搜索多少条数据。通过queryForPage方法获取查询结果。但是你真实的数据集合还需通过getContent获取,总共有多少页可以通过getTotalPages获取,总共有多少条数据可以通过getTotalElement获取。
public Map<String, Object> search(Map paramMap){
// 获取keywords(搜索的关键词)
String keywords = String.valueOf(paramMap.get("keywords"));
// 获取当前页
Integer pageNo = Integer.parseInt(String.valueOf(paramMap.get("pageNo")));
// 获取每页数据多少
Integer pageSize = Integer.parseInt(String.valueOf(paramMap.get("pageSize")));
// 创建Solr查询对象
SimpleQuery query = new SimpleQuery();
// 创建查询条件 - 根据搜索关键词来查
// item_keywords是Solr暴露在外的关键字,用于搜索
Criteria item_keywords = new Criteria("item_keywords").is(keywords);
// 把查询条件添加到查询对象中去
query.addCriteria(item_keywords);
// 添加分页
if(pageNo == null || pageNo <= 0){
pageNo = 1;
}
// 设置 - 从哪条记录开始查询
Integer start = (pageNo - 1) * pageSize;
query.setOffset(start);
// 设置 - 查询多少条记录
query.setRows(pageSize);
// 获取查询结果 - 将查询结果封装成Items类型
// 注入Solr的模板对象solrTemplate
ScoredPage<Item> items = solrTemplate.queryForPage(query,Item.class);
// 返回结果
Map resMap = new HashMap();
// 设置查询到的数据集合
resMap.put("rows",items.getContent());
// 设置总共有多少页
resMap.put("totalPage",items.getTotalPages());
// 设置总记录数
resMap.put("total",items.getTotalElements());
return resMap;
}
高亮分页查询
有时候我们需要做这样一个需求,就是查询的部分需要在前端高亮显示出来,如下图。Solr给我们提供了一个高亮查询的解决方案,通过这个方案就可以设置高亮。但是这个方案有些复杂,具体的看代码吧。
/**
* 高亮查询
* @param paramMap
* @return
*/
public Map setHiglights(Map paramMap){
// 获取查询条件
String keywords = String.valueOf(paramMap.get("keywords"));
// 获取分页信息 - 当前页、每页展示多少条数据
Integer pageNo = Integer.parseInt(String.valueOf(paramMap.get("pageNo")));
Integer pageSize = Integer.parseInt(String.valueOf(paramMap.get("pageSize")));
// 创建高亮查询对象
SimpleHighlightQuery query = new SimpleHighlightQuery();
// 设置高亮选项
HighlightOptions highlightOptions = new HighlightOptions();
// 设置标题域需要高亮显示
highlightOptions.addField("item_title");
// 设置样式,给需要高亮的字段添加html标签
// 设置高亮前缀
highlightOptions.setSimplePrefix("<em style=\"color:red\">");
// 设置后缀
highlightOptions.setSimplePostfix("</em>");
// 将样式设置到高亮查询对象中去
query.setHighlightOptions(highlightOptions);
// 创建查询条件对象 - 根据item_keywords域来查
Criteria item_keywords = new Criteria("item_keywords").is(keywords);
// 将查询条件对象添加到高亮查询对象中去
query.addCriteria(item_keywords);
// 添加分页信息
if(pageNo == null || pageNo <= 0){
pageNo = 1;
}
// 从哪条记录开始查
Integer start = (pageNo - 1) * pageSize;
// 设置记录的开始
query.setOffset(start);
// 记录条数
query.setRows(pageSize);
// 高亮分页查询标题集合
HighlightPage<Item> items = solrTemplate.queryForHighlightPage(query, Item.class);
// 获取结果集中的标题域的集合
List<HighlightEntry<Item>> highlighted = items.getHighlighted();
// 存储标题的集合
List<Item> itemList = new ArrayList<>();
// 遍历标题域的集合
for (HighlightEntry<Item> itemHighlightEntry : highlighted) {
// 获取到原本不带高亮的实体对象
Item item = itemHighlightEntry.getEntity();
// 获取高亮集合
List<HighlightEntry.Highlight> highlights = itemHighlightEntry.getHighlights();
// 当高亮集合不为空时
if (highlights != null && highlights.size() > 0) {
// 获取到高亮标题集合
List<String> highlightTitle = highlights.get(0).getSnipplets();
if (highlightTitle != null && highlightTitle.size() > 0) {
// 终于获取到高亮的标题
String title = highlightTitle.get(0);
// 使用高亮的内容替换不带高亮的标题
item.setTitle(title);
}
}
itemList.add(item);
}
HashMap resMap = new HashMap();
resMap.put("rows",itemList);
resMap.put("totalPages",items.getTotalPages());
resMap.put("total",items.getTotalElements());
return resMap;
}
Solr内部十分复杂,很难去记忆,所以每次就通过断点调试往下找:
分类/分组查询
/**
* 查询分类
* @param paramMap
* @return
*/
private List<String> findCategory(Map paramMap) {
// 获取查询条件
String keywords = String.valueOf(paramMap.get("keywords"));
// 创建查询对象
SimpleQuery query = new SimpleQuery();
// 创建查询条件对象 - 根据item_keywords域来查
Criteria criteria = new Criteria("item_keywords").is(keywords);
// 给查询对象添加查询条件
query.addCriteria(criteria);
// 创建分组对象
GroupOptions groupOptions = new GroupOptions();
// 根据分类域进行分组
groupOptions.addGroupByField("item_category");
// 将分组放入到查询对象当中
query.setGroupOptions(groupOptions);
// 创建存储分类的集合
ArrayList<String> resList = new ArrayList<>();
// 分组查询分类集合
GroupPage<Item> items = solrTemplate.queryForGroupPage(query, Item.class);
// 获取结果集中的分类域的集合
GroupResult<Item> item_category = items.getGroupResult("item_category");
// 获取分类域中的实体集合
Page<GroupEntry<Item>> groupEntries = item_category.getGroupEntries();
// 遍历实体集合得到实体对象
for (GroupEntry<Item> groupEntry : groupEntries) {
String groupValue = groupEntry.getGroupValue();
// 将分组的内容放到数组中去
resList.add(groupValue);
}
return resList;
}
断点调试:
过滤查询
业务中,我们可能需要做一些过滤,比如过滤某品牌、分类、规格、价格等。Solr也为我们提供了相关解决方案,看如下代码:
// 根据分类过滤查询
if (category != null && !"".equals(category)) {
// 创建过滤查询对象
FilterQuery filterQuery = new SimpleFilterQuery();
// 创建条件对象
Criteria filterCriteria = new Criteria("item_category").is(category);
// 将条件对象放入过滤对象中
filterQuery.addCriteria(filterCriteria);
// 过滤对象放入查询对象中
query.addFilterQuery(filterQuery);
}
// 根据规格过滤查询 spec中的数据格式{网络:移动4G, 内存小大: 16G}
if (spec != null && !"".equals(spec)) {
Map<String, String> speMap = JSON.parseObject(spec, Map.class);
if (speMap != null && speMap.size() > 0) {
Set<Map.Entry<String, String>> entries = speMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
// 创建过滤查询对象
FilterQuery filterQuery = new SimpleFilterQuery();
// 创建条件对象
Criteria filterCriteria = new Criteria("item_spec_" + entry.getKey())
.is(entry.getValue());
// 将条件对象放入过滤对象中
filterQuery.addCriteria(filterCriteria);
// 过滤对象放入查询对象中
query.addFilterQuery(filterQuery);
}
}
}
// 根据价格过滤 0-500 500-1000 1000-1500 ..... 3000 - *
if (price != null && !"".equals(price)) {
//切分价格, 这个素组中有最小值和最大值
String[] split = price.split("-");
if (split != null && split.length == 2) {
// 说明大于等于最小值, 如果第一个最小值为0, 进入不到这里
if (!"0".equals(split[0])) {
//创建过滤查询对象
FilterQuery filterQuery = new SimpleFilterQuery();
//创建条件对象
Criteria filterCriteria = new Criteria("item_price").greaterThanEqual(split[0]);
//将条件对象放入过滤对象中
filterQuery.addCriteria(filterCriteria);
//过滤对象放入查询对象中
query.addFilterQuery(filterQuery);
}
// 说明小于等于最大值, 如果最后的元素也就是最大值为*, 进入不到这里
if (!"*".equals(split[1])) {
//创建过滤查询对象
FilterQuery filterQuery = new SimpleFilterQuery();
//创建条件对象
Criteria filterCriteria = new Criteria("item_price").lessThanEqual(split[1]);
//将条件对象放入过滤对象中
filterQuery.addCriteria(filterCriteria);
//过滤对象放入查询对象中
query.addFilterQuery(filterQuery);
}
}
}