工程搭建

1. 创建SpringBoot工程

以IntelliJ IDEA为例,创建“Spring Initializr”或“Maven”工程。
image.png
组件选择如下:

  • Developer Tools
    • Spring Boot DevTools
    • Lombok
  • Web
    • Spring Web
  • NoSQL
    • Spring Data for Apache Solr

image.png

2. 添加pom.xml依赖

如果创建的Maven工程,则可手动添加Solr集成相关依赖。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-solr</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-web</artifactId>
  8. </dependency>

Solr连接

1. 通过Solr URL连接

  • application.yml

    1. spring:
    2. application:
    3. name: solr-demo
    4. data:
    5. solr:
    6. host: http://bigdata-node1:8983/solr/db_sync
    7. server:
    8. port: 8081
  • application.properties

    1. spring.application.name=solr-springboot-example
    2. server.port=8081
    3. # solr Configuration
    4. spring.data.solr.host=http://bigdata-node1:8983/solr/db_sync

    2. 通过ZooKeeper连接

  • application.yml

    1. spring:
    2. application:
    3. name: solr-springboot-example
    4. data:
    5. solr:
    6. zk-host: 192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181
    7. server:
    8. port: 8081
  • application.properties

    1. spring.application.name=solr-springboot-example
    2. server.port=8081
    3. # solr Configuration
    4. spring.data.solr.zk-host=192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181

    案例

    01. 简单示例

    1. 分页查询

    ```java import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.io.IOException;

@SpringBootTest public class SolrSampleTests {

  1. @Autowired
  2. private SolrClient solrClient;
  3. /**
  4. * 测试:分页查找
  5. */
  6. @Test
  7. public void findPage() throws IOException, SolrServerException {
  8. /**
  9. * 集群版CloudSolrClient继承自SolrClient,需要进行强转
  10. * 单机版不需要,直接可以使用solrClient
  11. */
  12. CloudSolrClient cloudSolrClient = (CloudSolrClient) solrClient;
  13. // 设置默认的操作实例
  14. cloudSolrClient.setDefaultCollection("db_sync");
  15. // 设置查找的参数
  16. SolrQuery query = new SolrQuery();
  17. query.setQuery("title:中国");
  18. // 第?页
  19. query.setStart(0);
  20. // 每页记录数
  21. query.setRows(5);
  22. // 执行查找
  23. QueryResponse response = cloudSolrClient.query(query);
  24. SolrDocumentList documentList = response.getResults();
  25. for (SolrDocument entries : documentList) {
  26. String title = (String) entries.getFieldValue("title");
  27. String id = (String) entries.getFieldValue("id");
  28. System.out.println(id + "-->" + title);
  29. }
  30. }

}

  1. <a name="KHlW8"></a>
  2. ### 2. 高亮显示
  3. ```java
  4. import org.apache.solr.client.solrj.SolrClient;
  5. import org.apache.solr.client.solrj.SolrQuery;
  6. import org.apache.solr.client.solrj.SolrServerException;
  7. import org.apache.solr.client.solrj.impl.CloudSolrClient;
  8. import org.apache.solr.client.solrj.response.QueryResponse;
  9. import org.apache.solr.common.SolrDocument;
  10. import org.apache.solr.common.SolrDocumentList;
  11. import org.junit.jupiter.api.Test;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.boot.test.context.SpringBootTest;
  14. import java.io.IOException;
  15. @SpringBootTest
  16. public class SolrSampleTests {
  17. @Autowired
  18. private SolrClient solrClient;
  19. /**
  20. * 设置结果高亮显示
  21. *
  22. * @throws IOException
  23. * @throws SolrServerException
  24. */
  25. @Test
  26. public void highlight() throws IOException, SolrServerException {
  27. /**
  28. * 集群版CloudSolrClient继承自SolrClient,需要进行强转
  29. * 单机版不需要,直接可以使用solrClient
  30. */
  31. CloudSolrClient cloudSolrClient = (CloudSolrClient) solrClient;
  32. // 设置默认的操作实例
  33. cloudSolrClient.setDefaultCollection("db_sync");
  34. // 设置查找的参数
  35. SolrQuery query = new SolrQuery();
  36. query.setQuery("title:中国");
  37. // 开启高亮
  38. query.setHighlight(true);
  39. // 设置高亮字段
  40. query.addHighlightField("title");
  41. // 前缀
  42. query.setHighlightSimplePre("<font color='red'>");
  43. // 后缀
  44. query.setHighlightSimplePost("</font>");
  45. // 执行查找
  46. QueryResponse response = cloudSolrClient.query(query);
  47. // 打印高亮信息
  48. System.out.println(response.getHighlighting());
  49. }
  50. }

02. 综合示例

