终于到了Solr学习的最后一篇,也是我们最主要的一篇,前面所有的准备都是为了在项目中能使用Solr进行全文检索,接下来我就来介绍怎么在SSM项目中整合Solr。本文皆有博主测试成功,按照步骤来不会出问题。

项目里整合Solr用到的是这个HttpSolrServer,通过它连接Solr服务器,向Solr端发送我们的请求来获取数据。

Solr端

我们先看在solr管理端全文检索的效果

我的数据库中有这些数据

我现在输入富士苹果

可以看到我输入富士苹果后,name或者type里包含苹果的三条都被搜出来了,因为keyword被我添加了分词,所以当我输入富士苹果的时候,分词器会先把富士苹果分词,分为富士苹果两个词,凡是keyword中包含这两个词的都会被查出来,当然富士苹果这条数据一定是被排在最上面的,这涉及到Solr权重算法,这里不探究。

这其实就是全文检索最基本的概念,有人该问了,keyword是什么?不知道你们还记不记得我在上一篇 Solr学习(二)同步MongoDB数据 讲过的copyField字段,keyword就是这样一个字段,将name和type的值复制到keyword中,这样我们搜索的时候用keyword来搜,就可以同时搜索出name和type里包含值的数据了。

Solr管理端全文检索就是这样,接下来我们开始整合到项目中。

keyword

我们先在Solr里配置关键词字段,打开 D:\solr-8.2.0\server\solr\knowledgebase\conf目录下的managed-schema文件

我在这里单独定义了一个namekeywordfield,当然你可以起任何名,然后将nametype的值复制到keyword中,注意这里keyword和复制字段的type是我们第一篇文章中配置的分词器,text_ik类型。

后端

创建两个文件

  • spring-solr-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <!--定义solr的server-->
    <context:property-placeholder location="classpath:solr.properties" ignore-unresolvable="true"/>
    <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
        <constructor-arg index="0" value="${solr.Url}"/>
        <!-- 设置响应解析器 -->
        <property name="parser">
            <bean class="org.apache.solr.client.solrj.impl.XMLResponseParser"/>
        </property>
        <!-- 设置重试次数-->
        <property name="maxRetries" value="${solr.maxRetries}"/>
        <!-- 建立连接的最长时间 -->
        <property name="connectionTimeout" value="${solr.connectionTimeout}"/>
    </bean>
</beans>
  • solr.properties
solr.Url=http://192.168.1.162:8983/solr/knowledgebase 注意修改ip和corename
solr.maxRetries=2
solr.connectionTimeout=5000

再创建Solr接口和他的实现类

  • IBaseSolr
package com.seed.solr.base;

import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.common.SolrDocumentList;

import com.seed.pojo.SolrParam;

public interface IBaseSolr {

    /**
     * 普通检索
     */
   SolrDocumentList solrQuery(SolrParam solrParam) throws SolrServerException;

    /**
     * 关键词高亮检索
     */
   SolrDocumentList solrHighlightQuery(SolrParam solrParam) throws SolrServerException;

}
  • BaseSolrImpl
package com.seed.solr.base;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.seed.common.utils.StringUtil;
import com.seed.pojo.SolrParam;

@Repository
public class BaseSolrImpl implements IBaseSolr{

   @Autowired
    HttpSolrServer httpSolrServer;

   @Override
   public SolrDocumentList solrQuery(SolrParam solrParam) throws SolrServerException {

        SolrQuery solrQuery=new SolrQuery();
        String param = "*:*";
        if(StringUtil.isNotNullOrEmpty(solrParam.getQ())){
            param = solrParam.getQ();
        }
        solrQuery.setQuery(param);
        if(StringUtil.isNotNullOrEmpty(solrParam.getFl())){
           solrQuery.addField(solrParam.getFl());
        }
        Map<String, String> fqMap = solrParam.getFq();
        if(fqMap != null && !fqMap.isEmpty()){
           List<String> fqParam = new ArrayList<>();
           Set<String> fqSet = fqMap.keySet();
           for(String key : fqSet){
              fqParam.add(key+":" + fqMap.get(key));
           }
           String fq = StringUtil.listToString(fqParam," AND ");
           solrQuery.setFilterQueries(fq);
        }

        if(solrParam.getStart() != null){
           solrQuery.setStart(solrParam.getStart());
        }
        if(solrParam.getRows() != null){
           solrQuery.setRows(solrParam.getRows());
        }

        QueryResponse queryResponse = httpSolrServer.query(solrQuery);
        SolrDocumentList results = queryResponse.getResults();
        return results;
    }

