详解spring中使用solr的代码实现

在介绍solr的使用方法之前,我们需要安装solr的服务端集群。基本上就是安装zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可。由于本人并没有具体操作过如何进行solr集群的搭建。所以关于如何搭建solr集群,读者可以去网上查看其它资料,有很多可以借鉴。这里只介绍搭建完solr集群之后,我们客户端是如何访问solr集群的。

之前介绍过,spring封装nosql和sql数据库的使用,都是通过xxxTemplate。solr也不例外。

我们需要引入solr的jar包

<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-solr</artifactId>
      <version>1.0.0.RELEASE</version>
    </dependency> 

然后引入solr在spring中封装的配置

<bean id="orderInfoSolrServer" class="com.xxxx.SolrCloudServerFactoryBean">
  <property name="zkHost" value="${solr.zkHost}"/>
  <property name="defaultCollection" value="orderInfo"/>
  <property name="zkClientTimeout" value="6000"/>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate" scope="singleton">
  <constructor-arg ref="orderInfoSolrServer" />
</bean> 

<bean id="solrService" class="com.xxxx.SolrServiceImpl">
  <property name="solrOperations" ref="solrTemplate" />
</bean> 

然后重写我们的SolrServiceImpl就可以了。

但是,本文我们不用spring中封装的xxxTemplate这种格式做讲解。个人在使用spring封装solr的方式的时候遇到了各种各样的问题,可能是能力太low架控不了吧。下面我们主要讲解下如何使用solr的原生api进行访问。

首先:

引入solr的原生代码api的jar包

<dependency>
  <groupId>org.apache.solr</groupId>
  <artifactId>solr-solrj</artifactId>
  <version>4.7.2</version>
</dependency> 

其次:

在spring的配置文件中配置我们solr的FactoryBean类,此类是作为我们编写自己业务service类的属性来操作solr。

<bean id="orderInfoSolrServer" class="com.xxxx.SolrCloudServerFactoryBean">
  <property name="zkHost" value="${solr.zkHost}"/>
  <property name="defaultCollection" value="orderInfo"/>
  <property name="zkClientTimeout" value="6000"/>
</bean> 

solr.zkHost是我们配置的zookeeper集群

orderInfo是我们存储在solr中的数据结构bean

再次:

编写我们的SolrCloudServerFactoryBean类,其中使用了spring的FactoryBean<SolrServer>,和InitializingBean。关于这两者的含义读者可以参考其他资料,基本意思是spring容器在注册该bean之前,需要进行的一些初始化操作。通过afterPropertiesSet方法可以看到我们在使用solr之前做的一些初始化操作。

package com.jd.fms.prism.solr.service; 

import org.apache.http.client.HttpClient; 

/**
 * solrj spring integration
 *
 * @author bjchenrui
 */
public class SolrCloudServerFactoryBean implements FactoryBean<SolrServer>, InitializingBean { 

  private CloudSolrServer cloudSolrServer; 

  private String zkHost; 

  private String defaultCollection; 

  private int maxConnections = 1000; 

  private int maxConnectionsPerHost = 500; 

  private int zkClientTimeout = 10000; 

  private int zkConnectTimeout = 10000; 

  private Lock lock = new ReentrantLock(); 

  public SolrServer getObject() throws Exception {
    return cloudSolrServer;
  } 

  public Class<SolrServer> getObjectType() {
    return SolrServer.class;
  } 

  public boolean isSingleton() {
    return true;
  } 

  public void afterPropertiesSet() throws Exception {
    ModifiableSolrParams params = new ModifiableSolrParams();
    params.set(HttpClientUtil.PROP_MAX_CONNECTIONS, maxConnections);
    params.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, maxConnectionsPerHost);
    HttpClient client = HttpClientUtil.createClient(params);
    LBHttpSolrServer lbServer = new LBHttpSolrServer(client);
    lock.lock();
    try {
      if(cloudSolrServer == null) {
        cloudSolrServer = new CloudSolrServer(zkHost, lbServer);
      }
    } finally {
      lock.unlock();
    } 