1. 添加pom.xml依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.3.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.lonton.bigdata</groupId>
  12. <artifactId>solr-springboot-example</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>solr-springboot-example</name>
  15. <description>Solr SpringBoot集成</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-data-solr</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-web</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-devtools</artifactId>
  31. <scope>runtime</scope>
  32. <optional>true</optional>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.projectlombok</groupId>
  36. <artifactId>lombok</artifactId>
  37. <optional>true</optional>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-test</artifactId>
  42. <scope>test</scope>
  43. <!-- 这个是JUnit5中为了支持使用JUint4所做的一个过度
  44. 也就是说,你只需要在你的JUnit4旧项目中添加这个依赖,
  45. 就能完美过渡,而不用修改之前代码
  46. 这里用不到,自然也就排除了。
  47. -->
  48. <exclusions>
  49. <exclusion>
  50. <groupId>org.junit.vintage</groupId>
  51. <artifactId>junit-vintage-engine</artifactId>
  52. </exclusion>
  53. </exclusions>
  54. </dependency>
  55. <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
  56. <dependency>
  57. <groupId>commons-beanutils</groupId>
  58. <artifactId>commons-beanutils</artifactId>
  59. <version>1.9.3</version>
  60. </dependency>
  61. <dependency>
  62. <groupId>com.alibaba</groupId>
  63. <artifactId>fastjson</artifactId>
  64. <version>1.2.61</version>
  65. </dependency>
  66. <dependency>
  67. <groupId>org.assertj</groupId>
  68. <artifactId>assertj-core</artifactId>
  69. <version>3.11.1</version>
  70. <scope>compile</scope>
  71. </dependency>
  72. <dependency>
  73. <groupId>com.google.guava</groupId>
  74. <artifactId>guava</artifactId>
  75. <version>25.0-jre</version>
  76. <scope>compile</scope>
  77. </dependency>
  78. </dependencies>
  79. <build>
  80. <plugins>
  81. <plugin>
  82. <groupId>org.springframework.boot</groupId>
  83. <artifactId>spring-boot-maven-plugin</artifactId>
  84. </plugin>
  85. <!-- 将生成的jar复制到指定路径 -->
  86. <plugin>
  87. <artifactId>maven-antrun-plugin</artifactId>
  88. <executions>
  89. <execution>
  90. <!-- 在maven进行package的时候执行-->
  91. <phase>package</phase>
  92. <configuration>
  93. <tasks>
  94. <!-- jar包保存位置 -->
  95. <copy todir="E:\vagrant\mybox\bigdata_example\share">
  96. <!-- antrun自动生成的配置文件的保存位置,这里默认是父项目的target文件夹 -->
  97. <fileset dir="${project.build.directory}">
  98. <include name="*.jar"/>
  99. </fileset>
  100. </copy>
  101. </tasks>
  102. </configuration>
  103. <goals>
  104. <goal>run</goal>
  105. </goals>
  106. </execution>
  107. </executions>
  108. </plugin>
  109. </plugins>
  110. <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
  111. <plugins>
  112. <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
  113. <plugin>
  114. <artifactId>maven-clean-plugin</artifactId>
  115. <version>3.1.0</version>
  116. </plugin>
  117. <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
  118. <plugin>
  119. <artifactId>maven-resources-plugin</artifactId>
  120. <version>3.0.2</version>
  121. </plugin>
  122. <plugin>
  123. <groupId>org.apache.maven.plugins</groupId>
  124. <artifactId>maven-compiler-plugin</artifactId>
  125. <version>3.8.0</version>
  126. <configuration>
  127. <source>1.8</source>
  128. <target>1.8</target>
  129. <encoding>UTF-8</encoding>
  130. </configuration>
  131. </plugin>
  132. <plugin>
  133. <artifactId>maven-surefire-plugin</artifactId>
  134. <version>2.22.1</version>
  135. </plugin>
  136. <plugin>
  137. <artifactId>maven-jar-plugin</artifactId>
  138. <version>3.0.2</version>
  139. </plugin>
  140. <plugin>
  141. <artifactId>maven-install-plugin</artifactId>
  142. <version>2.5.2</version>
  143. </plugin>
  144. <plugin>
  145. <artifactId>maven-deploy-plugin</artifactId>
  146. <version>2.8.2</version>
  147. </plugin>
  148. <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
  149. <plugin>
  150. <artifactId>maven-site-plugin</artifactId>
  151. <version>3.7.1</version>
  152. </plugin>
  153. <plugin>
  154. <artifactId>maven-project-info-reports-plugin</artifactId>
  155. <version>3.0.0</version>
  156. </plugin>
  157. </plugins>
  158. </pluginManagement>
  159. </build>
  160. </project>

2. 工程配置

■ 配置(application.properties)

  1. spring.application.name=solr-springboot-example
  2. server.port=8081
  3. # solr Configuration
  4. spring.data.solr.zk-host=192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181

3. 编写关键类

■ SolrClient配置(以下方法二选其一)

  1. 启动加载类(SolrClientRunner.java) ```java import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component;

/**

  • Description 启动加载类(配置CloudSolrClient) */ @Component public class SolrClientRunner implements ApplicationRunner {

    @Autowired private SolrClient client;

    @Override public void run(ApplicationArguments args) throws Exception {

    1. /**
    2. * 集群版CloudSolrClient继承自SolrClient,需要进行强转
    3. * 单机版不需要,直接可以使用solrClient
    4. */
    5. // SolrClient client=SpringUtil.getBean(SolrClient.class);
    6. CloudSolrClient cloudSolrClient = (CloudSolrClient) client;
    7. // 设置默认的操作实例
    8. cloudSolrClient.setDefaultCollection("db_sync");

    }

}

  1. 2. **配置类(SolrClientConfig.java)**
  2. ```java
  3. import org.apache.solr.client.solrj.SolrClient;
  4. import org.apache.solr.client.solrj.impl.CloudSolrClient;
  5. import org.apache.solr.common.StringUtils;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import java.util.Arrays;
  10. import java.util.Optional;
  11. /**
  12. * Description 自定义配置类(SolrClinet)
  13. */
  14. @Configuration
  15. public class SolrClientConfig {
  16. @Value("${spring.data.solr.zk-host}")
  17. private String zk_host;
  18. /**
  19. * 集群版CloudSolrClient继承自SolrClient,需要进行强转
  20. * 单机版不需要,直接可以使用solrClient
  21. */
  22. @Bean(name = "solrClient")
  23. public SolrClient client() {
  24. if (StringUtils.isEmpty(zk_host)) {
  25. return null;
  26. }
  27. CloudSolrClient client = new CloudSolrClient.Builder(
  28. Arrays.asList(zk_host.split(",")),
  29. Optional.empty()
  30. ).build();
  31. // 设置默认的操作实例
  32. client.setDefaultCollection("db_sync");
  33. client.setZkClientTimeout(5000);
  34. client.setZkConnectTimeout(5000);
  35. return client;
  36. }
  37. }