   @Override
   public SolrDocumentList solrHighlightQuery(SolrParam solrParam) throws SolrServerException {

      SolrQuery solrQuery=new SolrQuery();
      String param = "*:*";
      if(StringUtil.isNotNullOrEmpty(solrParam.getQ())){
         param = solrParam.getQ();
      }
      solrQuery.setQuery(param);
        if(StringUtil.isNotNullOrEmpty(solrParam.getFl())){
           solrQuery.addField(solrParam.getFl());
        }
        solrQuery.setHighlight(true);
        String hlfl = solrParam.getHlfl();
        String[] hl = null;
        if(StringUtil.isNotNullOrEmpty(hlfl)){
           solrQuery.addHighlightField(hlfl);
           hl = hlfl.split(",");
        }
        Map<String,String> fqMap = solrParam.getFq();
        if(fqMap != null && !fqMap.isEmpty()){
           List<String> fqParam = new ArrayList<>();
           Set<String> fqSet = fqMap.keySet();
           for(String key : fqSet){
              fqParam.add(key+":" + fqMap.get(key));
           }
           String fq = StringUtil.listToString(fqParam," AND ");
           solrQuery.setFilterQueries(fq);
        }
      if(StringUtil.isNotNullOrEmpty(solrParam.getMm())){
         solrQuery.set("defType", "edismax");
         solrQuery.set("mm", solrParam.getMm());
      }
      if(StringUtil.isNotNullOrEmpty(solrParam.getDismax())){
         solrQuery.set("defType","dismax");
         solrQuery.set("qf", solrParam.getDismax());
      }
      solrQuery.setHighlightFragsize(100000); //设置高亮返回长度
        if(solrParam.getStart() != null){
           solrQuery.setStart(solrParam.getStart());
        }
        if(solrParam.getRows() != null){
           solrQuery.setRows(solrParam.getRows());
        }

        QueryResponse queryResponse = httpSolrServer.query(solrQuery);
        SolrDocumentList results = queryResponse.getResults();
        Long count = (Long) results.getNumFound();
        SolrDocumentList highlightResults = new SolrDocumentList();

        Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();

        SolrDocument document = new SolrDocument();

        for (int i = 0; i < results.size(); i++) {
         document = results.get(i); 
         for(String hlKey : hl) {

            String _id = (String) document.getFieldValue("_id");

            if (highlighting.get(_id) != null) {
               if(highlighting.get(_id).get(hlKey) != null) {
                  document.setField(hlKey, highlighting.get(_id).get(hlKey).get(0));
               }
            }

         }
         highlightResults.add(document);
      }
        highlightResults.setNumFound(count);
        return highlightResults;
    }
}
  • SolrParam 自定义的参数类
/**
 * 关键词搜索
 */
private String q;

/**
 * 过滤查询
 */
private Map<String,String> fq;

/**
 * 排序
 */
private String sort;

/**
 * 分页
 */
private Integer start;

private Integer rows;

/**
 * 显示哪些返回字段
 */
private String fl;

/**
 * 高亮字段
 */
private String hlfl;

/**
 * 搜索匹配度
 */
private String mm;

/**
 * 字段权重
 */
private String dismax;

两个方法分别是solrQuerysolrHighlightQuery,他俩查询上没有区别,solrHighlightQuery会把你输入的关键词前后拼上<em>标签 再返回,前台把<em>标签的字体颜色设为红色,就是我们看到类似百度查询的时候关键词高亮的样子。

我直接写一个最简单的高亮查询方法

public JSONObject synthesizeQuery(String keyword) {
      JSONObject returnData = null;
      SolrParam solrParam = new SolrParam();
      if (StringUtil.isNotNullOrEmpty(keyword)) {
         solrParam.setQ("keyword:" + keyword);
      }
      solrParam.setHlfl("name,type");
      SolrDocumentList result = baseSolr.solrHighlightQuery(solrParam);
      returnData.put("data", result);
   return returnData;
}

可以看到我们输入的关键词前后都套上了<em>标签,这样就能实现基本的高亮检索了。

当然我方法里用到的参数不全,API文档我放在这里SolrQuery,可以参照API文档增加查询条件。

结语

到这里Solr的学习就告一段落了,这三篇都是我项目中亲身实践过的,所以不用担心哪个地方用不了。因为当初用Solr的时候就感觉到了网上能借鉴的东西实在太少,前期的同步数据也费了很大的功夫,所以我一定要把我用成功的方法记录下来,造福你们,也给我自己做个记录,当然这不是最后一篇关于Solr的,后面我还会补充我项目中遇到的小问题,当然最近不打算写了,如果你们在使用Solr中遇到了什么问题,欢迎给我留言,我一定尽力帮你们解决。