Skip to content

使用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));
}

粤ICP备20009776号