■ 服务接口(SolrService.java)

  1. import org.apache.solr.client.solrj.SolrServerException;
  2. import org.apache.solr.common.SolrDocument;
  3. import org.apache.solr.common.SolrDocumentList;
  4. import java.io.IOException;
  5. import java.util.List;
  6. import java.util.Map;
  7. public interface SolrService {
  8. /**
  9. * 将Map对象添加(新增或修改)到Solr中
  10. *
  11. * @param map
  12. * @return
  13. */
  14. boolean saveOrUpdate(Map map);
  15. /**
  16. * 通过文档ID删除Solr中的文档
  17. *
  18. * @param id 索引ID
  19. * @return
  20. */
  21. boolean deleteById(String id);
  22. /**
  23. * 通过查询删除Solr中对应的数据集合
  24. *
  25. * @param query 查询条件
  26. */
  27. boolean deleteByQuery(String query);
  28. /**
  29. * 更新Solr中的文档,Map对象中必须存在id用于定位doc文档
  30. *
  31. * @param map Key<String>代表数据域名称,Value<Object>代表修改值
  32. * @return
  33. */
  34. boolean update(Map<String, Object> map);
  35. /**
  36. * 批量新增或更新记录
  37. *
  38. * @param entities 新增或更新对象列表
  39. * @param <T> 泛型
  40. * @return
  41. * @throws SolrServerException
  42. * @throws IOException
  43. */
  44. <T> boolean batchSaveOrUpdate(List<T> entities) throws SolrServerException, IOException;
  45. /**
  46. * 通过泛型获取Solr中的对象集合
  47. *
  48. * @param clz 泛型类对应java.lang.Class
  49. * @param query 数据域名称:数据域的值;查询全部*:*;多条件查询 name:Java AND age:20
  50. * @param filterQueries 过滤条件
  51. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  52. * @param page 第几页
  53. * @param rows 每页显示记录数
  54. * @return
  55. */
  56. <T> SolrResultInfo<T> query(Class<T> clz, String query,String filterQueries, List<String> hlFieldsList, Integer page, Integer rows);
  57. /**
  58. * 将SolrDocument转换成Bean
  59. *
  60. * @param record 索引对象
  61. * @param clz Class对象
  62. * @return
  63. */
  64. <T> T toBean(SolrDocument record, Class<T> clz);
  65. /**
  66. * 将SolrDocumentList转换成BeanList
  67. *
  68. * @param records 索引对象列表
  69. * @param clz Class对象
  70. * @return
  71. */
  72. <T> List<T> toBeanList(SolrDocumentList records, Class<T> clz);
  73. /**
  74. * 根据ID获取记录
  75. *
  76. * @param id 索引ID
  77. * @param clz Class对象
  78. * @param <T> 泛型
  79. * @return
  80. */
  81. <T> T getById(String id, Class<T> clz) throws IOException, SolrServerException;
  82. }