    cloudSolrServer.setDefaultCollection(defaultCollection);
    cloudSolrServer.setZkClientTimeout(zkClientTimeout);
    cloudSolrServer.setZkConnectTimeout(zkConnectTimeout);
  } 

  public void setCloudSolrServer(CloudSolrServer cloudSolrServer) {
    this.cloudSolrServer = cloudSolrServer;
  } 

  public void setZkHost(String zkHost) {
    this.zkHost = zkHost;
  } 

  public void setDefaultCollection(String defaultCollection) {
    this.defaultCollection = defaultCollection;
  } 

  public void setMaxConnections(int maxConnections) {
    this.maxConnections = maxConnections;
  } 

  public void setMaxConnectionsPerHost(int maxConnectionsPerHost) {
    this.maxConnectionsPerHost = maxConnectionsPerHost;
  } 

  public void setZkClientTimeout(int zkClientTimeout) {
    this.zkClientTimeout = zkClientTimeout;
  } 

  public void setZkConnectTimeout(int zkConnectTimeout) {
    this.zkConnectTimeout = zkConnectTimeout;
  } 

}

最后:

现在就可以编写我们的service类了,这里就是我们具体如何操作solr的地方。

package com.jd.fms.prism.solr.service.impl; 

import com.jd.fms.prism.common.utils.DateUtil; 

@Service("orderInfoSolrService")
public class OrderInfoNativeSolrServiceImpl { 

