服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|编程技术|正则表达式|C/C++|

服务器之家 - 编程语言 - JAVA教程 - 详解spring中使用solr的代码实现

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

2020-10-28 15:04周游列国之仕子 JAVA教程

本篇文章主要介绍了详解spring中使用solr的代码实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

我们需要引入solr的jar包

?
1
2
3
4
5
<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-solr</artifactId>
      <version>1.0.0.RELEASE</version>
    </dependency>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
<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包

?
1
2
3
4
5
<dependency>
  <groupId>org.apache.solr</groupId>
  <artifactId>solr-solrj</artifactId>
  <version>4.7.2</version>
</dependency>

其次:

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

?
1
2
3
4
5
<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之前做的一些初始化操作。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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的地方。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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在执行写入的时候,写入一条数据和写入多条数据都需要全量建索引,其执行时间是差不多的。

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

原文链接:http://blog.csdn.net/antao592/article/details/52807653

延伸 · 阅读

精彩推荐