主题
使用solr实现搜索功能
for 2020/2/17 15:28
提词
使用solr的term功能, 设置结果集个数
java
/**
* 自动提示
* @param termField 搜索的字段, 在solr配置
* @param value 用户输入值
* @return
*/
private List<String> term(String termField, String value) {
SolrQuery query = new SolrQuery();
// 不要结果集!!!
query.setStart(0);
query.setRows(0);
// 该字段已在sole配置
query.setQuery(termField + ":" + value);
// 提词
query.setTerms(true);
// 最多返回10条
query.setTermsLimit(10);
// 可选的. 设置最小统计个数
query.setTermsMinCount(1);
// query.setTermsMaxCount(10);
// query.setTermsRaw(true);
// 可选的. 这个term开始。如果不指定,使用空字符串,这意味着从头开始的。
// 效率更高!!!
query.setTermsLower(value);
// 可选的. 限制匹配,设置terms前缀是以什么开始的。
query.setTermsPrefix(value);
// 词频最高排序
query.setTermsSortString("count");
query.addTermsField(termField);
try {
QueryResponse result = client.query(query);
if (result.getStatus() == 0) {
TermsResponse termsResponse = result.getTermsResponse();
Map<String, List<TermsResponse.Term>> map = termsResponse.getTermMap();
List<TermsResponse.Term> list = map.get(termField);
if (Is.isNoEmpty(list)) {
ArrayList<String> strs = new ArrayList<>();
list.forEach(l -> {
strs.add(l.getTerm());
});
return strs;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
分页搜索
java
/**
* 调用solr搜索
*
* @param keywords 搜索的字段 + : + 搜索内容 , 如 "item_title:" + searchStr
* @param start 开始的索引 = (page - 1) * limit
* @param limit 每页显示的条数
* @param hlField 高亮的字段
* @param cla<T> 处理返回泛型的格式
* @return
*/
private <T> PageBean<List<T>> baseQuery(String keywords, int start, int limit, String hlField, Class<T> cla) {
SolrQuery query = new SolrQuery();
query.setStart(start);
query.setRows(limit);
// 该字段已在sole配置
query.setQuery(keywords);
query.addSort("score", SolrQuery.ORDER.desc);
query.setParam("q.op", "and");
// 高亮
boolean hlFlag = Is.isNoEmpty(hlField);
if (hlFlag) {
// 开启高亮
query.setHighlight(true);
// 高亮字段
query.addHighlightField(hlField);
// 高亮单词的前缀
query.setHighlightSimplePre("<font style='color:red'>");
// 高亮单词的后缀
query.setHighlightSimplePost("</font>");
//摘要最长1000个字符
query.setHighlightFragsize(500);
query.setSort("item_type", SolrQuery.ORDER.desc);
}
try {
QueryResponse result = client.query(query);
if (result.getStatus() == 0) {
//[5]遍历结果
// TermsResponse termsResponse = result.getTermsResponse();
// List<TermsResponse.Term> terms = termsResponse.getTerms("p_name");
// for (TermsResponse.Term term : terms) {
// System.out.println(term.getTerm() + ":\t"+ term.getFrequency());
// }
SolrDocumentList list = result.getResults();
// 总条数
Long total = list.getNumFound();
List<T> tList = new ArrayList<>();
for (SolrDocument p : list) {
try {
T t = cla.newInstance();
Collection<String> collection = p.getFieldNames();
for (String c : collection) {
// 判断有没有这个属性
if (!"_version_".equals(c) && cla.getDeclaredField(c) != null) {
//将属性的首字符大写,方便构造get,set方法
String name = c.substring(0, 1).toUpperCase() + c.substring(1);
System.out.print("set" + name);
Method m = cla.getMethod("set" + name, new Class[]{String.class});
m.invoke(t, (String) p.get(c));
}
}
if (hlFlag) {
//获取高亮字段name相应结果
NamedList<Object> response = result.getResponse();
//获取高亮字段name相应结果
NamedList<SimpleOrderedMap> highlighting = (NamedList<SimpleOrderedMap>) response.get("highlighting");
// 循环结果集,判断id
for (int i = 0; i < highlighting.size(); i++) {
String id = (String) cla.getMethod("getId").invoke(t);
// 判断id相等
if (highlighting.getName(i).equals(id)) {
SimpleOrderedMap map = highlighting.getVal(i);
ArrayList temp = (ArrayList) map.get(hlField);
if (Is.isNoEmpty(temp)) {
// 替换字符串
String val = (String) temp.get(0);
String name = hlField.substring(0, 1).toUpperCase() + hlField.substring(1);
cla.getMethod("set" + name, new Class[]{String.class}).invoke(t, val);
}
break;
}
}
}
tList.add(t);
} catch (Exception e) {
e.printStackTrace();
}
}
PageBean pageBean = new PageBean();
pageBean.setTotal(total);
pageBean.setRows(tList);
return pageBean;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Redis zset实现热搜排行
Zset
Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。适用于排行榜功能。
处理放入
java
@Override
public void hotStatics(PageBean<List<SolrBean>> pageBean, String content) {
// 判断成功搜索出词汇的词
if (pageBean != null && pageBean.getTotal() > 0 && Is.isNoEmpty(content)) {
// 成功搜索出词汇
content = Is.trim(content);
// 太长的词汇不加入热词搜索
if (content.length() <= 16) {
String key = SEARCH_RANK + TimeUtils.getTime(TimeUtils.FORMAT_TIME_YYYYMM);
Double score = redisTemplate.opsForZSet().score(key, content);
if (score != null) {
// 已经有这个词汇 + 1
redisTemplate.opsForZSet().incrementScore(key, content, 1);
} else {
// 判断有没这个keyset
boolean flag = redisTemplate.hasKey(key);
// 保存
redisTemplate.opsForZSet().add(key, content, 1);
if (!flag) {
// 这是第一次添加
redisTemplate.expire(key, 31, TimeUnit.DAYS);
log.info("添加词汇: {}", content);
} else {
log.info("热门词汇共有: {}, 剩余: {}秒", redisTemplate.opsForZSet().zCard(key), redisTemplate.getExpire(key, TimeUnit.SECONDS));
}
}
}
}
}
获取热搜排行
java
@Override
public ResultBean<List<String>> listSearchRecommend() {
String key = SEARCH_RANK + TimeUtils.getTime(TimeUtils.FORMAT_TIME_YYYYMM);
Set<String> set = redisTemplate.opsForZSet().reverseRange(key, 0, 10);
return ResultBean.success(new ArrayList<>(set));
}