  private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.FORMATER11);
  private static SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat(DateUtil.FORMATER4); 

  @Resource(name = "orderInfoSolrServer")
  private SolrServer solrServer; 

  /**
   * 创建索引
   *
   * @param orderInfo
   */
  public void creatIndex(OrderInfo orderInfo) throws IOException, SolrServerException {
    solrServer.addBean(orderInfo);
    solrServer.commit();
  }
  /**
   * 查询条件的生成。支持字段的精确查询,模糊查询,范围查询。
   * @param orderIdfilter
   * @param queryObj
   * @param queryTimeList
   * @param sorts
   * @return
   * @throws Exception
   */
  public SolrQuery iniFilter(String orderIdfilter,OrderInfo queryObj,List<QueryTime> queryTimeList, Sort... sorts) throws Exception {
    SolrQuery sQuery = new SolrQuery();
    String queryQ = "validTag:1";
    sQuery.setQuery(queryQ);
    StringBuilder filter = new StringBuilder();
    if(null != orderIdfilter){
      filter.append(orderIdfilter);
      queryObj.setOrderId(null);
    }
    //添加过滤条件
    Field[] fields = queryObj.getClass().getDeclaredFields(); 

    String fieldName = "";
    String fieldValue = "";
    for (Field field:fields){
      if(field.isAnnotationPresent(org.apache.solr.client.solrj.beans.Field.class)){
        field.setAccessible(true);
        fieldName = field.getName();
        fieldValue = String.valueOf(field.get(queryObj));
        if (null != fieldValue && !"null".equals(fieldValue) && !"".equals(fieldValue) && !"0.0".equals(fieldValue)){
          //如果是会员类型,则添加模糊查询
          if(fieldName.equals("memberId") || fieldName.equals("orderType")){
            fieldValue = "*" + fieldValue + "*";
          }
          filter.append(fieldName + ":" + fieldValue).append(" AND ");
        }
      }
    }
    if(queryTimeList!=null && queryTimeList.size() > 0) {
      Iterator<QueryTime> iterator = queryTimeList.iterator();
      while(iterator.hasNext()) {
        QueryTime queryTime = iterator.next();
        String beginDate = simpleDateFormat.format(queryTime.getBeginTime().getTime());
        String endDate = simpleDateFormat.format(queryTime.getEndTime().getTime());
        filter.append(queryTime.getFieldName() + ":" + "[" + beginDate + " TO " + endDate + "] AND ");
      }
    }
    if(filter.length()>0){
      filter.delete(filter.length()-5, filter.length());
    }
    sQuery.addFilterQuery(filter.toString());
    if(sQuery.toString().equals("")){
      sQuery.setQuery("*:*");
    }
    return sQuery;
  }
  /**
   * 查询代码,可以看到我们可以在solr中做聚合,做排序。而且整个过程都是秒级的。
   * @param map
   * @param queryObj
   * @param queryTimeList
   * @param page
   * @param sorts
   * @return
   * @throws Exception
   */
  public Page<OrderInfo> query(Map map,OrderInfo queryObj, List<QueryTime> queryTimeList, Pageable page, Sort... sorts) throws Exception {
    SolrQuery sQuery = iniFilter(null,queryObj,queryTimeList); 

    //添加分页
    if(page != null){
      sQuery.setStart(page.getPageNumber()*page.getPageSize());
      sQuery.setRows(page.getPageSize());
    }
    //添加排序
    /*if (null != sorts){
      sQuery.setSort("orderId",SolrQuery.ORDER.asc);
    }*/
    QueryResponse response = null;
    sQuery.setGetFieldStatistics("orderPrice");
    sQuery.setGetFieldStatistics("duePrice");
    sQuery.setGetFieldStatistics("diffPrice");
    try {
      response = solrServer.query(sQuery);
    } catch (SolrServerException e) {
      e.printStackTrace();
    }
    SolrDocumentList list = response.getResults();
    Map<String, FieldStatsInfo> mapSum = response.getFieldStatsInfo();
    String orderPriceSum = null;
    if(mapSum.get("orderPrice") != null && !mapSum.get("orderPrice").toString().equals("") ){
      orderPriceSum = mapSum.get("orderPrice").getSum().toString();
    }
    String duePriceSum = null;
    if(mapSum.get("duePrice") != null && !mapSum.get("duePrice").toString().equals("") ){
      duePriceSum = mapSum.get("duePrice").getSum().toString();
    }
    String diffPriceSum = null;
    if(mapSum.get("diffPrice") != null && !mapSum.get("diffPrice").toString().equals("") ){
      diffPriceSum = mapSum.get("diffPrice").getSum().toString();
    }
    List<OrderInfo> list1 = new ArrayList<OrderInfo>();
    DocumentObjectBinder binder = new DocumentObjectBinder();
    Iterator iterator = list.iterator();
    while(iterator.hasNext()){
      OrderInfo orderInfo = binder.getBean(OrderInfo.class, (SolrDocument) iterator.next());
      list1.add(orderInfo);
    }
    map.put("orderPriceSum", orderPriceSum);
    map.put("duePriceSum", duePriceSum);
    map.put("diffPriceSum", diffPriceSum);
    Page<OrderInfo> pageList = new PageImpl<OrderInfo>(list1,page,list.getNumFound());
    return pageList;
  }
  /**
   * 我们可以按照key值进行主键查询。
   * @param id
   * @return
   * @throws Exception
   */ 

  public List<OrderInfo> queryByOrderId(String id) throws Exception {
    SolrQuery sQuery = new SolrQuery();
    String filter = "orderId" + ":" + id;
    sQuery.setQuery(filter);
    QueryResponse response = null;
    try {
      response = solrServer.query(sQuery);
    } catch (SolrServerException e) {
      e.printStackTrace();
    }
    SolrDocumentList list = response.getResults();
    List<OrderInfo> list1 = new ArrayList<OrderInfo>();
    DocumentObjectBinder binder = new DocumentObjectBinder();
    Iterator iterator = list.iterator();
    while(iterator.hasNext()){
      OrderInfo orderInfo = binder.getBean(OrderInfo.class, (SolrDocument) iterator.next());
      list1.add(orderInfo);
    }
    return list1;
  } 

  public void deleteAll(OrderInfo orderInfo) throws IOException, SolrServerException {
    String sQuery = "*:*";
    solrServer.deleteByQuery(sQuery);
  } 

  public void deleteById(String id) {
  } 

  public void createIndexBatch(List<OrderInfo> orderInfoList) throws IOException, SolrServerException {
    solrServer.addBeans(orderInfoList);
    solrServer.commit(); 

  } 

  public void deleteBySolrQuery(String solrQuery) throws IOException, SolrServerException {
    solrServer.deleteByQuery(solrQuery);
    solrServer.commit();
  } 

  public SolrServer getSolrServer() {
    return solrServer;
  } 

  public void setSolrServer(SolrServer solrServer) {
    this.solrServer = solrServer;
  }
}