■ 服务实现类(SolrServiceImpl.java)

  1. import com.lonton.bigdata.service.SolrResultInfo;
  2. import com.lonton.bigdata.service.SolrService;
  3. import org.apache.commons.beanutils.BeanUtils;
  4. import org.apache.solr.client.solrj.SolrClient;
  5. import org.apache.solr.client.solrj.SolrQuery;
  6. import org.apache.solr.client.solrj.SolrServerException;
  7. import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
  8. import org.apache.solr.client.solrj.impl.CloudSolrClient;
  9. import org.apache.solr.client.solrj.response.QueryResponse;
  10. import org.apache.solr.client.solrj.response.UpdateResponse;
  11. import org.apache.solr.common.SolrDocument;
  12. import org.apache.solr.common.SolrDocumentList;
  13. import org.apache.solr.common.SolrInputDocument;
  14. import org.apache.solr.common.StringUtils;
  15. import org.slf4j.Logger;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import java.io.IOException;
  21. import java.lang.reflect.Field;
  22. import java.lang.reflect.InvocationTargetException;
  23. import java.sql.Time;
  24. import java.sql.Timestamp;
  25. import java.util.*;
  26. @Service
  27. public class SolrServiceImpl implements SolrService {
  28. private static final Logger log = LoggerFactory.getLogger(SolrServiceImpl.class);
  29. @Autowired
  30. private SolrClient client;
  31. /**
  32. * 将Map对象添加(新增或修改)到Solr中
  33. *
  34. * @param map Map对象
  35. * @return
  36. */
  37. @Override
  38. @Transactional(rollbackFor = Exception.class)
  39. public boolean saveOrUpdate(Map map) {
  40. Set kSet = map.keySet();
  41. SolrInputDocument doc = new SolrInputDocument();
  42. for (Object k : kSet) {
  43. Object val = map.get(k);
  44. if (val != null) {
  45. doc.addField(k.toString(), val);
  46. }
  47. }
  48. try {
  49. client.add(doc);
  50. UpdateResponse resp = client.commit();
  51. if (resp.getStatus() == 0) {
  52. log.debug("saveOrUpdate successfully!");
  53. return true;
  54. }
  55. } catch (Exception e) {
  56. log.error(e.getMessage());
  57. e.printStackTrace();
  58. }
  59. log.debug("saveOrUpdate failure!");
  60. return false;
  61. }
  62. /**
  63. * 通过文档ID删除Solr中的文档
  64. *
  65. * @param id 索引ID
  66. * @return
  67. */
  68. @Override
  69. @Transactional(rollbackFor = Exception.class)
  70. public boolean deleteById(String id) {
  71. try {
  72. client.deleteById(id);
  73. UpdateResponse resp = client.commit();
  74. if (resp.getStatus() == 0) {
  75. log.debug("delete successfully!");
  76. return true;
  77. }
  78. } catch (Exception e) {
  79. log.error(e.getMessage());
  80. e.printStackTrace();
  81. }
  82. log.debug("delete failure!");
  83. return false;
  84. }
  85. /**
  86. * 通过查询删除Solr中对应的数据集合
  87. *
  88. * @param query 查询条件
  89. */
  90. @Override
  91. @Transactional(rollbackFor = Exception.class)
  92. public boolean deleteByQuery(String query) {
  93. try {
  94. client.deleteByQuery(query);
  95. UpdateResponse resp = client.commit();
  96. if (resp.getStatus() == 0) {
  97. log.debug("delete successfully!");
  98. return true;
  99. }
  100. } catch (Exception e) {
  101. log.error(e.getMessage());
  102. e.printStackTrace();
  103. }
  104. log.debug("delete failure!");
  105. return false;
  106. }
  107. /**
  108. * 更新Solr中的文档,Map对象中必须存在id用于定位doc文档
  109. *
  110. * @param map Key<String>代表数据域名称,Value<Object>代表修改值
  111. * @return
  112. */
  113. @Override
  114. @Transactional(rollbackFor = Exception.class)
  115. public boolean update(Map<String, Object> map) {
  116. try {
  117. String id = (String) map.get("id");
  118. SolrInputDocument doc = new SolrInputDocument();
  119. doc.addField("id", id);
  120. for (String key : map.keySet()) {
  121. // 数据域Id忽略更新
  122. if (!"id".equals(key)) {
  123. Map fieldMap = new HashMap();
  124. fieldMap.put("set", map.get(key));
  125. doc.addField(key, fieldMap);
  126. }
  127. }
  128. client.add(doc);
  129. UpdateResponse resp = client.commit();
  130. if (resp.getStatus() == 0) {
  131. log.debug("update successfully!");
  132. return true;
  133. }
  134. } catch (Exception e) {
  135. log.error(e.getMessage());
  136. e.printStackTrace();
  137. }
  138. log.debug("update failure!");
  139. return false;
  140. }
  141. /**
  142. * 批量新增或更新记录
  143. *
  144. * @param entities 新增或更新对象列表
  145. * @param <T> 泛型
  146. * @return
  147. * @throws SolrServerException
  148. * @throws IOException
  149. */
  150. @Override
  151. @Transactional(rollbackFor = Exception.class)
  152. public <T> boolean batchSaveOrUpdate(List<T> entities) throws SolrServerException, IOException {
  153. DocumentObjectBinder binder = new DocumentObjectBinder();
  154. int total = entities.size();
  155. int count = 0;
  156. for (T t : entities) {
  157. SolrInputDocument doc = binder.toSolrInputDocument(t);
  158. client.add(doc);
  159. log.debug("添加数据到索引中,总共要添加 %d 条记录,当前添加第%d条 %n", total, ++count);
  160. }
  161. client.commit();
  162. log.debug("batch Save Or Update successfully!");
  163. return true;
  164. }
  165. /**
  166. * 通过泛型获取Solr中的对象集合
  167. *
  168. * @param clz 泛型类对应java.lang.Class
  169. * @param query 数据域名称:数据域的值;查询全部*:*;多条件查询 name:Java AND age:20
  170. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  171. * @param page 第几页
  172. * @param rows 每页显示记录数
  173. * @return
  174. */
  175. @Override
  176. public <T> SolrResultInfo<T> query(Class<T> clz, String query,String filterQueries, List<String> hlFieldsList, Integer page, Integer rows) {
  177. try {
  178. // 定义返回自定义数据结构对象
  179. SolrResultInfo<T> resultInfo = new SolrResultInfo<T>();
  180. SolrQuery q = new SolrQuery();
  181. // 查询条件
  182. q.set("q", query);
  183. // 过滤条件
  184. if(StringUtils.isEmpty(filterQueries)){
  185. q.set("fl", "*");
  186. }else{
  187. q.setFilterQueries(filterQueries);
  188. }
  189. // 高亮设置(开启)
  190. q.setHighlight(true);
  191. // 高亮显示字段
  192. String hlField = "";
  193. for (String s : hlFieldsList) {
  194. hlField = hlField + s + ",";
  195. }
  196. if (hlField.endsWith(",")) {
  197. hlField = hlField.substring(0, hlField.length() - 1);
  198. }
  199. q.set("hl.fl", hlField);
  200. // 高亮样式
  201. q.setHighlightSimplePre("<font color=\"red\">");
  202. q.setHighlightSimplePost("</font>");
  203. // 分页设置
  204. q.setStart((page - 1) * rows);
  205. q.setRows(rows);
  206. // 查询响应对象
  207. QueryResponse qr = client.query(q);
  208. Map<String, Map<String, List<String>>> hlMap = qr.getHighlighting();
  209. log.debug(hlMap.toString());
  210. // 高亮结果处理
  211. SolrDocumentList lst = qr.getResults();
  212. List<T> rtn = new ArrayList<T>();
  213. Long total = qr.getResults().getNumFound();
  214. for (SolrDocument doc : lst) {
  215. String id = (String) doc.getFieldValue("id");
  216. T t = clz.newInstance();
  217. // 获取自定义类所有属性名称
  218. Field[] fields = getField(clz);
  219. for (Field field : fields) {
  220. String fieldName = field.getName();
  221. String solrFldName = getSolrFieldName(clz, field);
  222. String fObj = getSingleValue(doc.getFieldValue(solrFldName));
  223. if (fObj == null) {
  224. continue;
  225. }
  226. if (field.getType() == java.sql.Date.class) {
  227. Date dt = new Date(fObj);
  228. fObj = new java.sql.Date(dt.getTime()).toString();
  229. }
  230. if (field.getType() == Timestamp.class) {
  231. Date dt = new Date(fObj);
  232. fObj = new Timestamp(dt.getTime()).toString();
  233. }
  234. if (field.getType() == Time.class) {
  235. Date dt = new Date(fObj);
  236. fObj = new Time(dt.getTime()).toString();
  237. }
  238. // 高亮显示数据形式:Map<ID, Map<FieldName,[MultiValue]>>
  239. if (hlFieldsList.contains(fieldName)) {
  240. //Map<FieldName,List<MultiValue>>
  241. Map<String, List<String>> fldMap = hlMap.get(id);
  242. Object hlObj = fldMap.get(fieldName);
  243. String hlVal = getSingleValue(hlObj);
  244. if (hlVal != null)
  245. fObj = hlVal;
  246. }
  247. if (fObj != null) {
  248. BeanUtils.setProperty(t, fieldName, fObj);
  249. }
  250. }
  251. rtn.add(t);
  252. }
  253. resultInfo.setList(rtn);
  254. resultInfo.setTotal(total);
  255. return resultInfo;
  256. } catch (Exception e) {
  257. log.error(e.getMessage());
  258. e.printStackTrace();
  259. }
  260. return null;
  261. }
  262. @Override
  263. public <T> T toBean(SolrDocument record, Class<T> clz) {
  264. T obj = null;
  265. try {
  266. obj = clz.newInstance();
  267. } catch (InstantiationException e1) {
  268. e1.printStackTrace();
  269. } catch (IllegalAccessException e1) {
  270. e1.printStackTrace();
  271. }
  272. Field[] fields = clz.getDeclaredFields();
  273. for(Field field:fields){
  274. Object value = record.get(field.getName());
  275. try {
  276. BeanUtils.setProperty(obj, field.getName(), value);
  277. } catch (IllegalAccessException e) {
  278. e.printStackTrace();
  279. } catch (InvocationTargetException e) {
  280. e.printStackTrace();
  281. }
  282. }
  283. return obj;
  284. }
  285. @Override
  286. public <T> List<T> toBeanList(SolrDocumentList records, Class<T> clz) {
  287. List<T> list = new ArrayList();
  288. for(SolrDocument record : records){
  289. list.add(toBean(record,clz));
  290. }
  291. return list;
  292. }
  293. @Override
  294. public <T> T getById(String id, Class<T> clz) throws IOException, SolrServerException {
  295. SolrDocument record = client.getById(id);
  296. return toBean(record,clz);
  297. }
  298. /**
  299. * 根据Class对象获取此类型的定义属性数据
  300. *
  301. * @param clz class对象
  302. * @return
  303. */
  304. private static Field[] getField(Class clz) {
  305. return clz.getDeclaredFields();
  306. }
  307. /**
  308. * 通过Field对象取得其上定义的注解名称
  309. *
  310. * @param clz
  311. * @param fld
  312. * @return
  313. */
  314. private static String getSolrFieldName(Class clz, Field fld) {
  315. org.apache.solr.client.solrj.beans.Field fld2 = fld.getAnnotation(org.apache.solr.client.solrj.beans.Field.class);
  316. if (fld2 == null) {
  317. return fld.getName();
  318. }
  319. if (fld2.value().equals("#default")) {
  320. return fld.getName();
  321. } else {
  322. return fld2.value();
  323. }
  324. }
  325. /**
  326. * 转化多值域为单值
  327. *
  328. * @param obj
  329. * @return
  330. */
  331. private static String getSingleValue(Object obj) {
  332. if (obj == null) {
  333. return null;
  334. }
  335. String val = obj.toString();
  336. if (val.startsWith("[") && val.endsWith("]")) {
  337. return val.substring(1, val.length() - 1);
  338. }
  339. return val;
  340. }
  341. }

