Loading... # 引言 续上一个博文,原生的实在是太麻烦了,这次我们用spring集成的工具类。这里我使用了SpringBoot。 # 正经一点的搜索 ## 分词器 我们这样搜索类似于写SQL语句,真正的在搜索栏中查询难道要用"*"或者是指定那个字段嘛?当然不是,为了更符合我们的习惯,我们要配置IK中文分词器和相关的域 IK下载地址:https://github.com/EugenePig/ik-analyzer-solr5 编译就不再赘述了,可以使用Maven打包进行生成jar文件,然后放到Solr的server/solr-webapp/webapp/WEB-INF/lib目录中。 接下来要把IK能力声明到我们的核心中,把下面的代码放到server/solr/mycore/conf/managed-schema中 ```xml <fieldType name="text_ik" class="solr.TextField"> <analyzer type="index" useSmart="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/> <analyzer type="query" useSmart="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType> ``` 然后重启我们的Solr ```bash Solr stop -all ``` 然后我们在自己定义的核心中选择Analysis,我们随便写一句话,让IK分词器为我们分词。 ![ik.png](https://www.zunmx.top/usr/uploads/2021/05/3166889118.png) ## 配置域 域大致分为三种:普通域、复制域、静态域 普通域:普通域就是我们在数据库中存放的字段,也就是能够显示在前端页面上的,但是不一定是作为查询依据的 复制域:作为搜索检索的字段 动态域:如果某个字段是不确定的参数,需要用动态域进行声明,好比我们生产很多产品,但是电视有尺寸,手机有网络制式,电视里的规格对于手机并不适用吧,所以像这样的规格就要用到动态域。 ## 实战 因为之前我们没有配置域,所以是默认生成的,我们需要自己手动创建,所以我们要删除原来的内容。在web中的Documents输入下面的代码,并且选择类型为XML ``` <delete><query>*:*</query></delete><commit/> ``` ### 设置域 因为我这里的实例时乌云镜像的数据库,所以我只对于相关的进行配置,大家做个参考就好。 找到自己定义的核心下面的managed-schema 路径\server\solr\mycore\conf\managed-schema > 这是标准域,也就是我需要反馈到前端页面上的数据字段。注意一下数据类型嗷 > > ```xml > <field name="wybug_title" type="string" indexed="true" stored="true" multiValued="false" docValues="false" /> > <field name="wybug_author" type="text_ik" indexed="true" stored="true" multiValued="false" docValues="false" /> > <field name="wybug_date" type="pdate" indexed="true" stored="true" multiValued="false" docValues="false" /> > <field name="wybug_type" type="string" indexed="false" stored="true" multiValued="false" docValues="false" /> > <field name="wybug_level" type="string" indexed="true" stored="true" multiValued="false" docValues="false" /> > <field name="wybug_detail" type="text_ik" indexed="true" stored="true" multiValued="false" docValues="false" /> > ``` > 这是复制域,就是我们提供搜索关键字的字段。 > > ```xml > <field name="keywords" type="text_general" indexed="true" stored="false" multiValued="true" /> > <copyField source="wybug_level" dest="keywords" /> > <copyField source="wybug_title" dest="keywords" /> > <copyField source="wybug_detail" dest="keywords" /> > ``` > 在这个Demo中没有用到动态域 ### 数据初始化 设置好后重启solr,并且执行原来的从数据库导入solr的代码// 由于时间关系,对于标准域中的字段,我没有完全添加,注意一下嗷。 ```java String solrUrl = "http://localhost:8983/solr/mycore"; HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl) .withConnectionTimeout(10000) // 连接超时时间 .withSocketTimeout(60000) // 通讯超时时间 .build(); // 构造 SolrInputDocument solrInputFields = new SolrInputDocument(); DruidDataSource source = new DruidDataSource(); source.setDriverClassName("com.mysql.cj.jdbc.Driver"); source.setUrl("jdbc:mysql://localhost:3306/wooyun?serverTimezone=UTC"); source.setUsername("study"); source.setPassword("123456"); DruidPooledConnection conn = source.getConnection(); Statement statement = conn.createStatement(); String sql = "SELECT * FROM bugs"; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { solrInputFields = new SolrInputDocument(); solrInputFields.addField("id", resultSet.getInt("id")); solrInputFields.addField("wybug_title", resultSet.getString("wybug_title")); solrInputFields.addField("wybug_date", resultSet.getString("wybug_date")); solrInputFields.addField("wybug_level", resultSet.getString("wybug_level")); solrInputFields.addField("wybug_detail", resultSet.getString("wybug_detail")); solrClient.add(solrInputFields); } solrClient.commit(); ``` ### 创建工程 现在我以springBoot作为实例进行演示,创建一个SpringBoot工程,进行如下配置。 #### 导入依赖 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.alc</groupId> <artifactId>springboot-solr</artifactId> <version>1.0</version> <name>springboot-solr</name> <description>Demo4SpringBoot2Solr</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-solr</artifactId> <version>2.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> ``` #### 配置application.yml ```properties spring: data: solr: host: http://localhost:8983/solr/mycore server: port: 8088 ``` #### 配置启动器(SpringbootApplication) ``` package com.alc.springbootsolr; import org.apache.solr.client.solrj.SolrClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.repository.config.EnableSolrRepositories; @SpringBootApplication @EnableSolrRepositories public class SpringbootSolrApplication { public static void main(String[] args) { SpringApplication.run(SpringbootSolrApplication.class, args); } // 这个不写也会进行自动注入的,只是IDE会报错,加上这个就会扫描到Bean,加上也不算错,不加也没问题。 // @Autowired // private SolrClient solrClient; // // @Bean // public SolrTemplate getSolrTemplate() { // return new SolrTemplate(solrClient); // } } ``` #### 定义实体 ```java package com.alc.springbootsolr.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.apache.solr.client.solrj.beans.Field; import org.springframework.data.annotation.Id; import org.springframework.data.solr.core.mapping.SolrDocument; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor @ToString @SolrDocument(solrCoreName = "mycore") public class Bugs implements Serializable { @Id @Field private Integer id; private String wybug_id; @Field private String wybug_title; private String wybug_corp; private String wybug_author; @Field private String wybug_date; private String wybug_open_date; private String wybug_type; private String wybug_level; private String wybug_rank_0; private String wybug_status; private String wybug_from; private String wybug_tags; @Field private String wybug_detail; private String wybug_reply; private String replys; private String wybug_level_fromcorp; private String wybug_rank_fromcorp; @Field private Integer Ranks; } ``` #### 定义solr操作的工具类 ```java package com.alc.springbootsolr.search; import com.alc.springbootsolr.entity.Bugs; 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.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.SolrParams; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.core.query.Criteria; import org.springframework.data.solr.core.query.Query; import org.springframework.data.solr.core.query.SimpleQuery; import org.springframework.data.solr.core.query.result.ScoredPage; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; @Component public class SolrUtil { @Autowired private SolrTemplate solrTemplate; // solr的工具类,由Spring.data.solr提供 public List<Bugs> search(String keywords, Long page) { System.out.println(keywords + "<===>" + page); // 显示一下我们的关键字和页码 Query query = new SimpleQuery(); Criteria filter; // 定义我们的过滤器 if (keywords.matches("[0-9]+")) { filter = new Criteria("id").is(keywords); // 如果是数字的话,就判断id } else { // 否则判断标题和内容 filter = new Criteria("wybug_title").contains(keywords); filter.or("wybug_detail").contains(keywords); // 或包含的关系,相当于满足一个就够了,不需要解释叭。 } query.setOffset(page);// 设置页码 query = query.addCriteria(filter); // 把我们的条件加进去 ScoredPage<Bugs> bugs = solrTemplate.queryForPage("", query, Bugs.class); List<Bugs> content = bugs.getContent(); return content; } public boolean deleteAll() { Query query = new SimpleQuery("*:*"); solrTemplate.delete("", query); solrTemplate.commit(""); return true; } } ``` #### 定义service层 ```java package com.alc.springbootsolr.service; import com.alc.springbootsolr.entity.Bugs; import com.alc.springbootsolr.search.SolrUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class SearchService { @Autowired private SolrUtil solrUtil; public List<Bugs> search(String keywords,Long page) { return solrUtil.search(keywords,page); } public boolean deleteAll() { return solrUtil.deleteAll(); } } ``` #### 定义controller层 ```java package com.alc.springbootsolr.controller; import com.alc.springbootsolr.entity.Bugs; import com.alc.springbootsolr.service.SearchService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class SearchController { @Autowired private SearchService searchService; @GetMapping("/search") public List<Bugs> search(String keywords,Long page) { return searchService.search(keywords,page); } @GetMapping("/deleteAll") public boolean deleteAll() { return searchService.deleteAll(); } } ``` ### 测试 访问我们的接口: > http://localhost:8088/search?keywords=sina&page=1 > > http://localhost:8088/search?keywords=sina&page=2 > > http://localhost:8088/search?keywords=sina&page=3 > > http://localhost:8088/search?keywords=sina&page=4 > > http://localhost:8088/deleteAll > > http://localhost:8088/search?keywords=sina&page=1 > > http://localhost:8088/search?keywords=sina 我们发现在删除之前是可以访问到数据的,删除之后就无法访问得到数据了。 ![search.png](https://www.zunmx.top/usr/uploads/2021/05/2770433993.png) ![delete.png](https://www.zunmx.top/usr/uploads/2021/05/1582243314.png) ![research.png](https://www.zunmx.top/usr/uploads/2021/05/3247877973.png) ## 解释 Q:为什么在查询、删除和提交的时候总有一个参数("")呢? > A:在我用的老版本确实不存在第一个参数,但是根据源码分析,String collection,里面是集合的名称,所以能够联想到是我们定义的core,因为我们在yml中配置了core了,所以就不需要进行写入任何内容了,如果说host是这样配置的 http://localhost:8983/solr/ ,那么就需要配置collection了,切记,不要重复配置,因为这样肯定会报错的,可以尝试一下。 > > 当配置出现问题的时候,或者是域配置错了,就会出现这样的错误 > > ``` > ERROR 19596 --- [nio-8088-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Error from server at http://localhost:8983/solr/mycore: Expected mime type application/octet-stream but got text/html. <html> > ****这里是HTML代码**** > ``` Q:@Field是干嘛的? > A:我们在写入对象的时候,定义实体bean和索引的关系,但是我的这个实例中,没有封装查询,所以在这个样例中是没有效果的,但是建议添加上。 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