当然solr的api不止于此,我们此处只是罗列了一些比较常用的使用方法。对于solr的查询,有以下几点需要注意。

1.    solr生成查询语句的时候,是有q查询和fq查询之分的。哪些查询条件放在q查询里,哪些查询条件放在fq查询里,对查询的效率还是有较大的影响的。一般固定不变的查询条件放在q查询里,经常变化的查询条件放在fq里。上述代码中validTag:1就放在了q查询里,循环里的字符串filter则放在了我们的fq查询里。

2.    solr查询时,要了解solr服务器集群的配置文件中使用的是什么样的分词器,不同分词器对模糊查询的结果是有影响的。比如常见的IK分词器和标准分词器(如果我们有一个字段的名称为:我是中国人,ik分词器在solr里的存储就成为了“我”,“中国人”,“中国”,“国人”。而标准分词器则会存储为“我”,“是”,“中”,“国”,“人”。如果我们使用全称查询,即查询:我是中国人,两者是没有问题的。但是使用模糊查询,比如查询“*我是*”,则两个分词器分词都查不出来结果,而如果我们的查询条件是“*中国人*”则在ik分词器下可以查询出结果,在标准分词器下查不出结果。)

3.    使用solr的过程中,需要定时执行solr的optimize函数来清理磁盘碎片,否则会影响读写效率。对于optimize的参数建议为(false,false,5)。