■ 返回对象封装类(SolrResultInfo.java)

  1. import java.util.List;
  2. /**
  3. * Description 查询Solr返回的对象,对象类型为T的集合,还包含Solr中符合条件记录总数
  4. */
  5. public class SolrResultInfo<T> {
  6. private List<T> list = null;
  7. private Long total = null;
  8. public List<T> getList() {
  9. return list;
  10. }
  11. public void setList(List<T> list) {
  12. this.list = list;
  13. }
  14. public Long getTotal() {
  15. return total;
  16. }
  17. public void setTotal(Long total) {
  18. this.total = total;
  19. }
  20. }

**■ 实体类(Article.java

  1. import lombok.AllArgsConstructor;
  2. import lombok.Data;
  3. import lombok.NoArgsConstructor;
  4. import org.apache.solr.client.solrj.beans.Field;
  5. import org.springframework.data.solr.core.mapping.SolrDocument;
  6. import java.io.Serializable;
  7. /**
  8. * Description VO-文章类
  9. */
  10. @Data
  11. @AllArgsConstructor
  12. @NoArgsConstructor
  13. @SolrDocument(solrCoreName = "db_sync")
  14. public class Article implements Serializable {
  15. @Field("id")
  16. private Integer id;
  17. @Field("content_id")
  18. private Long contentId;
  19. @Field("title")
  20. private String title;
  21. @Field("content")
  22. private String content;
  23. @Field("type")
  24. private Integer type;
  25. @Field("create_at")
  26. private Long createAt;
  27. @Field("publish_at")
  28. private Long publishAt;
  29. @Override
  30. public String toString() {
  31. return "Article{" +
  32. "id=" + id +
  33. ", contentId=" + contentId +
  34. ", title='" + title + '\'' +
  35. ", content='" + content + '\'' +
  36. ", type=" + type +
  37. ", createAt=" + createAt +
  38. ", publishAt=" + publishAt +
  39. '}';
  40. }
  41. }

■ 工具类(CommonUtils.java)

  1. import com.google.common.collect.Lists;
  2. import com.google.common.collect.Maps;
  3. import org.apache.commons.beanutils.BeanUtils;
  4. import org.springframework.cglib.beans.BeanMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. /**
  8. * Description 工具类(Map与JavaBean互转)
  9. */
  10. public class CommonUtils {
  11. /**
  12. * 把一个Map转换成指定类型的JavaBean对象
  13. *
  14. * @param map Map对象
  15. * @param clazz Class对象
  16. * @param <T> 泛型
  17. * @return
  18. */
  19. public static <T> T mapToBean(Map<String, ?> map, Class<T> clazz) {
  20. try {
  21. T bean = clazz.newInstance();
  22. BeanUtils.populate(bean, map);
  23. return bean;
  24. } catch (Exception e) {
  25. throw new RuntimeException(e);
  26. }
  27. }
  28. /**
  29. * 将List<Map<String,Object>>转换为List<T>
  30. *
  31. * @param maps Map对象列表
  32. * @param clazz Class对象
  33. * @return
  34. * @throws InstantiationException
  35. * @throws IllegalAccessException
  36. */
  37. public static <T> List<T> mapsToBeans(List<Map<String, Object>> maps, Class<T> clazz)
  38. throws InstantiationException, IllegalAccessException {
  39. List<T> list = Lists.newArrayList();
  40. if (maps != null && maps.size() > 0) {
  41. Map<String, Object> map;
  42. T bean;
  43. for (int i = 0, size = maps.size(); i < size; i++) {
  44. map = maps.get(i);
  45. bean = clazz.newInstance();
  46. list.add((T) mapToBean(map, bean.getClass()));
  47. }
  48. }
  49. return list;
  50. }
  51. /**
  52. * 将对象装换为map
  53. *
  54. * @param bean
  55. * @return
  56. */
  57. public static <T> Map<String, Object> beanToMap(T bean) {
  58. Map<String, Object> map = Maps.newHashMap();
  59. if (bean != null) {
  60. BeanMap beanMap = BeanMap.create(bean);
  61. for (Object key : beanMap.keySet()) {
  62. map.put(key + "", beanMap.get(key));
  63. }
  64. }
  65. return map;
  66. }
  67. /**
  68. * 将List<T>转换为List<Map<String, Object>>
  69. *
  70. * @param beanList
  71. * @return
  72. */
  73. public static <T> List<Map<String, Object>> beansToMap(List<T> beanList) {
  74. List<Map<String, Object>> list = Lists.newArrayList();
  75. if (beanList != null && beanList.size() > 0) {
  76. Map<String, Object> map;
  77. T bean;
  78. for (int i = 0, size = beanList.size(); i < size; i++) {
  79. bean = beanList.get(i);
  80. map = beanToMap(bean);
  81. list.add(map);
  82. }
  83. }
  84. return list;
  85. }
  86. }

4. 测试

■ 测试类(SolrServiceTests.java)

  1. import com.lonton.bigdata.vo.Article;
  2. import org.apache.solr.client.solrj.SolrServerException;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. import java.io.IOException;
  7. import java.util.ArrayList;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. /**
  12. * Description 单元测试(Solr服务)
  13. */
  14. @SpringBootTest
  15. public class SolrServiceTests {
  16. @Autowired
  17. private SolrService service;
  18. @Test
  19. public void saveOrUpdate() {
  20. Map<String, Object> map = new HashMap<>();
  21. map.put("id", "1");
  22. map.put("content_id", 101L);
  23. map.put("title", "北京");
  24. map.put("content", "test作为一个中国人,我感到很自豪。");
  25. map.put("type", 1);
  26. map.put("create_at", 1578912614123L);
  27. map.put("publish_at", 1578912614125L);
  28. assert service.saveOrUpdate(map);
  29. }
  30. @Test
  31. public void deleteById() {
  32. saveOrUpdate();
  33. assert service.deleteById("1");
  34. }
  35. @Test
  36. public void deleteByQuery() {
  37. saveOrUpdate();
  38. assert service.deleteByQuery("id:1");
  39. }
  40. @Test
  41. public void deleteAll() {
  42. assert service.deleteByQuery("*:*");
  43. }
  44. @Test
  45. public void update() {
  46. saveOrUpdate();
  47. Map<String, Object> map = new HashMap<>();
  48. map.put("id", "1");
  49. map.put("content_id", 999L);
  50. map.put("title", "update-test");
  51. map.put("content", "update-test");
  52. map.put("type", 1);
  53. map.put("create_at", 1578912614199L);
  54. map.put("publish_at", 1578912614199L);
  55. assert service.update(map);
  56. }
  57. @Test
  58. public void batchSaveOrUpdate() throws IOException, SolrServerException {
  59. List<Article> entities = new ArrayList<>();
  60. for (int i = 1; i <= 35; i++) {
  61. entities.add(new Article(i, i * 10L, "我是中国人,打倒小日本" + i, "作为一个中国人,我感到很自豪。", 1, 1578912614123L, 1578912614123L));
  62. }
  63. assert service.batchSaveOrUpdate(entities);
  64. }
  65. @Test
  66. public void query() throws IOException, SolrServerException {
  67. batchSaveOrUpdate();
  68. List<String> hlFieldsList = new ArrayList<>();
  69. hlFieldsList.add("title");
  70. hlFieldsList.add("content");
  71. int page = 1;
  72. int rows = 5;
  73. String filterQueries = "content_id:[1 TO *]";
  74. SolrResultInfo<Article> resultInfo = service.query(Article.class, "keywords:中国", filterQueries, hlFieldsList, page, rows);
  75. Long total = resultInfo.getTotal();
  76. System.out.println("total:" + total);
  77. if (total > 0) {
  78. for (Article article : resultInfo.getList()) {
  79. System.out.println(article);
  80. }
  81. }
  82. }
  83. }

■ 控制类(ArticleController.java)

  1. import com.alibaba.fastjson.JSONArray;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.alibaba.fastjson.TypeReference;
  4. import com.lonton.bigdata.service.SolrResultInfo;
  5. import com.lonton.bigdata.service.SolrService;
  6. import com.lonton.bigdata.util.CommonUtils;
  7. import com.lonton.bigdata.vo.Article;
  8. import org.apache.solr.client.solrj.SolrServerException;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.web.bind.annotation.*;
  11. import java.io.IOException;
  12. import java.util.ArrayList;
  13. import java.util.List;
  14. import java.util.Map;
  15. @RestController
  16. @RequestMapping("/solrService")
  17. public class ArticleController {
  18. @Autowired
  19. private SolrService service;
  20. // http://localhost:8082/solrService/saveOrUpdate/record
  21. /**
  22. * 将Map对象添加(新增或修改)到Solr中<br/>
  23. * {
  24. * "id":"1",
  25. * "content_id":10,
  26. * "title":"我是中国人",
  27. * "content":"作为一个中国人,我感到很自豪。ArticleController",
  28. * "type":1,
  29. * "create_at":1578912614123,
  30. * "publish_at":1578912614123
  31. * }
  32. *
  33. * @param jsonParam 接收前端JSON格式的数据
  34. * @return
  35. */
  36. @ResponseBody
  37. @RequestMapping(value = "/saveOrUpdate/record", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
  38. public boolean saveOrUpdate(@RequestBody JSONObject jsonParam) {
  39. Map<String, String> map = JSONObject.parseObject(jsonParam.toJSONString(), new TypeReference<Map<String, String>>() {
  40. });
  41. System.out.println(map);
  42. return service.saveOrUpdate(map);
  43. }
  44. // http://localhost:8082/solrService/deleteById/1
  45. /**
  46. * 根据id删除
  47. *
  48. * @param id 索引ID
  49. */
  50. @RequestMapping(value = "/deleteById/{id}", method = RequestMethod.POST)
  51. public boolean deleteById(@PathVariable("id") String id) {
  52. return service.deleteById(id);
  53. }
  54. // http://localhost:8082/solrService/deleteByQuery/id/1
  55. // http://localhost:8082/solrService/deleteByQuery/*/*
  56. /**
  57. * 通过查询删除Solr中对应的数据集合
  58. *
  59. * @param key 字段
  60. * @param value 值
  61. * @return
  62. */
  63. @RequestMapping(value = "/deleteByQuery/{key}/{value}", method = RequestMethod.POST)
  64. public boolean deleteByQuery(@PathVariable("key") String key, @PathVariable("value") String value) {
  65. return service.deleteByQuery(String.format("%s:%s", key, value));
  66. }
  67. // http://localhost:8082/solrService/update/record
  68. /**
  69. * 更新Solr中的文档,Map对象中必须存在id用于定位doc文档<br/>
  70. * {
  71. * "id":"1",
  72. * "content_id":10,
  73. * "title":"我是中国人update",
  74. * "content":"作为一个中国人,我感到很自豪。update",
  75. * "type":1,
  76. * "create_at":1578912614123,
  77. * "publish_at":1578912614123
  78. * }
  79. *
  80. * @param jsonParam 接收前端JSON格式的数据
  81. * @return
  82. */
  83. @ResponseBody
  84. @RequestMapping(value = "/update/record", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
  85. public boolean update(@RequestBody JSONObject jsonParam) {
  86. Map<String, Object> map = JSONObject.parseObject(jsonParam.toJSONString(), new TypeReference<Map<String, Object>>() {
  87. });
  88. System.out.println(map);
  89. return service.update(map);
  90. }
  91. // http://localhost:8082/solrService/batchSaveOrUpdate/record
  92. /**
  93. * 批量新增或更新记录
  94. * [{
  95. * "id": "1",
  96. * "contentId": 101,
  97. * "title": "我是中国人add",
  98. * "content": "作为一个中国人,我感到很自豪。add",
  99. * "type": 1,
  100. * "createAt": 1578912614123,
  101. * "publishAt": 1578912614123
  102. * },
  103. * {
  104. * "id": "2",
  105. * "contentId": 102,
  106. * "title": "我是中国人add",
  107. * "content": "作为一个中国人,我感到很自豪。add",
  108. * "type": 1,
  109. * "createAt": 1578912614123,
  110. * "publishAt": 1578912614123
  111. * }]
  112. *
  113. * @param jsonParam 接收前端JSON格式的数据
  114. * @return
  115. * @throws IllegalAccessException
  116. * @throws InstantiationException
  117. * @throws IOException
  118. * @throws SolrServerException
  119. */
  120. @ResponseBody
  121. @RequestMapping(value = "/batchSaveOrUpdate/record", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
  122. public boolean batchSaveOrUpdate(@RequestBody JSONArray jsonParam) throws Exception {
  123. List<Map<String, Object>> maps = (List<Map<String, Object>>) JSONArray.parse(jsonParam.toJSONString());
  124. // 方法1
  125. /*List<Article> list =new ArrayList<>();
  126. for (Map<String, String> map : maps) {
  127. // 1. 工具类将map转为JavaBean
  128. list.add(CommonUtils.mapToBean(map, Article.class));
  129. // 2. 从Map中取值对JavaBean进行setter
  130. *//*Article bean=new Article();
  131. bean.setId(Integer.valueOf(map.get("id")));
  132. bean.setContentId(Long.parseLong(String.valueOf(map.get("content_id"))));
  133. bean.setTitle(map.get("title"));
  134. bean.setContent(map.get("content"));
  135. bean.setType(Integer.valueOf(String.valueOf(map.get("type"))));
  136. bean.setCreateAt(Long.valueOf(String.valueOf(map.get("create_at"))));
  137. bean.setPublishAt(Long.valueOf(String.valueOf(map.get("publish_at"))));
  138. list.add(bean);*//*
  139. }*/
  140. // 方法2
  141. List<Article> list = CommonUtils.mapsToBeans(maps, Article.class);
  142. System.out.println(CommonUtils.beansToMap(list));
  143. return service.batchSaveOrUpdate(list);
  144. }
  145. // http://localhost:8082/solrService/getById/1
  146. /**
  147. * 根据id查询内容
  148. *
  149. * @param id 索引ID
  150. * @return
  151. * @throws IOException
  152. * @throws SolrServerException
  153. */
  154. @RequestMapping(value = "/getById/{id}", method = RequestMethod.GET)
  155. public Article getById(@PathVariable String id) throws IOException, SolrServerException {
  156. Article article = service.getById(id, Article.class);
  157. System.out.println(article);
  158. return article;
  159. }
  160. // http://localhost:8082/solrService/query/中国/1/5
  161. /**
  162. * 综合查询(条件、高亮等)
  163. *
  164. * @param keywords 关键字
  165. * @param page 第?页
  166. * @param rows 每页显示所少条记录
  167. * @return
  168. */
  169. @RequestMapping(value = "/query/{keywords}/{page}/{rows}", method = RequestMethod.GET)
  170. public SolrResultInfo query(@PathVariable String keywords, @PathVariable int page, @PathVariable int rows) {
  171. List<String> hlFieldsList = new ArrayList<>();
  172. hlFieldsList.add("title");
  173. hlFieldsList.add("content");
  174. String filterQueries = "content_id:[102 TO *]";
  175. SolrResultInfo<Article> resultInfo = service.query(
  176. Article.class,
  177. String.format("keywords:%s", keywords),
  178. filterQueries,
  179. hlFieldsList,
  180. page,
  181. rows
  182. );
  183. Long total = resultInfo.getTotal();
  184. System.out.println("total:" + total);
  185. if (total > 0) {
  186. for (Article article : resultInfo.getList()) {
  187. System.out.println(article);
  188. }
  189. }
  190. return resultInfo;
  191. }
  192. }

5. 完整示例代码

源码:solr-springboot-example-src.zip

参考

简书:Spring Boot和Solr整合
https://www.jianshu.com/p/05a161add1a6
CSDN:Spring Boot整合Solr,手把手教你使用Solr
https://blog.csdn.net/weixin_42129558/article/details/82682265
博客园:Spring Boot整合Solr
https://www.cnblogs.com/wdfordream/p/11377161.html
CSDN:Spring Boot+Solr配置入门
https://blog.csdn.net/hello_word2/article/details/80757396