4.    写solr数据的时候,尽量使用createIndexBatch方法,这是因为solr在执行写入的时候,写入一条数据和写入多条数据都需要全量建索引,其执行时间是差不多的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解java整合solr5.0之solrj的使用

    1.首先导入solrj需要的的架包 2.需要注意的是低版本是solr是使用SolrServer进行URL实例的,5.0之后已经使用SolrClient替代这个类了,在添加之后首先我们需要根据schema.xml配置一下我们的分词器 这里的msg_all还需要在schema.xml中配置 它的主要作用是将msg_title,msg_content两个域的值拷贝到msg_all域中,我们在搜索的时候可以只搜索这个msg_all域就可以了, solr默认搜索需要带上域,比如 solr更改默认搜索域的地

  • java多线程处理执行solr创建索引示例

    复制代码 代码如下: public class SolrIndexer implements Indexer, Searcher, DisposableBean { //~ Static fields/initializers ============================================= static final Logger logger = LoggerFactory.getLogger(SolrIndexer.class); private static fi

  • Solr通过特殊字符分词实现自定义分词器详解

    前言 我们在对英文句子分词的时候,一般采用采用的分词器是WhiteSpaceTokenizerFactory,有一次因业务要求,需要根据某一个特殊字符(以逗号分词,以竖线分词)分词.感觉这种需求可能与WhiteSpaceTokenizerFactory相像,于是自己根据Solr源码自定义了分词策略. 业务场景 有一次,我拿到的数据都是以竖线"|"分隔,分词的时候,需要以竖线为分词单元.比如下面的这一堆数据: 有可能你拿到的是这样的数据,典型的例子就是来自csv文件的数据,格式和下面这种

  • solr在java中的使用实例代码

    SolrJ是操作Solr的Java客户端,它提供了增加.修改.删除.查询Solr索引的JAVA接口.SolrJ针对 Solr提供了Rest 的HTTP接口进行了封装, SolrJ底层是通过使用httpClient中的方法来完成Solr的操作. jar包的引用(maven pom.xml): <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId

  • 详解spring中使用solr的代码实现

    在介绍solr的使用方法之前,我们需要安装solr的服务端集群.基本上就是安装zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可.由于本人并没有具体操作过如何进行solr集群的搭建.所以关于如何搭建solr集群,读者可以去网上查看其它资料,有很多可以借鉴.这里只介绍搭建完solr集群之后,我们客户端是如何访问solr集群的. 之前介绍过,spring封装nosql和sql数据库的使用,都是通过xxxTemplate.solr也不例外. 我们需要引入solr的j

  • 详解spring中使用Elasticsearch的代码实现

    在使用Elasticsearch之前,先给大家聊一点干货. 1.      ES和solr都是作为全文搜索引擎出现的.都是基于Lucene的搜索服务器. 2.   ES不是可靠的存储系统,不是数据库,它有丢数据的风险. 3.  ES不是实时系统,数据写入成功只是trans log成功(类似于MySQL的bin log),写入成功后立刻查询查不到是正常的.因为数据此刻可能还在内存里而不是进入存储引擎里.同理,删除一条数据后也不是马上消失.写入何时可查询?ES内部有一个后台线程,定时将内存中的一批数

  • 详解Spring 中 Bean 的生命周期

    前言 这其实是一道面试题,是我在面试百度的时候被问到的,当时没有答出来(因为自己真的很菜),后来在网上寻找答案,看到也是一头雾水,直到看到了<Spring in action>这本书,书上有对Bean声明周期的大致解释,但是没有代码分析,所以就自己上网寻找资料,一定要把这个Bean生命周期弄明白! ​ 网上大部分都是验证的Bean 在面试问的生命周期,其实查阅JDK还有一个完整的Bean生命周期,这同时也验证了书是具有片面性的,最fresh 的资料还是查阅原始JDK!!! 一.Bean 的完整

  • 详解Spring中的Transactional属性

    一.Transactional 声明式事务管理建立在AOP之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 简而言之,@Transactional注解在代码执行出错的时候能够进行事务的回滚. 二.使用说明 在启动类上添加@EnableTransactionManagement注解. 用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义. 在项目中

  • 一篇文章从无到有详解Spring中的AOP

    前言 AOP (Aspect Orient Programming),直译过来就是 面向切面编程.AOP 是一种编程思想,是面向对象编程(OOP)的一种补充.面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面. 从<Spring实战(第4版)>图书中扒了一张图: 从该图可以很形象地看出,所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块. <?xml version="1.0" encoding="UTF-8&qu

  • 详解Spring中Bean的作用域与生命周期

    目录 一.Bean的作用域 二.Bean的生命周期 使用代码演示Bean的生命周期 一.Bean的作用域 通过Spring容器创建一个Bean的实例时,不仅可以完成Bean的实例化,还可以使用Bean的scope属性为bean设置作用域. 语法格式:<bean id="别名" scope="作用域" class="对应实现类"> 作用域的种类:(sing) singleton和prototype区别:(该两种比较常用) ① singl

  • 详解Spring中的Environment外部化配置管理

    目录 profiles ProfileService 声明一个配置类 定义测试方法 profiles总结 Properties environment的应用 指定profile属性 @Value注解的使用 SpringEnvironment原理设计 Environment的中文意思是环境,它表示整个spring应用运行时的环境信息,它包含两个关键因素 profiles properties profiles profiles这个概念相信大家都已经理解了,最常见的就是不同环境下,决定当前sprin

  • 详解Spring中Bean后置处理器(BeanPostProcessor)的使用

    目录 一.BeanPostProcessor接口 二.案例 三.总结 一.BeanPostProcessor接口 Bean后置处理:对Spring 工厂创建的对象进行二次加工处理,即预初始化和后初始化. PostProcessor中文意思就是后置处理器. BeanPostProcessor 接口也被称为Bean后置处理器,通过该接口可以自定义调用初始化前后执行的操作方法. 该接口中包含了两个方法:before方法(预初始化)和after方法(后厨是化) postProcessBeforeInit

  • 详解Spring中BeanUtils工具类的使用

    目录 简介 Spring的BeanUtils方法 Spring的BeanUtils与Apache的BeanUtils区别 实例 简介 说明 本文介绍Spring的BeanUtils工具类的用法. 我们经常需要将不同的两个对象实例进行属性复制,比如将DO对象进行属性复制到DTO,这种转换最原始的方式就是手动编写大量的 get/set代码,很繁琐.为了解决这一痛点,就诞生了一些方便的类库,常用的有 Apache的 BeanUtils,Spring的 BeanUtils, Dozer,Orika等拷贝

  • 详解Spring中@Valid和@Validated注解用法

    目录 案例引入 @Valid 详解 @Validated 详解 @Valid 和 @Validated 比较 案例引入 下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 和 @Validated 注解用法详解. 那么,首先,我们会有一个员工对象 Employee,如下 : /** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee { /** 姓名 */ public St

随机推荐